Ada 2020 (45日目) - その他のその他の変更

まとめていきます。 目指せ完結。

固定小数点数関連

“*”演算子

固定小数点型の値と任意の整数型の値とで掛け算("*")ができるように受け取れる文章が修正されました。 既定義で用意されるのは Integer との演算です。

サブプログラム関連

同じオブジェクトを複数の引数に使うケースの緩和

@ のときに「ひとつの文中で in out パラメータに渡したオブジェクトを同時に他に使うことはできません。」と説明しました。 4日目を参照。

この判定のためのルールが若干緩和、というより本来の意図通りの文面に修正されました。 この変更はAARMでもwording changeに分類されるぐらい影響は超レアなのですが折角読み解いたので書かせてください。

まず、先のざっくりした説明では説明しきれていないことがあります。 参照渡しなら同じ呼び出しでは同時に複数引数に渡してもセーフです。

declare
   type By_Ref is tagged null record;
   procedure P1 (A, B : in out By_Ref);
   -- P1の実装は省略
   X : By_Ref;
begin
   P1 (X, X);

参照渡しでも同じ呼び出しでなければ評価順の影響を受けますのでアウトです。

declare
   function F1 (A : in out By_Ref) return Integer;
   -- F1の実装は省略
   Z : Integer;
begin
   Z := F1 (X) + F1 (X); -- error

値渡しは同じ呼び出しでも評価順の影響を受けますので全部アウトです。 値渡しの out パラメータは値渡しして値返しされた結果を書き戻すことで実現されます。 この書き戻しの順番が同じ呼び出し内でも不定です。

複合型は値渡しも参照渡しもあります。 最適化で変化する可能性もあります。 例では確実に参照渡しにするためにtagged型にしています。

基本型は常に値渡しです。

……というのを踏まえまして。

影響するのは4引数以上のサブプログラム呼び出しで、2つの引数が同じ複合型、残り2つの引数が基本型の場合のみです。 モードは全部 in outout のどちらかとします。

複合型の2つの引数は参照渡しならセーフ、値渡しならアウトです。 基本型の2つの引数は常に値渡しのため確実に別のオブジェクトでなければなりません。

このように規格の文面はまず複合型か基本型かで分けられてその次に参照渡しか値渡しかで分けられています。 実際にはもっと細かいですが。

で、基本型のときの文面が複合型のほうに影響してしまっていました。

つまり本来セーフの次の例がアウトになっていました。

declare
   procedure P2 (A, B : in out By_Ref; C, D : in out Integer);
   -- P2の実装は省略
   X : By_Ref;
   Y, Z : Integer;
begin
   P2 (X, X, Y, Z);

複合型の方のルールでは参照渡しですのでセーフです。 基本型の方のルールが問題で2つの引数は YZ という別々のオブジェクトを渡しているにも関わらず、複合型の方に両方 X を渡しているのが引っかかってしまっていました。

この超細かい問題がAda 2020で修正されました。

パッケージ関連

incomplete型とfreezing

特殊な用法においてincomplete型がサブプログラムの引数に使える場面が増えました。

incomplete型は名前だけ先行宣言された型です。 C言語でいうopaque型です。 freezingされるより前に完全な宣言を行う必要があります。

freezingは型の定義をそれ以降変更できなくなることです。 42日目を参照。

よく使われる例としてはリンクリストの定義があります。

declare
   type Node;
   type Node_Access is access Node;
   type Node is
      record
         Previous : Node_Access;
         Element : Float;
      end record;

ひとつ目の type Node; がincomplete型としての宣言です。 このような相互に参照しあう型の他、 genericlimited with のためにも活用されます。

少々特殊な用法としてbody部内の型をprivate部でincomplete型として宣言して使うことができます。 この用法自体は昔からあります。

package Pkg1 is
   procedure Proc1;
private
   type T;
   X : access T;
end Pkg1;

package body Pkg1 is

   procedure Proc1 is
   begin
      null;
   end Proc1;

   type T is null record;

end Pkg1;

ところがこの用法ではincomplete型をサブプログラムの引数にできませんでした。

package Pkg2 is
private
   type T;
   X : access T;
   procedure Proc2 (Object : in T); -- error
end Pkg2;
-- bodyは省略

本来incomplete型はサブプログラムの引数にできます。 先程の Node の例で途中にサブプログラムの宣言を挟んでも問題ありません。

declare
   type Node;
   type Node_Access is access Node;
   procedure Add (Root : in out Node; Element : in Float);
   type Node is
      record
         Previous : Node_Access;
         Element : Float;
      end record;

