AI22-0147-2

!standard 3.4(17/2)                                    26-05-30  AI22-0147-2/03

!standard 3.4(18/3)

!standard 3.9.2(1/5)

!standard 3.9.2(2/3)

!standard 3.9.2(10/2)

!standard 6.1(13/2)

!standard 6.1(15/5)

!standard 6.1(21)

!standard 6.1(24/2)

!class Amendment 26-05-09

!status work item 26-05-09

!status received 26-05-09

!assigned author Niklas Holsti

!assigned author Tucker Taft

!submitter Tucker Taft

!priority Low

!difficulty Medium

!subject Controlling what is a controlling parameter

!summary

Use the abs reserved word on a formal parameter or result type to absolve it from being considered a controlling parameter/result and, if the subprogram is inherited upon type derivation, from changing to match the derived type.

!issue

There are situations where a particular subprogram should not be inherited by derived types, or when derived, one or more parameter or result types should not change to match the derived type.

Should we provide a mechanism to indicate that a formal parameter or result of a function is not to be considered a controlling/parameter result? (Yes.)

!recommendation

We recommend allowing the insertion of the reserved word abs[a][b][c][d][e][f] before the subtype of a formal parameter or the result of a subprogram, to prevent or absolve that parameter/result from being considered a "controlling" parameter or result, or in the untagged case, from causing the subprogram to be inherited for derived types.

If some other formal parameters, or the result, are of the same type, but are not declared with abs, the subprogram is still inherited on derivation from this type, but the parameters, or the result, of this type, but with abs, remain as the parent type in the profile of the inherited subprogram — they do not change to match the derived type, and they are still abs.

Adding abs to a formal parameter or result does not introduce a new type, nor does it affect how the parameter or result is passed, nor does it change the set of primitive operations of the type or the conformance rules for subprogram profiles.

For example, when computing with physical quantities with dimensions, we could have:

type Real is digits 15;
function Scale (Quantity, Factor : Real) return Real is
 
(Quantity * Factor);
type Length is new Real;

 

Type Length inherits the Scale function, with all occurrences of Real changed to Length, so the profile of the inherited function is:

function Scale (Quantity, Factor : Length) return Length;

 

However, we do not want to scale a Length with another Length, but with a dimensionless number, a Real. With the recommended addition to Ada, we can fix the Factor parameter as type Real:

function Scale (Quantity : Real; Factor : abs Real) return Real …

 

Now the profile of the inherited operation is:

function Scale (Quantity : Length; Factor : abs Real) return Length;[g][h]

 

Similarly[i][j][k], we could have a Ratio function, where we want the result to be dimensionless although the parameters are type Length. We can then fix the result type:

function Ratio (Num, Den : Real  ) return abs Real is (Num / Den);[l][m]

The inherited Ratio function takes two Length operands and returns a Real result (with abs on the result).[n]

For an example of the tagged and dispatching case, given a case such as the following:

type Holder_Storage_Pool is new Root_Storage_Pool_With_Subpools ...
type Holder is tagged ...
function Create_Subpool (HSP : Holder_Storage_Pool; H : Holder)
  return Subpool_Handle;

 

we would be violating RM 3.9.2(12) which precludes having a subprogram that is a dispatching operation of two tagged types. To avoid this problem, we could add the abs reserved word to one of the formal parameter types to indicate that this parameter is not a controlling parameter. In this case, we choose to do that on the storage pool type:

type Holder_Storage_Pool is new Root_Storage_Pool_With_Subpools ...
type Holder is tagged ...
function Create_Subpool (HSP : abs Holder_Storage_Pool; H : Holder)
 
return Subpool_Handle;[o][p]

 

!wording

Modify 3.4(17/2):

For each user-defined primitive subprogram (other than a user-defined equality operator — see below) of the parent type or of a progenitor type that already exists at the place of the derived_type_definition{ and is not absolved from derivation for the parent type or progenitor type (see 6.1), as the case may be}, there exists a corresponding inherited primitive subprogram of the derived type with the same defining name. Primitive user-defined equality operators of the parent type and any progenitor types are also inherited by the derived type, except when {absolved from derivation as above, or when }the derived type is a nonlimited record extension, and the inherited operator would have a profile that is type conformant with the profile of the corresponding predefined equality operator; in this case, the user-defined equality operator is not inherited, but is rather incorporated into the implementation of the predefined equality operator of the record extension (see 4.5.2).

