Ada 202x (54日目) - 12月9日の電子会議の結果

12月9日に行われた電子会議の議事録が公開されました。 次回の会議は1月20日になるそうです。 案の定2020年内にまとまりませんでしたね……先例からして呼称はAda 2021にはならずAda 2020のままになると思われます。 結局Ada 2022になりました。

aliased引数を持つ関数呼び出し

関数呼び出しではaliased引数に渡す実引数は呼び出し箇所よりも浅いレベルのものでなければなりませんでした。

type T1 is record
   E1 : aliased Integer;
end record;

function F1 (A1 : aliased T1) return not null access Integer is
begin
   return A1.E'Access;
end F1;

Global : aliased T1;
function Caller1 return not null access Integer is
begin
   return F1 (Global);
end Caller1;

function Caller2 (Argument : aliased T1) return not null access Integer is
begin
   return F1 (Argument);
end Caller1;

function Caller3 (A1 : aliased T1) return not null access Integer is
   Local : aliased T1;
begin
   return F1 (Local); -- error
end Caller1;

この例では Caller3 はローカル変数の要素への参照を返そうとしていますのでエラーです。

このチェックは返値がaccess型を含まなくても、引数に aliased が付いているかどうかだけが条件でした。

function F2 (A1 : aliased T1) return Integer is
begin
   return A1.E;
end F1;

function Caller4 (A1 : aliased T1) return Integer is
   Local : aliased T1;
begin
   return F1 (Local); -- error
end Caller1;

このような関数呼び出しが、返値の型が(匿名のaccess型を除く)基本型の場合は許されるようになります。

Stable_Propertiesの修正

細かい点が詰められています。

  • out引数には影響しないことが明記されました。

  • access引数には影響しないことが明記されました。

  • 使用される "=" 演算子は in 式で用いられるものと同じものとされました。 可視性の上で別に定義した "=" 演算子が見えていても使われません。 ("and" 演算子についても同じ議論がなされたようですがこちらは明記されませんでした。)

  • 派生型で指定された Stable_Properties が継承されて overriding されていないプリミティブにも影響する、とされました。

  • pragma Assertion_Policy (Post, ...);Stable_Properties にも影響することが明記されました。 Post'Class も同様です。

  • 事後条件の中で Stable_Properties で指定された関数が使われてさえすれば Stable_Properties による事後条件は生成されないことになっています。 この際引数については特に問われておらず関係の無さそうな呼び出しでも事後条件が生成されません。 この動作がこれで良いことが確認されました。

  • in引数には影響するようです。 ただし Stable_Properties に指定された関数、null 手続き、既定義の演算子、列挙型の要素名、Global => null が付けられたものには影響しません。 (副作用が無ければチェックも不要ということでしょうか。 in引数を実は変更するサブプログラムもデフォルトの Global => all のままか、でなければ Global => overriding in out ... が明記されているかのはずですのでカバーされます。)

  • renames されたプリミティブにも影響するようです。

継承で使い辛そうなのは見直されてないです。 (くどい)

package引数のAccessibility_Checkの修正

genericのpackage引数に対して静的なAccessibility_Checkが行われないようになります。 実行時のAccessibility_Checkは引き続き行われます。

package引数に関してはgeneric側だけ見てレキシカルに浅い深いを判定しても不要なエラーになるだけ……っぽいです。 勿論ここで言う静的なチェックとはtemplate型とコード共有型両方に適用できる静的なチェックのことで、template型のコンパイラであれば展開の過程で実行時のAccessibility_Checkを前倒しして警告できるはずです。

Preelaborable_Initialization属性

実はBounded版コンテナは規格上実装できなかったことが発覚してしまいました。

Bounded版コンテナにはpragma Preelaborable_Initialization が付けられています。 グローバル変数として宣言した場合に実行時の初期化を必要としてはいけません。

Bounded版コンテナは追加のアロケーションを行わずに仮引数 Element_Type を持つコンテナです。 Element_Type の領域はコンテナの中に持たないといけませんが、どうやっても Element_Type の初期化が必要となります。 (Tucker先生が挑戦されている様子をAIの中で読むことができます。)

