From 612beda0d12310ee8c0bcb7314da55291c6160b9 Mon Sep 17 00:00:00 2001 From: QuentinOchem Date: Mon, 4 May 2020 13:37:23 -0400 Subject: [PATCH 01/20] Initial proposal --- considered/oop-remastered.rst | 454 ++++++++++++++++++++++++++++++++++ 1 file changed, 454 insertions(+) create mode 100644 considered/oop-remastered.rst diff --git a/considered/oop-remastered.rst b/considered/oop-remastered.rst new file mode 100644 index 00000000..c2e07e7f --- /dev/null +++ b/considered/oop-remastered.rst @@ -0,0 +1,454 @@ +- Feature Name: Standard OOP model +- Start Date: May 5th 2020 +- RFC PR: +- RFC Issue: + +Summary +======= + +The objective of this proposal is to draft an OOP model for a hypothetical new version of Ada, closer to the models languages +such as C++ and Java developers are accustomed to, fixing a number of oddities and vulnerabilities of the current Ada language. + +Motivation +========== + +The current Ada OOP model is source of a number of confusions and errors from people accustomed to OOP, in particular in +other languages such as C++ or Java. This proposal aims at adjusting the model to implement missing concept, fix Ada-specific +vulnerabilities and retain some of the Ada advantages. + +This proposal also attempts at keeping capabilities that do not need to be specifically removed to fit within a more intuitive +OOP model, such as e.g. discriminants and aggregates. + +Guide-level explanation +======================= + +The new design retains the difference between "regular" types and "classes". For consistency, both will be altered and consider that +https://github.com/AdaCore/ada-spark-rfcs/pull/13 is implemented. + +Class declaration +----------------- + +The new class model is incompatible with the current tagged object model. In order to make the distinction, new tagged types will +be marked with the new class reserved word: + +.. code-block:: ada + + type A_Class is class record + null; + end A_Class; + +Primitives and component declarations +------------------------------------- + +Under this new model, the notion of controlling primitive disapears. Instead, primitives have to be added in the declarative +scope of the type. For consistency, this is possible for both class record and regular record. The first parameter of the +primitive has to be of the type of the record. This allows the user to decide on the naming convention, as well as the mode +of such parameter (in, out, in out, access, aliased). A record and and class record can have primitives declared both in the +public and the private part. This is possibilty is extended to other components as well. The existence of a private part needs +to be specified in the public part of a package with the notation with private. The following demonstrates the above: + +.. code-block:: ada + + package P is + type T1 is record + F : Integer; + + procedure P (Self : in out T1; V : Integer); + end T1 + with private; + + type T2 is class record + F : Integer; + + procedure P (Self : in out T2; V : Integer); + end T2 + with private; + + private + + type T1 is record + F2 : Integer; + + procedure P2 (Self : in out T1; V : Integer); + end T1; + + type T2 is class record + F2 : Integer; + + procedure P2 (Self : in out T2; V : Integer); + end T2; + + end P; + + package body P is + + type T1 is record + procedure P (Self : in out T1; V : Integer) is + begin + Self.F := V; + end P; + + procedure P2 (Self : in out T1; V : Integer) is + begin + Self.F2 := V; + end P2; + end T1; + + type T2 is record + procedure P (Self : in out T2; V : Integer) is + begin + Self.F := V; + end P; + + procedure P2 (Self : in out T2; V : Integer) is + begin + Self.F2 := V; + end P2; + end T2; + + end P; + +In order to keep some legacy compatibility with Ada, the concept of primitive is kept for static subprograms outside of the scope +of the type. They cannot be used for dynamic dispatching. + +Other ways to declare types are kept. For example, it's still possible to declare a private type and implement it through a +record, but of course in this case properties are not available: + +.. code-block:: ada + + package P is + type T1 is private; + + type T2 is private; + + private + + type T1 is record + F2 : Integer; + + procedure P2 (Self : in out T1; V : Integer); + end T1; + + type T2 is class record + F2 : Integer; + + procedure P2 (Self : in out T2; V : Integer); + end T2; + + end P; + +As for tagged types, there's a shortcut for a class private type, which means no public primitives or components: + +.. code-block:: ada + + package P is + type T1 is class private; + private + type T1 is class record + F2 : Integer; + + procedure P2 (Self : in out T1; V : Integer); + end T1; + end P; + +Class record can still be limited or have discriminants, in which cases the set of constaints that they have follow similar rules +as for tagged types. + +Overriding and extensions +------------------------- + +Extension of class record types works similarly to tagged records: + +.. code-block:: ada + + package P is + type T1 is class record + procedure P (Self : in out T1); + end T1; + + type T2 is new T1 with record + procedure P (Self : in out T1); + end T2; + end P; + +Primitives can be marked optionally overriding, following Ada 2005 rules. Inheritance model is single interitance of a class, +multiple inheritance of interfaces. + +Interfaces and abstract types +----------------------------- + +Intefaces and abstract types work the same way as for tagged types. Interfaces are specified differently, through +"interface record", but otherwise operate as other interfaces (no concrete components or primitive): + +.. code-block:: ada + + package P is + type I is interface record + procedure P (Self : in out T1) is abstract; + end T1; + end P; + +Access types +------------ + +This topic is to be considered in the context of a larger overall of access types. However, in the absence of such proposal, +the idea here is to have an access type declared implicitely at the same level as the type and accessible through the 'Ref notation. +An attribute 'Unchecked_Free is also declared, doing unchecked deallocation. 'Unchecked_Free can also be called directly on +the object. These are also available for definite view 'Super and 'Specific, with the only difference being that calling on these +access types will not dispatch. For example: + +.. code-block:: ada + + package P is + type T1 is class record + procedure P (Self : in out T1); + + procedure P2 (Self : in out T1); + end T1; + end P; + + procedure Some_Procedure is + V : T1'Ref := new T1; + V2 : T1'Ref := new T1; + begin + T1'Unchecked_Free (V); + V2'Unchecked_Free; + end Some_Procedure; + +For homogenity, 'Ref and 'Unchecked_Free are available to all Ada type - including pointers themesleves. It's now possible to write: + +.. code-block:: ada + + V : T1'Ref'Ref := new T1'Ref; + +'Ref access types for a given class object are compatible in the case of upcast, but need explicit conversions to downcase. You +can write: + +.. code-block:: ada + + package P is + type A is class record + procedure P (Self : in out T1); + end A; + + type B is new T1 with record + procedure P (Self : in out T1); + end B; + end P; + + procedure Some_Procedure is + A1 : A'Ref := new A; + A2 : A'Ref; + + B1 : B'Ref := new B; + B2 : B'Ref; + begin + A2 := B1; -- OK, upcast, no need for pointer conversion + B2 := A1; -- Illegal, downcast + B2 := B'Ref (A1); -- OK, explicit downcast. + end Some_Procedure; + + +Dispatching +----------- + +A view to the type is dispatching, no matter if it's referenced in a primitive or not. So for example: + +.. code-block:: ada + + package P is + type T1 is class record + procedure P (Self : in out T1); + + procedure P2 (Self : in out T1); + end T1; + end P; + + package P is + type T1 is class record + procedure P (Self : in out T1) is + begin + Self.P2; -- Dispatching + end P; + end T1; + end P; + +As a result, the reference to a class record is always indefinite. + +In some cases, it's needed to reference a specific type for a non-dispatching call. In this case, there are two possibilities: + +(1) only reference to the parent class is needed, this can be accessed through 'Super. If 'Super is applied on a type, this +refers to its direct parent. If it's applied on an object, it refers to the parent of the type of this object + +(2) a reference to a specific object. Rules are the same as above, with the usage of 'Specific (either refering to a non +dispatching specific type, or the specific view of the object): + +For example: + +.. code-block:: ada + + package P is + type T1 is class record + procedure P (Self : in out T1); + end T1; + + type T2 is new T1 with record + procedure P (Self : in out T1); + end T2; + end P; + + package body P is + type T1 is class record + procedure P (Self : in out T1) is + begin + null; + end P; + end T1; + + type T2 is new T1 with record + procedure P (Self : in out T1) is + begin + Self'Super.P; + T2'Super (Self).P; + Self'Specific.P; + T2'Specific (Self).P; + end P; + end T2; + end P; + +Global object hierarchy +----------------------- + +All class object implicitely derive from a top level object, Ada.Classes.Object, defined as follows: + +.. code-block:: ada + + package Ada.Classes is + type Object is class record + function Image (Self : Object) return String; + + function Hash (Self : Object) return Integer; + end Object; + end Ada.Classes; + +Other top level primitives may be needed here. + +Constructors, copy and destructors +---------------------------------- + +There is no Controlled object in class record. Instead, class record can declare constructors and one destructor. The constructor +needs to be a procedure of the name of the object, taking an in out or access reference to the object. Destructors are named + +.. code-block:: ada + + package P is + type T1 is class record + procedure T1 (Self : in out T1); + procedure T1 (Self : in out T1; Some_Value : Integer); + + procedure finalize (Self : in out T1); + end T1; + end P; + +This specific proposals is linked to an overal finalization proposal. If a different reserved word is used, it will be used for +the destructor notation as well. + +As soon as a constructor exist, and object cannot be created without calling one of the available constructors. This call is made +on the object creation, e.g.: + +.. code-block:: ada + + V : T1; -- OK, parameterless constructor + V2 : T1 (42); -- OK, 1 parameter constructor + V3 : T1'Ref := new T1; + V4 : T1'Ref := new T1 (42); + +When combined with discriminants, the discriminants values must be provided before the constructor values: + +.. code-block:: ada + + package P is + type T1 (L : Integer) is class record + procedure T1 (Self : in out T1); + procedure T1 (Self : in out T1; V : Integer); + + X : Some_Array (1 .. L); + end T1; + end P; + + V : T1 (10)(10); + +Note that the above can create ambiguous situations in corner cases, which are to be detected at compile time and resolved +through e.g. naming: + +TODO: copy? + +.. code-block:: ada + + package P is + type T1 (L : Integer := 0) is class record + procedure T1 (Self : in out T1); + procedure T1 (Self : in out T1; V : Integer); + + case L is + when 0 => + X : Integer; + when others => + null; + end case; + end T1; + end P; + + V : T1 (10); -- Illegal, is this a discriminant with default constructor or a default discriminant with a constructor? + V2 : T1 (L => 10); -- Legal + V3 : T1 (V => 10); -- Legal + +Operators and exotic primitives +------------------------------- + +Class record do not provide mutiple dispatching call, or dispatching on results. If you declare primitives with references to +the type other than the first parameter, they will not be used for controlling. This means that parameters that are the same +at top level may differ when deriving: + +Operators can be declared as primitives: + +.. code-block:: ada + + package P is + type T1 is class record + procedure "=" (Left, Right : T1); + end T1; + + type T2 is new T1 with record + procedure "=" (Left : T2; Right : T1); + end T1; + end P; + +Reference-level explanation +=========================== + + +Rationale and alternatives +========================== + + +Drawbacks +========= + + +Prior art +========= + + +Unresolved questions +==================== + +This proposal relies on the convergence of the unified record syntax proposal, and will need to be updated in light of potential +revamped access model and finalization models. + +Future possibilities +==================== + +One important aspect of Ada is to allow data to be as static as possible. OOP typically requires the use of pointer. The Max_Size +proposal is a independent proposal to allow polymorphic object residing in automatic memory section such as fields or stack. + From 7ddd1e5be715cdc5cd5573f6440c23172593d934 Mon Sep 17 00:00:00 2001 From: QuentinOchem Date: Mon, 4 May 2020 14:22:29 -0400 Subject: [PATCH 02/20] Added further details. --- considered/oop-remastered.rst | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/considered/oop-remastered.rst b/considered/oop-remastered.rst index c2e07e7f..822265e9 100644 --- a/considered/oop-remastered.rst +++ b/considered/oop-remastered.rst @@ -423,6 +423,19 @@ Operators can be declared as primitives: procedure "=" (Left : T2; Right : T1); end T1; end P; + +Other removed capabilities +-------------------------- + +Although discriminants are kept, coextensions should be removed under this proposal. They introduce various level of complexity and +have not yet been fully implemented. + +Tagged types +------------ + +Under this proposal, tagged records and class record can co-exist, as they live in completely distinct hierarchies. Howeer, tagged +types should only be considered for a comptability and migration standpoint. Most tagged record use cases should be relatively easy +to move to class records. Reference-level explanation =========================== @@ -446,9 +459,14 @@ Unresolved questions This proposal relies on the convergence of the unified record syntax proposal, and will need to be updated in light of potential revamped access model and finalization models. +A number of the capabilities of the standard run-time library rely today on tagged type. A thorough review should be made to +identify which should be removed (e.g. controlled type), which should be migrated, and which can actually be implemented without +relying on classes altogether (things such as streams or pools come to mind). The removal of coextensions types also supposes a +different model for general iteration, as it currently relies on user-defined references (implemented through coextensions). + Future possibilities ==================== One important aspect of Ada is to allow data to be as static as possible. OOP typically requires the use of pointer. The Max_Size -proposal is a independent proposal to allow polymorphic object residing in automatic memory section such as fields or stack. +proposal (https://github.com/QuentinOchem/ada-spark-rfcs/blob/max_size/considered/max_size.rst) is a independent proposal to allow polymorphic object residing in automatic memory section such as fields or stack. From e827cc08df9a5cbf06d972f225b2c2ccf822ab6a Mon Sep 17 00:00:00 2001 From: QuentinOchem Date: Mon, 4 May 2020 14:47:45 -0400 Subject: [PATCH 03/20] Fixed typos. --- considered/oop-remastered.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/considered/oop-remastered.rst b/considered/oop-remastered.rst index 822265e9..add0916b 100644 --- a/considered/oop-remastered.rst +++ b/considered/oop-remastered.rst @@ -184,8 +184,8 @@ Intefaces and abstract types work the same way as for tagged types. Interfaces a package P is type I is interface record - procedure P (Self : in out T1) is abstract; - end T1; + procedure P (Self : in out I) is abstract; + end I; end P; Access types @@ -237,7 +237,7 @@ can write: end P; procedure Some_Procedure is - A1 : A'Ref := new A; + A1 : A'Ref := new B; A2 : A'Ref; B1 : B'Ref := new B; From 638e34b054ebe0d7358d8b19e3102739451aa276 Mon Sep 17 00:00:00 2001 From: QuentinOchem Date: Mon, 4 May 2020 14:56:35 -0400 Subject: [PATCH 04/20] Added extra information on constructor calls --- considered/oop-remastered.rst | 40 ++++++++++++++++++++++++++++++++--- 1 file changed, 37 insertions(+), 3 deletions(-) diff --git a/considered/oop-remastered.rst b/considered/oop-remastered.rst index add0916b..0b40984d 100644 --- a/considered/oop-remastered.rst +++ b/considered/oop-remastered.rst @@ -363,6 +363,41 @@ on the object creation, e.g.: V3 : T1'Ref := new T1; V4 : T1'Ref := new T1 (42); +A constructor of a child class always call its parent constructor before its own. It's either implicit (parameterless constructor) +or explicit. When explicit, it's provided through the Super aspect, specified on the body of the constructor, for example: + +.. code-block:: ada + + package P is + type T1 is class record + procedure T1 (Self : in out T1; V : Integer); + end T1; + + type T2 is new T1 with record + procedure T2 (Self : in out T1); + end T2; + end P; + + package body P is + type T1 is class record + procedure T1 (Self : in out T1; V : Integer) is + begin + null; + end T1; + end T1; + + type T2 is new T1 with record + procedure T2 (Self : in out T1) + with Super (0) -- special notation for calling the super constructor. First parameter is omitted + is + null; + end T2; + end T2; + +Destructors are implicitely called in sequence - the parent destructor is always called after it child. + +TODO: copy? + When combined with discriminants, the discriminants values must be provided before the constructor values: .. code-block:: ada @@ -381,8 +416,6 @@ When combined with discriminants, the discriminants values must be provided befo Note that the above can create ambiguous situations in corner cases, which are to be detected at compile time and resolved through e.g. naming: -TODO: copy? - .. code-block:: ada package P is @@ -428,7 +461,8 @@ Other removed capabilities -------------------------- Although discriminants are kept, coextensions should be removed under this proposal. They introduce various level of complexity and -have not yet been fully implemented. +have not yet been fully implemented. Their functionality can be completely replaced by constructors (it's possible to mandate an +object to be used in the construction of the class) and destructors (that same object can always be destroyed in the destructor). Tagged types ------------ From 8a5c5d6e4775d150bb121b4943f17ea6f001b7a5 Mon Sep 17 00:00:00 2001 From: QuentinOchem Date: Mon, 4 May 2020 15:14:32 -0400 Subject: [PATCH 05/20] Added missing information on constructors and final classes. --- considered/oop-remastered.rst | 99 ++++++++++++++++++++++++++++++++++- 1 file changed, 98 insertions(+), 1 deletion(-) diff --git a/considered/oop-remastered.rst b/considered/oop-remastered.rst index 0b40984d..e7e92ceb 100644 --- a/considered/oop-remastered.rst +++ b/considered/oop-remastered.rst @@ -396,7 +396,20 @@ or explicit. When explicit, it's provided through the Super aspect, specified on Destructors are implicitely called in sequence - the parent destructor is always called after it child. -TODO: copy? +A special constructor, a copy constructor, can be identified with the "Copy" aspect. It's called upon the copy of an object (for +example, an assignment). It can also be called explicitely, and needs to call parent constructors. It needs to be a constructor with +two values of the same type. For example: + +.. code-block:: ada + + package P is + type T1 is class record + procedure T1 (Self : in out T1; Source : T1) + with Copy; + end T1; + +Constructors and discriminants +------------------------------ When combined with discriminants, the discriminants values must be provided before the constructor values: @@ -435,7 +448,91 @@ through e.g. naming: V : T1 (10); -- Illegal, is this a discriminant with default constructor or a default discriminant with a constructor? V2 : T1 (L => 10); -- Legal V3 : T1 (V => 10); -- Legal + +Constructors default values and and aggregates +---------------------------------------------- + +Aggregates are still possible with class records. The order of evaluation for fields is: + + - their default value. Always computed + - the constructor + - any value from the aggregate + +The rationale for this order is to go from the generic to the specific. This is a departure from the existing Ada model where +aggregate override default initialization. In class records, there is no way to override default initialization - if initialization +should only be done some times and not others, it is to be done in the constructor. + + For example: + +.. code-block:: ada + + package P is + type T1 is class record + procedure T1 (Self : in out T1; Val : Integer); + + Y : Integer := 0; + end T1; + end P; + + package body P is + type T1 is class record + procedure T1 (Self : in out T1; Val : Integer) is + begin + -- Y is 0 here + Self.Y := Val; + -- Y is val here + end T1; + end T1; + + V : T1 := (Y => 2); -- V.Y = 2 + V2 : T1'Ref := new T1 (1)'(Y => 2); -- V.Y = 2 + end P; +Final fields and classes +------------------------ + +Class record support constant fields, which are field which value cannot be changed after the constructor call, not even during +aggregate which is considered as a shortcut for assignment. For example: + + package P is + type T1 is class record + procedure T1 (Self : in out T1; Val : Integer); + + Y : final Integer := 0; + end T1; + end P; + + package body P is + type T1 is class record + procedure T1 (Self : in out T1; Val : Integer) is + begin + -- Y is 0 here + Self.Y := Val; -- Legal + -- Y is val here + end T1; + end T1; + + V : T1 := (Y => 2); -- Illegal, Y is final + end P; + +class record also implement the concept of final classes, which is a class not deriveable. + +.. code-block:: ada + + package P is + type T1 is class record + null; + end T1; + + type T2 is final new T1 with record + null; + end T2; + + type T3 is new T2 with record -- Illegal, T2 is final + null; + end T3; + end P; + Operators and exotic primitives ------------------------------- From a2505f1227b70efcbde5199d78e08ed4d31c67f2 Mon Sep 17 00:00:00 2001 From: QuentinOchem Date: Mon, 4 May 2020 15:25:32 -0400 Subject: [PATCH 06/20] Added comment on final classes and definite. --- considered/oop-remastered.rst | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/considered/oop-remastered.rst b/considered/oop-remastered.rst index e7e92ceb..4353ae0d 100644 --- a/considered/oop-remastered.rst +++ b/considered/oop-remastered.rst @@ -488,8 +488,8 @@ should only be done some times and not others, it is to be done in the construct V2 : T1'Ref := new T1 (1)'(Y => 2); -- V.Y = 2 end P; -Final fields and classes ------------------------- +Final fields +------------ Class record support constant fields, which are field which value cannot be changed after the constructor call, not even during aggregate which is considered as a shortcut for assignment. For example: @@ -515,7 +515,15 @@ aggregate which is considered as a shortcut for assignment. For example: V : T1 := (Y => 2); -- Illegal, Y is final end P; -class record also implement the concept of final classes, which is a class not deriveable. +Final fields +------------ + +class record also implement the concept of final classes, which is a class not deriveable. There are two advantages of final classes: + + - In terms of design, this makes it clear that this class is not intended to be derived. It's ofen the case where derivation is + used just to have a class in a given framework but isn't prepared to be itself modified. + - A very significant one: a final class is effectively a definite type. As a result, it can be stored on the stack or as a component, + calls to a view of a final class are not dispatching (the target is statically known). .. code-block:: ada @@ -533,6 +541,8 @@ class record also implement the concept of final classes, which is a class not d end T3; end P; + + Operators and exotic primitives ------------------------------- From b22395e2edfc4c18b5fd8a9908f40a823845ad4e Mon Sep 17 00:00:00 2001 From: QuentinOchem Date: Mon, 4 May 2020 15:29:57 -0400 Subject: [PATCH 07/20] Fixed formatting. --- considered/oop-remastered.rst | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/considered/oop-remastered.rst b/considered/oop-remastered.rst index 4353ae0d..0113c55e 100644 --- a/considered/oop-remastered.rst +++ b/considered/oop-remastered.rst @@ -454,15 +454,15 @@ Constructors default values and and aggregates Aggregates are still possible with class records. The order of evaluation for fields is: - - their default value. Always computed - - the constructor - - any value from the aggregate +- their default value. Always computed +- the constructor +- any value from the aggregate The rationale for this order is to go from the generic to the specific. This is a departure from the existing Ada model where aggregate override default initialization. In class records, there is no way to override default initialization - if initialization should only be done some times and not others, it is to be done in the constructor. - For example: +For example: .. code-block:: ada @@ -494,6 +494,8 @@ Final fields Class record support constant fields, which are field which value cannot be changed after the constructor call, not even during aggregate which is considered as a shortcut for assignment. For example: +.. code-block:: ada + package P is type T1 is class record procedure T1 (Self : in out T1; Val : Integer); @@ -515,15 +517,15 @@ aggregate which is considered as a shortcut for assignment. For example: V : T1 := (Y => 2); -- Illegal, Y is final end P; -Final fields ------------- +Final classes +------------- class record also implement the concept of final classes, which is a class not deriveable. There are two advantages of final classes: - - In terms of design, this makes it clear that this class is not intended to be derived. It's ofen the case where derivation is - used just to have a class in a given framework but isn't prepared to be itself modified. - - A very significant one: a final class is effectively a definite type. As a result, it can be stored on the stack or as a component, - calls to a view of a final class are not dispatching (the target is statically known). +- In terms of design, this makes it clear that this class is not intended to be derived. It's ofen the case where derivation is + used just to have a class in a given framework but isn't prepared to be itself modified. +- A very significant one: a final class is effectively a definite type. As a result, it can be stored on the stack or as a component, + calls to a view of a final class are not dispatching (the target is statically known). .. code-block:: ada From 30a69669662739383be35bfddff67f6da1dad413 Mon Sep 17 00:00:00 2001 From: QuentinOchem Date: Mon, 4 May 2020 15:37:46 -0400 Subject: [PATCH 08/20] Minor changes. --- considered/oop-remastered.rst | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/considered/oop-remastered.rst b/considered/oop-remastered.rst index 0113c55e..b985be61 100644 --- a/considered/oop-remastered.rst +++ b/considered/oop-remastered.rst @@ -273,7 +273,7 @@ A view to the type is dispatching, no matter if it's referenced in a primitive o end T1; end P; -As a result, the reference to a class record is always indefinite. +As a result, the reference to a class record is indefinite, unless it's declared final (described in a point below). In some cases, it's needed to reference a specific type for a non-dispatching call. In this case, there are two possibilities: @@ -595,11 +595,12 @@ Drawbacks Prior art ========= +This proposal is heavily influence by C++, C# and Java (which arguably have influenced one another quite a lot). Unresolved questions ==================== -This proposal relies on the convergence of the unified record syntax proposal, and will need to be updated in light of potential +This proposal relies on the unified record syntax proposal, and will need to be updated in light of potential revamped access model and finalization models. A number of the capabilities of the standard run-time library rely today on tagged type. A thorough review should be made to From 97ce12c9c192ded66ed68376d9d9ded38b4e45da Mon Sep 17 00:00:00 2001 From: QuentinOchem Date: Mon, 4 May 2020 16:14:45 -0400 Subject: [PATCH 09/20] Minor fixes and precisions. --- considered/oop-remastered.rst | 105 ++++++++++++++++++++++++---------- 1 file changed, 74 insertions(+), 31 deletions(-) diff --git a/considered/oop-remastered.rst b/considered/oop-remastered.rst index b985be61..69dc68b5 100644 --- a/considered/oop-remastered.rst +++ b/considered/oop-remastered.rst @@ -40,12 +40,12 @@ be marked with the new class reserved word: Primitives and component declarations ------------------------------------- -Under this new model, the notion of controlling primitive disapears. Instead, primitives have to be added in the declarative -scope of the type. For consistency, this is possible for both class record and regular record. The first parameter of the -primitive has to be of the type of the record. This allows the user to decide on the naming convention, as well as the mode -of such parameter (in, out, in out, access, aliased). A record and and class record can have primitives declared both in the -public and the private part. This is possibilty is extended to other components as well. The existence of a private part needs -to be specified in the public part of a package with the notation with private. The following demonstrates the above: +Under this new model, controlling primitives are declared within the lexical scope of their type. For consistency, this is possible +for both class record and regular record. The first parameter of the primitive has to be of the type of the record. This allows the +user to decide on the naming convention, as well as the mode of such parameter (in, out, in out, access, aliased). A record and +and class record can have primitives declared both in the public and the private part. This is possibilty is extended to other +components as well. The existence of a private part needs to be specified in the public part of a package with the notation "with +private". The following demonstrates the above: .. code-block:: ada @@ -154,10 +154,12 @@ As for tagged types, there's a shortcut for a class private type, which means no Class record can still be limited or have discriminants, in which cases the set of constaints that they have follow similar rules as for tagged types. +Visibilty rules are the same as for types today. In particular, a class instance as access to private components of other instances of the same class. + Overriding and extensions ------------------------- -Extension of class record types works similarly to tagged records: +Extension of class record types work similarly to tagged records: .. code-block:: ada @@ -221,7 +223,7 @@ For homogenity, 'Ref and 'Unchecked_Free are available to all Ada type - includi V : T1'Ref'Ref := new T1'Ref; -'Ref access types for a given class object are compatible in the case of upcast, but need explicit conversions to downcase. You +'Ref access types for a given class object are compatible in the case of upcast, but need explicit conversions to downcast. You can write: .. code-block:: ada @@ -252,7 +254,7 @@ can write: Dispatching ----------- -A view to the type is dispatching, no matter if it's referenced in a primitive or not. So for example: +A view to a (non-final) class record is dispatching, no matter if it's referenced in a primitive or not. So for example: .. code-block:: ada @@ -273,14 +275,14 @@ A view to the type is dispatching, no matter if it's referenced in a primitive o end T1; end P; -As a result, the reference to a class record is indefinite, unless it's declared final (described in a point below). +As a result, the reference to a class record is indefinite, unless it's declared final (see later). In some cases, it's needed to reference a specific type for a non-dispatching call. In this case, there are two possibilities: (1) only reference to the parent class is needed, this can be accessed through 'Super. If 'Super is applied on a type, this refers to its direct parent. If it's applied on an object, it refers to the parent of the type of this object -(2) a reference to a specific object. Rules are the same as above, with the usage of 'Specific (either refering to a non +(2) a reference to a specific definite type. Rules are the same as above, with the usage of 'Specific (either refering to a non dispatching specific type, or the specific view of the object): For example: @@ -293,7 +295,7 @@ For example: end T1; type T2 is new T1 with record - procedure P (Self : in out T1); + procedure P (Self : in out T2); end T2; end P; @@ -306,7 +308,7 @@ For example: end T1; type T2 is new T1 with record - procedure P (Self : in out T1) is + procedure P (Self : in out T2) is begin Self'Super.P; T2'Super (Self).P; @@ -315,11 +317,23 @@ For example: end P; end T2; end P; + +Note that these can also be used to declare definite parameters, results or even variables: + +.. code-block:: ada + + package P is + type T1 is class record + procedure P (Self : in out T1); + end T1; + + V1 : T1; -- Illegal, T1 is indefinite; + V : T1'Specific; -- Legal Global object hierarchy ----------------------- -All class object implicitely derive from a top level object, Ada.Classes.Object, defined as follows: +All class object implicitely derives from a top level object, Ada.Classes.Object, defined as follows: .. code-block:: ada @@ -336,8 +350,8 @@ Other top level primitives may be needed here. Constructors, copy and destructors ---------------------------------- -There is no Controlled object in class record. Instead, class record can declare constructors and one destructor. The constructor -needs to be a procedure of the name of the object, taking an in out or access reference to the object. Destructors are named +There is no controlled object in class records. Instead, class record can declare constructors and one destructor. The constructor +needs to be a procedure of the name of the object, taking an in out or access reference to the object. Destructors are named "final". .. code-block:: ada @@ -346,15 +360,18 @@ needs to be a procedure of the name of the object, taking an in out or access re procedure T1 (Self : in out T1); procedure T1 (Self : in out T1; Some_Value : Integer); - procedure finalize (Self : in out T1); + procedure final (Self : in out T1); end T1; + + type T2 is class record + procedure T2 (Self : in out T2; Some_Value : Integer); + end T2; end P; -This specific proposals is linked to an overal finalization proposal. If a different reserved word is used, it will be used for -the destructor notation as well. +This specific proposals is linked to an overal finalization proposal. It may alter the actual syntax / reserved word for destructors. -As soon as a constructor exist, and object cannot be created without calling one of the available constructors. This call is made -on the object creation, e.g.: +As soon as a constructor exist, and object cannot be created without calling one of the available constructors, omitting the +self parameter. This call is made on the object creation, e.g.: .. code-block:: ada @@ -362,6 +379,7 @@ on the object creation, e.g.: V2 : T1 (42); -- OK, 1 parameter constructor V3 : T1'Ref := new T1; V4 : T1'Ref := new T1 (42); + V5 : T2; -- NOT OK, there's no parameterless constructor A constructor of a child class always call its parent constructor before its own. It's either implicit (parameterless constructor) or explicit. When explicit, it's provided through the Super aspect, specified on the body of the constructor, for example: @@ -394,7 +412,7 @@ or explicit. When explicit, it's provided through the Super aspect, specified on end T2; end T2; -Destructors are implicitely called in sequence - the parent destructor is always called after it child. +Destructors are implicitely called in sequence - the parent destructor is always called after its child. A special constructor, a copy constructor, can be identified with the "Copy" aspect. It's called upon the copy of an object (for example, an assignment). It can also be called explicitely, and needs to call parent constructors. It needs to be a constructor with @@ -460,7 +478,7 @@ Aggregates are still possible with class records. The order of evaluation for fi The rationale for this order is to go from the generic to the specific. This is a departure from the existing Ada model where aggregate override default initialization. In class records, there is no way to override default initialization - if initialization -should only be done some times and not others, it is to be done in the constructor. +should only be done some times and not others, it is to be done in the constructor. With class records, aggreates are a shortcut for field by field assignment after iniitalization. For example: @@ -488,6 +506,29 @@ For example: V2 : T1'Ref := new T1 (1)'(Y => 2); -- V.Y = 2 end P; +Note that it's of course always possible (and useful) to use an aggreate within a constructor, still as a shortcut to field by +field assignment: + + package P is + type T1 is class record + procedure T1 (Self : in out T1); + + A, B, C : Integer; + end T1; + end P; + + package body P is + type T1 is class record + procedure T1 (Self : in out T1) is + begin + Self := (1, 2, 3); + end T1; + end T1; + + V : T1 := (A => 99, others => <>); -- V.A = 99, V.B = 2, V.C = 3. + end P; + + Final fields ------------ @@ -522,7 +563,7 @@ Final classes class record also implement the concept of final classes, which is a class not deriveable. There are two advantages of final classes: -- In terms of design, this makes it clear that this class is not intended to be derived. It's ofen the case where derivation is +- In terms of design, this makes it clear that this class is not intended to be derived. It's often the case where derivation is used just to have a class in a given framework but isn't prepared to be itself modified. - A very significant one: a final class is effectively a definite type. As a result, it can be stored on the stack or as a component, calls to a view of a final class are not dispatching (the target is statically known). @@ -541,14 +582,16 @@ class record also implement the concept of final classes, which is a class not d type T3 is new T2 with record -- Illegal, T2 is final null; end T3; + + V1 : T1; -- Illegal, T1 is indefinite + V2 : T2; -- Legal, T2 is final. end P; - Operators and exotic primitives ------------------------------- -Class record do not provide mutiple dispatching call, or dispatching on results. If you declare primitives with references to +Class record do not provide dispatching on multiple parameters, or dispatching on results. If you declare primitives with references to the type other than the first parameter, they will not be used for controlling. This means that parameters that are the same at top level may differ when deriving: @@ -566,12 +609,12 @@ Operators can be declared as primitives: end T1; end P; -Other removed capabilities --------------------------- +Coextensions +------------ -Although discriminants are kept, coextensions should be removed under this proposal. They introduce various level of complexity and -have not yet been fully implemented. Their functionality can be completely replaced by constructors (it's possible to mandate an -object to be used in the construction of the class) and destructors (that same object can always be destroyed in the destructor). +Under the current model, coextensions are replaced by constructors (it's possible to mandate an object to be used in the construction +of the class) and destructors (that same object can always be destroyed in the destructor). There is no way to create a coextension +on a class record. Tagged types ------------ From 67e7dd7cbf4a9b8396c4e3a1608607840590be82 Mon Sep 17 00:00:00 2001 From: QuentinOchem Date: Mon, 4 May 2020 17:08:59 -0400 Subject: [PATCH 10/20] Added details on copy constructors. --- considered/oop-remastered.rst | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/considered/oop-remastered.rst b/considered/oop-remastered.rst index 69dc68b5..f808db2a 100644 --- a/considered/oop-remastered.rst +++ b/considered/oop-remastered.rst @@ -196,8 +196,7 @@ Access types This topic is to be considered in the context of a larger overall of access types. However, in the absence of such proposal, the idea here is to have an access type declared implicitely at the same level as the type and accessible through the 'Ref notation. An attribute 'Unchecked_Free is also declared, doing unchecked deallocation. 'Unchecked_Free can also be called directly on -the object. These are also available for definite view 'Super and 'Specific, with the only difference being that calling on these -access types will not dispatch. For example: +the object. For example: .. code-block:: ada @@ -414,6 +413,9 @@ or explicit. When explicit, it's provided through the Super aspect, specified on Destructors are implicitely called in sequence - the parent destructor is always called after its child. +Copy constructor +---------------- + A special constructor, a copy constructor, can be identified with the "Copy" aspect. It's called upon the copy of an object (for example, an assignment). It can also be called explicitely, and needs to call parent constructors. It needs to be a constructor with two values of the same type. For example: @@ -425,6 +427,13 @@ two values of the same type. For example: procedure T1 (Self : in out T1; Source : T1) with Copy; end T1; + +Note that to the difference of the Adjust function of controlled types, the copy constructor is responsible to do the actual copy from +Source to Self - it's not done ahead of time. + +If not specified, a default constructor is automatically generated. It componses - it will will call the parent copy constructor, +then copy field by field its additional components, calling component constructors if necessary. + Constructors and discriminants ------------------------------ From 33260bd37de0ed178b8666087f57185d4bd6e8a7 Mon Sep 17 00:00:00 2001 From: QuentinOchem Date: Mon, 4 May 2020 18:53:54 -0400 Subject: [PATCH 11/20] Minor fixes and updates. --- considered/oop-remastered.rst | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/considered/oop-remastered.rst b/considered/oop-remastered.rst index f808db2a..3eb36eed 100644 --- a/considered/oop-remastered.rst +++ b/considered/oop-remastered.rst @@ -119,7 +119,7 @@ record, but of course in this case properties are not available: package P is type T1 is private; - type T2 is private; + type T2 (<>) is private; -- T2 is completed by a class, it has to be indefinite private view private @@ -518,6 +518,8 @@ For example: Note that it's of course always possible (and useful) to use an aggreate within a constructor, still as a shortcut to field by field assignment: +.. code-block:: ada + package P is type T1 is class record procedure T1 (Self : in out T1); From 87cf2f13af7dcd3bdf3b1df2a5b0bc20615f0a41 Mon Sep 17 00:00:00 2001 From: QuentinOchem Date: Tue, 5 May 2020 08:38:38 -0400 Subject: [PATCH 12/20] Added considerations, in particular on regular records. --- considered/oop-remastered.rst | 53 +++++++++++++++++++++++++---------- 1 file changed, 38 insertions(+), 15 deletions(-) diff --git a/considered/oop-remastered.rst b/considered/oop-remastered.rst index 3eb36eed..ff8f6fe6 100644 --- a/considered/oop-remastered.rst +++ b/considered/oop-remastered.rst @@ -19,6 +19,10 @@ vulnerabilities and retain some of the Ada advantages. This proposal also attempts at keeping capabilities that do not need to be specifically removed to fit within a more intuitive OOP model, such as e.g. discriminants and aggregates. +Experience shows that whenever possible, capabilities that can be enabled to regular records should as well. A good exemple of that +is the trend observed of people declaring Ada types tagged just for the purpose of having access to prefix notation, while such notation +does not require inheritance or dispatching. + Guide-level explanation ======================= @@ -37,14 +41,15 @@ be marked with the new class reserved word: null; end A_Class; -Primitives and component declarations -------------------------------------- +Primitives and components declarations +-------------------------------------- + +This new way of organizing components and primitives is available to both class records and simple records. -Under this new model, controlling primitives are declared within the lexical scope of their type. For consistency, this is possible -for both class record and regular record. The first parameter of the primitive has to be of the type of the record. This allows the -user to decide on the naming convention, as well as the mode of such parameter (in, out, in out, access, aliased). A record and -and class record can have primitives declared both in the public and the private part. This is possibilty is extended to other -components as well. The existence of a private part needs to be specified in the public part of a package with the notation "with +Under this new model, controlling primitives are declared within the lexical scope of their type. The first parameter of the +primitive has to be of the type of the record. This allows the user to decide on the naming convention, as well as the mode of +such parameter (in, out, in out, access, aliased). A record and and class record can have primitives declared both in the public +and the private part. This is possibilty is extended to other components as well. The existence of a private part needs to be specified in the public part of a package with the notation "with private". The following demonstrates the above: .. code-block:: ada @@ -193,6 +198,8 @@ Intefaces and abstract types work the same way as for tagged types. Interfaces a Access types ------------ +This capability is available to all types (including simple records). + This topic is to be considered in the context of a larger overall of access types. However, in the absence of such proposal, the idea here is to have an access type declared implicitely at the same level as the type and accessible through the 'Ref notation. An attribute 'Unchecked_Free is also declared, doing unchecked deallocation. 'Unchecked_Free can also be called directly on @@ -346,8 +353,10 @@ All class object implicitely derives from a top level object, Ada.Classes.Object Other top level primitives may be needed here. -Constructors, copy and destructors ----------------------------------- +Constructors and destructors +---------------------------- + +Constructors are available to both class record and simple records. There is no controlled object in class records. Instead, class record can declare constructors and one destructor. The constructor needs to be a procedure of the name of the object, taking an in out or access reference to the object. Destructors are named "final". @@ -416,6 +425,8 @@ Destructors are implicitely called in sequence - the parent destructor is always Copy constructor ---------------- +Copy constructors are available to both class records and simple records. + A special constructor, a copy constructor, can be identified with the "Copy" aspect. It's called upon the copy of an object (for example, an assignment). It can also be called explicitely, and needs to call parent constructors. It needs to be a constructor with two values of the same type. For example: @@ -438,6 +449,8 @@ then copy field by field its additional components, calling component constructo Constructors and discriminants ------------------------------ +These considerations are applicatble to both class records and simple records. + When combined with discriminants, the discriminants values must be provided before the constructor values: .. code-block:: ada @@ -479,6 +492,8 @@ through e.g. naming: Constructors default values and and aggregates ---------------------------------------------- +These considerations are applicatble to both class records and simple records. + Aggregates are still possible with class records. The order of evaluation for fields is: - their default value. Always computed @@ -486,8 +501,9 @@ Aggregates are still possible with class records. The order of evaluation for fi - any value from the aggregate The rationale for this order is to go from the generic to the specific. This is a departure from the existing Ada model where -aggregate override default initialization. In class records, there is no way to override default initialization - if initialization -should only be done some times and not others, it is to be done in the constructor. With class records, aggreates are a shortcut for field by field assignment after iniitalization. +aggregate override default initialization. Under this model, there is no more way to override default initialization for records - +if initialization should only be done some times and not others, it is to be done in the constructor (which is available for records +and class records). With class records, aggreates are a shortcut for field by field assignment after iniitalization. For example: @@ -543,6 +559,8 @@ field assignment: Final fields ------------ +Final fields are available to both class records and simple records. + Class record support constant fields, which are field which value cannot be changed after the constructor call, not even during aggregate which is considered as a shortcut for assignment. For example: @@ -602,9 +620,9 @@ class record also implement the concept of final classes, which is a class not d Operators and exotic primitives ------------------------------- -Class record do not provide dispatching on multiple parameters, or dispatching on results. If you declare primitives with references to -the type other than the first parameter, they will not be used for controlling. This means that parameters that are the same -at top level may differ when deriving: +Class record do not provide dispatching on multiple parameters, on parameters other than the first, or dispatching on results. +If you declare primitives with references to the type other than the first parameter, they will not be used for controlling. This +means that parameters that are the same at top level may differ when deriving: Operators can be declared as primitives: @@ -666,5 +684,10 @@ Future possibilities ==================== One important aspect of Ada is to allow data to be as static as possible. OOP typically requires the use of pointer. The Max_Size -proposal (https://github.com/QuentinOchem/ada-spark-rfcs/blob/max_size/considered/max_size.rst) is a independent proposal to allow polymorphic object residing in automatic memory section such as fields or stack. +proposal (https://github.com/QuentinOchem/ada-spark-rfcs/blob/max_size/considered/max_size.rst) is a independent proposal to allow +polymorphic object residing in automatic memory section such as fields or stack. + +Some of the notations introduced could be extended to other types, such as protected or tasks type. +The "with private;" notation should also be extended to nested packages, allowing to differenciate to nest the private part of a +nested package in the private part of its enclosing package. From 17238316224d1795b39ad05d4c8bed397b170dad Mon Sep 17 00:00:00 2001 From: QuentinOchem Date: Tue, 5 May 2020 08:44:27 -0400 Subject: [PATCH 13/20] Added considerations for non-dispatching operations. --- considered/oop-remastered.rst | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/considered/oop-remastered.rst b/considered/oop-remastered.rst index ff8f6fe6..27c8b14c 100644 --- a/considered/oop-remastered.rst +++ b/considered/oop-remastered.rst @@ -335,7 +335,31 @@ Note that these can also be used to declare definite parameters, results or even V1 : T1; -- Illegal, T1 is indefinite; V : T1'Specific; -- Legal + +Non-dispatching operations +-------------------------- + +The 'Specific notaton described above can also be used to declare non-primitive operations of a type. In this case, these operations +can be called through the usual prefix notation, but they cannot be overriden and can't be used for dispatching. For example: +.. code-block:: ada + + package P is + type T1 is class record + procedure P (Self : in out T1'Specific); + end T1; + + type T2 is new T1 with null record; + + end P; + + procedure Some_Procedure is + V : T1; + V2 : T2; + begin + V.P; -- Legal, P is an operation of T1 + V2.P; -- Legal P is also an operation of T2, statically called + Global object hierarchy ----------------------- From 71b08ab3dd3476782f848bea03f7c5296c1513b8 Mon Sep 17 00:00:00 2001 From: QuentinOchem Date: Tue, 5 May 2020 09:33:17 -0400 Subject: [PATCH 14/20] Added comment on package bodies. --- considered/oop-remastered.rst | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/considered/oop-remastered.rst b/considered/oop-remastered.rst index 27c8b14c..e24c9a44 100644 --- a/considered/oop-remastered.rst +++ b/considered/oop-remastered.rst @@ -662,6 +662,12 @@ Operators can be declared as primitives: end T1; end P; +Body-only classes +----------------- + +One limitation of the tagged type to lift under this system is the ability to declare a class only in the body of a package. This +should be legal under this new system. + Coextensions ------------ From 858a8d2795e8ef5dc716d33ca91198c9d5b2d9c7 Mon Sep 17 00:00:00 2001 From: QuentinOchem Date: Tue, 5 May 2020 09:39:04 -0400 Subject: [PATCH 15/20] Added specific body syntax for type implementation --- considered/oop-remastered.rst | 32 ++++++++++++++++++++++++-------- 1 file changed, 24 insertions(+), 8 deletions(-) diff --git a/considered/oop-remastered.rst b/considered/oop-remastered.rst index e24c9a44..0ae8476d 100644 --- a/considered/oop-remastered.rst +++ b/considered/oop-remastered.rst @@ -87,7 +87,7 @@ private". The following demonstrates the above: package body P is - type T1 is record + type body T1 is record procedure P (Self : in out T1; V : Integer) is begin Self.F := V; @@ -99,7 +99,7 @@ private". The following demonstrates the above: end P2; end T1; - type T2 is record + type body T2 is record procedure P (Self : in out T2; V : Integer) is begin Self.F := V; @@ -273,7 +273,7 @@ A view to a (non-final) class record is dispatching, no matter if it's reference end P; package P is - type T1 is class record + type body T1 is class record procedure P (Self : in out T1) is begin Self.P2; -- Dispatching @@ -429,14 +429,14 @@ or explicit. When explicit, it's provided through the Super aspect, specified on end P; package body P is - type T1 is class record + type body T1 is class record procedure T1 (Self : in out T1; V : Integer) is begin null; end T1; end T1; - type T2 is new T1 with record + type body T2 is new T1 with record procedure T2 (Self : in out T1) with Super (0) -- special notation for calling the super constructor. First parameter is omitted is @@ -542,7 +542,7 @@ For example: end P; package body P is - type T1 is class record + type body T1 is class record procedure T1 (Self : in out T1; Val : Integer) is begin -- Y is 0 here @@ -569,7 +569,7 @@ field assignment: end P; package body P is - type T1 is class record + type body T1 is class record procedure T1 (Self : in out T1) is begin Self := (1, 2, 3); @@ -599,7 +599,7 @@ aggregate which is considered as a shortcut for assignment. For example: end P; package body P is - type T1 is class record + type body T1 is class record procedure T1 (Self : in out T1; Val : Integer) is begin -- Y is 0 here @@ -667,6 +667,22 @@ Body-only classes One limitation of the tagged type to lift under this system is the ability to declare a class only in the body of a package. This should be legal under this new system. + +.. code-block:: ada + + package body P is + type T2 is class record + F : Integer; + procedure P (Self : in out T2; V : Integer); + end T2; + + type body T2 is class record + procedure P (Self : in out T2; V : Integer) is + begin + Self.F := V; + end P; + end T2; + end P; Coextensions ------------ From 94e29ac92f711b990698848e612c972cae4a12d0 Mon Sep 17 00:00:00 2001 From: QuentinOchem Date: Tue, 5 May 2020 12:06:02 -0400 Subject: [PATCH 16/20] Modified considerations on primitives. --- considered/oop-remastered.rst | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/considered/oop-remastered.rst b/considered/oop-remastered.rst index 0ae8476d..abc511c0 100644 --- a/considered/oop-remastered.rst +++ b/considered/oop-remastered.rst @@ -113,18 +113,23 @@ private". The following demonstrates the above: end P; -In order to keep some legacy compatibility with Ada, the concept of primitive is kept for static subprograms outside of the scope -of the type. They cannot be used for dynamic dispatching. +In this model, it is not possible to write record types primitives outside of the scope anymore. Subprograms declared outside of such +scope are just regular subprograms. -Other ways to declare types are kept. For example, it's still possible to declare a private type and implement it through a -record, but of course in this case properties are not available: +As a consequence, it's not possible anymore to have a record or a class record as a completion of a private type. This type now needs +to be marked either record private, or be a regular record with a private extension. For example .. code-block:: ada package P is - type T1 is private; + type T1 is record private; - type T2 (<>) is private; -- T2 is completed by a class, it has to be indefinite private view + type T2 (<>) is record private; -- T2 is completed by a class, it has to be indefinite private view + + type T3 is record + procedure P (Self : T3); + end T3 + with private; private @@ -140,6 +145,9 @@ record, but of course in this case properties are not available: procedure P2 (Self : in out T2; V : Integer); end T2; + type T3 is record + null; + end T3; end P; As for tagged types, there's a shortcut for a class private type, which means no public primitives or components: @@ -737,3 +745,6 @@ Some of the notations introduced could be extended to other types, such as prote The "with private;" notation should also be extended to nested packages, allowing to differenciate to nest the private part of a nested package in the private part of its enclosing package. + +The scoped primitive notation is currently specific to record types. It could be extended to all types (which would have the effect +or re-enabling the possibility to complete a simple private type by a record). From b83de3bb6c1895236c99f0ea19940f28c45db554 Mon Sep 17 00:00:00 2001 From: Quentin Ochem Date: Tue, 21 Nov 2023 12:07:56 -0500 Subject: [PATCH 17/20] initial revision --- considered/rfc-ghost-fields-and-parameters.md | 160 ++++++++++++++++++ 1 file changed, 160 insertions(+) create mode 100644 considered/rfc-ghost-fields-and-parameters.md diff --git a/considered/rfc-ghost-fields-and-parameters.md b/considered/rfc-ghost-fields-and-parameters.md new file mode 100644 index 00000000..920d78b5 --- /dev/null +++ b/considered/rfc-ghost-fields-and-parameters.md @@ -0,0 +1,160 @@ +- Feature Name: ghost fields +- Start Date: 2022-02-22 +- RFC PR: #88 +- RFC Issue: (leave this empty) + +Summary +======= + +Motivation +========== + +Guide-level explanation +======================= + +Ghost Fields +------------ + +The Ghost aspect can be specified for a record component. An assignment +statement to such a component (in full or part) is a ghost statement. The rules +for referencing a ghost component are the same as for any other ghost +entity. Ghost entities should now be allowed inside the expression +corresponding to a ghost component inside an aggregate. Here is an example of +use of a ghost component: + +```Ada +type Pair is record + X, Y : Integer; + Area : Integer with Ghost; -- ghost component +end record; + +function Compute_Area (X, Y : Integer) return Integer is (X * Y) + with Ghost; + +function Create (X, Y : Integer) return Pair is + (X => X, Y => Y, Area => Compute_Area (X, Y)); -- ghost reference + +procedure Assign (P : out Pair; X, Y : Integer) is +begin + P.X := X; + P.Y := Y; + P.Area := Compute_Area (X, Y); -- ghost statement +end Assign; +``` + +Ghost fields may or may not be present at compile-time. As a consequence, the +size and layout of a data structure may differ depending on wether or not +ghost code is activated. In order to control this duality, a new set of +attributes "Ghost_Size", "Ghost_Value_Size" and "Ghost_Object_Size" are added +to refer to the size of the ghost objects when compiled whereas "Size", +"Value_Size" and "Object_Size" refer to the value of the objects as compiled. +As a consequence, Size, Value_Size and Object_Size may vary depending on +wether ghost code is active or not: + +Note that as a consequence, "Size", "Value_Size" and "Object_Size" can't be +specified on a record type that has ghost fields as this size is not constant. +Instead, one would use "Concrete_Size", "Concrete_Value_Size" +and "Concrete_Object_Size" which refers to the object without its ghost +components. These could also be turned into attributes. For example: + +```Ada +type Pair is record + X, Y : Integer; + Area : Integer with Ghost; -- ghost component +end record + with Ghost_Size => 12 * 8, + Concrete_Size => 8 * 8; + +S1 : Integer := Pair'Size; -- 8 or 12 bytes +S2 : Integer := Pair'Ghost_Size; -- 12 bytes +S3 : Integer := Pair'Concrete_Size; -- 8 bytes +``` + +When representation is necessary, it has to describe both concrete and ghost +fields, the compiler will check consistency of both cases. For example: + +```Ada +for Pair'Ghost_Size => 12 * 8, +for Pair'Concrete_Size => 8 * 8, + +for Pair use record + X at 0 range 0 .. 31; + Y at 4 range 0 .. 31; + Area at 8 range 0 .. 31; +end record; +``` + +Ghost components do not participate in the default equality, so that two +``Pair`` objects which only differ in their ``Area`` component should be +equal: + +```Ada +P1 := (X => 1, Y => 2, Area => 8); +P2 := (X => 1, Y => 2, Area => 12); +pragma Assert (P1 = P2); -- true assertion +``` + +Note that subtype predicates cannot refer to ghost entities, including ghost +components, as they are evaluated in type membership tests. Type invariants can +refer to ghost entities, including ghost components. + +Ghost Parameters +---------------- + +The Ghost aspect can be specified for a parameters. E.g.: + +```Ada +procedure Some_Procedure (X : Integer; Y : Integer with Ghost); +``` + +Ghost parameters need to be valuated at call time (unless they have default +values), and can be with expression containing ghost entities. E.g.: + +```Ada + V1 : Integer; + V2 : Integer with Ghost; + + Some_Procedure (V1, V1 + V2); +``` + +When Ghost code is not compiled, the expression valuating ghost parameter is +not evaluated and no parameters is passed. + +Inside the body of a procedure or a function, ghost parameters behave like +Ghost variable and can only be used in the context of ghost code. + + +Reference-level explanation +=========================== + +TDB + +Rationale and alternatives +========================== + + +Drawbacks +========= + +Alternate layout for ghost and non-ghost record may create additional +difficulties when developping application that heavily depends on reprentations. +Some of these applications may as a consequence not be able to rely on run-time +ghost fields. The [Multiple Ghost fields proposal](https://github.com/QuentinOchem/ada-spark-rfcs/blob/multiple_ghost/considered/rfc-multiple_ghost_levels.md) +would allow to cater for these cases. + +Prior art +========= + +Other programming languages that target formal program verification include +ghost fields: [Why3](http://why3.lri.fr/doc/syntaxref.html#modules), +[Dafny](https://dafny-lang.github.io/dafny/DafnyRef/DafnyRef.html#33-declaration-modifiers). + +Ghost code is not executable in these languages, so the above discussion +regarding alternatives for ghost components is not relevant for them. + +Unresolved questions +==================== + + +Future possibilities +==================== From 96849eb5d2c8a629b1725ab4473927303699bd7b Mon Sep 17 00:00:00 2001 From: Quentin Ochem Date: Sun, 28 Jan 2024 22:27:22 -0500 Subject: [PATCH 18/20] updated following live discussion --- considered/rfc-ghost-fields-and-parameters.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/considered/rfc-ghost-fields-and-parameters.md b/considered/rfc-ghost-fields-and-parameters.md index 920d78b5..1e1740da 100644 --- a/considered/rfc-ghost-fields-and-parameters.md +++ b/considered/rfc-ghost-fields-and-parameters.md @@ -98,6 +98,10 @@ Note that subtype predicates cannot refer to ghost entities, including ghost components, as they are evaluated in type membership tests. Type invariants can refer to ghost entities, including ghost components. +Note that one of the consequence of using ghost fields is that overlays and +unchecked conversion may fail at compile time. This is expected, and is a known +limitation of ghost fields. + Ghost Parameters ---------------- @@ -117,12 +121,18 @@ values), and can be with expression containing ghost entities. E.g.: Some_Procedure (V1, V1 + V2); ``` +As a consequence, ghost parameters are used for name resolution and overloading +rules. + When Ghost code is not compiled, the expression valuating ghost parameter is not evaluated and no parameters is passed. Inside the body of a procedure or a function, ghost parameters behave like Ghost variable and can only be used in the context of ghost code. +Controlling parameters cannot be marked as being ghost (as the tag of the object +is always needed at run-time to resolve dispatching). + Reference-level explanation =========================== From cee0c01ab21558efb230a4e1c77a44d0a79a75c6 Mon Sep 17 00:00:00 2001 From: Quentin Ochem Date: Wed, 11 Sep 2024 15:53:12 -0400 Subject: [PATCH 19/20] Removed file added by mistake. --- considered/oop-remastered.rst | 750 ---------------------------------- 1 file changed, 750 deletions(-) delete mode 100644 considered/oop-remastered.rst diff --git a/considered/oop-remastered.rst b/considered/oop-remastered.rst deleted file mode 100644 index abc511c0..00000000 --- a/considered/oop-remastered.rst +++ /dev/null @@ -1,750 +0,0 @@ -- Feature Name: Standard OOP model -- Start Date: May 5th 2020 -- RFC PR: -- RFC Issue: - -Summary -======= - -The objective of this proposal is to draft an OOP model for a hypothetical new version of Ada, closer to the models languages -such as C++ and Java developers are accustomed to, fixing a number of oddities and vulnerabilities of the current Ada language. - -Motivation -========== - -The current Ada OOP model is source of a number of confusions and errors from people accustomed to OOP, in particular in -other languages such as C++ or Java. This proposal aims at adjusting the model to implement missing concept, fix Ada-specific -vulnerabilities and retain some of the Ada advantages. - -This proposal also attempts at keeping capabilities that do not need to be specifically removed to fit within a more intuitive -OOP model, such as e.g. discriminants and aggregates. - -Experience shows that whenever possible, capabilities that can be enabled to regular records should as well. A good exemple of that -is the trend observed of people declaring Ada types tagged just for the purpose of having access to prefix notation, while such notation -does not require inheritance or dispatching. - -Guide-level explanation -======================= - -The new design retains the difference between "regular" types and "classes". For consistency, both will be altered and consider that -https://github.com/AdaCore/ada-spark-rfcs/pull/13 is implemented. - -Class declaration ------------------ - -The new class model is incompatible with the current tagged object model. In order to make the distinction, new tagged types will -be marked with the new class reserved word: - -.. code-block:: ada - - type A_Class is class record - null; - end A_Class; - -Primitives and components declarations --------------------------------------- - -This new way of organizing components and primitives is available to both class records and simple records. - -Under this new model, controlling primitives are declared within the lexical scope of their type. The first parameter of the -primitive has to be of the type of the record. This allows the user to decide on the naming convention, as well as the mode of -such parameter (in, out, in out, access, aliased). A record and and class record can have primitives declared both in the public -and the private part. This is possibilty is extended to other components as well. The existence of a private part needs to be specified in the public part of a package with the notation "with -private". The following demonstrates the above: - -.. code-block:: ada - - package P is - type T1 is record - F : Integer; - - procedure P (Self : in out T1; V : Integer); - end T1 - with private; - - type T2 is class record - F : Integer; - - procedure P (Self : in out T2; V : Integer); - end T2 - with private; - - private - - type T1 is record - F2 : Integer; - - procedure P2 (Self : in out T1; V : Integer); - end T1; - - type T2 is class record - F2 : Integer; - - procedure P2 (Self : in out T2; V : Integer); - end T2; - - end P; - - package body P is - - type body T1 is record - procedure P (Self : in out T1; V : Integer) is - begin - Self.F := V; - end P; - - procedure P2 (Self : in out T1; V : Integer) is - begin - Self.F2 := V; - end P2; - end T1; - - type body T2 is record - procedure P (Self : in out T2; V : Integer) is - begin - Self.F := V; - end P; - - procedure P2 (Self : in out T2; V : Integer) is - begin - Self.F2 := V; - end P2; - end T2; - - end P; - -In this model, it is not possible to write record types primitives outside of the scope anymore. Subprograms declared outside of such -scope are just regular subprograms. - -As a consequence, it's not possible anymore to have a record or a class record as a completion of a private type. This type now needs -to be marked either record private, or be a regular record with a private extension. For example - -.. code-block:: ada - - package P is - type T1 is record private; - - type T2 (<>) is record private; -- T2 is completed by a class, it has to be indefinite private view - - type T3 is record - procedure P (Self : T3); - end T3 - with private; - - private - - type T1 is record - F2 : Integer; - - procedure P2 (Self : in out T1; V : Integer); - end T1; - - type T2 is class record - F2 : Integer; - - procedure P2 (Self : in out T2; V : Integer); - end T2; - - type T3 is record - null; - end T3; - end P; - -As for tagged types, there's a shortcut for a class private type, which means no public primitives or components: - -.. code-block:: ada - - package P is - type T1 is class private; - private - type T1 is class record - F2 : Integer; - - procedure P2 (Self : in out T1; V : Integer); - end T1; - end P; - -Class record can still be limited or have discriminants, in which cases the set of constaints that they have follow similar rules -as for tagged types. - -Visibilty rules are the same as for types today. In particular, a class instance as access to private components of other instances of the same class. - -Overriding and extensions -------------------------- - -Extension of class record types work similarly to tagged records: - -.. code-block:: ada - - package P is - type T1 is class record - procedure P (Self : in out T1); - end T1; - - type T2 is new T1 with record - procedure P (Self : in out T1); - end T2; - end P; - -Primitives can be marked optionally overriding, following Ada 2005 rules. Inheritance model is single interitance of a class, -multiple inheritance of interfaces. - -Interfaces and abstract types ------------------------------ - -Intefaces and abstract types work the same way as for tagged types. Interfaces are specified differently, through -"interface record", but otherwise operate as other interfaces (no concrete components or primitive): - -.. code-block:: ada - - package P is - type I is interface record - procedure P (Self : in out I) is abstract; - end I; - end P; - -Access types ------------- - -This capability is available to all types (including simple records). - -This topic is to be considered in the context of a larger overall of access types. However, in the absence of such proposal, -the idea here is to have an access type declared implicitely at the same level as the type and accessible through the 'Ref notation. -An attribute 'Unchecked_Free is also declared, doing unchecked deallocation. 'Unchecked_Free can also be called directly on -the object. For example: - -.. code-block:: ada - - package P is - type T1 is class record - procedure P (Self : in out T1); - - procedure P2 (Self : in out T1); - end T1; - end P; - - procedure Some_Procedure is - V : T1'Ref := new T1; - V2 : T1'Ref := new T1; - begin - T1'Unchecked_Free (V); - V2'Unchecked_Free; - end Some_Procedure; - -For homogenity, 'Ref and 'Unchecked_Free are available to all Ada type - including pointers themesleves. It's now possible to write: - -.. code-block:: ada - - V : T1'Ref'Ref := new T1'Ref; - -'Ref access types for a given class object are compatible in the case of upcast, but need explicit conversions to downcast. You -can write: - -.. code-block:: ada - - package P is - type A is class record - procedure P (Self : in out T1); - end A; - - type B is new T1 with record - procedure P (Self : in out T1); - end B; - end P; - - procedure Some_Procedure is - A1 : A'Ref := new B; - A2 : A'Ref; - - B1 : B'Ref := new B; - B2 : B'Ref; - begin - A2 := B1; -- OK, upcast, no need for pointer conversion - B2 := A1; -- Illegal, downcast - B2 := B'Ref (A1); -- OK, explicit downcast. - end Some_Procedure; - - -Dispatching ------------ - -A view to a (non-final) class record is dispatching, no matter if it's referenced in a primitive or not. So for example: - -.. code-block:: ada - - package P is - type T1 is class record - procedure P (Self : in out T1); - - procedure P2 (Self : in out T1); - end T1; - end P; - - package P is - type body T1 is class record - procedure P (Self : in out T1) is - begin - Self.P2; -- Dispatching - end P; - end T1; - end P; - -As a result, the reference to a class record is indefinite, unless it's declared final (see later). - -In some cases, it's needed to reference a specific type for a non-dispatching call. In this case, there are two possibilities: - -(1) only reference to the parent class is needed, this can be accessed through 'Super. If 'Super is applied on a type, this -refers to its direct parent. If it's applied on an object, it refers to the parent of the type of this object - -(2) a reference to a specific definite type. Rules are the same as above, with the usage of 'Specific (either refering to a non -dispatching specific type, or the specific view of the object): - -For example: - -.. code-block:: ada - - package P is - type T1 is class record - procedure P (Self : in out T1); - end T1; - - type T2 is new T1 with record - procedure P (Self : in out T2); - end T2; - end P; - - package body P is - type T1 is class record - procedure P (Self : in out T1) is - begin - null; - end P; - end T1; - - type T2 is new T1 with record - procedure P (Self : in out T2) is - begin - Self'Super.P; - T2'Super (Self).P; - Self'Specific.P; - T2'Specific (Self).P; - end P; - end T2; - end P; - -Note that these can also be used to declare definite parameters, results or even variables: - -.. code-block:: ada - - package P is - type T1 is class record - procedure P (Self : in out T1); - end T1; - - V1 : T1; -- Illegal, T1 is indefinite; - V : T1'Specific; -- Legal - -Non-dispatching operations --------------------------- - -The 'Specific notaton described above can also be used to declare non-primitive operations of a type. In this case, these operations -can be called through the usual prefix notation, but they cannot be overriden and can't be used for dispatching. For example: - -.. code-block:: ada - - package P is - type T1 is class record - procedure P (Self : in out T1'Specific); - end T1; - - type T2 is new T1 with null record; - - end P; - - procedure Some_Procedure is - V : T1; - V2 : T2; - begin - V.P; -- Legal, P is an operation of T1 - V2.P; -- Legal P is also an operation of T2, statically called - -Global object hierarchy ------------------------ - -All class object implicitely derives from a top level object, Ada.Classes.Object, defined as follows: - -.. code-block:: ada - - package Ada.Classes is - type Object is class record - function Image (Self : Object) return String; - - function Hash (Self : Object) return Integer; - end Object; - end Ada.Classes; - -Other top level primitives may be needed here. - -Constructors and destructors ----------------------------- - -Constructors are available to both class record and simple records. - -There is no controlled object in class records. Instead, class record can declare constructors and one destructor. The constructor -needs to be a procedure of the name of the object, taking an in out or access reference to the object. Destructors are named "final". - -.. code-block:: ada - - package P is - type T1 is class record - procedure T1 (Self : in out T1); - procedure T1 (Self : in out T1; Some_Value : Integer); - - procedure final (Self : in out T1); - end T1; - - type T2 is class record - procedure T2 (Self : in out T2; Some_Value : Integer); - end T2; - end P; - -This specific proposals is linked to an overal finalization proposal. It may alter the actual syntax / reserved word for destructors. - -As soon as a constructor exist, and object cannot be created without calling one of the available constructors, omitting the -self parameter. This call is made on the object creation, e.g.: - -.. code-block:: ada - - V : T1; -- OK, parameterless constructor - V2 : T1 (42); -- OK, 1 parameter constructor - V3 : T1'Ref := new T1; - V4 : T1'Ref := new T1 (42); - V5 : T2; -- NOT OK, there's no parameterless constructor - -A constructor of a child class always call its parent constructor before its own. It's either implicit (parameterless constructor) -or explicit. When explicit, it's provided through the Super aspect, specified on the body of the constructor, for example: - -.. code-block:: ada - - package P is - type T1 is class record - procedure T1 (Self : in out T1; V : Integer); - end T1; - - type T2 is new T1 with record - procedure T2 (Self : in out T1); - end T2; - end P; - - package body P is - type body T1 is class record - procedure T1 (Self : in out T1; V : Integer) is - begin - null; - end T1; - end T1; - - type body T2 is new T1 with record - procedure T2 (Self : in out T1) - with Super (0) -- special notation for calling the super constructor. First parameter is omitted - is - null; - end T2; - end T2; - -Destructors are implicitely called in sequence - the parent destructor is always called after its child. - -Copy constructor ----------------- - -Copy constructors are available to both class records and simple records. - -A special constructor, a copy constructor, can be identified with the "Copy" aspect. It's called upon the copy of an object (for -example, an assignment). It can also be called explicitely, and needs to call parent constructors. It needs to be a constructor with -two values of the same type. For example: - -.. code-block:: ada - - package P is - type T1 is class record - procedure T1 (Self : in out T1; Source : T1) - with Copy; - end T1; - -Note that to the difference of the Adjust function of controlled types, the copy constructor is responsible to do the actual copy from -Source to Self - it's not done ahead of time. - -If not specified, a default constructor is automatically generated. It componses - it will will call the parent copy constructor, -then copy field by field its additional components, calling component constructors if necessary. - - -Constructors and discriminants ------------------------------- - -These considerations are applicatble to both class records and simple records. - -When combined with discriminants, the discriminants values must be provided before the constructor values: - -.. code-block:: ada - - package P is - type T1 (L : Integer) is class record - procedure T1 (Self : in out T1); - procedure T1 (Self : in out T1; V : Integer); - - X : Some_Array (1 .. L); - end T1; - end P; - - V : T1 (10)(10); - -Note that the above can create ambiguous situations in corner cases, which are to be detected at compile time and resolved -through e.g. naming: - -.. code-block:: ada - - package P is - type T1 (L : Integer := 0) is class record - procedure T1 (Self : in out T1); - procedure T1 (Self : in out T1; V : Integer); - - case L is - when 0 => - X : Integer; - when others => - null; - end case; - end T1; - end P; - - V : T1 (10); -- Illegal, is this a discriminant with default constructor or a default discriminant with a constructor? - V2 : T1 (L => 10); -- Legal - V3 : T1 (V => 10); -- Legal - -Constructors default values and and aggregates ----------------------------------------------- - -These considerations are applicatble to both class records and simple records. - -Aggregates are still possible with class records. The order of evaluation for fields is: - -- their default value. Always computed -- the constructor -- any value from the aggregate - -The rationale for this order is to go from the generic to the specific. This is a departure from the existing Ada model where -aggregate override default initialization. Under this model, there is no more way to override default initialization for records - -if initialization should only be done some times and not others, it is to be done in the constructor (which is available for records -and class records). With class records, aggreates are a shortcut for field by field assignment after iniitalization. - -For example: - -.. code-block:: ada - - package P is - type T1 is class record - procedure T1 (Self : in out T1; Val : Integer); - - Y : Integer := 0; - end T1; - end P; - - package body P is - type body T1 is class record - procedure T1 (Self : in out T1; Val : Integer) is - begin - -- Y is 0 here - Self.Y := Val; - -- Y is val here - end T1; - end T1; - - V : T1 := (Y => 2); -- V.Y = 2 - V2 : T1'Ref := new T1 (1)'(Y => 2); -- V.Y = 2 - end P; - -Note that it's of course always possible (and useful) to use an aggreate within a constructor, still as a shortcut to field by -field assignment: - -.. code-block:: ada - - package P is - type T1 is class record - procedure T1 (Self : in out T1); - - A, B, C : Integer; - end T1; - end P; - - package body P is - type body T1 is class record - procedure T1 (Self : in out T1) is - begin - Self := (1, 2, 3); - end T1; - end T1; - - V : T1 := (A => 99, others => <>); -- V.A = 99, V.B = 2, V.C = 3. - end P; - - -Final fields ------------- - -Final fields are available to both class records and simple records. - -Class record support constant fields, which are field which value cannot be changed after the constructor call, not even during -aggregate which is considered as a shortcut for assignment. For example: - -.. code-block:: ada - - package P is - type T1 is class record - procedure T1 (Self : in out T1; Val : Integer); - - Y : final Integer := 0; - end T1; - end P; - - package body P is - type body T1 is class record - procedure T1 (Self : in out T1; Val : Integer) is - begin - -- Y is 0 here - Self.Y := Val; -- Legal - -- Y is val here - end T1; - end T1; - - V : T1 := (Y => 2); -- Illegal, Y is final - end P; - -Final classes -------------- - -class record also implement the concept of final classes, which is a class not deriveable. There are two advantages of final classes: - -- In terms of design, this makes it clear that this class is not intended to be derived. It's often the case where derivation is - used just to have a class in a given framework but isn't prepared to be itself modified. -- A very significant one: a final class is effectively a definite type. As a result, it can be stored on the stack or as a component, - calls to a view of a final class are not dispatching (the target is statically known). - -.. code-block:: ada - - package P is - type T1 is class record - null; - end T1; - - type T2 is final new T1 with record - null; - end T2; - - type T3 is new T2 with record -- Illegal, T2 is final - null; - end T3; - - V1 : T1; -- Illegal, T1 is indefinite - V2 : T2; -- Legal, T2 is final. - end P; - - -Operators and exotic primitives -------------------------------- - -Class record do not provide dispatching on multiple parameters, on parameters other than the first, or dispatching on results. -If you declare primitives with references to the type other than the first parameter, they will not be used for controlling. This -means that parameters that are the same at top level may differ when deriving: - -Operators can be declared as primitives: - -.. code-block:: ada - - package P is - type T1 is class record - procedure "=" (Left, Right : T1); - end T1; - - type T2 is new T1 with record - procedure "=" (Left : T2; Right : T1); - end T1; - end P; - -Body-only classes ------------------ - -One limitation of the tagged type to lift under this system is the ability to declare a class only in the body of a package. This -should be legal under this new system. - -.. code-block:: ada - - package body P is - type T2 is class record - F : Integer; - procedure P (Self : in out T2; V : Integer); - end T2; - - type body T2 is class record - procedure P (Self : in out T2; V : Integer) is - begin - Self.F := V; - end P; - end T2; - end P; - -Coextensions ------------- - -Under the current model, coextensions are replaced by constructors (it's possible to mandate an object to be used in the construction -of the class) and destructors (that same object can always be destroyed in the destructor). There is no way to create a coextension -on a class record. - -Tagged types ------------- - -Under this proposal, tagged records and class record can co-exist, as they live in completely distinct hierarchies. Howeer, tagged -types should only be considered for a comptability and migration standpoint. Most tagged record use cases should be relatively easy -to move to class records. - -Reference-level explanation -=========================== - - -Rationale and alternatives -========================== - - -Drawbacks -========= - - -Prior art -========= - -This proposal is heavily influence by C++, C# and Java (which arguably have influenced one another quite a lot). - -Unresolved questions -==================== - -This proposal relies on the unified record syntax proposal, and will need to be updated in light of potential -revamped access model and finalization models. - -A number of the capabilities of the standard run-time library rely today on tagged type. A thorough review should be made to -identify which should be removed (e.g. controlled type), which should be migrated, and which can actually be implemented without -relying on classes altogether (things such as streams or pools come to mind). The removal of coextensions types also supposes a -different model for general iteration, as it currently relies on user-defined references (implemented through coextensions). - -Future possibilities -==================== - -One important aspect of Ada is to allow data to be as static as possible. OOP typically requires the use of pointer. The Max_Size -proposal (https://github.com/QuentinOchem/ada-spark-rfcs/blob/max_size/considered/max_size.rst) is a independent proposal to allow -polymorphic object residing in automatic memory section such as fields or stack. - -Some of the notations introduced could be extended to other types, such as protected or tasks type. - -The "with private;" notation should also be extended to nested packages, allowing to differenciate to nest the private part of a -nested package in the private part of its enclosing package. - -The scoped primitive notation is currently specific to record types. It could be extended to all types (which would have the effect -or re-enabling the possibility to complete a simple private type by a record). From 4080638d0d12eedacffde08c232c77faec9d6209 Mon Sep 17 00:00:00 2001 From: Quentin Ochem Date: Tue, 10 Dec 2024 13:59:09 -0500 Subject: [PATCH 20/20] added considerations on component-wise operations --- considered/rfc-ghost-fields-and-parameters.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/considered/rfc-ghost-fields-and-parameters.md b/considered/rfc-ghost-fields-and-parameters.md index 1e1740da..3f5762fb 100644 --- a/considered/rfc-ghost-fields-and-parameters.md +++ b/considered/rfc-ghost-fields-and-parameters.md @@ -102,6 +102,13 @@ Note that one of the consequence of using ghost fields is that overlays and unchecked conversion may fail at compile time. This is expected, and is a known limitation of ghost fields. +Ghost fields are copied under assignment. Other component-wise operations are +not impacted by ghostfield. For example, equality yields the same result even +if ghost fields values are different, stream attributes do not stream ghost +value, put_line does not output ghost fields, etc. This preserves the property +that ghost code does not influence non-ghost code (with the exception of 'Size +where the solution is described above). + Ghost Parameters ----------------