Modify 3.4(18/3):

The profile of an inherited subprogram (including an inherited enumeration literal) is obtained from the profile of the corresponding (user-defined) primitive subprogram of the parent or progenitor type, after systematic replacement of each subtype of its profile (see 6.1) that is of the parent or progenitor type, other than those subtypes {specified for formal parameters or results which are absolved from derivation (see 6.1) and those subtypes }found in the designated profile of an access_definition, with a corresponding subtype of the derived type. For a given subtype of the parent or progenitor type, the corresponding subtype of the derived type is defined as follows:

Modify 3.9.2(1/5):

The primitive subprograms of a tagged type, the subprograms declared by formal_abstract_subprogram_declarations, the Put_Image attribute (see 4.10) of a specific tagged type, and the stream attributes of a specific tagged type that are available (see 13.13.2) at the end of the declaration list where the type is declared are called dispatching operations{ unless the subprogram is absolved from derivation for this tagged type (see 6.1). [Redundant: A subprogram can be a primitive subprogram of more than one tagged type, but can be a dispatching operation for at most one of those types]}. [Redundant: A dispatching operation can be called using a statically determined controlling tag, in which case the body to be executed is determined at compile time. Alternatively, the controlling tag can be dynamically determined, in which case the call dispatches to a body that is determined at run time;] such a call is termed a dispatching call. [Redundant: As explained below, the properties of the operands and the context of a particular call on a dispatching operation determine how the controlling tag is determined, and hence whether or not the call is a dispatching call. Run-time polymorphism is achieved when a dispatching operation is called by a dispatching call.]

Modify 3.9.2(2/3):

A call on a dispatching operation is a call whose name or prefix denotes the declaration of a dispatching operation. A controlling operand in a call on a dispatching operation of a tagged type T is one whose corresponding formal parameter {is not absolved from derivation and }is of type T or is of an anonymous access type with designated type T; the corresponding formal parameter is called a controlling formal parameter. If the controlling formal parameter is an access parameter, the controlling operand is the object designated by the actual parameter, rather than the actual parameter itself. If the call is to a (primitive) function with result type T {where the result is not absolved from derivation }(a function with a controlling result), then the call has a controlling result — the context of the call can control the dispatching. Similarly, if the call is to a function with an access result type designating T {where the result is not absolved from derivation }(a function with a controlling access result), then the call has a controlling access result, and the context can similarly control dispatching.

Modify 3.9.2(10/2), TBC:[q][r]

In the declaration of a dispatching operation of a tagged type, everywhere a subtype of the tagged type appears as a subtype of the profile (see 6.2), {except where it specifies the subtype of a parameter or result that is absolved from derivation, }it shall statically match the first subtype of the tagged type. If the dispatching operation overrides an inherited subprogram, it shall be subtype conformant with the inherited subprogram. The convention of an inherited dispatching operation is the convention of the corresponding primitive operation of the parent or progenitor type. The default convention of a dispatching operation that overrides an inherited primitive operation is the convention of the inherited operation; if the operation overrides multiple inherited operations, then they shall all have the same convention. An explicitly declared dispatching operation shall not be of convention Intrinsic.

Replace 6.1(13/2) with:

parameter_and_result_profile ::=

             [formal_part] return [abs] [null_exclusion] subtype_mark

   | [formal_part] return [abs] access_definition

Replace 6.1(15/5) with:

parameter_specification ::=

     defining_identifier_list : [abs] [aliased] mode [null_exclusion] subtype_mark

[:= default_expression] [aspect_specification]

   | defining_identifier_list : [abs] access_definition

[:= default_expression] [aspect_specification]

Add after 6.1(21):

If a parameter_specification or a parameter_and_result_profile contains the reserved word abs followed by an access_definition, that access_definition shall denote an access-to-object type.

Add after 6.1(24/2):

If a parameter_specification includes the reserved word abs, those formal parameters are absolved from derivation. If a parameter_and_result_profile includes the reserved word abs after the reserved word return, the result of the subprogram is absolved from derivation. A subprogram that operates on a type is absolved from derivation for that type if:

!discussion

This AI has the same aims as its predecessor, AI22-0147-1, and some text and examples are copied from there, with modification. Please refer to the !discussion and !appendix in the predecessor.