そして Element_Type に実行時の初期化を必要とする型が実引数として渡されることを防ぐ方法はありません。 その場合pragma Preelaborable_Initialization は破られます。

さて前回(53日目)、Preelaborable_Initialization がアスペクトとして書けるようになりました。 pragmaとアスペクトの違いは値を持つことです。 名前だけ書かれているように見えても実際には => True が省略されています。 Boolean 型の値を持つということは論理式が書けます。

というわけで、今度は型が Preelaborable_Initialization かどうかを属性の形で取得できるようになりました。 これで Nonblocking 等と同様に仮引数を参照して切り替えることができるようになりました。

generic
   type Element_Type is private;
   -- 省略
package Ada.Containers.Bounded_Doubly_Linked_Lists is
   -- 省略
   type List is private
      with Preelaborable_Initialization =>
         Element_Type'Preelaborable_Initialization,
      -- 省略

私も考えてみました。

Root_Storage_Pool がPreelaborable_Initializationになることは確定していました(35日目を参照)ので、専用のストレージプールを作って抱え込めば Element_Type の初期化を実際に Append 等が呼ばれるまで遅らせることができます。

Adjust をどうやって実装するかが難しいですね……。 割り当て済みの領域を Element_Type にキャストして再代入すれば間接に Element_TypeAdjust を呼ぶことはできると思いますが、1回の代入で2回のコピーが必要になります。

Bounded版コンテナに本来不要な終了処理も必要になり、効率は悪いですね。 こうすれば実装できそうというだけです。

ただこうでもしないと実装できないはずの Bounded_Indefinite_Holders のAI(AI12-0254-1)を見ますと実装依存の最適化に期待するようなことが書かれてまして、GNATに Bounded_Indefinite_Holders が実装される時にはこれらを効率的に実装できるような拡張を期待していいんでしょうね?

"=" 演算子が隠されている場合のgeneric中の動作

型引数の(unlimitedな)抽象型には "=" 演算子が付いてきます。

generic
   type T2 is private;
package Gen is
   function F (Left, Right : T2) return (Left = Right); -- "=" を使用
end Gen;

ところがこのようなgenericはプリミティブな "=" 演算子を禁止した型でもインスタンス化できてしまいます。

type T3 is new Integer;
function "=" (Left, Right : T3) return Boolean is abstract;
package P is new Gen (T3);

この時の動作が明確化されました。

実引数が基本型か基本型の配列の時は(in 演算子同様に)既定義の "=" が使用されます。 実引数が複合型の時はコンパイルエラーまたは Program_Error になります。

基本型の "=" 演算子はユーザー定義しても既定義のものが使われる場面が多々あって信用なりませんね……。

pragmaからアスペクトへの書き換え

パッケージの種類を表す PurePreelaborate 等が全面的にアスペクトに書き換えられるようです。

その他特記事項

変数のqualified式のrenamesはエラーになるようになりました。 53日目を参照。

これは非互換になるとTucker先生が指摘されたようです。 AIそのものも前回からこの非互換を反映して修正されています。 非互換性に追記しました。

他には、abstract型のプリミティブの Pre'Class からabstract関数を呼べるようになる修正が採択されています。 AI自体がまだ修正されそうですので紹介は次に回したいと思います。

その他文面等の修正

例によって文面等の修正やglossary(用語集)へ単語の追加が行われています。

大きい変更としては「null procedure」と「expression function」の使われ方が、宣言のみとされました。 普通のサブプログラムのように宣言されbody側で is null; としたものは「null procedure」に対する様々なルールの適用外ということが明確になります。

関連AI

  • AI12-0402-1 Master of a function call with elementary result type

  • AI12-0404-1 Presentation issues from Draft 26 review

  • AI12-0405-1 Fixups for stable properties

  • AI12-0406-1 Clarifying static accessibility

  • AI12-0407-1 Fixups from Draft 26 review - part 1

  • AI12-0408-1 Definition of “null procedure” and “expression function”

  • AI12-0409-1 Preelaborable_Initialization and Bounded Containers

  • AI12-0413-1 Reemergence of "=" when defined to be abstract

  • AI12-0414-1 Replace categorization pragmas with aspects