Version 1.1 of ais/ai-00295.txt

Unformatted version of ais/ai-00295.txt version 1.1
Other versions for file ais/ai-00295.txt

!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.

****************************************************************


Questions? Ask the ACAA Technical Agent