The difference is that this AI implements the aims by marking the formal parameters and results that should have special treatment (here, be absolved from derivation) without introducing new types, while AI22-0147-1 introduces a whole new category of types, the “rooted types”, and the attribute T’Rooted to denote the rooted type derived from a specific type T. Formal parameters and results of a rooted type are then (in our terms) absolved from derivation. The effect on operation inheritance and dispatching is achieved by the normal typing rules (with minor additions to implicitly convert between T and T’Rooted). But at the time of writing, AI22-0147-1 does not prevent use of rooted types in other ways, for example for declaring stand-alone objects or components, which seems useless and possibly confusing.

This use of abs on formal parameters and function results was first suggested by Jean-Pierre Rosen. The choice has been opposed, with use proposed as a better alternative.[s][t][u][v][w][x] To help decide, here are a number of possible parameter specifications with the two keywords, assuming that they chosen keyword is placed at the front, as in the modified syntax above:

P : abs T

P : abs in out T[y][z]

P : abs aliased T

P : abs aliased in out T

P : abs access T

P : abs not null access T

P : use T

P : use in out T

P : use aliased T

P : use aliased in out T

P : use access T

P : use not null access T

And here are examples of return-type specifications:

return abs T

return abs not null T

return abs access T

return abs not null access T

return use T

return use not null T

return use access T

return use not null access T

 

Note that the special treatment described in 3.4(16.b) for some predefined operations on derivation could be defined with absolved parameters/result (or with the rooted types in AI22-0147-1): in the Static Semantics section of 4.5.2, all the “return Boolean” could be “return abs Boolean”, and 4.5.6(8) could be

function “**” (Left : T; Right : abs Natural) return T;

 

This AI does not change the rules for conformance of subprogram profiles in 6.3.1. Since those rules (understandably) do not mention abs, two conformant profiles remain conformant if abs is inserted in some places, even if abs occurs in different places in the two profiles. To achieve the same result in AI22-0147-1 the conformance rules would have to be changed to equate T and T’Rooted.

For example, the following is illegal because the profiles conform, and all calls would be ambiguous:

function Scale (Quantity, Factor : Real) return Real;

function Scale (Quantity: Real; Factor : abs Real) return Real;

 

This AI is yet incomplete at least in the following respects[aa]:

!example

Here are some examples of use of the abs reserved word to absolve parameters or results from derivation:

type Bit_Set is new Set with ...
function Make_Bit_Set (Num_Bits : Positive) return abs Bit_Set;

 

This constructor function is not to be inherited.  At a minimum, it would probably want to have a different name in any derived type.

type A is tagged ...
type B is new Some_Interface with ...
procedure Process (X : in out A; Y : abs B);[ac][ad][ae][af][ag]

 

In this case, type B is visibly extended from some useful progenitor interface, but other than that, there was no reason for B to be tagged, and making the Process operation dispatching on B and inherited by B's descendants would not make sense.

!ACATS test

This test would probably need both B tests and C tests to verify that inheritance does not happen when it is not supposed to happen and that the types of derivation-absolved parameters and results are unchanged in the inherited subprogram.

!appendix

(The start of this text was copied from AI22-0147-1, with the examples updated to use the proposed syntax.)

See ARG GitHub issue #125.


 

From: Christoph Grein

Sent: November 18, 2025

