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

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

!standard 4.3.1(14)          08-10-15 AI05-0115-1/01
!standard 4.3.2(5/2)
!class binding interpretation 08-10-15
!status work item 08-10-15
!status received 08-07-25
!priority Medium
!difficulty Medium
!qualifier Omission
!subject Aggregates with components that are not visible
!summary
An aggregate is illegal if it has components that are not visible.
!question
Consider the following:
package Pak1 is type T1 is tagged private; private type T1 is tagged record C1 : Integer; end record; end Pak1;
with Pak1; package Pak2 is type T2 is new Pak1.T1 with record C2 : Integer; end record; end Pak2;
with Pak2; package Pak1.Pak3 is type T3 is new Pak2.T2 with record C3 : Integer; end record; procedure Foo; end Pak1.Pak3;
package body Pak1.Pak3 is
procedure Foo is R : T3; N : Integer; begin N := R.C1; -- (A: Error.) R := (C1 => 1, C2 => 2, C3 => 3); -- (B: Legal? No.) R := (C2 => 2, C3 => 3, others => 1); -- (C: Legal? No.) R := (others => 4); -- (D: Legal? No.) end Foo;
end Pak1.Pak3;
(A) is illegal by 7.3.1(4). Although the component F1 of the grandparent type has become visible in the body of Pak1.Pak3, 7.3.1(4) speaks about "additional characteristics of the parent type" becoming visible, and there isn't any place where the hidden component F1 of the type T2 becomes visible. AI95-0157 confirms this interpretation.
That means that (B) is illegal, because one of the component names is not visible. But what about (C) and (D), which define a value for the component without mentioning its name?
!recommendation
(See Summary.)
!wording
Modify 4.3.1(14):
If the type of a record_aggregate is a record extension, then it shall be a descendant of a record type, through one or more record extensions (and no private extensions). {Moreover, the parent type of each of those record extensions shall have been a record type or record extension at the point of the record extension declaration.}
[Editor's Note: An alternative way of wording this would simply require that all components are visible. However, care would be needed to avoid making private types which are null records always legal, as that would break privacy. One way of wording that would be something like:
A record_aggregate is illegal if any possible components of the composite value defined by the type of the aggregate are not visible or not declared.
It's not clear that this is any actual improvement; it's pretty vague about "possible components". It also doesn't work as well for the extension aggregate case. Thus I didn't use it. End Editor's Note.]
AARM Note:
The last sentence prevents aggregates where not all of the components are visible, even though all of the types are full views at the point of the aggregate. For example:
package Pak1 is type T1 is tagged private; private type T1 is tagged record C1 : Integer; end record; end Pak1;
with Pak1; package Pak2 is type T2 is new Pak1.T1 with record C2 : Integer; end record; end Pak2;
with Pak2; package Pak1.Pak3 is type T3 is new Pak2.T2 with record C3 : Integer; end record; procedure Foo; end Pak1.Pak3;
package body Pak1.Pak3 is
procedure Foo is R : T3; begin R := (others => 4); -- (Illegal.) end Foo;
end Pak1.Pak3;
By requiring that all of the parent types are record types or record extensions at the point of the derivation, we avoid this problem.
Modify 4.3.2(5/2):
... The type of the extension_aggregate shall be derived from the type of the ancestor_part, through one or more record extensions (and no private extensions). {Moreover, the parent type of each of those record extensions shall have been a record extension at the point of the record extension declaration.}
!discussion
Logically, an aggregate is a shorthand for setting each component individually. If setting a component explicitly is illegal because of a visibility issue, then the same rule ought to apply to the equivalent aggregate. Thus all of the example aggregates are illegal.
The best way to fix this is to fix 4.3.1(14) to make it clear that what matters whether the parent type was a partial view at the point of the derivation (whether the parent type is a partial view at the point of the aggregate is irrelevant).
Note that a similar case can be constructed for extension aggregates: Make type T1 in the example derived from a root type, then aggregates like:
R := (Root with C1 => 1, C2 => 2, C3 => 3); -- (Illegal.) R := (Root with C2 => 2, C3 => 3, others => 1); -- (Illegal.) R := (Root with others => 4); -- (Illegal.)
[Editor's Second Note: I'm not completely convinced that there isn't some weird case where the components get declared in the body. There are such cases for operations that are declared in nested packages, but I think that components don't have any such cases because a nested package can't add any components to an outer type. Steve Baird may prove me wrong...]
!corrigendum 4.3.1(14)
Replace the paragraph:
If the type of a record_aggregate is a record extension, then it shall be a descendant of a record type, through one or more record extensions (and no private extensions).
by:
If the type of a record_aggregate is a record extension, then it shall be a descendant of a record type, through one or more record extensions (and no private extensions). Moreover, the parent type of each of those record extensions shall have been a record type or record extension at the point of the record extension declaration.
!corrigendum 4.3.2(5/2)
Replace the paragraph:
If the ancestor_part is a subtype_mark, it shall denote a specific tagged subtype. If the ancestor_part is an expression, it shall not be dynamically tagged. The type of the extension_aggregate shall be derived from the type of the ancestor_part, through one or more record extensions (and no private extensions).
by:
If the ancestor_part is a subtype_mark, it shall denote a specific tagged subtype. If the ancestor_part is an expression, it shall not be dynamically tagged. The type of the extension_aggregate shall be derived from the type of the ancestor_part, through one or more record extensions (and no private extensions). Moreover, the parent type of each of those record extensions shall have been a record extension at the point of the record extension declaration.
!ACATS Test
B-Tests for examples like these should be constructed.
!appendix

!topic Component visibility question
!reference 4.3.1(14), 7.3.1(4), AI95-157
!from Adam Beneschan 08-07-25
!discussion

I'm having trouble figuring out how the visibility and private-operation rules
apply in this case:
    
    package Pak1 is
        type T1 is tagged private;
    private
        type T1 is tagged record
            F1 : Integer;
        end record;
    end Pak1;

    with Pak1;
    package Pak2 is
        type T2 is new Pak1.T1 with record
            F2 : Integer;
        end record;
    end Pak2;

    with Pak2;
    package Pak1.Pak3 is
        type T3 is new Pak2.T2 with record
            F3 : Integer;
        end record;
        procedure Foo;
    end Pak1.Pak3;

    package body Pak1.Pak3 is

        procedure Foo is
            R : T3;
            N : Integer;
        begin
            N := R.F1;                            -- (A)
            R := (F1 => 1, F2 => 2, F3 => 3);     -- (B) 
            R := (F2 => 2, F3 => 3, others => 1); -- (C)
            R := (others => 4);                   -- (D)
        end Foo;

    end Pak1.Pak3;

I'm pretty sure that the component selection in (A) is illegal, based on
the wording in 7.3.1(4).  Although the component F1 of the grandparent type
has become visible in the body of Pak1.Pak3, 7.3.1(4) speaks about "additional
characteristics of the parent type" becoming visible, and there isn't any place
where the hidden component F1 of the type T2 becomes visible.  I think this
case is covered by AI95-157 also.

(B) is a rather different case.  Normally, an aggregate would be illegal for
a derived type if the ultimate ancestor were a private type (rather than a
record type), or if any private extensions were involved (4.3.1(14)).  In this
case, though, there are no private extensions; and at the point where statement
(B) occurs, the type is derived from a record type, not a private type, since
the full view of T1 is available and T1 is a record type. Should 4.3.1(14)
somehow be interpreted so that, in this case, T3 is still considered a
descendant of a "private type" rather than a "record type" since there's an
intermediate parent type for which "additional characteristics" have not become
visible?

Even if the aggregate in (B) is not illegal by 4.3.1(14), it would still seem
to be illegal since one of the component names, F1, is not actually a visible
component of the type (for the same reason as in (A)).  Assuming that's the
case, then what about (C) and (D)?  Are they illegal or not?

I'm guessing that AI95-157 applies to (B), (C), and (D) also, and that any
record aggregate of type T3 is illegal regardless of what components are or
are not named using named notation.  [Extension aggregates are still OK.]
But it's not 100% clear to me that the principle applies, given that the
wording of 4.3.1(14) doesn't seem to use any of the terms that 7.3.1(4)
or AI95-157 discuss.  (Is the ability to specify an aggregate an "operation"
of a record type?  I don't think the RM answers that definitively.)

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

From: Adam Beneschan
Sent: Friday, August 8, 2008  5:25 PM

Since I posted this, I found that this question affects a publicly available
open-source Ada suite, PolyORB.  If I'm right, then the Ada source for that
suite is illegal (but GNAT apparently does not catch it).  If possible, I'd
appreciate some opinion on whether this code is actually illegal, before I
make a bug report to the maintainers.

Here are the relevant constructs from the source:

    package PolyORB.SOAP_P.Message is
       type Object is tagged private;
    private
       type Object is tagged record
          Name_Space   : Unbounded_String;
          Wrapper_Name : Unbounded_String;
          P            : SOAP_P.Parameters.List;
       end record;
    end PolyORB.SOAP_P.Message;

    package PolyORB.SOAP_P.Message.Response is
       type Object is new Message.Object with null record;
    end PolyORB.SOAP_P.Message.Response;

    with PolyORB.SOAP_P.Message.Response.Error;
    package body PolyORB.SOAP_P.Message.XML is

       (... in a Load_Response function:)

       return new Message.Response.Object'       -- LEGAL???
          (Null_Unbounded_String,
           S.Wrapper_Name,
           S.Parameters);

    end PolyORB.SOAP_P.Message.XML;

If my interpretation is correct, then the components of the derived type
Message.Response.Object (and, indeed, the fact that it's a record) do not
become visible until a point in PolyORB.SOAP_P.Message.Response where the
private part of PolyORB.SOAP_P.Message is visible, i.e. the private part of
PolyORB.SOAP_P.Message.Response.  And, since the private part of
PolyORB.SOAP_P.Message.Response is not at all visible inside
PolyORB.SOAP_P.Message.XML, the inherited components of Message.Response.Object
are not visible either, and therefore the aggregate should not be legal.

Thoughts?

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

From: Randy Brukardt
Sent: Friday, August 8, 2008  7:34 PM

Without considering the case very fully, it seems clear that the intent of
4.3.1(14) is that a record aggregate is not allowed for a record extension
if any of the ancestors is a partial view rather than a record of some sort.
And clearly this needs to be "frozen" once an extension is done which is
out of scope of the original type, just as for components. So I think that
an aggregate should be illegal (given the current rules; I would have
preferred that we find a way to allow aggregates for private types, but
since that failed we shouldn't be allowing funny holes).

But there clearly is no current reason to think that the Standard answers
this question at all. The only reason to think this way is that the model of
AI95-0157 should apply here, too, and that will require a new rule of some
sort. So it's probably impossible to say definitely that a compiler that
works some other way is wrong. Whether to send in a bug report is therefore
impossible to answer.

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



    

Questions? Ask the ACAA Technical Agent