!standard 3.3(23) 07-10-01 AI05-0008-1/05 !standard 3.10.2(26/2) !standard 4.1(9) !standard 6.4.1(17) !standard 8.5.1(5/2) !class binding interpretation 06-03-17 !status Amendment 201Z 08-11-26 !status WG9 Approved 07-11-08 !status ARG Approved 6-0-4 07-06-02 !status work item 06-12-14 !status ARG Approved 9-0-3 06-11-18 !status work item 06-03-17 !status received 05-10-13 !priority High !difficulty Medium !qualifier Error !subject General access values that might designate constrained objects !summary (See recommendation.) !question What should happen if the target of the assignment is an unconstrained aliased object or component and the source value would change the discriminants? (Raise C_E.) For example, given the following types: type Rec (D : Boolean := False) is record case D is when False => I : aliased Integer; when True => C : Character; end case; end record; type Acc_Rec is access all Rec; consider these objects: R : aliased Rec; Acc_R : Acc_Rec := R'Access; and an assignment such as: Acc_R.all := (D => True, C => 'X'); The Ada 2005 RM implies that this assignment should succeed without raising an exception. The actual subtype of the left-hand side is determined by the designated object (R), and since R's actual subtype is the unconstrained subtype Rec, the object is unconstrained and so the (implicit) conversion of the right-hand side to the subtype of Acc_R.all shouldn't require a check. This is obviously not right, as it would require including a 'constrained bit with all aliased objects. There is a related issue with renaming of a depends-on-discriminant component. O : Natural renames Acc_R.I; -- OK? (Illegal.) Here, we don't know at compile-time whether Acc_R designates a constrained object or an unconstrained object. If it's unconstrained, the renames clearly cannot be allowed (else the component would disappear). Similar issues arise for 'Access. !recommendation Redo 4.1(9) to require that discriminants not be changed through the dereference of an access type that might designate an object that is constrained by its initial value. Define the term "known to be constrained" and redo the wording of 3.10.2(26/2) and 8.5.1(5/2) to require that an object must be "known to be constrained" before components that depend on discriminants may be either renamed or used as the prefix of an Access attribute. Specify that passing a discriminated object as an actual parameter where the nominal subtype of the corresponding formal parameter is constrained and then modifying a discriminant of the actual before the call has completed results in erroneous execution. !wording Add after 3.3(23): A view of a composite object is "known to be constrained" if: - its nominal subtype is constrained, and is not an untagged partial view; or - its nominal subtype is indefinite; or - its type is a protected type, a task type, or an explicitly limited record type; or - it is part of a stand-alone constant (including a generic formal object of mode in); or - it is part of a formal parameter of mode in; or - it is part of the object denoted by a function_call or aggregate; or - it is part of a constant return object of an extended_return_statement; or - it is a dereference of a pool-specific access type, and there is no partial view of its type that is constrained. AARM NOTE: We do not include dereferences of general access types because they might denote stand-alone aliased unconstrained variables. That's true even for access-to-constant types. END AARM Note. For the purposes of determining within a generic body whether an object is known to be constrained, a subtype is not considered indefinite if it is a descendant of an untagged generic formal derived type, nor is an access type considered pool-specific if it is a descendant of a formal access type. Replace 3.10.2(26/2): The view shall not be a subcomponent that depends on discriminants of a variable whose nominal subtype is unconstrained, unless this subtype is indefinite, or the variable is constrained by its initial value. with the following: The view shall not be a subcomponent that depends on discriminants of an object unless the object is known to be constrained. In addition to the places where Legality Rules normally apply, this rule applies also in the private part of an instance of a generic unit. Modify 4.1(9) as follows: If the type of the name in a dereference is some access-to-object type T, then the dereference denotes a view of an object, the nominal subtype of the view being the designated subtype of T. {If the designated subtype has unconstrained discriminants, the (actual) subtype of the view is constrained by the values of the discriminants of the designated object, except when there is a partial view of the type of the designated subtype that does not have discriminants, in which case the dereference is not constrained by its discriminant values.} AARM NOTE (added after the Ada 95 ones): The last sentence is different from Ada 95; it is necessary because general access types can designate unconstrained objects in Ada 2005 (that was not possible in Ada 95). Thus, the rules that had this effect in Ada 95 (the object being constrained by its initial value) don't work in Ada 2005 and we have to say this explicitly. The "except" part of the last sentence prevents privacy "breaking", so that if a private type has discriminants only in the full view, they don't interfere with freely interassigning values between objects of the type, even when the objects live in the heap. Retain the AARM note 4.1(9.d/2) from the Ada 2005 AARM. Add after 6.4.1(17): Erroneous Execution If the nominal subtype of a formal parameter with discriminants is constrained or indefinite, and the parameter is passed by reference, then the execution of the call is erroneous if the value of any discriminant of the actual is changed while the formal parameter exists (that is, before leaving the corresponding callable construct). Replace 8.5.1(5/2): The renamed entity shall not be a subcomponent that depends on discriminants of a variable whose nominal subtype is unconstrained, unless this subtype is indefinite, or the variable is constrained by its initial value. A slice of an array shall not be renamed if this restriction disallows renaming of the array. In addition to the places where Legality Rules normally apply, these rules apply also in the private part of an instance of a generic unit. These rules also apply for a renaming that appears in the body of a generic unit, with the additional requirement that even if the nominal subtype of the variable is indefinite, its type shall not be a descendant of an untagged generic formal derived type. with the following: The renamed entity shall not be a subcomponent that depends on discriminants of an object whose nominal subtype is unconstrained unless the object is known to be constrained. A slice of an array shall not be renamed if this restriction disallows renaming of the array. In addition to the places where Legality Rules normally apply, these rules apply also in the private part of an instance of a generic unit. [Note: The last sentence of the original paragraph is redundant with the new text in 3.3, and thus is removed.] !example The need for the 6.4.1 change is illustrated by the following example: declare type Component is tagged null record; type By_Reference (D : Boolean := False) is record C : Component; end record; subtype Constrained is By_Reference (False); Global_To_P : By_Reference; procedure P (X : Constrained) is begin Global_To_P := (D => True, C => <>); -- erroneous pragma Assert (X in Constrained); -- ok to generate no code for pragma end P; begin P (Global_To_P); end; This was intended to be covered by 3.7.2(4), but that rule doesn't apply to an IN parameter passed by reference as the parameter itself is not a name denoting a subcomponent that depends on discriminants (the actual parameter might be, but the formal parameter is not a subcomponent). The new wording for 6.4.1 needs to include "indefinite" so that problems with untagged derived types are covered. Consider: package P is pragma Elaborate_Body; type T (D : Integer) is private; private type T (D : Integer) is tagged record C : String (1 .. D); end record; end P; with P; package Q is type NT (ND : Integer := 3) is new T (ND); X : NT; Y : NT (0); end Q; with Q, Ada.Text_IO; package body P is procedure P2 (Z : in out T) is -- Assume pass-by-reference C_Ren : Character renames Z.C(2); -- OK, Z is indefinite. begin Q.X := Q.Y; -- Erroneous. Ada.Text_IO.Put (C_Ren); -- Doesn't exist anymore. end P2; begin P2 (T(Q.X)); -- OK. end P; !discussion The word "object" is used instead of "variable" in the new wording for 3.10.2(26/2) and 8.5.2(5/2). This excludes constants which are not known to be constrained, such as access-to-constant dereferences. The definition of "known to be constrained" includes the case of an in-mode subprogram parameter with an unconstrained nominal subtype. This opens the door slightly for misuse of the Unchecked_Access attribute, but it is no worse than what can be done with a formal parameter whose nominal subtype is constrained. In both cases, the problem would be the conversion of a reference to a subcomponent of a parameter that depends on discriminants into a value of a global access type which outlives the subprogram call. After the call has terminated, the enclosing object is modified so that the referenced subcomponent no longer exists. The existing wording of 13.10(1) seems sufficiently general to handle this case. In an effort to keep the wording as simple as possible, the definition of "known to be constrained" does not mention some cases (e.g., view conversions, the current instance of a type, implementation-defined attributes, objects of a generic formal discriminated private type) which are irrelevant in the two places where this term is used. Following this reasoning, the words "or aggregate" could be deleted from the definition (although echoing the wording of AI05-0015 seems nice) as well as the mention of "a task type," (which follows 3.10(9/2)), and "and is not an untagged partial view" (which seems valuable to avoid confusion about uncovered cases). !corrigendum 3.3(23) @dinsa At the place where a view of an object is defined, a @i is associated with the view. The object's @i (that is, its subtype) can be more restrictive than the nominal subtype of the view; it always is if the nominal subtype is an @i. A subtype is an indefinite subtype if it is an unconstrained array subtype, or if it has unknown discriminants or unconstrained discriminants without defaults (see 3.7); otherwise the subtype is a definite subtype (all elementary subtypes are definite subtypes). A class-wide subtype is defined to have unknown discriminants, and is therefore an indefinite subtype. An indefinite subtype does not by itself provide enough information to create an object; an additional constraint or explicit initialization expression is necessary (see 3.3.1). A component cannot have an indefinite nominal subtype. @dinss A view of a composite object is @i if: @xbullet @xbullet @xbullet @xbullet); or> @xbullet; or> @xbullet or @fa; or> @xbullet; or> @xbullet For the purposes of determining within a generic body whether an object is known to be constrained, a subtype is not considered indefinite if it is a descendant of an untagged generic formal derived type, nor is an access type considered pool-specific if it is a descendant of a formal access type. !corrigendum 3.10.2(26/2) @drepl @xinbull @dby @xinbull !corrigendum 4.1(9) @drepl If the type of the @fa in a dereference is some access-to-object type @i, then the dereference denotes a view of an object, the @i of the view being the designated subtype of @i. @dby If the type of the @fa in a dereference is some access-to-object type @i, then the dereference denotes a view of an object, the @i of the view being the designated subtype of @i. If the designated subtype has unconstrained discriminants, the (actual) subtype of the view is constrained by the values of the discriminants of the designated object, except when there is a partial view of the type of the designated subtype that does not have discriminants, in which case the dereference is not constrained by its discriminant values. !corrigendum 6.4.1(17) @dinsa After normal completion and leaving of a subprogram, for each @b or @b parameter that is passed by copy, the value of the formal parameter is converted to the subtype of the variable given as the actual parameter and assigned to it. These conversions and assignments occur in an arbitrary order. @dinst @s8<@i> If the nominal subtype of a formal parameter with discriminants is constrained or indefinite, and the parameter is passed by reference, then the execution of the call is erroneous if the value of any discriminant of the actual is changed while the formal parameter exists (that is, before leaving the corresponding callable construct). !corrigendum 8.5.1(5/2) @drepl The renamed entity shall not be a subcomponent that depends on discriminants of a variable whose nominal subtype is unconstrained, unless this subtype is indefinite, or the variable is constrained by its initial value. A @fa of an array shall not be renamed if this restriction disallows renaming of the array. In addition to the places where Legality Rules normally apply, these rules apply also in the private part of an instance of a generic unit. These rules also apply for a renaming that appears in the body of a generic unit, with the additional requirement that even if the nominal subtype of the variable is indefinite, its type shall not be a descendant of an untagged generic formal derived type. @dby The renamed entity shall not be a subcomponent that depends on discriminants of an object whose nominal subtype is unconstrained unless the object is known to be constrained. A @fa of an array shall not be renamed if this restriction disallows renaming of the array. In addition to the places where Legality Rules normally apply, these rules apply also in the private part of an instance of a generic unit. !ACATS test An ACATS B-Test and an ACATS C-Test should be created to test these cases. !ASIS No ASIS impact. !appendix From: Gary Dismukes Date: Monday, February 20, 2006 7:45 AM In the process of working on part of AI-363 in GNAT I've uncovered what appear to be some semantic problems relating to the changes for aliased components and declared objects: specifically, the rule changes permitting them to be unconstrained when their type has defaulted discriminants (that is, no longer requiring them to be constrained by their initial values). I'm interested to see if others agree that there are problems with the RM rules, or whether I'm just misunderstanding or overlooking something and simply confused. :-) Problem #1: The first issue has to do with assignments through access values when the designated type is unconstrained with defaulted discriminants. What should happen if the target of the assignment is an unconstrained aliased object or component and the source value would change the discriminants? For example, given the following types: type Rec (D : Boolean := False) is record case D is when False => I : aliased Integer; when True => C : Character; end case; end record; type Acc_Rec is access all Rec; consider these objects: R : aliased Rec; Acc_R : Acc_Rec := R'Access; and an assignment such as: Acc_R.all := (D => True, C => 'X'); I believe the RM implies that this assignment should succeed without raising an exception. The actual subtype of the left-hand side is determined by the designated object (R), and since R's actual subtype is the unconstrained subtype Rec, the object is unconstrained and so the (implicit) conversion of the right-hand side to the subtype of Acc_R.all shouldn't require a check. The problem here is that we no longer know at compile time whether Acc_R.all is constrained, and so the "constrainedness" of the target has to be a run-time determination. Obviously that's not the answer we want, since this would imply having to store an implicit Constrained flag with all aliased objects of type Rec, something that was never necessary in Ada 83/95. It seems clear that what's needed is an assume-the-worst rule, so that assignments through a dereference _always_ treat the target as if it were "constrained by its initial value" in the case where the designated type has defaulted discriminants, even though some designated objects might actually be unconstrained. I suppose it was the intent that assignments always be checked in this case, but it doesn't follow from the rules as far as I can tell. (Note that of course we don't want this to apply when the designated type has a partial view that is constrained, in which case even allocated objects can be unconstrained and have their discriminants changed, which is one of the principal semantic changes made by AI-363.) Problem #2: AI-363 also changes the rules for when it's legal to rename or apply 'Access to a subcomponent that depends on a discriminant. Specifically, 3.10.2(26/2) and 8.5.1(5/2) are revised so that they no longer allow this for aliased (enclosing) variables in general, but rather limit the allowance to cases where the variable is "constrained by its initial value". Consider the following example based on the earlier declarations. The type Rec has a discriminant-dependent component I (declared aliased to permit 'Access), and let's assume the aliased object R again: R : aliased Rec; -- Default initialized and unconstrained in Ada 2005 Now what happens if we rename or apply 'Access to R's integer component: Renamed_Int : Integer renames R.I; type Acc_Int is access all Integer; Accessed_Int : Acc_Int := R.I'Access; The above are both legal in Ada 95, but illegal in Ada 2005, because R is unconstrained, and so its discriminant can change. These are both acknowledged incompatibilities. (These can be worked around by either adding an explicit constraint to the declaration of R, or by making the less localized change of removing the defaults from the type's discriminants.) Now let's consider similar cases, but where the prefix is a dereferenced access value: Acc_R : Acc_Rec := R'Access; -- or could be ":= new Rec'(False, 7)" Renamed_Int : Integer renames Acc_R.I; -- OK in Ada 95; but in Ada 05?? Access_Int : Acc_Int := Acc_R.I'Access; -- OK in Ada 95; but in Ada 05?? Again, the above two declarations are both legal in Ada 95, but what about Ada 2005? Well, because of the changes to rules 3.10.2(26/2) and 8.5.1(5/2), we have to answer the question of whether the object Acc_R.all is "constrained by its initial value". If the answer to that is "yes", then the declarations are legal, and if it's "no" then they're illegal. As in the earlier assignment example, the problem is again that we don't know at compile time. If the object was created by an allocator, then the answer is yes, but if Acc_R denotes an aliased object declared by an (unconstrained) object declaration then the answer is no. So it would again seem that we need to have an assume-the-worst rule. Here we have to assume that the object Acc_R.all is not constrained by its initial value, that is, we need to assume it could be unconstrained (the opposite of the assumption need in the dereferenced assignment case discussed above). It seems clear to me that 3.10.2(26/2) and 8.5.1(5/2) are broken. The use of the phrase "constrained by its initial value", replacing "aliased" in these rules, just doesn't fly for a static semantic rule, since this characteristic is no longer known at compile time in the case of dereferenced access values (unless the type has a constrained partial view...). (I'm not sure what the right fix is here. We want to say something like "might be constrained by its initial value", though I don't think that would be acceptable RM-ese. Maybe we have to add an extra statement along the lines "If the variable is a dereferenced access value, then the variable is presumed to be constrained by its initial value", though we also have to make sure to exempt the case where there's a constrained partial view.) ---- In summary, it seems that the extra flexibility of being able to declare unconstrained aliased objects creates two problems. Assignments still need to do constraint checks (when done through access values) even though the target might really be unconstrained, and cases of renamings and 'Access that were legal in Ada 95 become illegal even when they might be safe (such as when the dereferenced object was created by an allocator). Using a worst-case rule for the assignment case doesn't seem like a big deal, since it's consistent with assignments through access values in Ada pre-2005, though it's perhaps a little surprising that assigning to an aliased object through a formal parameter should behave differently than assignment to that same object through an access value. In any case, I believe this requires a correction to the current rules, since otherwise the RM seems to imply a need for a constrained flag within objects. What concerns me more is the case of 'Access applied to subcomponents of an aliased object, where we have an incompatiblity in the access dereference case that's not necessarily easy to work around (unlike cases involving aliased stand-alone objects, where you can just add a discriminant constraint to avoid the incompatibility). The only available workaround in the dereferencing case appears to be to remove discriminant defaults, but that may not be feasible in applications that have a significant dependence on declaring mutable objects. The only alternative I see at the moment to adding worst-case corrections to the rules would be to limit the cases where objects and components can be unconstrained to when the type has a constrained partial view. This would have the benefit of limiting the incompatibilities and would be consistent with the main thrust of the AI, but might be considered overly restrictive, since we'd lose the (mostly) nice ability to have unconstrained aliased objects for all types with defaulted discriminants. Thoughts? **************************************************************** From: Randy Brukardt Date: Monday, February 20, 2006 5:52 PM ... > I'm interested to see if others agree that there are problems with the RM > rules, or whether I'm just misunderstanding or overlooking something and > simply confused. :-) I think you're confused, but you've confused me enough that I'm not absolutely certain. > Problem #1: > > The first issue has to do with assignments through access values when > the designated type is unconstrained with defaulted discriminants. > > What should happen if the target of the assignment is an unconstrained > aliased object or component and the source value would change the > discriminants? For example, given the following types: > > type Rec (D : Boolean := False) is record > case D is > when False => I : aliased Integer; > when True => C : Character; > end case; > end record; > > type Acc_Rec is access all Rec; This has to be a private type to trigger these rules; they don't happen for non-private types. That's critical to making them work, because most of the things you are worrying about can't happen outside of the package body. I found out how critical that is when trying to explain these rules to John for the Rationale. If you leave out the privateness, none of it makes any sense. I.e., you need a constrained private type here: package P is type Rec is private; private type Rec (D : Boolean := False) is record case D is when False => I : aliased Integer; when True => C : Character; end case; end record; type Acc_Rec is access all Rec; end; > consider these objects: > > R : aliased Rec; > > Acc_R : Acc_Rec := R'Access; > > and an assignment such as: > > Acc_R.all := (D => True, C => 'X'); > > I believe the RM implies that this assignment should succeed without raising > an exception. The actual subtype of the left-hand side is determined by the > designated object (R), and since R's actual subtype is the unconstrained > subtype Rec, the object is unconstrained and so the (implicit) conversion of > the right-hand side to the subtype of Acc_R.all shouldn't require a check. Yes, of course. (With the revised types. With the original types, the rules aren't changed for this, and the Constraint_Error still happens - the object appears to be constrained by its initial value.) > The problem here is that we no longer know at compile time whether Acc_R.all > is constrained, and so the "constrainedness" of the target has to be a > run-time determination. Obviously that's not the answer we want, since this > would imply having to store an implicit Constrained flag with all aliased > objects of type Rec, something that was never necessary in Ada 83/95. Why do you say this? There isn't anything that could be on the LHS that could be constrained. You've not provided any example, and I don't think you can... ... > Problem #2: > > AI-363 also changes the rules for when it's legal to rename or apply > 'Access to a subcomponent that depends on a discriminant. Specifically, > 3.10.2(26/2) and 8.5.1(5/2) are revised so that they no longer allow this > for aliased (enclosing) variables in general, but rather limit the allowance > to cases where the variable is "constrained by its initial value". Correct. ... > Now let's consider similar cases, but where the prefix is a dereferenced > access value: > > Acc_R : Acc_Rec := R'Access; -- or could be ":= new Rec'(False, 7)" R is not "constrained by its initial value"! Nor is Acc_R. You need "constant" for that to be the case for R, but that then doesn't match the access type. And it's never the case for a variable. Note that the rules for that were changed a lot; in particular, "aliased" no longer means "constrained by its initial value". Note that allocators aren't "constrained by their initial value" for these types, either. > Renamed_Int : Integer renames Acc_R.I; > -- OK in Ada 95; but in Ada 05?? > > Access_Int : Acc_Int := Acc_R.I'Access; > -- OK in Ada 95; but in Ada 05?? These are of course illegal. > Again, the above two declarations are both legal in Ada 95, but what > about Ada 2005? Well, because of the changes to rules 3.10.2(26/2) > and 8.5.1(5/2), we have to answer the question of whether the object > Acc_R.all is "constrained by its initial value". If the answer to > that is "yes", then the declarations are legal, and if it's "no" then > they're illegal. As in the earlier assignment example, the problem > is again that we don't know at compile time. If the object was > created by an allocator, then the answer is yes, but if Acc_R denotes > an aliased object declared by an (unconstrained) object declaration > then the answer is no. So it would again seem that we need to have > an assume-the-worst rule. Here we have to assume that the object > Acc_R.all is not constrained by its initial value, that is, we need > to assume it could be unconstrained (the opposite of the assumption > need in the dereferenced assignment case discussed above). This is throughly confused. I don't see anything anywhere in any of your examples that is "constrained by its initial value" (that mainly happens for "constant", and for non-private types). **************************************************************** From: Tucker Taft Date: Monday, February 20, 2006 6:51 PM I think it is very nice that adding "aliased" doesn't make as much difference as it did in Ada 95, so I would not want to lose that feature. I agree that "constrained by its initial value" doesn't quite cut it as a replacement for "aliased." Since it is used in a static semantic rule, clearly we need to interpret it statically. For the purposes of constraint checks, it seems that blah.all should be presumed constrained by its initial value if a corresponding allocator would be. For the purposes of renaming and 'access of discriminant-dependent components, blah.all should be presumed unconstrained if blah is a general access value and an aliased stand-alone object of the designated subtype would be unconstrained. Renaming and 'access of discriminant-dependent components doesn't seem like a very important capability to me, so I don't mind the loss of functionality. **************************************************************** From: Tucker Taft Date: Monday, February 20, 2006 11:52 PM Unfortunately, we can't fix this by defining whether a dereference is "constrained by its initial value," because we want the answer to be different depending on whether it is a constraint check or a renaming or 'Access. Instead it seems like we have to formalize the "assume the worst" that Gary suggests. And probably generics interact with this as well... So this implies we need to make the following changes: 3.10.2(26/2) 8.5.1(5/2) In these places, we need to assume the worst, namely that a dereference might denote an unconstrained object, if the access value is a general access value and the nominal subtype is unconstrained. 4.1(9) Here we need to say the subtype of a dereference is always considered constrained by its *current* discriminant values, unless the nominal subtype is unconstrained and there is a partial view that is constrained. **************************************************************** From: Gary Dismukes Date: Tuesday, February 21, 2006 1:03 AM > I think you're confused, but you've confused me enough that I'm not > absolutely certain. Sorry if what I wrote wasn't clear. I was trying to be moderately detailed in my explanation, without weighing it down with too much RM exegesis. At least Tuck understood what I was saying. :-) > > Problem #1: > > > > The first issue has to do with assignments through access values when > > the designated type is unconstrained with defaulted discriminants. > > > > What should happen if the target of the assignment is an unconstrained > > aliased object or component and the source value would change the > > discriminants? For example, given the following types: > > > > type Rec (D : Boolean := False) is record > > case D is > > when False => I : aliased Integer; > > when True => C : Character; > > end case; > > end record; > > > > type Acc_Rec is access all Rec; > > This has to be a private type to trigger these rules; they don't happen for > non-private types. That's critical to making them work, because most of the > things you are worrying about can't happen outside of the package body. Incorrect. I think if you look carefully at what the rules say you'll see that privateness isn't required for any of what I was talking about. I thought I had made it clear that I wasn't referring to cases where there was a constrained private view. The rules have changed so that aliased objects with defaulted discriminants can be unconstrained even if there isn't a private type involved. Take a close look at 3.3.1(9/2). I've retained the old text and enclosed it in << >> so it will stand out: Dynamic Semantics 9/2 {AI95-00363-01} {constraint (of an object)} If a composite object declared by an object_declaration has an unconstrained nominal subtype, then if this subtype is indefinite or the object is constant <> the actual subtype of this object is constrained. The constraint is determined by the bounds or discriminants (if any) of its initial value; {constrained by its initial value} the object is said to be constrained by its initial value. {actual subtype (of an object)} {subtype (of an object): See actual subtype of an object} <<[In the case of an aliased object, this initial value may be either explicit or implicit; in the other cases, an explicit initial value is required.]>> When not constrained by its initial value, the actual and nominal subtypes of the object are the same. {constrained (object)} {unconstrained (object)} If its actual subtype is constrained, the object is called a constrained object. The critical change here is that the rules for when an object with an unconstrained nominal subtype is constrained no longer apply when the object is aliased. There's nothing mentioned here about private views. This change was quite intentional in AI-363 AFAIK, and at least Tucker also seems to agree that was the intent. > I found out how critical that is when trying to explain these rules to John > for the Rationale. If you leave out the privateness, none of it makes any > sense. I wouldn't go so far as to say it doesn't make any sense, but it does lead to the problems I was trying to describe (apparently inadequately). :-) > I.e., you need a constrained private type here: > > package P is > type Rec is private; > private > type Rec (D : Boolean := False) is record > case D is > when False => I : aliased Integer; > when True => C : Character; > end case; > end record; > > type Acc_Rec is access all Rec; > end; Again, not correct. My examples were intended specifically to address the nonprivate case. > > consider these objects: > > > > R : aliased Rec; > > > > Acc_R : Acc_Rec := R'Access; > > > > and an assignment such as: > > > > Acc_R.all := (D => True, C => 'X'); > > > > I believe the RM implies that this assignment should succeed without raising > > an exception. The actual subtype of the left-hand side is determined by the > > designated object (R), and since R's actual subtype is the unconstrained > > subtype Rec, the object is unconstrained and so the (implicit) conversion of > > the right-hand side to the subtype of Acc_R.all shouldn't require a check. > > Yes, of course. (With the revised types. With the original types, the rules > aren't changed for this, and the Constraint_Error still happens - the object > appears to be constrained by its initial value.) I wasn't talking about the "revised type" case (i.e., with privateness). Given the types I meant, now you have to provide the exegesis that implies that a constraint check is required when Acc.all denotes an unconstrained stand-alone object, and show where the notion of "the object appears to be constrained by its initial value" is defined. I agree that something like that needs to be the case, but I don't believe it follows from the rules. > > The problem here is that we no longer know at compile time whether Acc_R.all > > is constrained, and so the "constrainedness" of the target has to be a > > run-time determination. Obviously that's not the answer we want, since this > > would imply having to store an implicit Constrained flag with all aliased > > objects of type Rec, something that was never necessary in Ada 83/95. > > Why do you say this? There isn't anything that could be on the LHS that > could be constrained. You've not provided any example, and I don't think you > can... Again I'm talking about the *nonprivate* case, where some aliased objects are constrained and some are unconstrained (though if you aren't convinced yet that that can happen I can see why didn't understand my example). > ... > > Problem #2: > > > > AI-363 also changes the rules for when it's legal to rename or apply > > 'Access to a subcomponent that depends on a discriminant. Specifically, > > 3.10.2(26/2) and 8.5.1(5/2) are revised so that they no longer allow this > > for aliased (enclosing) variables in general, but rather limit the > allowance > > to cases where the variable is "constrained by its initial value". > > Correct. > > ... > > Now let's consider similar cases, but where the prefix is a dereferenced > > access value: > > > > Acc_R : Acc_Rec := R'Access; -- or could be ":= new Rec'(False, 7)" > > R is not "constrained by its initial value"! Nor is Acc_R. You need > "constant" for that to be the case for R, but that then doesn't match the > access type. And it's never the case for a variable. Note that the rules for > that were changed a lot; in particular, "aliased" no longer means > "constrained by its initial value". > > Note that allocators aren't "constrained by their initial value" for these > types, either. If by "these types" you mean the case where there's a constrained partial view, you're right, and I understand that, but I'm talking about the nonprivate case. For that case, R is indeed not constrained, but heap-allocatred objects *are* constrained. > > Renamed_Int : Integer renames Acc_R.I; > > -- OK in Ada 95; but in Ada 05?? > > > > Access_Int : Acc_Int := Acc_R.I'Access; > > -- OK in Ada 95; but in Ada 05?? > > These are of course illegal. > > > Again, the above two declarations are both legal in Ada 95, but what > > about Ada 2005? Well, because of the changes to rules 3.10.2(26/2) > > and 8.5.1(5/2), we have to answer the question of whether the object > > Acc_R.all is "constrained by its initial value". If the answer to > > that is "yes", then the declarations are legal, and if it's "no" then > > they're illegal. As in the earlier assignment example, the problem > > is again that we don't know at compile time. If the object was > > created by an allocator, then the answer is yes, but if Acc_R denotes > > an aliased object declared by an (unconstrained) object declaration > > then the answer is no. So it would again seem that we need to have > > an assume-the-worst rule. Here we have to assume that the object > > Acc_R.all is not constrained by its initial value, that is, we need > > to assume it could be unconstrained (the opposite of the assumption > > need in the dereferenced assignment case discussed above). > > This is throughly confused. I don't see anything anywhere in any of your > examples that is "constrained by its initial value" (that mainly happens for > "constant", and for non-private types). Well, I think you're confused because you keep looking at my examples through private-tinted glasses. ;-) If you take another look at my examples with the understanding that these are based on regular types with defaulted discriminants things may start to make more sense. Of course that requires your being convinced that you can have unconstrained aliased objects of such types. In the *nonprivate* case, I assert that an aliased stand-alone object can be unconstrained (hence, not "constrained by its initial value), but heap-allocated objects of the type still are "constrained by their initial value". And the problem is that in the dereferenced case you don't know statically whether or not the designated object is constrained by its initial value, so you can't apply the legality rules for renamings and 'Access. Consequently, some sort of revision is required to the rules so that they'll work for these cases, because the current rule isn't checkable at compile-time in all cases. **************************************************************** From: Gary Dismukes Date: Tuesday, February 21, 2006 1:10 AM > Unfortunately, we can't fix this by defining whether > a dereference is "constrained by its initial value," > because we want the answer to be different depending > on whether it is a constraint check or a renaming > or 'Access. That's right. The "assume the worst" cuts in opposite directions in the two cases (Problem #1 vs. #2). > Instead it seems like we have to formalize > the "assume the worst" that Gary suggests. And > probably generics interact with this as well... Doesn't surprise me that generics come into the picture, though I haven't thought about the implications there. > So this implies we need to make the following changes: > > 3.10.2(26/2) > 8.5.1(5/2) > > In these places, we need to assume the worst, namely that > a dereference might denote an unconstrained object, if the > access value is a general access value and the nominal > subtype is unconstrained. Yes. > 4.1(9) > > Here we need to say the subtype of a dereference is always > considered constrained by its *current* discriminant values, > unless the nominal subtype is unconstrained and there > is a partial view that is constrained. Yes. **************************************************************** From: Pascal Leroy Date: Tuesday, February 21, 2006 2:49 AM I suppose that's another action item for Tuck: prepare an AI for consideration at the Oporto meeting. We should of course ask ourselves whether we want to fix theses problems before sending the Amendment to WG9. My gut feeling is that this is not the last bug that we'll find in Ada 2005, and that it can fixed later on. Although crafting the words may be hard, the intent is clear: we don't want to be able to change the discriminants of constrained objects, and we don't want to have to store a constrained bit in every mutable object. If someone in the group thinks that we need a fix before sending the Amendment to WG9, speak up now! ... PS: I realize that, technically, the informal review period is over. I also realize that the decision of what changes go into the Amendment rests with AXE Consultants. But we have a moral obligation to give guidance to AXE Consultants... **************************************************************** From: Dan Eilers Date: Tuesday, February 21, 2006 11:11 AM I would be in favor of adding an RM note stating that the intent of the rules is to disallow changing the discriminants of constrained objects, and to avoid having to store a constrained bit in every mutable object. Such a note presumably could be added without any impact to the schedule, and would provide cover for any implementer and/or textbook writer wanting not to take the existing rules too literally. **************************************************************** From: John Barnes Date: Tuesday, February 21, 2006 12:09 PM Seems a reasonable suggestion. **************************************************************** From: Randy Brukardt Date: Tuesday, February 21, 2006 1:51 PM I do think we ought to do *something* now (at least in the AARM), because the fix will require widening the incompatibility. It is important that implementers and users be aware of that fact. But I agree that fixing the words themselves is more likely to cause problems than to fix them at this stage. This will need careful consideration, not a 5-minute fix. So, if we were to add a user note, where would it go? It seems that there are three known places where this would have an impact, and it seems like too much to add something to all of them. Suggestions? **************************************************************** From: Tucker Taft Date: Tuesday, February 21, 2006 6:05 PM I would not bother adding a user note. I would add an AARM implementation note. **************************************************************** From: Pascal Leroy Date: Wednesday, February 22, 2006 3:12 AM I agree. A user note would have to say "don't trust the rules above, they are not exactly right". That's embarrassing for us, and confusing for the user. An AARM note is preferable, as it provide guidance to implementers and textbook writers regarding the intent of the language. **************************************************************** From: Robert Dewar Date: Wednesday, February 22, 2006 6:46 AM > I agree. A user note would have to say "don't trust the rules above, they > are not exactly right". That's embarrassing for us That's not a valid argument, we are not in the embarassment-avoidance business :-) , and confusing for the > user. I don't see that a carefully written note here would be confusing. We do seem to have a non-obvious consequence here, and I think a note would be helpful. An AARM note is preferable, as it provide guidance to implementers > and textbook writers regarding the intent of the language. The AARM is not an official document and should not be needed by textbook writers. it is occasionally useful for implementors, though I often find that when I go to look for something, the very place that needs a comment has none :-) **************************************************************** From: John Barnes Date: Wednesday, February 22, 2006 1:38 AM Put the note in the place where it is most important and put references to it in the other places. Sounds like we need one of those To be Honest notes or perhaps in this case To Be Dishonest. Where are the three places? **************************************************************** From: Gary Dismukes Date: Wednesday, February 22, 2006 12:23 PM The three relevant places are 4.1(9), 3.10.2(26/2), and 8.5.1(5/2). The first is where it makes sense to centralize notes/annotations about dereferences. **************************************************************** From: Randy Brukardt Date: Thursday, February 23, 2006 8:17 PM > > I agree. A user note would have to say "don't trust the rules above, they > > are not exactly right". That's embarrassing for us > > That's not a valid argument, we are not in the embarrassment-avoidance > business :-) I agree with this. > , and confusing for the user. > > I don't see that a carefully written note here would be confusing. > We do seem to have a non-obvious consequence here, and I think a > note would be helpful. But disagree with this. A "carefully written note" begs the question of why we bothered to write it instead of fixing the silly rules themselves. In any case, I can't come up with a note that would be both understandable and useful. Those of you who want a user note, please suggest some wording for such a note so that we can consider it ASAP. > An AARM note is preferable, as it provide guidance to implementers > > and textbook writers regarding the intent of the language. > > The AARM is not an official document and should not be needed by > textbook writers. If they want to know "why", the AARM is a useful reference. If they don't want to know "why", I wonder about the quality of the textbook... > it is occasionally useful for implementers, > though I often find that when I go to look for something, the > very place that needs a comment has none :-) It's probably too late to fix such things this time around. (And yes, I've had that same experience, although mostly when writing AIs.) If you find something that seems important, please let us know (post here or on Ada comment) -- there is an AI including a list of things that might be fixed in the AARM "next time". If we don't know, it certainly won't happen. **************************************************************** From: Randy Brukardt Date: Thursday, February 23, 2006 8:51 AM > The three relevant places are 4.1(9), 3.10.2(26/2), and 8.5.1(5/2). > The first is where it makes sense to centralize notes/annotations > about dereferences. I don't think the latter two have anything (interesting) to do with dereferences. The point is that a dereference is (almost) always a constrained view. I thought that was the language rule, which is why I was so confused about your question. It certainly ought to be the language rule. Here's the AARM notes I added to 4.1(9): 9.c/2{AI95-00363-01} If an allocator for the access-to-object type T is one that creates objects that are constrained by their initial value (see 4.8), the nominal subtype is constrained even if the designated type of T is not. We don't want the effect of the dereference to depend on the designated object. This matters because general access-to-unconstrained can designate both allocated objects (which are constrained at birth) and aliased stack objects (which aren't necessarily constrained). This is a wording bug that was discovered after the completion of Amendment 1 when it was too late to fix it; we expect that it will be corrected by an early Ada 2005 AI. 9.d/2Implementation Note: {AI95-00363-01} Since we don't depend on whether the designated object is constrained, it is not necessary to include a constrained bit in every object that could be designated by a general access type. And for 3.10.2(26/2): 26.h/2To be honest: {AI95-00363-01} If X is a subcomponent that depends on discriminants, and the subcomponent is a dereference of a general access type whose designated type is unconstrained and whose allocators create objects that are constrained by their initial values (see 4.8), the view is illegal. This is necessary as such a general access type can designate an unconstrained (stack) object, and we can't have legality depending on the designated object (as that is not known at compile-time). This is a wording bug that was discovered after the completion of Amendment 1 when it was too late to fix it; we expect that it will be corrected by an early Ada 2005 AI. And for 8.5.1(5/2): 26.h/2To be honest: {AI95-00363-01} If the renamed entity is a subcomponent that depends on discriminants, and the subcomponent is a dereference of a general access type whose designated type is unconstrained and whose allocators create objects that are constrained by their initial values (see 4.8), the view is illegal. This is necessary as such a general access type can designate an unconstrained (stack) object, and we can't have legality depending on the designated object (as that is not known at compile-time). This is a wording bug that was discovered after the completion of Amendment 1 when it was too late to fix it; we expect that it will be corrected by an early Ada 2005 AI. Improvements welcome. **************************************************************** From: Tucker Taft Date: Friday, February 24, 2006 2:21 PM > Here's the AARM notes I added to 4.1(9): > > 9.c/2{AI95-00363-01} If an allocator for the access-to-object type T is one > that creates objects that are constrained by their initial value (see 4.8), > the nominal subtype is constrained even if the designated type of T is not. Oh dear, you don't want to change the nominal subtype. It must remain as it is. What you want to say is that the *subtype* of the view, not the "nominal subtype," is constrained. It is the subtype, not the nominal subtype, that determines the 'Constrained bit. It is the subtype of the target object, not the nominal subtype of the target object, that is the subtype to which the RHS of an assignment is converted. ... > And for 3.10.2(26/2): > > 26.h/2To be honest: {AI95-00363-01} If X is a subcomponent that depends on > discriminants, and the subcomponent is a dereference of a general access > type whose designated type is unconstrained and whose allocators create > objects that are constrained by their initial values (see 4.8), the view is > illegal. You can make this a bit simpler by simply saying that for a discrmininant dependent subcomponent of a dereference of a general access subtype, if the designated subtype has unconstrained discriminants with defaults, 'Access is not permitted. ... > And for 8.5.1(5/2): > > 26.h/2To be honest: {AI95-00363-01} If the renamed entity is a subcomponent > that depends on discriminants, and the subcomponent is a dereference of a > general access type whose designated type is unconstrained and whose > allocators create objects that are constrained by their initial values (see > 4.8), the view is illegal. This is necessary as such a general access type > can designate an unconstrained (stack) object, and we can't have legality > depending on the designated object (as that is not known at compile-time). > This is a wording bug that was discovered after the completion of Amendment > 1 when it was too late to fix it; we expect that it will be corrected by an > early Ada 2005 AI. Again, I would simply say that if the designated subtype is unconstrained with defaults for discriminants, the renaming is illegal. **************************************************************** From: Randy Brukardt Date: Thursday, February 24, 2006 8:19 PM ... > Oh dear, you don't want to change the nominal subtype. It must > remain as it is. What you want to say is that the *subtype* of > the view, not the "nominal subtype," is constrained. It is the > subtype, not the nominal subtype, that determines the 'Constrained > bit. It is the subtype of the target object, not the nominal > subtype of the target object, that is the subtype to which > the RHS of an assignment is converted. OK, I dropped the word "nominal". ... > > And for 3.10.2(26/2): > > > > 26.h/2To be honest: {AI95-00363-01} If X is a subcomponent that depends on > > discriminants, and the subcomponent is a dereference of a general access > > type whose designated type is unconstrained and whose allocators create > > objects that are constrained by their initial values (see 4.8), the view is > > illegal. > > You can make this a bit simpler by simply saying that for a discrmininant > dependent subcomponent of a dereference of a general access subtype, > if the designated subtype has unconstrained discriminants with defaults, > 'Access is not permitted. OK. "and has discriminants with defaults". (I don't like saying "unconstrained discriminants", because it sounds like the discriminants are unconstrained, not the type with them.) > > ... This is necessary as such a general access type can designate an > > unconstrained (stack) object, and we can't have legality depending on the > > designated object (as that is not known at compile-time). This is a wording > > bug that was discovered after the completion of Amendment 1 when it was too > > late to fix it; we expect that it will be corrected by an early Ada 2005 AI. I've concluded that this isn't really a wording bug (although it would be best to change the wording to be clear). The wording says "unless ... the variable is constrained by its initial value". This is not true for a general access type as above; you can't know whether it is or not (and this is a legality rule), the rule says "is", not "might"! So I changed the paragraph to: To be honest: {AI95-00363-01} If X is a subcomponent that depends on discriminants, and the subcomponent is a dereference of a general access type whose designated type is unconstrained and whose discriminants have defaults, the attribute is illegal. Such a general access type can designate an unconstrained (stack) object. Since such a type might not designate an object constrained by its initial value, the 'Access is illegal — the rule says “is” constrained by its initial value, not “might be” constrained by its initial value. No other interpretation makes sense, as we can't have legality depending on something (which object is designated) that is not known at compile-time, and we surely can't allow this for unconstrained objects. The wording of the rule should be much clearer on this point, but this was discovered after the completion of Amendment 1 when it was too late to fix it. [I still expect that we'll fix it, but there's no critical reason to do so here; only 4.1(9) is actually wrong.] > > And for 8.5.1(5/2): Same changes here. **************************************************************** Summary of private e-mail of December 13-14, 2006 Involving Randy Brukardt, Pascal Leroy, and Stephen Baird. We accidentally uncovered a new problem with the AI. Here's the sequence of events: Randy thought a rule was missing from the AI, and asked Pascal and Steve about it, appending an example from AI95-184 showing the need for the rule. Unfortunately, he appended the *wrong* example from that AI (there are two), commenting that it showed that view conversions might actually matter in the 3.3(23) rules. Pascal replied that this is handled properly by the language, and there isn't any view conversion in the example, but that there was a way to get the effect of a view conversion for that example. Randy ignored most of Pascal's note and sent the correct example (which was about generics). Steve pointed out that the needed wording is in fact in the AI (although Randy missed it completely). He then went on to comment that Pascal's example is in fact a real problem, and suggested a solution based on it being part of the second example Randy sent (rather than the first). Randy responded extensively to Steve's note before realizing that Pascal's example was meant to be part of the *first* example sent. And it wasn't quite legal, as it was missing a conversion: in fact a view conversion, the one that prompted the example in the first place. The net effect is an amazingly confused thread of e-mail. So just a summary of what actually matters is presented here. ------------------------------------------------------------------- Consider the following example (based on AI95-184). package P is pragma Elaborate_Body; type T (D : Integer) is private; private type T (D : Integer) is tagged record C : String (1 .. D); end record; end P; with P; package Q is type NT (ND : Integer := 3) is new T (ND); X : NT; Y : NT (0); end Q with Q, Ada.Text_IO; package body P is procedure P2 (Z : in out T) is -- Assume pass-by-reference C_Ren : Character renames Z.C(2); begin Q.X := Q.Y; Ada.Text_IO.Put (C_Ren); -- Ouch, C_Ren no longer exists. end P2; begin P2 (T(Q.X)); -- OK. end P; Note that T(Q.X) is a view conversion in this context. If we're assuming pass-by-reference, then we have a situation very similar to the one for constrained parameters. Pascal suggested changing 6.4.1(17.1) to say "constrained or indefinite", so that the new rule would also apply to this case. ****************************************************************