AI22-0141-1

!standard 4.1.6(1/7)                              26-04-30  AI22-0141-1/06

!standard 4.1.6(5.1/4)

!standard 4.1.6(6.1/7)

!standard 4.2.1(6/5)

!standard 4.2.1(11/5)

!standard 4.3.5(2/5)

!standard 4.3.5(10/6)

!standard 4.3.5(12/5)

!standard 13.1.1(18.8/6)

!class binding interpretation 25-07-25

!status Revision-202Y  25-12-16

!status ARG Approved  9-0-0  25-12-11

!status work item 25-07-25

!status received 25-05-28

!assigned author Stephen Baird

!submitter Stephen Baird

!priority Medium

!difficulty Easy

!qualifier Omission

!subject Aspects unspecifiable for categories of types

!summary

This is only a presentation issue; a new term is introduced which allows us to simplify some existing rules, but these changes do not modify the semantics (static or dynamic) of the language.

!issue

In several places in the RM, we have rules saying that

To help factor out this redundancy, we define what it means for an aspect to be "unspecifiable" for a category of types and then replace existing rules with uses of that term.

!recommendation

The general approach taken here is to define a new term "unspecifiable" in 13.1.1 (following the example of the existing term "nonoverridable") and then replace the duplicated wording with uses of this new term. That is why the 13.1.1 changes are given first in the !wording section – we present the definition of the new term before describing where it is used.

!wording

Add after 13.1.1 (18.8/6): [still in the “Legality Rules” section]

Certain type-related aspects are defined to be unspecifiable for a given category of types, for instance, "unspecifiable for array types". If an aspect is defined to be unspecifiable for a category of types then that aspect shall not be specified for a type belonging to that category. Furthermore, the completion of a private type for which the aspect is specified shall not belong to that category. [Redundant: These rules are not enforced in the private part of an instance of a generic package; see 12.3.]

Modify 4.1.6(1/7):

Given a type T [, such that T is  neither an array type nor an access type,] the following type-related, operational aspects may be specified {(subject to the rules given below)}:

Add after 4.1.6(5.1/4):

The Constant_Indexing and Variable_Indexing aspects are unspecifiable for array types (see 13.1.1). These aspects shall not be specified for an access type.

Delete 4.1.6(6.1/7):

The full type for a private type that has at least one of the aspects Constant_Indexing or Variable_Indexing specified shall not be an array type.

Add after 4.2.1(6/5):

The Integer_Literal and Real_Literal aspects are unspecifiable for numeric types (see 13.1.1). The String_Literal aspect is unspecifiable for string types.

Delete 4.2.1(11/5):

The Integer_Literal or Real_Literal aspect shall not be specified for a type T if the full view of T is a numeric type. The String_Literal aspect shall not be specified for a type T if the full view of T is a string type.

Replace 4.3.5(2/5) with:

The following type-related operational aspect may be specified for any type[redundant:, other than an array type]:

Delete 4.3.5(10/6):

If the container type of an Aggregate aspect is a private type, the full type of the container type shall not be an array type.

Add after 4.3.5(12/5):

The Aggregate aspect is unspecifiable for array types (see 13.1.1).

!discussion

This AI was first created in a mistaken response to a perceived oversight in the RM which seemed to require a correction. It turned out that there was no problem that required a correction, but the wording cleanup associated with this AI still seemed desirable.


 

User-defined indexing aspects are NOT "unspecifiable for access types"; this is controversial. This is a case where seeing the completion of a private type can cause a construct that is legal if only the partial view is visible to become illegal. Further changes in this area are being considered, but are outside of the scope of this AI.


 

It would be nice to have an index entry that lists all the aspects to which this new term applies, along with an AARM note in 13.1.1 analogous to the AARM 13.1.1(18.f) comment about a similar index entry for nonoverridable aspects.[a] However, given the limitations of index entries, this isn’t possible, as we need to index three things together: “unspecificable”, the category for which it is unspecifiable, and the aspect. Just indexing “unspecifiable” and an aspect would be very confusing, as it implies something very different than the actual meaning.