I do not see the difference to ‘Class.

 function Create_Subpool

      (SP : My_Storage_Pool'Class; Storage : My_Holder) return Subpool_Handle;

 function Create_Subpool

      (SP : My_Storage_Pool; Storage : My_Holder'Class) return Subpool_Handle;

versus

 function Create_Subpool

      (SP : abs My_Storage_Pool; Storage : My_Holder) return Subpool_Handle;

 function Create_Subpool

      (SP : My_Storage_Pool; Storage : abs My_Holder) return Subpool_Handle;


 

From: Tucker Taft

Sent: December 5, 2025

When a formal parameter is of a class-wide type, it means the actual parameter might be of any descendent of the type.  For abs My_Holder, we are restricting it to the type My_Holder, but like for a class-wide type, it does not cause the operation to be a primitive of My_Holder.


 

[a]The suggestion has merit - but I strongly object to the abs keyword. (I can see the intended abbr.)

abs has always meant the absolute value, there is no other interpretation; using it as a modifier for a parameter (especially for a numeric one as in the example) is horribly misleading. So I propose alternatives.

1. Currently, for tagged types, T'Class is used to absolve the parameter from taking part in the inheritance. This attribute is not available for other types, but wouldn't its introduction for this purpose invoke less confusion?

This is just an extension of current use.

2. We have other reserved words with many different uses, sometimes with tormented or far-fetched interpretation - the keyword use comes to mind (pun intended).

function F (X: Num; Y: use Num) return Num;

effectively says what is intended: After derivation, Y still has the type Num.

type New_Num is new Num;

function F (X: New_Num; Y: Num) return New_Num;

[Note that after derivation, the keyword is no longer needed.]

[b]We brainstormed for alternatives, and continue to do so.  "use" is an interesting suggestion.  We have adopted "use" for specifying the default for a generic formal type, and elsewhere in the syntax "use" precedes the name of a type, so this proposed usage would be somewhat consistent with those existing usages (so to speak!).

At least in English, "abs." is often an abbreviation for "absent" (when voting or when late for a class at school).  So perhaps "absent" would be a more familiar de-abbreviation of "abs".

[c]Other possibilities might be: at, do, of, rem (for remains unchanged), with (probably used too much already), limited, protected, separate. 

I'll admit that in the examples, "abs Real" looks particularly confusing, wherea "use Real" does look better.

[d]rem is also an operator like abs.

Imho, use is unbeatable.

[e]I still prefer "abs" to "use", despite the above arguments. I don't like "use" for two reasons: first, it is an imperative, which jars me when used in a parameter declaration. Second, the fact that it already has some meanings when applied to types, but different meanings from the meaning proposed here, easily leads to confusion. For "abs", I think it is an advantage that it has no previous meaning in connection with types or parameters, so there is nothing to interfere with making it mean /something/ in this entirely new context.

I am not as stuck on the de-abbreviation of "abs", it can be "absolve" or "absent" or "absolute", or something else, perhaps even something not starting with "abs".

[f]Could you provide a number of examples of various alternatives?  I believe it is only through examples that it is possible to understand the impact of a syntactic choice like this.

[g]Christoph added a comment here, but it ended up inline, making a mess:

function Scale (Quantity : Length; Factor : Real) return Length;

After derivation, the keyword is no longer needed, its only purpose is gone, so drop it!

[h]Personally, I think that would be even more confusing. It's harmless, and the less change made by derivation, the better.

[i]Would it be allowed to have to similar declarations, one with and the other one without abs?

F.e.:

function Scale (Quantity : Length; Factor : abs Real) return Length;

and 

function Scale (Quantity : Length; Factor : Real) return Length;

Of course, all calls to Scale would be ambiguous (and disallowed), but once the type is derived, it would result in two different profiles, and hence acceptable.

[j]I will first assume that the question is not how you posed it, but with Real instead of Length (that is, before deriving Length as new Real).

As I understand AI22-0147-1, those declarations would be allowed because T and T'Rooted are different types, but calls would be ambiguous because of the implicit conversions from T to T'Rooted and vice versa. So let's assume the same for AI22-0147-2 (although my preference is the opposite, to forbid such too-similar declarations).

If we now derive Length is new Real, we get two inherited functions for Length, one with Factor : abs Real and one with Factor : Length, so no ambiguity.

Returning to the question as you posed it, we can derive either Distance is new Length, or More_Real is new Real. In the former case, we get two inherited functions for Distance, but they are still ambiguous because Factor is still abs Real in one, and plain Real in the other. In the latter case, we get only one inherited function for More_Real, where Factor is More_Real. The Scale function with parameters (Quantity : Length; Factor : abs Real) is absolved from derivation for type Real, so it does not produce an inherited function for More_Real.

This is connected to the question of whether two profiles that differ only in the presence or absence of abs (or in T vs T'Rooted) should be conformant. I don't have a fixed opinion on that, but perhaps a preference to ignore abs when deciding conformance.

[k]Yes, my mistake, I meant Real, not Length

[l]A similar comment from Christoph:

function Ratio (Num, Den : Length) return Real is (Num / Den);

Same as above, drop the keyword after derivation.

[m]Tuck doesn't even show the result after derivation, and I don't think it helps much to do so.

[n]We also suggested illustrating the similar situation with the result of the relational operators on the Boolean type, and the exponent operand of the exponentiation operator on the Integer type, which remain the same type upon derivation.

[o]Christoph had mistakenly added some text directly here which was intended to be a question. I've moved it here:

function Create_Subpool (HSP : Holder_Storage_Pool’Class; H : Holder)

return Subpool_Handle;

Is there a deep intrinsic difference hidden between the two declarations?

[p]I think the answer is yes: A classwide type matches all types rooted at the prefix (Holder_Storage_Pool), while an "abs" type only matches Holder_Storage_Pool exactly. That's true without the "abs" as well, but because the type is inherited, it may not look that way from a usage perspective (dispatching calls also match everything).

[q]What does this mean??

[r]It means "to be confirmed" and means that I am not sure if this paragraph should be changed in this way, as also noted in the !discussion.

[s]I would like to seriously consider the alternative of using the reserved word "at" instead.  The only current uses of "at" are in the obsolescent annex J, so there is no danger of confusion!  "At" could convey that the parameter or result type is "at" a particular point in the hierarchy, and doesn't change.  It could also be treated as an abbreviation for "Always the Type ...". ;-)

Here are examples:

P : at T

P : at in out T

P : at aliased T

P : at aliased in out T

P : at access T

P : at not null access T

return at T

return at not null T

return at access T

return at not null access T

[t]at is also used in a component_clause 13.5.1.

I'm not sure it is a better choice than use; in any case it's better than abs.

[u]> at is also used in a component_clause 13.5.1.

Good point.  In any case, "at" is used very little, so less chance of any sort of collision syntactically or semantically.

[v]I think "at" is better that "use", and I don't object to "at". Do you have suggestions for alternatives to "parameter absolved from derivation" if "at" is used? Perhaps something like "parameter of a pinned type"? Or just continue using the "absolved" wording?

[w]Here is one approach: we define the notion of an "inheritable primitive subprogram" in 3.2.3 which would require that at least one of its uses of the type in its profile is not "affixed" to the original type, where a type in a profile is "affixed" to the original type by inserting the "at" reserved word in the definition of the formal parameter or result.  In 3.4, only the "inheritable primitives" would be inherited as part of deriving from the parent/progenitor.

[x]Better than "affixed" would be "attached", with "at" as a legitimate abbreviation for "attached"!  So unless the parameter/result is attached to the original type, it can participate in inheritance.

[y]Christoph also commented on this incorrectly: Here's the whole comment (removed from the text).

Perhaps this is sophisticated or nitpicking, but I see a difference:

P : in T

P : in out T

P : aliased T

P : aliased in out T

P : access T

P : not null access T

All these are parameter modifiers, meaning they modify the treatment of the parameter P.

use (at, abs - whatever it will be named) is different: it is a type modifier, it modifies the treatment of the type T. Thus I plead for putting it directly before the type.

P : in use T

P : in out use T

P : aliased use T

P : aliased in out use T

P : access use T

P : not null access use T

[z]I agree with Christoph and would prefer to place the keyword directly before the type.

[aa]We need to consider the effect of this on aspect inheritance as well as for generic formal abstract subprograms. For aspects, the rules are designed so that a derived type is guaranteed to have the operation with the appropriate types (that won't happen if it is absolved from inheritance). Also, class-wide pre/post are designed to require operations that exist with the correct types (typically, via dispatching). Finally, generic abstract formal subprograms have to be a dispatching operation. Do those rules need adjustment??

[ab]That would only make sense for tagged types. There are no restrictions on abstract subprograms for untagged types (they mean something rather different).

[ac]Still need some examples showing the placement of the "abs" keyword.  All of the examples so far do not have explicit modes, "access" parameters, etc.  If you think the placement is still to be decided, please provide a set of examples of both, where either it comes immediately after the ":", or immediately before the subtype_mark.

[ad]Exemplum gratum:

function Scale (Quantity : Real; Factor : abs Real := abs ABC) return Real;

[ae]I added such examples in the !discussion, with both "abs" and "use". As for placing the keyword at the start, or immediately before the subtype_mark, I think at the start is good enough.

[af]Re the "exemplum gratum": I understand that this is meant to illustrate the confusion that could come from using "abs", since it could occur also in the default value expression. However, I have never used "abs" in a default value expression, so this example feels contrived.

[ag]For what its worth, I have yet to be convinced that any of these choices are obvious enough for the violence that they will do to the Ada model of inheritance/primitiveness. And indeed, I'm unconvinced that this whole idea is worth the pain (which seems to be considerable -- separating two concepts which have been linked since Ada 95 is very likely to be error prone.

Did I ask about the effect of this on the rules for class-wide preconditions and postconditions? On nonoverridable aspects? Both of those depend on the assumption that primitive subprograms are inherited.