AI22-0086-1

!standard 4.6(24.16/2)                                        24-04-24  AI22-0086-1/05

!class binding interpretation 23-12-07

!status Amendment 1-2022  24-02-22

!status WG9 Approved 24-06-10

!status ARG Approved  14-0-1  24-02-22

!status work item 23-12-07

!status received 22-04-14

!submitter Stephen Baird

!priority Low

!difficulty Easy

!qualifier Clarification

!subject Conversions of general access-to-object values

!summary

Conversions of general access-to-object values are legal if both the target and operand designated types are unconstrained discriminated untagged records, even if those designated subtypes do not statically match.

!issue

Conversions of general access-to-object types require static matching if the designated types are untagged, with the exception of discriminated records when one of the types is unconstrained. (See 4.6(24.16/2)).

Before Ada 2012, all unconstrained discriminated record types statically matched. However, Ada 2012 introduced Dynamic_Predicates, and those can be different for different subtypes without adding a constraint.

As previously mentioned, 4.6(24.16/2) says that one of the types has to be unconstrained. But it does not specify if it means "exactly one" or "at least one" of the types has to be unconstrained. This matters if both types are unconstrained but do not statically match.

Consider:

   type No_Pred (N : Natural) is null record;
   subtype Pred is No_Pred with Dynamic_Predicate => Pred.N /= 123;
   type No_Pred_Ref is access all No_Pred;
   type Pred_Ref is access all Pred;
   function Cvt1 (Np : No_Pred_Ref) return Pred_Ref is
                      (Pred_Ref (Np)); -- (1)
   function Cvt2 (P : Pred_Ref) return No_Pred_Ref is
                      (No_Pred_Ref (P)); -- (2)

 

If 4.6(24.16/2) means "exactly one", the conversions (1) and (2) are illegal. If 4.6(24.16/2) means "at least one", the conversions (1) and (2) are legal.

Are these conversions legal? (Yes.)

!recommendation

(See summary.)

!wording

Modify 4.6(24.16/2):

!discussion

We can allow these conversions, so long as we are certain that the representations of the two designated record subtypes are the same. (Converting an access value to some other type where the representation of the designated type is different would be a disaster.)

The requirement that the designated subtypes shall have the same set of applicable predicate specifications is not strictly required, but it still seems desirable.

Therefore, we adjust the wording to allow conversions when both types are unconstrained but they do not statically match.

---

There are other reasons that an untagged record subtype might not statically match. For instance, different Object_Sizes, Nonblocking aspects, or Global aspects, all prevent static matching.

For different Object_Sizes, it should be clear that the conversion should not be allowed, regardless of whether the types are constrained or unconstrained. That makes it a different question, which probably should be answered in a separate AI. [The author thinks that it might be necessary to add an Object_Size equality requirement to 4.6(24.16/2), but compatibility also needs to be taken into account.]

We did not try to determine if differences in Nonblocking or Global aspects would cause problems for general access conversions. These fall into the "question not asked" category.

!example

(See issue.)

!corrigendum 4.6(24.16/2)

@drepl

@xbullet{the designated type shall be discriminated in its full view and unconstrained in any partial view, and one of the designated subtypes shall be unconstrained;}

@dby

@xbullet{the designated type shall be discriminated in its full view and unconstrained in any partial view, at least one of the designated subtypes shall be unconstrained, and any predicate aspect specification that applies to one of the designated subtypes shall also apply to the other;}

!ACATS test

Create an ACATS C-Test similar to the example in the !issue.

!appendix

From: Steve Baird [in private mail]

Sent: Thursday, April 14, 2022   4:51 PM

Before predicates were introduced, any two unconstrained subtypes of the same

discriminated type had to statically match, right?

So there was no question in the 4.6(24.16) wording

   "....  , and one of the designated subtypes shall be unconstrained"

about whether that meant "exactly one" or "at least one". If they were both unconstrained, then the earlier 24.15 case had to apply and we wouldn't even be looking at 24.16.

But now that we have predicates, that reasoning no longer applies.

So do we need clarification regarding the meaning of "one" in 24.16 ?


 

From: Randy Brukardt [in private mail]

Sent: Thursday, April 14, 2022  10:10 PM

>Before predicates were introduced, any two unconstrained subtypes of the same

>discriminated type had to statically match, right?

 

I think so. Constraints statically match if "both are null constraints" ("null constraints" being the name for no constraint, ugh).

>So there was no question in the 4.6(24.16) wording

>   "....  , and one of the designated subtypes shall be unconstrained"

