Version 1.1 of ai05s/ai05-0154-1.txt

Unformatted version of ai05s/ai05-0154-1.txt version 1.1
Other versions for file ai05s/ai05-0154-1.txt

!standard 3.10.2(27)          09-06-01 AI05-0154-1/00
!class Amendment 09-06-01
!status work item 09-06-01
!status received 09-03-25
!priority Medium
!difficulty Medium
!subject Unconstrained 'Access on array components
!summary
** TBD **
!problem
Ada 95 has a rule that if you declare an aliased array, you can't use an 'Access of the array as a pointer-to-unconstrained-array type unless the nominal type of the array is unconstrained. This means that if you want to use the array access in such a manner, you have to provide an initializer:
type String_P is access String; Ptr : String_P;
A1 : aliased String(1..100); A2 : aliased String := (1..100 => <>);
Ptr := A1'access; -- Illegal Ptr := A2'access; -- OK
That's annoying, but if the same case occurs for a record component, there is no workaround as the component is required to be constrained:
type Rec_Type is record A_Name : aliased String(1..100); ... other fields end record; Rec_Object : Rec_Type;
Ptr := Rec_Object.A_Name'access; -- Illegal
This can be limiting in some circumstances.
The purpose of this rule is to allow implementations avoid generating dope for constrained array objects. If the dope isn't generated, it may not be in the place the compiler expects it for access-to-unconstrained (if it is expected to be contiguous, for example).
!proposal
Provide syntax to declare that any aliased constrained array object (including components) can be used to provide an access-to-unconstrained. This would force the compiler to create the needed dope at that point, in the same way that the unconstrained object A2 creates that dope.
Syntax TBD.
Suggestions:
type Rec_Type is record A_Name : aliased String(range <> => 1..100); ... other fields end record;
[Seems to be in the wrong place - part of the index - to me - RLB.]
type Rec_Type is record A_Name : aliased <> String(1..100); ... other fields end record;
[Still weird.]
type Rec_Type is record A_Name : aliased array String(1..100); ... other fields end record;
[Not improving. :-)]
type Rec_Type is record A_Name : aliased <> array String(1..100); ... other fields end record;
[And I give up... - RLB]
!wording
** TBD **
--!corrigendum 3.10.2(27)
!ACATS test
Add an ACATS test for this new feature.
!appendix

!topic Unconstrained 'Access on constrained arrays
!reference RM 3.10.2(27)
!from Adam Beneschan 09-03-25
!discussion


Ada 95 has a rule that if you declare an aliased array, you can't use an 'Access
of the array as a pointer-to-unconstrained-array type unless the nominal type of
the array is unconstrained.  This means that if you want to use the array access
in such a manner, you have to provide an initializer:

   type String_P is access String;
   Ptr : String_P;

   A1 : aliased String(1..100);
   A2 : aliased String := (1..100 => <>);

   Ptr := A1'access;      -- ILLEGAL
   Ptr := A2'access;      -- LEGAL

At least in Ada 2005 you can use <> as the initializer; in Ada 95 you had to
think up some pointless value to use.  This has been discussed in a recent
comp.lang.ada thread.  I've explained what I believe is the motive for this
somewhat strange rule (i.e. not requiring dope on every aliased constrained
array), but so far everyone is unimpressed. Simon Wright is having a current
problem with this:

    type Stream_Type
      (Buffer : access Ada.Streams.Stream_Element_Array)
    is new Ada.Streams.Root_Stream_Type with private;
    --  Provides an in-memory Stream over the elements of Buffer.
    --
    --  When one of these Stream_Types is created, it is notionally
    --  empty. If Buffer is not in fact empty (perhaps it has been read
    --  from a datagram socket), use Set_Last to indicate the index of
    --  the last valid element.

He says that "having to initialize a buffer of size 2048 (say) with useless data
and then overwrite it by reading from a socket goes against the grain".

It does seem to me that there should be a better way to deal with this problem
than forcing users to provide a useless initializer.

A worse problem, however, is that the "Unconstrained Nominal Type" (UNT)
workaround (declaring an unconstrained array and providing an initializer) is
illegal for a component, including a record or protected type component.  Thus,
you cannot declare a record

   use Ada.Streams;
   type Rec_Type is record
      Buffer : aliased Stream_Element_Array (1..2048);
      ... other fields
   end record;