!corrigendum 4.1.6(1/7)

@drepl

Given a type @i{T}, such that @i{T} is neither an array type nor an access type, the following type-related, operational aspects may be specified:

@dby

Given a type @i{T}, the following type-related, operational aspects may be specified (subject to the rules given below):

!corrigendum 4.1.6(5.1/4)

@dinsa

The Constant_Indexing and Variable_Indexing aspects are nonoverridable (see @ref{13.1.1}).

@dinst

The Constant_Indexing and Variable_Indexing aspects are unspecifiable for array types (see @ref{13.1.1}). These aspects shall not be specified for an access type.

!corrigendum 4.1.6(6/5)

!comment We have to undo the change of AI22-0136-1. There is no change here, we need this to force a conflict..

@drepl

If an ancestor of a type @i{T} is an indexable container type, then any explicit specification of the Constant_Indexing or Variable_Indexing aspects shall be confirming; that is, the specified @fa{name} shall match the inherited aspect (see @ref{13.1.1}).

@dby

If an ancestor of a type @i{T} is an indexable container type, then any explicit specification of the Constant_Indexing or Variable_Indexing aspects shall be confirming; that is, the specified @fa{name} shall match the inherited aspect (see @ref{13.1.1}).

!corrigendum 4.1.6(9/5)

!comment We have to undo the change of AI22-0136-1. We need this to force a conflict..

@drepl

In addition to the places where Legality Rules normally apply (see @ref{12.3}), these rules apply also in the private part of an instance of a generic unit.

@dby

In addition to the places where Legality Rules normally apply (see @ref{12.3}), this rule applies also in the private part of an instance of a generic unit.

!corrigendum 4.2.1(6/5)

@dinsa

User-defined literal aspects are nonoverridable (see @ref{13.1.1}).

@dinst

The Integer_Literal and Real_Literal aspects are unspecifiable for numeric types (see @ref{13.1.1}). The String_Literal aspect is unspecifiable for string types.

!corrigendum 4.2.1(11/5)

@ddel

The Integer_Literal or Real_Literal aspect shall not be specified for a type @i{T} if the full view of @i{T} is a numeric type. The String_Literal aspect shall not be specified for a type @i{T} if the

full view of @i{T} is a string type.

!corrigendum 4.3.5(2/5)

@drepl

For a type other than an array type, the following type-related operational aspect may be specified:

@dby

The following type-related operational aspect may be specified for any type, other than an array type:

!corrigendum 4.3.5(10/6)

@ddel

If the container type of an Aggregate aspect is a private type, the full type of the container type shall not be an array type.

!corrigendum 4.3.5(12/5)

@dinsa

The Aggregate aspect is nonoverridable (see @ref{13.1.1}).

@dinst

The Aggregate aspect is unspecifiable for array types (see @ref{13.1.1}).

!corrigendum 13.1.1(18.8/6)

@dinsa

In addition to the places where Legality Rules normally apply (see @ref{12.3}), these rules about nonoverridable aspects also apply in the private part of an instance of a generic unit.

@dinst

Certain type-related aspects are defined to be @i{unspecifiable} for a given category of types, for instance, "unspecifiable for array types". If an aspect is defined to be unspecifiable for a category of types then that aspect shall not be specified for a type belonging to that category. Furthermore, the completion of a private type for which the aspect is specified shall not belong to that category. These rules are not enforced in the private part of an instance of a generic package; see @ref{12.3}.

!example

No example is needed or possible for a semantics-preserving change.

!ACATS test

No test is needed or possible for a semantics-preserving change.

!appendix

Thanks to Randy and Tuck for their feedback in refining this proposal.


 

From: Randy Brukardt

Sent: Thursday, September 25, 2025  11:41 PM

Discussion with Tucker on AI22-0141-1 has made me realize that I don't buy the argument that he made to get AI22-0136-1 approved. It sounded legit at the time, but on further reflection, I think we were sold a bill of goods. Luckily, AI22-0141-1 as proposed will fix the problem (which is why we've been discussing this).