では何が駄目なのかといいますと、パッケージの仕様部の最後まで到達しますと宣言されたサブプログラムの引数に使われている型が全部freezingされるルールに引っかかっていました。 周りくどいルールですが必要だからあるルールではあるのです。 しかしincomplete型と完全な宣言が分かれる特殊な用法を考慮されていませんでした。

Ada 2020で修正され Proc2 のようなサブプログラムも存在できるようになります。

private procedureとprivate with

private procedure の仕様部から親パッケージの private with が見えるようになりました。

package Lib is
   type T is null record;
end Lib;

private with Lib;
package Parent is
end Parent;

private procedure Parent.Child (A : Lib.T);

アスペクト関連

自己参照の禁止

X : constant Natural := 1
  with Atomic => X > 0;

この手の自己参照が禁止されました。 (GNATはAIの中で出された例は弾きましたがちょっと変えてこうするとクラッシュしました。 わはは。)

抽象型と内部表現のアスペクト

抽象型には内部表現のアスペクトは付けられなくなりました。

package Pkg3 is
   type T is private with Size => 8; -- error
private
   type T is mod 2 ** 8;
end Pkg3;

内部表現はprivate部に書く必要があります。

package Pkg4 is
   type T is private;
private
   type T is mod 2 ** 8 with Size => 8;
end Pkg4;

Remote_Call_Interface関連

‘Read/’Write属性の実装禁止

Remote_Call_Interface パッケージで宣言されたサブプログラムの呼び出しはRPC(remote procedure call)に変換されます。 主にCORBA(死語)やO/Rマッピング等に使われます。

その中で定義した型の 'Read/'Write 属性をユーザー定義の実装に置き換えることは禁止されました。

private with Ada.Streams;
package RCI with Remote_Call_Interface is
   type T is null record;
   procedure RPC (X : in T);
private
   procedure Read
     (Stream : not null access Ada.Streams.Root_Stream_Type'Class;
      Item : out T);
   procedure Write
     (Stream : not null access Ada.Streams.Root_Stream_Type'Class;
      Item : in T);
   for T'Read use Read;   -- error
   for T'Write use Write; -- error
end RCI;

RPCにおいて引数をバイト列にして転送するために 'Read/'Write 属性が使われます。 ところが Remote_Call_Interface パッケージ中で定義された 'Read/'Write 属性の実装になるサブプログラムはそれ自体もRPCの対象になってしまいます。

'Read/'Write 属性をカスタマイズしたければ型ごと別のパッケージに分離する必要があります。

この修正はAda 2012に入るはずでしたが間に合いませんでした。

Nonblocking

Remote_Call_Interface パッケージのサブプログラムや Remote_Types パッケージの型の Nonblocking アスペクトは False でなければなりません。

他言語関連

C言語側でinパラメータを書き換えてはいけない

小見出しの通りです。

はっきり言って今までも駄目でした。 念押しの記述が追加されました。

正直変更ではないのですが意図を汲んで私からも念押ししておきます。

with Interfaces.C;
package Imported is
   type t is
      record
         a, b : Interfaces.C.int;
      end record
        with Convention => C;
   procedure c_ffi1 (x : in t)
     with Import, Convention => C;
end Imported;
/* This is BAD example! */
struct t {
  int a, b;
};
void c_ffi1 (struct t *x)
{
  x->a = 1;
}

Ada側で in としているものをC側で変更するな! ……とのことです。

よほど守らない奴がいてSteve先生も腹に据えかねたのでしょう。

Fortranのバージョンの更新

規格が参照しているFortranのバージョンがISO/IEC 1594-1:2018に更新されました。

Fortran 2018になります。

もしFortranに古いイメージをお持ちでしたら調べてみると面白いかもしれません。

関連AI

  • AI12-0002-1 RCI units do not allow specification of user-defined stream-oriented attributes

  • AI12-0155-1 Freezing of an incomplete view has no effect

  • AI12-0181-1 Self-referencing representation aspects

  • AI12-0216-1 6.4.1(6.16-17/3) should never apply to composite objects

  • AI12-0219-1 Clarify C interfacing advice

  • AI12-0222-1 Representation aspects and private types

  • AI12-0224-1 Use of Fortran C Interfacing features

  • AI12-0261-1 Conflict in “private with” rules

  • AI12-0283-1 Nonblocking and remote calls

  • AI12-0300-1 Annex G text for Fixed * integer