From d89a40ff806db5b94861e26ec91f37db367fa1f7 Mon Sep 17 00:00:00 2001 From: Quentin Ochem Date: Thu, 30 May 2024 15:28:09 -0400 Subject: [PATCH 1/4] isolated file from the oop branch --- considered/rfc-oop-dispatching.rst | 97 ++++++++++++++++++++++++++++++ 1 file changed, 97 insertions(+) create mode 100644 considered/rfc-oop-dispatching.rst diff --git a/considered/rfc-oop-dispatching.rst b/considered/rfc-oop-dispatching.rst new file mode 100644 index 00000000..4fe519e0 --- /dev/null +++ b/considered/rfc-oop-dispatching.rst @@ -0,0 +1,97 @@ +- Feature Name: Standard OOP model +- Start Date: May 5th 2020 +- RFC PR: +- RFC Issue: + +Summary +======= + +Motivation +========== + +Guide-level explanation +======================= + +Dispatching and Class Wide Views +-------------------------------- + +A new library / partition level pragma is introduced, Default_Dispatching_Calls. +When active, calls to primitives are dispatching (when the tag can be +determined statically, the compiler may optimize the call to be static) unless +explicitelly marked as static (for example, through 'Super). E.g: + +.. code-block:: ada + + pragma Default_Dispatching_Calls (On); + + type Root is tagged null record; + + procedure P (Self : in out Root); + + type Child is new Root with null record; + + procedure P (Self : in out Root); + + type A_Root is access all Root; + + procedure P (V : in out Root) is + begin + V.P2; -- Dispatching + end P; + + V : A_Root := new Child; + + V.P; -- dispatching + +Default_Dispatching_Calls is On by default on new version of the language. + +Reference-level explanation +=========================== + + +Rationale and alternatives +========================== + +It is a potential vulnerability not to call an overriden primitive. This may +lead to an object to be in an state that has not been anticipated, in particular +when the role of the overriden primitive is to keep the state of the derived +object consistant. It's also commonly the case in most OOP languages that +dispatching is the default expected behavior and non dispatching the exception. + +This also fix a common confusion in Ada, where the dispatching parameter of A +primitive is itself non-dispatching and requires so-called redispatching. The +following code becomes then much more natural to write: + +.. code-block:: ada + + package P is + type T1 is tagged record + procedure P (Self : in out T1); + + procedure P2 (Self : in out T1); + end T1; + end P; + + package P is + type body T1 is tagged record + procedure P (Self : in out T1) is + begin + Self.P2; -- Dispatching + end P; + end T1; + end P; + + +Drawbacks +========= + + +Prior art +========= + + +Unresolved questions +==================== + +Future possibilities +==================== From 8b4ad2607245f00e3ca09db37e1fa176c9c2f7f1 Mon Sep 17 00:00:00 2001 From: Quentin Ochem Date: Fri, 31 May 2024 13:36:32 -0400 Subject: [PATCH 2/4] updated following comments --- considered/rfc-oop-dispatching.rst | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/considered/rfc-oop-dispatching.rst b/considered/rfc-oop-dispatching.rst index 4fe519e0..d9cd1034 100644 --- a/considered/rfc-oop-dispatching.rst +++ b/considered/rfc-oop-dispatching.rst @@ -18,7 +18,7 @@ Dispatching and Class Wide Views A new library / partition level pragma is introduced, Default_Dispatching_Calls. When active, calls to primitives are dispatching (when the tag can be determined statically, the compiler may optimize the call to be static) unless -explicitelly marked as static (for example, through 'Super). E.g: +explicitely marked as static (for example, through 'Super). E.g: .. code-block:: ada @@ -58,27 +58,26 @@ when the role of the overriden primitive is to keep the state of the derived object consistant. It's also commonly the case in most OOP languages that dispatching is the default expected behavior and non dispatching the exception. -This also fix a common confusion in Ada, where the dispatching parameter of A +This also fixes a common confusion in Ada, where the dispatching parameter of A primitive is itself non-dispatching and requires so-called redispatching. The -following code becomes then much more natural to write: +following code illustrates the improvement: .. code-block:: ada package P is - type T1 is tagged record - procedure P (Self : in out T1); + type T1 is tagged null record; - procedure P2 (Self : in out T1); - end T1; + procedure P (Self : in out T1); + + procedure P2 (Self : in out T1); end P; package P is - type body T1 is tagged record - procedure P (Self : in out T1) is - begin - Self.P2; -- Dispatching - end P; - end T1; + procedure P (Self : in out T1) is + begin + T1'Class (Self).P2; -- Dispatching in all cases + Self.P2; -- Only dispatching with Default_Dispatching_Calls (On) + end P; end P; From 2a708f858dbdb90347b0dfeafa07983139f49331 Mon Sep 17 00:00:00 2001 From: Quentin Ochem Date: Mon, 24 Feb 2025 10:34:20 -0500 Subject: [PATCH 3/4] added considerations on multi parameters and returned types --- considered/rfc-oop-dispatching.rst | 53 ++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/considered/rfc-oop-dispatching.rst b/considered/rfc-oop-dispatching.rst index d9cd1034..78bfc430 100644 --- a/considered/rfc-oop-dispatching.rst +++ b/considered/rfc-oop-dispatching.rst @@ -45,6 +45,59 @@ explicitely marked as static (for example, through 'Super). E.g: Default_Dispatching_Calls is On by default on new version of the language. +Multi-Parameter Dispatching +--------------------------- + +Under this new pragma Default_Dispatching_Calls, primitives with more than +one controlling parameter behave in the following way: + +- If they're called on a dispatching call, then everything works as if all + controlling parameters were converted to class wide view. And indeed, in that + case, we would have dynamic tag check as you do today in Ada. + +- If it's a non-dispatching call, today that's through 'Super, then you will + statically select the primitive, and will need to be able to statically check + the static type of all parameters. + +For example: + +.. code-block:: ada + + type Root is tagged null record; + + procedure Prim (A, B : Root); + + type Child is new Root with null record; + + overriding procedure Prim (A, B : Child); + + R1, R2 : Root; + C1, C2 : Child; + + C1'Super.Prim (R2); -- static, legal + C1'Super.Prim (C2'Super); -- static, legal + C1'Super.Prim (C2); -- illegal, C2 is of the wrong type + +Note that this is a problem when integrating with current Ada, pedantic Flare +does not support multi-parameter dispatching. + +Dispactching on Returned Types +------------------------------ + +A tag indeterminate disatching call is illegal (as it is the case today). For +example: + +.. code-block:: ada + + pragma Default_Dispatching_Calls; + type T is tagged ... ; + function Make return T; -- primitive + Obj1 : T'Class := ... + Obj2 : T'Class := Make; -- illegal + begin + Obj1 := Make; -- legal; use Obj1'Tag to dispatch + + Reference-level explanation =========================== From c50aefbe7f1d205ffe27e4700adb7d5a7de6adad Mon Sep 17 00:00:00 2001 From: Quentin Ochem Date: Mon, 31 Mar 2025 16:21:06 -0400 Subject: [PATCH 4/4] added consideration on access types and precisions --- considered/rfc-oop-dispatching.rst | 115 ++++++++++++++++++++++++++++- 1 file changed, 114 insertions(+), 1 deletion(-) diff --git a/considered/rfc-oop-dispatching.rst b/considered/rfc-oop-dispatching.rst index 78bfc430..c119c191 100644 --- a/considered/rfc-oop-dispatching.rst +++ b/considered/rfc-oop-dispatching.rst @@ -45,6 +45,53 @@ explicitely marked as static (for example, through 'Super). E.g: Default_Dispatching_Calls is On by default on new version of the language. +Note that the decision to dispatch or not is made at the point of the call, not +the subprogram declaration. For example: + +.. code-block:: ada + + package A is + + pragma Default_Dispatching_Calls (Off); + + type Root is tagged null record; + + procedure P (Self : in out Root); + + type Child is new Root with null record; + + procedure P (Self : in out Root); + + end A; + + package body A is + + [... some code ...] + + procedure Something (X : Root) is + begin + X.P; -- NOT Dispatching + end Something; + + [... some code ...] + + end A; + + package body B is + + pragma Default_Dispatching_Calls (On); + + [... some code ...] + + procedure Something_Else (X : Root) is + begin + X.P; -- Dispatching + end Something_Else; + + [... some code ...] + + end B; + Multi-Parameter Dispatching --------------------------- @@ -63,6 +110,8 @@ For example: .. code-block:: ada + pragma Default_Dispatching_Calls (On); + type Root is tagged null record; procedure Prim (A, B : Root); @@ -89,7 +138,7 @@ example: .. code-block:: ada - pragma Default_Dispatching_Calls; + pragma Default_Dispatching_Calls (On); type T is tagged ... ; function Make return T; -- primitive Obj1 : T'Class := ... @@ -98,6 +147,70 @@ example: Obj1 := Make; -- legal; use Obj1'Tag to dispatch +Access to Subprograms +--------------------- + +Access to dispatching primitives are dispatching if their access is taken in +a scope where Default_Dispatching_Calls is On. For example: +.. code-block:: ada + + package A is + + pragma Default_Dispatching_Calls (Off); + + type Root is tagged null record; + + procedure P (Self : in out Root); + + type Acc is access all procedure (Self : in out Root); + + A_D : Acc; + A_ND : Acc; + + end A; + + package body B is + + pragma Default_Dispatching_Calls (On); + + [... some code ...] + + A_D := A.P'Access; -- This will be dispatching. + + [... some code ...] + + end B; + + package body C is + + pragma Default_Dispatching_Calls (Off); + + [... some code ...] + + A_ND := A.P'Access; -- This will not be dispatching. + + [... some code ...] + + end C; + + package body D is + + [... some code ...] + + A_D.all (Obj); -- This dispatches + A_ND.all (Obj); -- This doesn't dispatches + + [... some code ...] + + end D; + +In other words, the decision on how a call to an access to subprogram behaves +is taken at the point where the access value is created (through 'Access). A +potential implementation could be to have a wrapper generated to provide a +different version of the subprogram depending on the context. Note that +this means that subsequent usage of the 'Access attribute may not yield the +same address, which is allowed. + Reference-level explanation ===========================