During the discussion of AI22-0136-1, we had discussed having a special rule allowing indexing on private types even when the full type is an access type (which does not allow indexing). The minutes say the following on the topic:

We never want user-defined indexing on an array type, because that would lead to confusion between the predefined indexing and the user-defined versions. Thus we make it illegal even when hidden. We wonder why the access type is different. That follows from the way prefixed views work (they can be used on private types completed by access types). Tucker makes the claim that private types are often completed by access types, but almost never by array types. Randy is dubious that a private type can be usefully completed with an access type; he's always ended up needing other data and having to wrap the access in a record. Others agree with Tucker. In any case, indexing won't work (just like prefixed notation won't work) when the full (access) type is visible. But it still will work for clients. The notion that more visibility is subtracting something is uncomfortable, but we already agreed to that for prefixed views and this is just sugar on top of that.

I now think this discussion focused on the wrong points, and almost nothing mentioned in it about private types completed by access types is relevant.

First of all, the decision to allow prefixed views on private types completed with access types is mostly about compatibility. There is no syntax to differentiate private types that allow prefixed views from those that do not (I had in fact proposed such syntax, but it was considered goofy), and as such there would be no way for clients to tell if it was allowed (and such a rule would be very privacy breaking). That would not make sense. The fact that capability disappears when the full type is visible is seriously uncomfortable, but there seems to be no alternative.

OTOH, indexing aspects are explicitly declared on a private type; there are none by default. So one can always tell if they are allowed. As such, there is no need to expand the seriously wonky disappearing capabilities. This is brand-new capability on such private types, there is no compatibility issue to worry about. Moreover, this is not really "just sugar" because of the need to declare these as opposed to the automatic appearance of prefixed views.

Secondly, if we did disallow indexing on private types completed by an access type, the workaround of wrapping the access value in a record is easy to apply (it only requires changes in the package itself, it has no effect on clients). The cost of applying this workaround in the rare case it is needed (see next item) is assuredly less than the mental cost of disappearing capabilities.

Thirdly, I do not believe that many new ADTs are going to be defined with a full type that is a bare access type. More detail is inappropriate here; I've written a blog entry on this topic -- read it at http://www.rrsoftware.com/html/blog/harmful.html. There is no reason to worry about things that are not going to exist.

There may be plenty of existing ADTs that use full types that are bare access types (according to Tucker), but those have significant flaws. Because many of the uses of a private type completed by an access type are very old or dubious (again, see the blog), I don't think people are likely to be touching them to add indexing anyway. Additionally, uses that are abstract references should not be given any indexing operations in the first place: such operations always belong on the referenced object, not associated with any reference (lest ambiguities arise in use). Moreover, many ADTs (private types) have no use for indexing in the first place (consider File_Type, one of Tucker's examples -- no one is defining any indexing operations for that type). For all of these reasons, I would be very surprised if many users would run into this limitation even in its strongest form.

Finally, and ironically, the loss of capability for prefixed views will discourage people from using access types in many contexts, and that will especially include full types for private types. Some people are going to get bit hard by that limitation, and they will learn to avoid access types (at least calls involving access objects) in response.

For all of these reasons, I see no important reason to make Ada less regular to avoid a restriction that few will bump into (other than Tucker, I suppose :-), and is simple to work around.


 

From:Tucker Taft

Sent: Monday, September 29, 2025  3:24 PM

I realize that some folks try to avoid using access types in almost every context.  However, I believe we should not go very far in imposing our own views of coding style on Ada programmers in general, particularly given that many Ada systems have been in use for years.  It is quite unlikely someone would want to make a change merely to satisfy some coding style imposed from outside.  

When we add language features that provide incremental improvements, it seems particularly important that those features can be used without having to alter aspects of an application that are working well as is.  It is true that you can wrap an access type in a record, but that might likely change parameter passing from by-copy to by reference, which can create subtle performance and aliasing changes, and will certainly require almost every line of the implementation of the abstraction to change.  As a data point, I surveyed several large libraries to which I have access, and I would say that about 25% of the private types were completed by access types.

