Version 1.1 of acs/ac-00146.txt

Unformatted version of acs/ac-00146.txt version 1.1
Other versions for file acs/ac-00146.txt

!standard 4.9.1(1.2/2)          07-08-03 AC95-00146/01
!class confirmation 07-08-03
!status received no action 07-08-03
!status received 07-06-15
!subject Streaming access discriminants
!summary
!appendix

From: Stephen W Baird
Sent: Friday, June 15, 2007  5:02 PM

Consider the default implementation of the Input attribute for a
nonlimited type T with access discriminants.

Is this function allowed to return a value which would fail the check of
6.5(21/2) relative to the master that elaborated the declaration of T?

In other words (very roughly speaking), does/should 6.5(21/2) somehow
apply to T'Input ?

The default implementation of T'Input for a specific discriminated type
without default discriminant values is intended to be equivalent (with
respect to dynamic semantics) to:

  function Input
             (Stream : not null access
                       Ada.Streams.Root_Stream_Type'Class) return T is
    D1 : constant First_Discriminant_Type
                := First_Discriminant_Type'Input (Stream);
    ...
    Dn : constant Nth_Discriminant_Type
                := Nth_Discriminant_Type'Input (Stream);

    Result : T (D1, ... , DN);
  begin
    T'Read (Stream, Result);
    return Result;
  end Input;

By this argument, there ought to be an accessibility check.

On the other hand, the RM says nothing about any such check.

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

From: Tucker Taft
Sent: Friday, June 15, 2007  5:53 PM

Ooooh, that's a nasty one.  The default input
routine for an access value is pretty lame,
since it can generally only work if the
corresponding output was performed during the
same execution, or if the value is null.
These default input routines can't really
do much in the way of checking either,
since knowing whether one has a legitimate
access value is pretty hard.  I suppose we
could check when the designated type is tagged
that the tag matches some reasonable tag.
But it seems that one is pretty much forced
into treating the 'input of an access value
as an unchecked conversion.  Given that,
accessibility checks seem hopeless.

Bottom line: it seems like it would make
sense to *permit* an implementation to
perform an accessibility check, but not require it.

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

From: Randy Brukardt
Sent: Friday, June 15, 2007  6:28 PM

Let's use the actually wording of the Standard here (13.13.2(35/2)):

"For each component that is of an access type, if the implementation can
detect that the value returned by Read for the component is not a value of
its subtype, Constraint_Error is raised. If the value is not a value of its
subtype and this error is not detected, the component has an abnormal value,
and erroneous execution can result (see 13.9.1)."

This rule applies only to components. For stand-alone attributes, there is
similar wording in 13.9.1(6/2) where abnormal is defined; since an access
type is non-scalar, that rule applies.

Keep in mind that even null could have a different representation in
different places (I think that would be weird, but nothing in the language
seems to require a single representation for null).

As such, I don't think there is much useful that can be guarenteed about
such attributes.

> I suppose we could check when the designated type is tagged
> that the tag matches some reasonable tag.
> But it seems that one is pretty much forced
> into treating the 'input of an access value
> as an unchecked conversion.  Given that,
> accessibility checks seem hopeless.
>
> Bottom line: it seems like it would make
> sense to *permit* an implementation to
> perform an accessibility check, but not require it.

Well, it is already permitted, in the sense that the program is very likely
to be erroneous. The only problem is that you couldn't make the check until
the discriminant is accessed somehow.

Humm, what about the case where the access attributes are overridden? The
default attribute of a composite type would then use those overridden
attributes. In that case, the program is not likely to be erroneous, and
Steve's concerns would seem to be more valid. No, actually these
discriminants would have to be anonymous.

Shouldn't the default T'Read raise Program_Error for an anonymous access
type? It cannot do anything useful [presuming that you don't think reading
in null is useful]; even the storage pool could differ for different
occurrences. An alternative would be to require overriding for any type that
contains an anonymous access component (that is already true for external
streaming, which is really the only interesting kind of streaming). A worse
alternative is to just say that reading an anonymous access value is
erroneous. Any of these rules would eliminate anomalies like Steve's.

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

From: Stephen W Baird
Sent: Monday, June 18, 2007  8:29 PM

> Let's use the actually wording of the Standard here (13.13.2(35/2)):
>
> "For each component that is of an access type, if the implementation can
> detect that the value returned by Read for the component is not a value of
> its subtype, Constraint_Error is raised. If the value is not a value of its
> subtype and this error is not detected, the component has an abnormal value,
> and erroneous execution can result (see 13.9.1)."

I think the discussion has strayed away from my original question.

I'm asking about the nonerroneous case where the object designated by
the streamed access discriminant value still exists at the time of the call
to T'Input, although that designated object has a shorter lifetime than T.

For example:

    type T (D : access Float) is null record;

    type Global_Ref is acccess T;
    Global_Ptr : Global_Ref;

    procedure P (S1, S2 : not null
                          access Ada.Streams.Root_Stream_Type'Class) is
        -- assume S1 and S2 initially empty

        Designated : aliased Float;

        X : T (Designated'Access);

        type Local_Ref is access T;
        Local_Ptr : Local_Ref;
     begin
        begin
           T'Output (S1, X);
           Global_Ptr := new T'(T'Input (S1));
        exception
           when Program_Error => ... ;
        end;

        begin
           T'Output (S2, X);
           Local_Ptr := new T'(T'Input (S2));
        exception
           when Program_Error => ... ;
        end;
     end P;

Consider the first allocator. Either the first call to T'Input itself
fails a 6.5(21/2)-ish accessibility check or else T'Input somehow
returns enough information to allow the accessibility check of
4.8(10.1/2) to be performed  (and, in particular, to fail).

This distinction isn't really very important in the case of the first
allocator, so consider the second allocator. If T'Input performs the
check, then the second call to T'Input fails in the same way as the
first. On the other hand, if T'Input somehow returns an accessibility
level or something like that in order to allow the 4.8(10.1/2) check to be
performed, then in this case the allocator's check will succeed.

Having T'Input perform a 6.5(21/2)-ish accessibility check is easy
to implement and it seems like a cleaner definition because it
follows the equivalence rule.

On the other hand, there is no mention of this check in the RM and
it could cause potentially useful constructs such as the second allocator
in the example to fail at runtime.

Tuck proposes a third alternative: "treating the 'input of an access value
as an unchecked conversion". If this means that the first allocator is allowed
to create a dangling reference, then I don't think the RM either allows or
should allow this interpretation. Tuck - is this what you meant?

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

From: Randy Brukardt
Sent: Monday, June 18, 2007  9:02 PM

...
> I think the discussion has strayed away from my original question.
>
> I'm asking about the nonerroneous case where ...

And we're discussing whether there *are* any nonerroneous cases. (If there
are not, your question is moot.)