>about whether that meant "exactly one" or "at least one". If they were both

>unconstrained, then the earlier 24.15 case had to apply and we wouldn't

>even be looking at 24.16.

>

>But now that we have predicates, that reasoning no longer applies.

 

It's not just predicates. Look at the list of reasons that a subtype might not conform -- it includes Object_Size, Nonblocking aspects, and Global aspects. At least some of those are subtype-specific and thus one could have an unconstrained subtype with a different Global. (That usually would happen with predicates, but nothing prevents it from happening at any other time.)

>So do we need clarification regarding the meaning of "one" in 24.16 ?

 

I  don't think so, at least I can't think of an obvious problem if both are unconstrained. If you are thinking that the unconstrained one is *completely* unconstrained, that's certainly a different rule and saying "only one" doesn't change anything.

 

That is, if you have a target with an unconstrained designated type with a predicate and a source designated type without that predicate that otherwise meets 24.16, then are we trying to enforce that predicate? In such a case, we either would have to do that as an extra operation (for which we'd need a rule), or ban such cases (which I think was likely the intent). "The one ... shall ..." doesn't matter if that's a problem.

If we have:

         package Vect is new Ada.Containers.Bounded_Vector (Character); use Vect;

 

         subtype Lower_Vector is Vector

                with Dynamic_Predicate =>

                    (for I in 1 Lower_Vector.Length => Lower_Vector(I) in 'a' .. 'z');

 

         subtype Short_Vector is Vector(10); -- Capacity is constrained.

 

        type P1 is access Lower_Vector;

        type P2 is access Short_Vector;

 

        Ptr : P2 := new Short_Vector'(1..10 => 'A');

 

        Ptr2 : P1 := P1(Ptr) ;

 

This type conversion looks legal by 4.6(24.16), but Ptr2 designates something that does not meet it's predicate. And there is only one unconstrained type in sight here. So I don't think the problem is with "one" here, it's not dealing with reasons that the type might be incompatible even if unconstrained. (I suspect that Global and Nonblocking already handle such cases, or at least tried. Object_Size would definitely be a problem! And surely are predicates.)

 

We could fix the predicate case with a predicate check on such a conversion, but I don't think that helps for Object_Size or the other cases.


 

From: Randy Brukardt [in private mail]

Sent: Thursday, April 14, 2022  10:55 PM

>We could fix the predicate case with a predicate check on such a

>conversion, but I don't think that helps for Object_Size or the other

>cases.

Actually, 4.6(50) mandates such a check (it’s the same one needed if one converts to a constrained designated type). It says "any check associated with evaluating a conversion", so predicates surely are included.

Ergo, I don't think there is any problem here, even if both types are unconstrained but they don't statically match, and one has a predicate. A check will be made if needed. And I don't see any reason to disallow such a conversion.

There might be a problem with Object_Size or Global or Nonblocking, but I'd be happy to leave those in the "question not asked" category.


 

From: Steve Baird [in private mail]

Sent: Friday, April 15, 2022  10:55 PM

Regarding the question of whether "one" in this context means "at least one" or "exactly one", I hadn't gotten as far as advocating one interpretation or the other. Regardless of which interpretation we want, it needs to be clearly defined; IMO opinion, the current wording no longer meets that requirement (although it probably did back when it was written).

More specifically, the legality of the following example ought to be well-defined (one way or the other):

           type No_Pred (N : Natural) is null record;

           subtype Pred is No_Pred with Dynamic_Predicate => Pred.N /= 123;

           type No_Pred_Ref is access all No_Pred;

           type Pred_Ref is access all Pred;

           function Cvt1 (Np : No_Pred_Ref) return Pred_Ref is (Pred_Ref (Np));

           function Cvt2 (P : Pred_Ref) return No_Pred_Ref is (No_Pred_Ref (P));

Your arguments convince me that there is no problem with it being legal. I claim that with the current wording, it is not clear whether this is the case. It seems like we ought to replace "one" with "at least one" in 4.6(24.16) just as a clarification, not as a change to the language.


 

[a]We need the predicates and object size to match.

[b]Steve handled predicates, but not Object_Size

[c]If you are talking about the Object_Size of the designated subtype, I don't see how we can expect them to match, since one of them might be constrained and the other is unconstrained.   I suppose if they are both unconstrained, we could require a match.  If they are not both unconstrained, perhaps we require that the constrained one is no bigger than the unconstrained one?  Or perhaps we require that Object_Size is not specified, unless it is confirming.