It seems unwise for an attempt to "clean up" wording (and fix some subtle wording bugs), which is what AI22-0141-1 is trying to do, to also *intentionally* change the semantics of an approved AI (AI22-0136-1).  It seems OK to suggest a new AI that reopens the semantics of an approved AI, but a "wording cleanup" AI like AI22-0141-1 should not do so as a somewhat hidden side-effect.  The rationale presented for the original AI's allowance of adding indexing to a private type completed by an access type was explicit and clear and not hidden in any way, and in fact was mentioned in the !discussion and elsewhere.  So let's reopen the semantics of AI22-0136-1 if necessary, but let's not muddy up the discussion of a wording "cleanup" with a debate about the semantics of a different AI.


 

From: Randy Brukardt

Sent: Tuesday, September 30, 2025  12:12 AM

> It seems unwise for an attempt to "clean up" wording (and fix some

> subtle wording bugs), which is what AI22-0141-1 is trying to do, to

> also *intentionally* change the semantics of an approved AI (AI22-0136-1).

I agree with this sentiment in general, but in this particular case, one of the "subtle wording bugs" is caused by the wording that you are defending so strongly. Moreover, you deleted Steve's attempt to point this out as "confusing". (I put it back with a cleaned-up intro.)

Steve made the point that allowing private types completed by access types causes a situation much like the Goodenough trick. (All of these rules had such a problem before, but moving the others to a Legality Rule plugs it up.) Given the usual equivalence between regular private types and generic private types, that is a necessary problem anytime a private type loses capability on the full type: there is always an equivalent generic that restores the functionality.

I gave an example of this effect in the latest version of the AI.

It would be better to allow the aspects on all access types, even though the result necessarily will not work (as the equivalence will not result). I find that silly, but otherwise we have the break principles that we've held to for the entire lifetime of Ada (even when keeping those principles has been uncomfortable).

In any case, the access type rule in this case was one of the rules that we identified as having "subtle wording bugs", and the AI as written (AI22-0141-1/02) does not address those bugs at all for access types. So that makes this a completely appropriate topic for discussion in/about AI22-0141-1.

It occurs to me that the *best* solution here would be to separately define user-defined indexing for access types, specifically so that the "equivalence" was to something that actually resolved and was legal. (Arrays and access-to-array would remain illegal in the way Steve has proposed.) I can't think of a reason off-hand that we *have to* make user-defined indexing equivalent to a prefixed view; I think we did that because it simplified the description. But since it doesn't work for access types, perhaps a different definition for user-defined indexing on untagged types would be better. (Can't change the equivalence for tagged types, because of compatibility concerns, but there aren't any tagged access types.) That would be a lot more work, but also would resolve the conflict by eliminating the root problem (and then no restriction on access types other than access-to-array would be needed). (Yes, that would be a separate AI, but we would need to do it first before we could approve AI22-0141-1, as 141 needs to address the problems with access types somehow.)


 

From: Tucker Taft

Sent: Tuesday, September 30, 2025  11:52 AM

I have made suggestions about AI22-0141-1 which I believe address the problem.  Also, it would help if you would remind us of what is the "Goodenough Trick" because I at least have forgotten what it is.

One point I noticed in your commentary was that you implied that the introductory statement in RM 4.1.6(1/7) is not a legality rule.  It says:

   Given a type T, such that T is neither an array type nor an access type, the following type-related, operational aspects may be specified:

I believe there is a general Legality Rule that makes it clear that these introductory statements *are* legality rules.  RM 13.1.1(14/3), which is in the Legality Rule section, includes the sentence:

   The aspect identified by the aspect_mark shall be an aspect that can be specified for the associated entity (or view of the entity defined by the associated declaration).

I believe this means that all such introductory statements are also legality rules.  But perhaps I am misunderstanding your statement about legality rules and rechecking on instantiation...


 

From: Bob Duff

Sent: Tuesday, September 30, 2025  12:08 PM

> ... Also, it would help if you would remind us of what is the

> "Goodenough Trick" because I at least have forgotten what it is.

In Ada 83, it's illegal to declare "=" on a nonlimited type.