As far as I can tell, the above wording allows any streaming of an access value
(using the default stream attributes) to produce an abnormal access value,
because there is no definition in the language of what "a value of its subtype"
means for an access value. (It would have been better for the language to
simply say this, because then we wouldn't have to worry about anomalies.)
Moreover, depending on the storage pool, I can imagine circumstances where
uncontrolled duplication of access values would destroy invariants and lead
directly to erroneous execution. (I can especially imagine anonymous access
types being implemented this way, as they are already special.)

My preference is to admit defeat here, say the result of T'Input and T'Read
is abnormal if it is an access value, and not bend my brain around yet
another Baird anomaly that no one in the right mind would ever try to use.

....
> Tuck proposes a third alternative: "treating the 'input of an access value
> as an unchecked conversion". If this means that the first allocator is allowed
> to create a dangling reference, then I don't think the RM either allows or
> should allow this interpretation. Tuck - is this what you meant?

Not speaking for Tuck, I believe that the resulting object from your T'Input
calls has an abnormal discriminant. Any touching of that discriminant makes
the program erroneous, and we do not care what happens after that.

Note that no language change is strictly required. Any implementation can
define the read of the discriminant to fail to produce a valid value (and
this requires no implementation changes). If, on the other hand, you can't
bring yourself to consider this erroneous, then you'll have to figure out
how to make the checks where the language requires - which is not inside of
T'Input! (I don't recommend this.)

It probably would be better to be clearer that using the default stream
attributes to read an access value from a stream is abnormal (which of course
doesn't mean that it can't work, it just isn't guaranteed to work). Given it is
already completely determined by the implementation (and unspecified by the
language) as to whether or not it will work, eliminating any suggestion that it
might work portably would be an improvement in my view.

The thing that I hate is that an implementation is not allowed to raise
Program_Error for such dangerous reads. (Note that the value is abnormal, so
if the program doesn't ever read it, there is no justification for raising
an exception.) But I suspect that would be considered too much change to the
language.

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


Questions? Ask the ACAA Technical Agent