and use Rec_Object.Buffer'Access where an
access-to-unconstrained-Stream_Element_Array is needed.  But you can't work
around this the same way:

   type Rec_Type is record
      Buffer : aliased Stream_Element_Array := (1..2048 => <>);
      ... other fields
   end record;

because 3.6(10) says that a component_definition must have a definite subtype,
and this applies to record and protected components also even though this is in
the section on arrays.

My first thought was that perhaps the restriction in 3.6(10) was unnecessary in
the case of record and protected components with default initializers.  That
could cause problems if the initializer is a function call, though, instead of
an aggregate.  Perhaps rewording 3.6(10) to say that a component_definition can
be an indefinite subtype only if it is part of a component_declaration that has
a default_expression and only if the default_expression is an aggregate might
work; for record types, it might also be helpful to specify that in any
aggregate of the record type, the expression associated with that component must
have a constraint that matches that of the default expression, so that you can't
say

   R : Rec_Type := (Buffer => (1..4000 => 0), ...)

and change the constraint.

But all of this seems like a very clunky to save the UNT workaround for record
and protected components.  Perhaps it would be better to provide a different
syntax, so that one can define an aliased object or component with a constrained
array subtype and still indicate that its 'Access can be used as an
access-to-unconstrained.  The compiler would know it has to add extra dope if
the implementation requires it.  I'm sure that a change like that would be less
confusing to at least a couple of comp.lang.ada posters, as well as allowing
this kind of 'Access on record components.

I don't know what the syntax should be, though.  I suggested something on
comp.lang.ada but it wasn't entirely serious.  Maybe something like this copying
the "range <>" syntax from the syntax for unconstrained array type definitions,
to make it clear that this object can be "seen as", or accessed as, an
unconstrained array?

   type Rec_Type is record
      Buffer : aliased Stream_Element_Array (range <> => 1..2048);
   end record;

But that's just off the top of my head...

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

From: Randy Brukardt
Date: Wednesday, March 25, 2009  6:14 PM

> Ada 95 has a rule that if you declare an aliased array, you can't use
> an 'Access of the array as a pointer-to-unconstrained-array type
> unless the nominal type of the array is unconstrained.  This means
> that if you want to use the array access in such a manner, you have to
> provide an initializer:

You are focusing on arrays, but you have to keep in mind that the same rule
applies to record types and there it prevents all kinds of evil:

    type Rec (B : Boolean := True) is record
        case B is
            when True => Now_You_See_It : Natural;
            when False => null;
        end case;
    end record;

    type Acc_Con_Rec is Rec (True);

    Obj : aliased Rec := (True, 0);

    Acc : Acc_Con_Rec := Obj'Access; -- Currently illegal.
    OK : Natural renames Acc.Now_You_See_It; --??
  begin
    Obj := (B => False);
    if OK > 0 then -- Where did the object go?
       null;
    elsif Acc.Now_You_See_It > 0 then  -- Oops!

Here, you would have to do variant discriminant checks on every reference even
though the access type is constrained. (Effectively, you would have to ignore
the constraint.) And you'd have to disallow the renames statically, again
ignoring the constraint. Yuck.

The array case doesn't have these problems because you can't change the bounds
after the fact. (Which is annoying, but that's a different issue.) But having
very different rules for arrays and records is annoying at best.

For the record, Janus/Ada actually implemented all of these "implicit"
conversions originally, because early version of Ada 9x didn't have the
statically matching rule. But the array cases leaked memory (we were adding
array descriptors on the fly, but they can't be recovered until the access type
goes away -- which is usually when the program ends). When I complained about
being unable to fix this to the Ada 9x team, their solution was the static
matching rules that everyone loves to hate. But I don't see a real alternative
unless we're willing to add warts all over the language.

[This is a classic case of what Tucker Taft likes to describe as the "bump under
the carpet". He says that you can move the bump around to different places under
the carpet, but you can't get rid of the bump without tearing the carpet up
(which is a huge hassle and in many cases just too much change).]

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

Questions? Ask the ACAA Technical Agent