Ada 202x (42日目) - その他のaccessの変更 ======================================== .. post:: Jan 11, 2020 :tags: ada, ada_2022 神は細部に宿るらしいです。 (意味分かんないで使ってる。) access型と可視性 ---------------- 次のコードはコンパイラによってコンパイルできたりできなかったりしました。 .. code-block:: ada with Ada.Unchecked_Deallocation; package Pkg1 is type T (<>) is limited private; -- unconstrained and limited type T_Access is access T; private type T is new String (1 .. 10); -- constrained and unlimited procedure Free is new Ada.Unchecked_Deallocation (T, T_Access); -- ? end Pkg1; 公開部では ``T`` はunconstrainedかつlimitedの抽象型です。 ``T_Access`` は宣言時点ではそのような ``T`` へのaccess型です。 その後private部で ``T`` の実体はconstrainedかつunlimitedと明かされました。 このとき ``T_Access`` の示す先の型の可視性がどちらに従うかという話です。 宣言時点に従うならunconstrainedかつlimitedの抽象型、使用箇所に従うなら ``Pkg1`` のprivate部やbody部ではconstrainedかつunlimitedの ``new String (1 .. 10)`` となります。 ``Ada.Unchecked_Deallocation`` は割り当てる型とそのaccess型を引数に取ります。 ところが ``T_Access`` の示す先が宣言時点のものですと ``Ada.Unchecked_Deallocation`` をインスタンス化している ``?`` の行は ``T`` の実体と ``T_Access`` の示す先の抽象型が一致せずコンパイルエラーになります。 Ada 202xで有効なコードであることが確定しました。 コンパイルできるのが正しいです。 珍しくJanus/Adaが修正を迫られることになった件。 GNATとICC/Adaは元からこの挙動でした。 access型とfreezing ------------------ Adaには型のfreezingと呼ばれる概念があります。 型はその型の詳細が必要になった時点でfreezingされます。 freezingされた型はその後に内部表現を指定したりできなくなります。 (良い訳として何がいいでしょうね。) .. code-block:: ada declare type T is mod 2 ** 8; X : T; for T'Size use 8; -- error この例では型 ``T`` に対してその型の変数 ``X`` を宣言した後から内部表現を指定しようとしておりエラーになります。 その上で細かい話になります。 access型の指す先のサブプログラムの引数や返値の型はfreezingされるような規格の文面になっていました。 .. code-block:: ada declare type T is mod 2 ** 8; type A is access procedure (X : T); for T'Size use 8; -- error 勿論access型ですから宣言時点では型の詳細は必要ないです。 そして実際にはfreezingしないことを求めるようなACATSのテストがありました。 Ada 202xでfreezingしないと規格の文面が修正されました。 Adaを名乗るコンパイラはACATSのテストを通っていますので動作が変わるわけではないです。 匿名のaccess型とprotected ------------------------- ``protected`` の中にサブプログラムを指す匿名のaccess型があった場合その匿名のaccess型まで ``protected`` になってしまっていました。 .. code-block:: ada protected Prot1 is procedure High_Order (Callback : access procedure); end Prot1; このコードは次のコードと同じでした。 .. code-block:: ada protected Prot2 is procedure High_Order (Callback : access protected procedure); end Prot2; Ada 202xで修正され、``protected`` を付けていない場合は匿名のaccess型は普通のサブプログラムを指すようになりました。 ``protected`` に属するサブプログラムを指したい場合は ``protected`` を書く必要があります。 匿名のaccess型と呼び出し規約 ---------------------------- ``record`` の中にサブプログラムを指す匿名のaccess型があった場合呼び出し規約は ``record`` に従うようになりました。 .. code-block:: ada type R is record Callback : access procedure; end record with Convention => C; この ``Callback`` の呼び出し規約は従来は ``Ada`` でした。 Ada 202xからは ``C`` になります。 便利になったのではあるのでしょうけれども。 呼び出し規約は細かく指定しましょう。 .. code-block:: ada type R is record Callback : access procedure with Convention => Ada; end record with Convention => C; 匿名のaccess型の暗黙の型変換と生存期間 -------------------------------------- 次の例を見てください。 .. code-block:: ada declare type Integer_Access is access constant Integer; Global : Integer_Access; function Get_Access (X : aliased in Integer) return access constant Integer is begin return X'Access; end Get_Access; begin declare Local : aliased Integer; begin Global := Get_Access (Local); Global := Integer_Access (Get_Access (Local)); -- error end; 関数の返値が匿名のaccess型の場合その寿命は引数から推定されます。 実際この例では ``Get_Access`` は単に引数のアクセス値を返しているだけです。 それを他のaccess型の変数に代入する場合は寿命のチェックが行われます。 ここで ``Integer_Access`` に明示的に型変換している行はエラーになるのですが、暗黙の型変換は見逃されていました。 Ada 202xで修正され、暗黙の型変換でもチェックが行われるようになりました。 access型のnot nullとtagged型のプリミティブ ------------------------------------------ tagged型のプリミティブは引数がaccess型の場合暗黙に ``not null`` ということになります。 .. code-block:: ada package Pkg2 is type T is tagged null record; procedure Primitive (Object : access T); end Pkg2; -- bodyは省略 procedure Renamed (Object : not null access Pkg2.T) renames Pkg2.Primitive; しかし ``private`` にしてしまいますと、公開部の情報では ``not null`` ではないということになります。 .. code-block:: ada package Pkg3 is type T is private; procedure Primitive (Object : access T); private type T is tagged null record; end Pkg3; ``Pkg3.Primitive`` に ``null`` を渡すと実行時 ``Constraint_Error`` になります。 ところが現在のGNATは可視性を無視して次の ``renames`` を許してしまいます。 .. code-block:: ada procedure Renamed (Object : not null access Pkg3.T) renames Pkg3.Primitive; Janus/Adaはこの ``renames`` をエラーにします。 またJanus/Adaはなんと本体側をもエラーにしてしまいます。 .. code-block:: ada package body Pkg3 is procedure Primitive (Object : access T) is begin null; end Primitive; end Pkg3; ここでは可視性の上で ``T`` が ``tagged`` であると見えていますのでこの本体側の ``Primitive`` は ``not null`` 付きと見做されるようです。 そのため ``not null`` なしの仕様部と一致しなくなります。 この仕様がAda 202xで詰められました。 簡単に言えばJanus/Adaの勝利です。 Ada 95時代に書かれたコードは仕様部に ``not null`` を付けて回らないとエラーになります。 既にAda 95からAda 2005までの期間よりも、Ada 2005以降の方が長いんですよね……。 あー、未だにAda 95までの情報で書いてある記事滅びねえかなあ……。 access型のnot nullとrenamesとgeneric ------------------------------------ 次の例を見てください。 .. code-block:: ada type Float_Access is access Float; generic A : in Float_Access; package Gen1 is procedure Proc1; end Gen1; package body Gen1 is procedure Proc1 is R : not null Float_Access renames A; begin null; end Proc1; end Gen1; ``R`` では ``not null`` を付けて ``generic`` の引数を ``renames`` しています。 勿論 ``A`` に ``null`` を渡すと実行時に ``Constraint_Error`` になります。 つまり気をつけないと危ないわけです。 ``in out`` の時は既に ``not null`` を一致させないとエラーになっていましたが ``in`` は違いました。 Ada 202xでは ``in`` も含めてコンパイル時エラーになります。 関連AI ------ - `AI12-0140-1`_ **Access to unconstrained partial view when full view is constrained** - `AI12-0186-1`_ **Profile freezing for the Access attribute** - `AI12-0207-1`_ **Convention of anonymous access types** - `AI12-0278-1`_ **Implicit conversions of anonymous return types** - `AI12-0287-1`_ **Legality Rules for null exclusions in renaming are too fierce** - `AI12-0289-1`_ **Implicitly null excluding anonymous access types and conformance** .. _`AI12-0140-1`: http://www.ada-auth.org/cgi-bin/cvsweb.cgi/ai12s/ai12-0140-1.txt .. _`AI12-0186-1`: http://www.ada-auth.org/cgi-bin/cvsweb.cgi/ai12s/ai12-0186-1.txt .. _`AI12-0207-1`: http://www.ada-auth.org/cgi-bin/cvsweb.cgi/ai12s/ai12-0207-1.txt .. _`AI12-0278-1`: http://www.ada-auth.org/cgi-bin/cvsweb.cgi/ai12s/ai12-0278-1.txt .. _`AI12-0287-1`: http://www.ada-auth.org/cgi-bin/cvsweb.cgi/ai12s/ai12-0287-1.txt .. _`AI12-0289-1`: http://www.ada-auth.org/cgi-bin/cvsweb.cgi/ai12s/ai12-0289-1.txt