John Goodenough figured out a way around that rule.

A generic formal limited private type can have "=", and you can

pass a nonlimited type, so in the instance you have "="

on a nonlimited type.


 

From: Bob Duff

Sent: Tuesday, September 30, 2025  12:14 PM

> John Goodenough figured out a way around that rule.

...so I decided to ask google about it. At first, I got

some AI-generated nonsense about a "good enough trick",

but after some fiddling with search terms, I found this:

https://dl.acm.org/doi/pdf/10.1145/998416.998417

I believe the "Goodenough Trick" was part of the reason for

repealing the limit on "=" in Ada 95 -- it's silly to forbid

something in a way that doesn't forbid it.


 

From: Tucker Taft

Sent: Tuesday, September 30, 2025  12:31 PM

Got it, thanks!


 

From: Stephen Baird

Sent: Tuesday, September 30, 2025  3:12 PM

I think you are right; we missed this point in previous discussions of this issue. Does that eliminate the *need* for this AI? One might argue that it still helps in clarifying the language definition, but (based on your observation about  RM 13.1.1(14/3)) it looks like the hole in the language definition that this AI was intended to fix does not exist. So does the "if it ain't broke ..." principle apply?


 

From: Randy Brukardt

Sent: Tuesday, September 30, 2025  10:53 PM

>>I believe there is a general Legality Rule that makes it clear that

>>these introductory statements *are* legality rules.

>>RM 13.1.1(14/3), which is in the Legality Rule section, includes the sentence:

>>The aspect identified by the aspect_mark shall be an aspect that can

>>be specified for the associated entity (or view of the entity defined

>>by the associated declaration).

>>I believe this means that all such introductory statements are also

>>legality rules.  But perhaps I am misunderstanding your statement

>>about legality rules and rechecking on instantiation...

>I think you are right; we missed this point in previous discussions of

>this issue.

That's not quite how I would interpret it, although I am mostly splitting hairs here. The introductory text is a static semantics definition, it is not itself a Legality Rule. For the purposes of ACATS testing, definitions are generally not tested by themselves -- it is the Legality Rules that are tested. (Although if a definition is distributed enough, I might test the various sentences individually.)

>Does that eliminate the *need* for this AI?

Yes, but...

>One might argue that it still helps in clarifying the language

>definition, but (based on your observation about  RM 13.1.1(14/3)) it

>looks like the hole in the language definition that this AI was

>intended to fix does not exist. So does the "if it ain't broke ..." principle apply?

I don't think so. Here's why:

(1) The three of us have discussed this more than a year (off and on) without anyone raising the point that the premise was wrong. That strongly suggests that we need *something* in this case.

(2) Whenever we identify a rule that needs rechecking and it seems surprising (which is pretty much any case that needs rechecking), we need an AI to explain that, and it needs to be referenced from the appropriate parts of the AARM. Determining whether a particular rule needs rechecking is HARD, and we shouldn't be making implementers and testers (and to a lesser extent, users) figure out when that is the case.

(3) Following on to (2), implementers and testers need the RM to be clear about which rules need to be rechecked. Moreover, they need to be clear as to which rules need to be checked in the private part and which do not. Nothing in 13.1.1 implies that any of the rules outside of the nonoverridable ones need to be rechecked.

Rechecking is important to be clear because it is almost always implemented only when an ACATS test exists to require the checks. That's in large part because implementers have enough to do without looking through the RM to find places that make trouble. Moreover, rechecking in most compilers is done by separate code, so each recheck is implemented individually. (Reusing the original compilation code would be hard, because the need to not recheck in the private part for a few rules, including the one we are talking about, and because name resolution should never be repeated [it might get a different answer].) For Janus/Ada, we do rechecks as part of the duplication [macro expansion] of a generic, which is mainly copying and modifying symboltable entries (a very complex process as the connections between items needs to be copied as well). Those are completely separate from the original checks for compilation (which are typically done when the syntactic definitions are processed).

