!standard 3.07.01 (7/1) 02-05-16 AI95-00291/01 !class binding interpretation 02-05-16 !status received 02-05-16 !qualifier Error !priority Medium !difficulty Hard !subject Another violation of constrained access subtypes !summary !question The following illustrates a case of access subtype constraint violation which is not caught by 3.7.1(7/1): procedure Foo is generic type T is private; Val1, Val2 : T; type Ref is access all T; Ptr : in out Ref; package G is end G; package body G is type A is array (Boolean) of aliased T; X : A := (others => Val1); begin Ptr := X (False)'Access; X := (others => Val2); end G; type Rec (Is_Big : Boolean := True) is record case Is_Big is when False => null; when True => Big_Field : String (1 .. 100); end case; end record; type Ref is access all Rec; Ptr : Ref (Is_Big => True); package I is new G (T => Rec, Val1 => (Is_Big => True, Big_Field => (others => 'B')), Val2 => (Is_Big => False), Ref => Ref, Ptr => Ptr); begin -- at this point, Ptr.all.Is_Big = False null; end Foo; The solution typically used in cases like these (i.e. impose an "assume the worst in a generic body" rule) seems very restrictive in this case. Rejecting the declaration of Foo.G.A on the grounds that its aliased component's subtype might turn out to be unconstrained seems extreme. Should this case be checked for at runtime, as part of the elaboration of the component subtype? I do not see an obvious solution. !recommendation !wording !discussion !ACATS test !appendix From: Steve Baird Sent: Monday, May 13, 2002 4:21 PM The following illustrates a case of access subtype constraint violation which is not caught by AI-00168 and RM 3.7.1(7/1): procedure Foo is generic type T is private; Val1, Val2 : T; type Ref is access all T; Ptr : in out Ref; package G is end G; package body G is type A is array (Boolean) of aliased T; X : A := (others => Val1); begin Ptr := X (False)'Access; X := (others => Val2); end G; type Rec (Is_Big : Boolean := True) is record case Is_Big is when False => null; when True => Big_Field : String (1 .. 100); end case; end record; type Ref is access all Rec; Ptr : Ref (Is_Big => True); package I is new G (T => Rec, Val1 => (Is_Big => True, Big_Field => (others => 'B')), Val2 => (Is_Big => False), Ref => Ref, Ptr => Ptr); begin -- at this point, Ptr.all.Is_Big = False null; end Foo; The solution typically used in cases like these (i.e. impose an "assume the worst in a generic body" rule) seems very restrictive in this case. Rejecting the declaration of Foo.G.A on the grounds that its aliased component's subtype might turn out to be unconstrained seems extreme. Should this case be checked for at runtime, as part of the elaboration of the component subtype? I do not see an obvious solution. **************************************************************** From: Tucker Taft Sent: Tuesday, May 14, 2002 4:38 PM There seem to be at least two ways to address this problem: 1) Adjust 3.6(11) to disallow "aliased T" in a component_definition in a generic body if T might be discriminated with defaults. (The "assume the worst" model would suggest this.) We would require that 3.6(11) be enforced in the private part of a generic instance. 2) Require that for a formal IN OUT object of a general access type whose designated type is discriminated, the subtype of the actual must match statically the formal nominal subtype. (1) seems like a "normal" generalization of the assume-the-worst, and the workaround is to move the composite type declaration up into the private part of the generic. (2) is a smaller change, but sounds more like yet another wart on the language related to access subtypes. **************************************************************** From: Steve Baird Sent: Wednesday, May 15, 2002 1:20 PM > There seem to be at least two ways to address this problem: > ... > 2) Require that for a formal IN OUT object of a general access type > whose designated type is discriminated, the subtype of the actual must > match statically the formal nominal subtype. I don't believe that this solves the problem. The following variation does not involve any IN OUT objects: procedure Foo is generic type T is private; Val1, Val2 : T; type Ref is access all T; package G is function F return Ref; procedure Invalidate; end G; package body G is type A is array (Boolean) of aliased T; X : A := (others => Val1); function F return Ref is begin return X (False)'Access; end F; procedure Invalidate is begin X := (others => Val2); end Invalidate; end G; type Rec (Is_Big : Boolean := True) is record case Is_Big is when False => null; when True => Big_Field : String (1 .. 100); end case; end record; type Ref is access all Rec; package I is new G (T => Rec, Val1 => (Is_Big => True, Big_Field => (others => 'B')), Val2 => (Is_Big => False), Ref => Ref); Ptr : Ref (Is_Big => True) := I.F; begin I.Invalidate; -- at this point, Ptr.all.Is_Big = False ... end Foo; **************************************************************** From: Tucker Taft Sent: Wednesday, May 15, 2002 3:48 PM Good point. I guess that argues for (1), where we assume the worst in a generic body as far as whether we allow use of "aliased" in a component_definition, if the type might be discriminated-with-defaults. **************************************************************** From: Steve Baird Sent: Wednesday, May 15, 2002 4:18 PM That seems harsh. How about making it a runtime check? Program_Error is raised during the elaboration of a an aliased comonent_definition if the component subtype is unconstrained and discriminated. This would not be the first time that a construct which is statically forbidden outside of an instance body is checked for at runtime within an instance body (e.g. accessibility violations). **************************************************************** From: Randy Brukardt Sent: Wednesday, May 15, 2002 4:54 PM Steve may be right. Consider what types "might be discriminanted-with-defaults": generic private types, generic limited private types, and various generic derived types. The last case isn't very interesting, but the former is: type Priv is private; qualifies, and that is probably the most common generic formal parameter. It's hard to imagine that no one ever has used an aliased object or component of a generic private type. So we're talking about potentially breaking a lot of code. Steve did not make clear that this doesn't require a component to occur; it also could occur with just a stand-alone object declaration: V : aliased T;. Moreover, it could occur in a local scope using Unchecked_Access. (Does anyone really use 'Access anyway? I've never found an object case where I COULD use it.) Since that is true, the "work-around" of moving it to the private part doesn't work. So I have to agree with Steve -- I don't think we can mandate a compile-time check. It will have to be a run-time check as the accessibility one is (the problem is very similar, after all). **************************************************************** From: Tucker Taft Sent: Thursday, May 16, 2002 8:47 AM >It's hard to imagine that no one ever has used an aliased object or >component of a generic private type. So we're talking about potentially >breaking a lot of code. It only applies to declaring a composite type in a generic body with an aliased component of such a type. It only applies to non-limited types, since limited types don't allow assignment. >Steve did not make clear that this doesn't require a component to occur; it >also could occur with just a stand-alone object declaration: V : aliased T;. No, stand-alone objects are no problem, since any aliased object is constrained by its initial value, and any assignment would check to be sure the discriminant wasn't changed. The problem is that composite assignment doesn't check constraints on components. I suppose an alternative is to require checks on constraints of all aliased components in a composite assignment. >Moreover, it could occur in a local scope using Unchecked_Access. (Does >anyone really use 'Access anyway? I've never found an object case where I >COULD use it.) Since that is true, the "work-around" of moving it to the >private part doesn't work. It is just the *type* definition that needs to be moved, not any object declarations. >So I have to agree with Steve -- I don't think we can mandate a compile-time >check. It will have to be a run-time check as the accessibility one is (the >problem is very similar, after all). I find introducing additional run-time checks that are effectively enforcing contract model violations pretty distasteful. I would see that as an absolute last resort. On the other hand, if we relax the rules on declaring aliased components, and enforce constraint checks on composite assignments if any aliased components are discriminated with defaults, we actually expand the language rather than restrict it, and eliminate the distinction between generic and non-generic code. **************************************************************** From: Steve Baird Sent: Thursday, May 16, 2002 2:20 PM > I find introducing additional run-time checks that are > effectively enforcing contract model violations pretty > distasteful. It isn't pretty, I agree. > On the other hand, if we relax the rules on declaring > aliased components, and enforce constraint checks on > composite assignments if any aliased components are > discriminated with defaults, we actually expand the > language rather than restrict it, and eliminate > the distinction between generic and non-generic code. If the check is performed during the elaboration of a an aliased component_definition, then the outcome of the check will always be known at compile time for a replicated-instantiation implementation. In the non-error case, there would be no associated overhead. The same does not hold for the "check some (but not all) component subtypes as part of composite object assignment" proposal. Can we at least agree that 3.6(11)'s prohibition of "bad" aliased components ought to extend into the private parts of instances? **************************************************************** From: Steve Baird Sent: Thursday, May 16, 2002 3:36 PM > The same does not hold for the "check some (but not all) > component subtypes as part of composite object assignment" > proposal. There would only be run-time overhead in those cases that would have (or should have ;-) been illegal before. > > Can we at least agree that 3.6(11)'s prohibition of > "bad" aliased components ought to extend into the > private parts of instances? Unless we repeal 3.6(11)... 3.7.1(7) was an attempt to fix a problem with 3.6(11) that had to do with private types, and now we have to fix another hole that has to do with generic formal private types. On the other hand, I guess a run-time check on a composite assignment is kind of a bad time to find out that something is wrong... So, yes, I guess replacing 3.6(11) with a run-time check on composite assignment is a bad idea. Here is an alternative far out idea: Disallow access subtypes for general access types. This would allow us to safely repeal 3.6(11), and recognize that aliased components can change their discriminant values as a side-effect of a composite assignment, or as part of a view conversion (presuming we also allow view-converting array-of-aliased to/from array-of-nonaliased). In fact, we could generally allow the discriminants of aliased objects to change, so long as they are guaranteed to *not* be allocated in the heap. All objects allocated in the heap would still be constrained. We would simply say that the dereference of an access value is a constrained view, but an aliased object that is a component or a stand-alone object is not necessarily constrained. This has the nice side-effect of allowing one to add "aliased" on a component of a stand-alone object declaration without breaking any existing code. As it is now, we allow aliased components to change discriminant values only if they happen to be declared at a place where the component type is private. Losing access subtypes for general access types seems like a small price to pay for simplifications in several other places, and less negative impact of adding "aliased." **************************************************************** From: Randy Brukardt Sent: Thursday, May 16, 2002 4:56 PM > It only applies to declaring a composite type in a generic body with an > aliased component of such a type. It only applies to non-limited types, > since limited types don't allow assignment. The first is clearly wrong (see below). The second is true, but, as I said, the most common formal type is "type P is private", and it clearly covers that. > >Steve did not make clear that this doesn't require a component to occur; it > >also could occur with just a stand-alone object declaration: > V : aliased T;. > > > > No, stand-alone objects are no problem, since any aliased object is > constrained by its initial value, and any assignment would check > to be sure the discriminant wasn't changed. The problem is that > composite assignment doesn't check constraints on components. > I suppose an alternative is to require checks on constraints of > all aliased components in a composite assignment. No, this is not correct. The rule (3.3.1(9)) says that the object is constrained if the object has an "unconstrained nominal subtype". Certainly the "nominal" subtype is constrained in this case, it can not have constraints added to it (see 3.2(9)). Moreover, requiring some sort of constraint check for subtypes without a whiff of constraints would be a significant overhead in Janus/Ada. We'd have to pass an "aliased" flag to all assignment thunks (generic or non-generic). And we'd have to generate discriminant check code into every such thunk. I wrote a test program (attached below), and not a single compiler that I tried made the check on a regular private type, and only one did on the generic private type. So there is little enforcement of this even if it is required (by some language lawyer hoops that I can't figure out). There is a validation test for this area (C3A0014). It uses a generic formal derived type. It's too bad that they didn't use a generic formal private type, because then we could have had this discussion years ago. > >Moreover, it could occur in a local scope using Unchecked_Access. (Does > >anyone really use 'Access anyway? I've never found an object case where I > >COULD use it.) Since that is true, the "work-around" of moving it to the > >private part doesn't work. > > It is just the *type* definition that needs to be moved, not any object > declarations. Sorry, it happens anywhere that "aliased" can be specified on a "nominally" constrained type. > On the other hand, if we relax the rules on declaring aliased components, > and enforce constraint checks on composite assignments if any aliased > components are discriminated with defaults, we actually expand the > language rather than restrict it, and eliminate > the distinction between generic and non-generic code. That would be a good idea if it worked, but it doesn't, as the checks currently aren't required in many cases. Adding them would be incompatible, possibly in ways that would not show up until systems are fielded. Later: >Here is an alternative far out idea: >Disallow access subtypes for general access types. >This would allow us to safely repeal 3.6(11), and >recognize that aliased components can change >their discriminant values as a side-effect of >a composite assignment, or as part of a view conversion >(presuming we also allow view-converting >array-of-aliased to/from array-of-nonaliased). > >In fact, we could generally allow the discriminants >of aliased objects to change, so long as they >are guaranteed to *not* be allocated in the heap. >All objects allocated in the heap would still be >constrained. We would simply say that the dereference >of an access value is a constrained view, but an >aliased object that is a component or a stand-alone >object is not necessarily constrained. Right. That's already true for private types (at least in practice, if not in the standard). >This has the nice side-effect of allowing one to add >"aliased" on a component of a stand-alone object declaration >without breaking any existing code. > >As it is now, we allow aliased components to change >discriminant values only if they happen to be declared >at a place where the component type is private. And the same for stand-alone objects. >Losing access subtypes for general access types seems >like a small price to pay for simplifications in >several other places, and less negative impact of >adding "aliased." I agree. But that is a pretty big change to the language (at least conceptually). It's obviously incompatible, but only at compile-time. It has the advantage of eliminating a bunch of junk rules. I suppose it wouldn't be too large to do in the amendment. I wonder what Robert's customers would think of that (do they use access subtypes on general access types??) Randy. ---- My test program: with Ada.Text_IO; procedure Tuck is subtype Short is Integer range 1 .. 20; type Tuck1 (Dumb : Short := 1) is null record; package P is type Tuck2 is private; C1 : constant Tuck2; C2 : constant Tuck2; private type Tuck2 (Dumb : Short := 1) is null record; C1 : constant Tuck2 := (Dumb => 1); C2 : constant Tuck2 := (Dumb => 2); end P; generic type Tuck3 is private; C1 : in Tuck3; C2 : in Tuck3; Code : in Character; package G is procedure Change; end G; package body G is procedure Change is V3 : Tuck3 := C1; AV3 : aliased Tuck3 := C1; begin V3 := C2; -- OK. begin AV3 := C2; -- ?? Might raise C_E by 3.3.1(9), but the -- nominal subtype is constrained. Ada.Text_IO.Put_Line ("-- AV3 no C_E (" & Code & ')'); exception when Constraint_Error => Ada.Text_IO.Put_Line ("-- AV3 raises C_E (" & Code & ')'); end; end Change; end G; V1 : Tuck1 := (Dumb => 1); V2 : P.Tuck2 := P.C1; AV1 : aliased Tuck1 := (Dumb => 1); AV2 : aliased P.Tuck2 := P.C1; package GP1 is new G (Tuck1, C1 => (Dumb => 1), C2 => (Dumb => 2), Code => '3'); package GP2 is new G (P.Tuck2, P.C1, P.C2, '4'); begin V1 := (Dumb => 2); -- OK. V2 := P.C2; -- OK. begin AV1 := (Dumb => 2); -- Raises C_E by 3.3.1(9) Ada.Text_IO.Put_Line ("** AV1 missed"); exception when Constraint_Error => null; end; begin AV2 := P.C2; -- ?? Might raise C_E by 3.3.1(9), but the -- nominal subtype is constrained. Ada.Text_IO.Put_Line ("-- AV2 no C_E"); exception when Constraint_Error => Ada.Text_IO.Put_Line ("-- AV2 raises C_E"); end; GP1.Change; GP2.Change; end Tuck; **************************************************************** From: Steve Baird Sent: Monday, May 20, 2002 3:07 PM As I read it, the problem illustrated by Randy's example is that in a case like package P is type T is private; private type T (D : Integer := 10) is record ... end record; end P; X : aliased P.T; , the actual subtype of X is thought to be unconstrained. It is claimed that 3.3.1(9) does not apply in this case because the nominal subtype of X is constrained. This boils down to a question whether 3.3.1(9) refers to the full view of P.T (which is unconstrained) or to the partial view that is visible at the point of the object declaration (which is not). Clearly X must be constrained in this example, and if 3.3.1(9) does not already imply this, then it needs to be fixed. I don't see any problem here - Dynamic Semantics rules generally ignore privacy - but it wouldn't hurt to state this explicitly. In any case, this issue is completely separate from the problem with aliased component subtypes in instance bodies that began this thread. **************************************************************** From: Randy Brukardt Sent: Friday, May 24, 2002 7:05 PM > Clearly X must be constrained in this example, and if > 3.3.1(9) does not already imply this, then it needs to be > fixed. I don't see any problem here - Dynamic Semantics > rules generally ignore privacy - but it wouldn't hurt > to state this explicitly. Well, this is messy and expensive to implement in a generic code shared environment, because it means that assignment operations (thunks) can't be composed, or that you have to pass quite a bit of information about the context of the use at runtime. Either option seems like another nail in the coffin of useful code sharing (that's getting to be quite a bed of nails...), since to avoid it you have to know the body of the generic. And this is to support a rather rare case, and one for a rule which is quite obnoxious to begin with. > In any case, this issue is completely separate from the > problem with aliased component subtypes in instance bodies > that began this thread. Not at all. I wanted to show that there were more problems with this rule than just your original one. Tucker's radical solution of repealing 3.3.1(9) (thus eliminating your original problem), and then eliminating the cause of the problems that led to it in the first place, seems like a useful simplification of the language. The only question is whether it would be too incompatible. Do people actually use access subtypes on general access types? **************************************************************** From: Robert Dewar Sent: Friday, May 24, 2002 9:45 PM Perhaps it is time to declare general generic code sharing to be dead. **************************************************************** From: Randy Brukardt Sent: Friday, May 24, 2002 10:02 PM Why the heck doesn't anybody answer the important question of whether making access subtype illegal on general access types is too incompatible? Because if we have to keep them, we're going to have to adopt a very obnoxious assume-the-worst rule or an obnoxious run-time check. It seems better to kill off 3.3.1(9), which is a pain in the butt anyway (because it prevents adding "aliased" to an unconstrained variable). **************************************************************** From: Robert Dewar Sent: Friday, May 24, 2002 10:07 PM If current compilers allow this, then making it illegal has unknown consequences. The effort of finding out these consequences in advance is huge. **************************************************************** From: Randy Brukardt Sent: Friday, May 24, 2002 10:32 PM Of course, but it is relatively easy to disprove the assertion that no one uses access subtypes on general access types. Simply provide a significant example. Proving the assertion is much harder, of course, but I'd like to know whether we need to put some effort in here. After all, the consequences of abandoning the possibility of any generic code sharing(*) in the Ada language also are huge. It is likely that many, possibly all compilers make assumptions about generics which would no longer be true in this model. (one presumes that most of the rules specifically inserted for generic sharing reasons would be repealed). And not solving the problem isn't really an option, either. It's a rather nasty hole which exists only with a particular bad combination of features. So I fear we're going to have to do something "huge" here. Randy. (*) I say "any generic code sharing" because I don't consider any sharing which requires analysis of the body as well as the actual parameters and specification to be "generic code sharing". Such post-compilation sharing can be done on any code sequences that happen to be similar, and really has nothing to do with generics. Indeed, we used to do something much like that in the early days of Janus/Ada in order to get it to fit in 56K Z-80 machines. **************************************************************** From: Robert Dewar Sent: Saturday, May 25, 2002 8:28 AM <> That's the wrong point of view. If current compilers are allowing this, then programs may or may not be using significant or insignificant examples of this usage. I know this is FUD, but we have to be very sure that it is worth while making something illegal that was previously legal, since it can be a huge pain. **************************************************************** From: Randy Brukardt Sent: Friday, June 7, 2002 6:54 PM No, that is the correct point of view, because I am already convinced that it is worthwhile to make this change. (The alternatives being much worse, IMHO, and it fixes other problems at the same time.) That may not be true for others. The main question in my mind is whether it is too incompatible -- that is, does it break too many existing programs. If no one can disprove the assertion, then I would probably spend the "huge" (as you put it) effort to try to find out, by creating a tool that can be run on large volumes of code by users to determine the usage of this (and probably other) features, and then distributing it as widely as possible. However, if there are significant uses of the feature such that there would be a lot of opposition to the change, I'd rather spend the time on something productive (like my resume :-). ****************************************************************