AdaCore / ada-spark-rfcs

Platform to submit RFCs for the Ada & SPARK languages
63 stars 28 forks source link

Extension for access to class-wide types #52

Open godunko opened 4 years ago

godunko commented 4 years ago

It would be helpful to have "subtypes" for access to class-wide types with allows to limit allowed values to to some derived type and types derived from it, and to avoid explicit type conversion. Here is an example of type declaration:

type T1 is tagged private;
type T1_Access is access all T1'Class;

type T2 is new T1 with private;
type T2_Access is access all T2'Class;
subtype T2_Access_Subtype is T1_Access for access all T2'Class;  -- new constructuion

and its use case:

procedure P (Item : T1_Access);

X1 : T2_Access;
X2 : T2_Access_Subtype;

P (T1_Access (X1));  --  type conversion is necessary
P (X2);              --  type conversion is not needed
simonjwright commented 4 years ago

I don’t see the problem with

type T2_Access is access all T2’Class;

and of course

procedure P (Item : T2_Access);
sttaft commented 4 years ago

I presume you meant to write: "type T2_Access is access all T2'Class;"

Note that it is already the case (since Ada 95) that if you use access parameters (parameters of an anonymous access type) with designated type T1'Class you don't need conversion if the actual is of a named type with designated type T2'Class (or anything "covered" by T1'Class -- see RM 8.6(26.1/3)). Does that satisfy the need?

-Tuck

On Mon, Jul 27, 2020 at 3:22 PM Vadim Godunko notifications@github.com wrote:

It would be helpful to have "subtypes" for access to class-wide types with allows to limit allowed values to to some derived type and types derived from it, and to avoid explicit type conversion. Here is an example of type declaration:

type T1 is tagged private; type T1_Access is access all T1'Class;

type T2 is new T1 with private; type T2_Access is access all T1'Class; subtype T2_Access_Subtype is T1_Access for access all T2'Class; -- new constructuion

and its use case:

procedure P (Item : T1_Access);

X1 : T2_Access; X2 : T2_Access_Subtype;

P (T1_Access (X1)); -- type conversion is necessary P (X2); -- type conversion is not needed

— You are receiving this because you are subscribed to this thread. Reply to this email directly, view it on GitHub https://github.com/AdaCore/ada-spark-rfcs/issues/52, or unsubscribe https://github.com/notifications/unsubscribe-auth/AANZ4FNPFURDRJ63XDJYEK3R5XHYRANCNFSM4PJE4UKQ .

godunko commented 4 years ago

It is common practice to have parameters of the access type in GUI programming. Actual objects for these parameters are allocated on heap only. No accessibility level checks are necessary at all. Anonymous access type provide nice mechanism to do type conversions, while raise some issues:

All of them may be detected only during execution, and sometimes in corner cases only.

Use of named access type for parameter requires to do type conversions each time, and makes code less understandable.

reznikmm commented 4 years ago

I like this idea, because 1) it fits Ada subtype spirit very well 2) I miss this feature in day-by-day development.

One example. When you coding in OOP, you often create a kind of register and populate it with objects from a type hierarchy.

type Abstract_Handler is tagged null record;
procedure Do_Work (Self : Abstract_Handler) is abstract;
type Abstract_Handler_Access is access all Abstract_Handler'Class;

procedure Register_Handler (Value : Abstract_Handler_Access);

To register a concrete object you need to declare an access to derived class and/or use an extra type conversion:

type First_Handler is new Abstract_Handler with record
   X : Integer;
end record;
type First_Handler_Access is access all First_Handler;

procedure Initialize is
   Reference : First_Handler_Access := new First_Handler;
begin
   --  Do initialization (sometimes rather complex)
   Reference.X := 55;
   Register_Handler (Abstract_Handler_Access (Reference));
end;

procedure Initialize_In_Other_Way is
   Reference : Abstract_Handler_Access := new First_Handler;
begin
   --  Do initialization (sometimes rather complex)
   First_Handler (Reference.all).X := 55;
   Register_Handler (Reference);
end;

Note: Often Abstract_Handler_Access is used only for registration purpose and nowhere else.

Both ways look not so clean. But if Ada allows new constraint kind:

scalar_constraint ::= range_constraint | digits_constraint | delta_constraint | classwide_access_constraint;

classwide_access_constraint := for access type_name;

Then we can write:

type First_Handler is new Abstract_Handler with record
   X : Integer;
end record;

procedure Initialize is
   Reference : Abstract_Handler_Access for access First_Handler :=
     new First_Handler;
begin
   --  Do initialization (sometimes rather complex)
   Reference.X := 55;
   Register_Handler (Reference);
end;

So

If there is any interest in this, I could prepare a more formal proposal.

@sttaft @raph-amiard @yannickmoy

reznikmm commented 3 years ago

I've prepared a RFC document in #69 PR.

sttaft commented 3 years ago

Note that Ada does have access subtypes, in particular, access-to-discriminated and access-to-array types can be constrained as part of defining an access subtype. So these should be included as precedents. These have also been notoriously tricky to handle properly from a language point of view, so it would be useful to try to verify that these access subtypes won't suffer from similar problems. See in particular 3.7.1(7/3): A discriminant_constraint http://www.ada-auth.org/standards/2xaarm/html/AA-3-7-1.html#S0064 is only allowed in a subtype_indication http://www.ada-auth.org/standards/2xaarm/html/AA-3-2-2.html#S0027 whose subtype_mark http://www.ada-auth.org/standards/2xaarm/html/AA-3-2-2.html#S0028 denotes either an unconstrained discriminated subtype, or an unconstrained access subtype whose designated subtype is an unconstrained discriminated subtype. However, in the case of an access subtype, a discriminant_constraint http://www.ada-auth.org/standards/2xaarm/html/AA-3-7-1.html#S0064 is legal only if any dereference of a value of the access type is known to be constrained (see 3.3 http://www.ada-auth.org/standards/2xaarm/html/AA-3-3.html). In addition to the places where Legality Rules normally apply (see 12.3 http://www.ada-auth.org/standards/2xaarm/html/AA-12-3.html), these rules apply also in the private part of an instance of a generic unit.

On Mon, Jan 11, 2021 at 10:18 AM Maxim Reznik notifications@github.com wrote:

I've prepared a RFC document in #69 https://github.com/AdaCore/ada-spark-rfcs/pull/69 PR.

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/AdaCore/ada-spark-rfcs/issues/52#issuecomment-758017125, or unsubscribe https://github.com/notifications/unsubscribe-auth/AANZ4FLYP2JRUBS4FV3ZMBDSZMJEBANCNFSM4PJE4UKQ .