Thus, to get rechecking tests, it has to be clear to whomever is tracking test objectives (moi!) that rechecking is needed and thus tests for rechecking on an instance are needed. These are usually fairly complex tests to write (the generic setup adds to the size and complexity of the tests), and nobody would want them to exist (or have effort spent on them) unless there is something interesting being rechecked. The RM (or at least AARM) should make this as clear as possible.

Lastly, you identified that access-to-array needs to be banned even as a completion of a private type (as that would cause an ambiguity given the implicit dereference). At a minimum, we would need to add that rule, and also check if any of the other cases you identified needs a restriction on private types (I think that at least one of those rules was missing somewhere, but I may have imagined that).


 

From: Randy Brukardt

Sent: Tuesday, September 30, 2025  10:58 PM

>... Also, it would help if you would remind us of what is the

>"Goodenough Trick" because I at least have forgotten what it is.

Gosh. That was one of the most famous hacks for Ada 83, and I remember that it was important to the Ada 9x team to remove it. I'm stunned that you forgot it (even if it was no longer top of mind). I hope this doesn't prove the start of cognitive decline... ;-)

P.S. Bob explained it adequately, so I won't add to the noise.


 

From: Randy Brukardt

Sent: Tuesday, September 30, 2025  11:17 PM

>In Ada 83, it's illegal to declare "=" on a nonlimited type.

>John Goodenough figured out a way around that rule.

>A generic formal limited private type can have "=", and you can pass a

>nonlimited type, so in the instance you have "="

>on a nonlimited type.

And you can export that type, so that anyone can use the result (it is not just restricted to the generic unit).

>I believe the "Goodenough Trick" was part of the reason for repealing

>the limit on "=" in Ada 95 -- it's silly to forbid something in a way

>that doesn't forbid it.

And that's why it was relevant here; it appeared to me (and I guess Steve) that a generic could be used to add indexing to access types (and export the resulting type). As such, the ban isn't very effective. (Tucker now disagrees with this conclusion, but that's new today, he never did so before during the long gestation of this topic and AI.)

I've since begun to wonder why we need to ban the combination of access types and indexing at all. The "obvious" equivalence doesn't work, but that's more of a problem with the equivalence than with the concept. *Ada* defines Access_Obj(...) in some cases, why can't a user do it in other cases if they want?? I mention this as a way to cut the Gordian knot here: rather than having a bad ban that violates good taste in private types (at a minimum), or a stronger ban that would upset Tucker because he might want to do this once ten years from now ;-), perhaps no ban would eliminate both of the concerns at once.


 

From: Randy Brukardt

Sent: Tuesday, September 30, 2025  11:38 PM

To expand on this a bit further:

A prefixed view is defined on objects of access-to-T as well as T for a first parameter of type T.

However, an indexed aspect can only be given on type T. As such, there would be no problem having the case when T is an access type for an indexing aspect not include the automatic dereference that is implicit in a prefixed view.

There would be an ambiguity if both T and access-to-T have defined indexing, but that's in the category of if it hurts when you do that, don't do that! (We would not allow defining indexing on access-to-array for the same reasons that we don't allow it on arrays.) I suppose we could ban access-to-T if T has defined indexing, but that could run into issues based on ordering.

Anyway, I think there is some value in looking at this more (especially as if it ever makes sense to define indexing on a type that is completed with an access type, then it makes the same sense directly on the access type).


 

From: Randy Brukardt

Sent: Thursday, April 30, 2026  9:38 PM

Brad Moore’s Editorial Review complained about the question about indexing at the end of the discussion, noting that no questions should be left open in a completed AI. It had been left a question as it doesn’t seem sensible to have such an index entry, but it probably makes more sense as a suggestion and rebuttal (which is how I rewrote it). The easier alternative would have been to delete it, but the information about why it doesn’t make sense as an index entry is worth preserving somewhere.


 

[a]I don't see how to make a useful index entry. One wants to know both the aspect and the category of types involved, but index entries can have only two parts and are limited in length. Indexing something like "unspecificable aspect" (which is how it is done for "nonoveridable") doesn't make sense, since it implies something rather different than the actual meaning.