Version 1.16 of ais/ai-00252.txt

Unformatted version of ais/ai-00252.txt version 1.16
Other versions for file ais/ai-00252.txt

!standard 04.01.03 (09)          05-02-05 AI95-00252/10
!standard 04.01.03 (13)
!standard 04.01.03 (15)
!standard 04.01.03 (17)
!standard 06.03.01 (10)
!class amendment 00-12-04
!status Amendment 200Y 03-10-23
!status WG9 Approved 03-12-12
!status ARG Approved 9-0-1 03-10-04
!status work item 00-12-04
!status received 00-12-04
!priority High
!difficulty Hard
!subject Object.Operation notation
!summary
An equivalence is proposed between Object.Operation(...) and Package.Operation(Object,...) to allow an object-oriented programming model that is based on applying operations to objects, rather than selecting operations from a package and then applying them to parameters.
!problem
In Ada 95, one must identify the particular package in which an operation is defined, in addition to the primary "controlling" object to which the operation is to be applied. Identifying both the package and the object is to some extent redundant, and makes object-oriented programming in Ada 95 wordier than necessary, and/or encourages heavy use of potentially confusing "use" clauses. Eliminating this redundancy would make object-oriented programming less error-prone, while also clarifying what object is the controlling object.
!proposal
We propose a syntactic shorthand Object.Operation(...) as an equivalent to Package.Operation(Object, ...) where "Package" is the package in which Object's type (or covering thereof) is declared. The equivalence would be based on a "use"-like visibility model, where after the ".", primitive and class-wide operations from the package in which an object's type is declared, as well as class-wide operations from packages in which types that "cover" the object's type are declared, would be effectively "use" visible. Further, the first parameter to the operations made "use" visible would (implicitly) be the object preceding the ".". This syntactic shorthand would be provided for objects of a tagged type, or objects designating a tagged type. Note: We considered generalizing this to allow non-tagged types to use this shorthand, but this becomes complex when the type is an access type, since both the access type itself, and its designated type must be considered. Furthermore, the benefit is lower since there is no issue of class-wide operations for non-tagged types, so all the "interesting" operations are all from a single package.
The "use"-like visibility model ensures that the operations made visible would be hidden by any components with the same identifier as the operation (since they are "directly" visible after the ".", rather than "use" visible). In addition, no "beaujolais" effects would be produced, because the operations made visible would all overload one another, rather than having operations from one package hide another.
Because of access parameters, if the type of the prefix is an access-to-tagged type, the package(s) in which the designated type and class-wide types that cover the designated type would be "use"d. Furthermore, to preserve the model that "." can result in an implicit dereference, an interpretation of the prefix as an implicit dereference is coupled with a "use" of the packages in which the designated type and its coverings are declared. In this implicit dereference case, the package where the access type itself is declared is irrelevant.
As a final addition, if the object is aliased, interpretations where 'Access would be needed after the object name are considered. That is, even if the prefix is not of an access type, operations with the first parameter being an access parameter designating the type of the prefix are considered. This would mean that access parameters can be used in general in primitives, without penalizing the user who actually declares an (aliased) object on the stack rather than using the heap.
'Access must not be applied to the view of the subprogram created by this shorthand, nor can we allow a renaming-as-body, since this would imply that one parameter of the call is already bound, while the others are not. Defining the convention to be Intrinsic for the this view of the subprogram prevents these complications.
One way of describing what is happening is that operations declared in the package immediately enclosing a tagged type declaration that have the type (or "access" type or type'Class) as their first parameter, are implicitly declared as components of a type, but with the first parameter removed. These implicitly declared components are then inherited when the type is extended. Use-visibility rules are used for these implicitly-declared components to avoid "beaujolais" effects.
Note that if the prefix type is neither tagged nor access to tagged, no shorthand is defined. If the type is tagged, it makes available the class-wide operations from the packages where "coverings" are declared, plus the primitives from the package where the type itself is declared. If the type is an access-to-tagged type, it makes available the operations that would have been made available for the designated type.
!wording
Add the following after 4.1.3(9):
* A view of a subprogram whose first formal parameter is of a tagged type or is an access parameter whose designated type is tagged:
The prefix (after any implicit dereference) shall resolve to denote an object or value of a specific tagged type T or class-wide type T'Class. The selector_name shall resolve to denote a view of a subprogram declared immediately within the declarative region in which an ancestor of the type T is declared. The first formal parameter of the subprogram shall be of type T, or a class-wide type that covers T, or an access parameter designating one of these types. The designator of the subprogram shall not be the same as that of a component of the tagged type visible at the point of the selected_component. The selected_component denotes a view of this subprogram that omits the first formal parameter.
Add the following after 4.1.3(13):
Legality Rules
If a selected_component other than an expanded name resolves to denote a view of a subprogram whose first parameter is an access parameter, the prefix shall denote an aliased view of an object.
Add the following after 4.1.3(15):
For a selected_component with a tagged prefix and a selector_name that denotes a view of a subprogram, a call on the view denoted by the selected_component is equivalent to a call on the subprogram with the first actual parameter being provided by the object or value denoted by the prefix (or the Access attribute of this object or value if the first formal parameter is an access parameter), and the remaining actual parameters given by the actual_parameter_part, if any.
Add the following to 4.1.3(17):
X.Activate -- a procedure call assuming X has (see 6.4)
-- a tagged type
Add the following after 6.3.1(10):
* the view of a subprogram denoted by a selected_component whose prefix denotes an object or value of a tagged type, and whose selector_name denotes a subprogram operating on the type (see 4.1.3).
!discussion
This AI grew out of an issue identified by Erhard Ploedereder and his graduate students, where it made OOP awkward to always identify (or "use") the specific package in which a dispatching operation was declared, particularly when the operation was inherited, and hence only implicitly declared in that package. Furthermore, the rules for calling class-wide operations and primitive operations were significantly different, where the class-wide operation was not inherited, and hence "remains" in the original package where it was declared, whereas the primitive operations were inherited, and hence got carried along into the package where the type was declared. The tendency was to "use" every package that might conceivably have an operation of interest, which can significantly add to the confusion.
We considered an Object'Operation(...) syntax, but that was felt to introduce possible conflicts with implementation-defined attributes. Also, the "." notation had the additional nice feature that a primitive function could be used to effectively provide a "read only" component, with the familiar "." syntax. Also, using the "." notation allows primitives defined outside a protected or a task type to be called in the same "obj.operation" notation used for entries and protected subprograms. This allows tagged primitive operations to be called with the same style, potentially making programs more consistent.
We considered only making primitive operations visible, but there are situations where an abstraction uses a class-wide operation very much like a primitive operation. For example in Claw, whether a given operation is class-wide or primitive is not particularly relevant to how it is used (though of course it is relevant to how it is handled in type extensions). The "package"-oriented approach, as opposed to the "primitive"-oriented approach, may also fit more cleanly into existing overload resolution algorithms, since it has more in common with the way "use" visibility works currently. Of course, any guess of implementation burden is hard to make, since compilers have so many different strategies.
When using this notation, class-wide operations can be used which are declared in any package which declares an ancestor of the type of the object. We considered only allowing class-wide operations declared in the same package as the specific type of the object. However, this would mean that a different set of operations would be visible for the calls Op (Obj, ...) and Obj.Op(...) when all of the ancestor packages have use clauses (a common situation). That means that changing from one form of the call to the other could potentially change the operation called.
The implicit .all and "'Access" are added because they seem like useful capabilities which do not significantly complicate the proposal. This does not change the requirements on the object; in particular, an access parameter's object must be an aliased view. This is a legality rule, because we don't want resolution to depend on whether something is aliased.
We talk about "covering" types rather than "ancestor" types for two reasons. One is that it is only operations on class-wide types that are being imported from packages other than the one in which the type itself is declared. Second, the notion of "covers" will presumably generalize better if we adopt the notion of abstract interfaces (see AI-251).
Note that operations on class-wide types can be used even if the package in which they are declared is not "with"ed. This is part of the whole point. A class-wide operation may be introduced anywhere in the type hierarchy, and it will be available via the object.operation(...) notation without having to identify the specific package. Since only packages where the "coverings" of the type are declared are considered, this "implicit with" is not actually creating any new semantic dependences. Essentially it is as though these class-wide operations are being inherited, but with no possibility of overriding. They are like "final" operations in Java.
We originally generalized this to support non-tagged types, but the added complexity this brought to handling access types seemed more than the anticipated benefit, since we would have to consider primitives of the access type itself as well as those of its designated type.
!example
Here is an example of use of the "object.operation" syntax:
package P is type T is tagged ... -- implicit declaration of T'Class procedure Prim(X : in out T); procedure Class_Wide(X : in out T'Class; Y : Integer); end P;
with P; package P2 is type T2 is new P.T with ... -- implicit declaration of T2'Class -- implicit declaration of Prim(X : in out T2); procedure Prim2(X : in out T2; B : Boolean); function Prim3(X : T2) return Float; end P2;
with P2; procedure Main is Obj : P2.T2; CObj : P2.T2'Class := ... begin Obj.Prim; -- call on inherited primitive Obj.Prim2(True); -- call on primitive CObj.Prim; -- dispatching call Obj.Class_Wide(Y => 77); -- call on class-wide op if CObj.Prim3 > 33.5 then -- dispatching call on primitive function ... end if; end Main;
Here is an example using a prefix that is of an access type.
with P2; package P3 is type T3 is new P2.T2 with ... procedure Prim4(A : access T3; C : Character); -- a primitive of T3 using an access parameter end P3;
with P3; package P4 is type AT3 is access all T3'Class; end P4;
with P4; procedure AccMain is Ptr : P4.AT3 := new ...; begin Ptr.Prim; -- Implicit dereference, equivalent to Ptr.all.Prim Ptr.Prim4(C => 'x'); -- No implicit dereference; operation from "P3" -- package can be used because desigated type -- declared there
end AccMain;
!corrigendum 4.1.3(9)
Insert after the paragraph:
the new paragraph:
!corrigendum 4.1.3(13)
Insert after the paragraph:
If the prefix does not denote a package, then it shall be a direct_name or an expanded name, and it shall resolve to denote a program unit (other than a package), the current instance of a type, a block_statement, a loop_statement, or an accept_statement (in the case of an accept_statement or entry_body, no family index is allowed); the expanded name shall occur within the declarative region of this construct. Further, if this construct is a callable construct and the prefix denotes more than one such enclosing callable construct, then the expanded name is ambiguous, independently of the selector_name.
the new paragraph:
Legality Rules
If a selected_component other than an expanded name resolves to denote a view of a subprogram whose first parameter is an access parameter, the prefix shall denote an aliased view of an object.
!comment corrigendum 4.1.3(15) - These changes were removed by AI-00407.
!comment
!comment @dinsa
!comment For a @fa<selected_component> that denotes a component of a @fa<variant>, a
!comment check is made that the values of the discriminants are such that the value or
!comment object denoted by the @fa<prefix> has this component. The exception
!comment Constraint_Error is raised if this check fails.
!comment @dinst
!comment For a @fa<selected_component> with a tagged @fa<prefix> and a @fa<selector_name>
!comment that denotes a view of a subprogram, a call on the view denoted by the
!comment @fa<selected_component> is equivalent to a call on the subprogram
!comment with the first actual parameter being provided by the object or value denoted
!comment by the @fa<prefix> (or the Access attribute of this object or value if the
!comment first formal parameter is an access parameter), and the remaining actual
!comment parameters given by the @fa<actual_parameter_part>, if any.
!corrigendum 4.1.3(17)
Replace:
Control.Seize -- an entry of a protected object (see 9.4)
by:
X.Activate -- a procedure call assuming X has (see 6.4) -- a tagged type Control.Seize -- an entry of a protected object (see 9.4)
!corrigendum 6.3.1(10)
Replace the paragraph:
by:
!ACATS test
Create ACATS tests for this notation.
!appendix

From: Tucker Taft
Sent: Sunday, November 26, 2000 10:49 AM
Subject: Object.Operation amendment AI

Here is an amendment AI that might be considered part of
the series of AIs designed to "round out" the OOP features.
This one was prompted in part by the reactions of Erhard's
grad students to the difficulty of having to both identify
the package containing an operation and the object on which
the operation is to be performed.  With both classwide
and primitive operations being relevant, and these operations
having essentially opposite rules about which package the
operation resides in (the ultimate ancestor for classwide,
and the ultimate descendant for primitive), some way to
eliminate the package from the syntax seemed useful.

The other prompting factor is the continual whining that Ada 95
is out of the mainstream of OOP languages because it lacks the
object.operation syntax.  This proposal defines the "object.op"
syntax as essentially a syntactic sugar on the pkg.op(object,...)
syntax.  This approach is pretty much what Modula-3 did.  It
provides for a "symmetric" notation when dealing with binary
operators, while also providing an "asymmetric" (object-oriented)
syntax when using operations that have a single controlling
operand.

As usual, any and all comments highly encouraged.
-Tuck
-------------
!standard 04.01.03 (05)                               00-11-25  AI95-xxx/01
!class amendment 00-11-25
!priority High
!difficulty Hard
!subject Tagged Types, Object.Operation Notation, Object-Oriented Programming

!summary

An equivalence is proposed between Object.Operation(...) and
Package.Operation(Object,...) to allow an object-oriented programming model
that is based on applying operations to objects, rather than selecting
operations from a package and then applying them to parameters.

!question

[Note: I am interpreting this "question" section as a statement of the problem
that might deserve an amendment.]

In Ada 95, one must identify the particular package in which an
operation is
defined, in addition to the primary "controlling" object to which the operation
is to be applied. Identifying both the package and the object is to some extent
redundant, and makes object-oriented programming in Ada 95 wordier than
necessary, and/or encourages heavy use of potentially confusing "use" clauses.
Would it be possible to eliminate this redundancy, while also clarifying what
object is the controlling object.

!recommendation

We propose an equivalence between Object.Operation(...) and
Package.Operation(Object, ...) where "Package" is the package in which Object's
type (or covering thereof) is declared. The equivalence would be based on a
"use"-like visibility model, where after the ".", operations from the package in
which an object's type is declared, as well as packages in which types that
"cover" the object's type are declared, would be effectively "use" visible.
Further, the first parameter to the operations made "use" visible would
(implicitly) be the object preceding the ".".

The "use"-like visibility model ensures that the operations made visible would be
hidden by any components or protected operations with the same identifier as the
operation (since they are "directly" visible after the ".", rather than "use"
visible). In addition, no "beaujolais" effects would be produced, because the
operations made visible would all overload one another, rather than having
operations from one package hide another.

Because of access parameters, if the type of the prefix is an access type, the
package(s) in which the designated type and types that cover the designated type
would be "use"d, in addition to the package in which the access type itself is
declared. Furthermore, to preserve the model that "." can result in an implicit
dereference, an interpretation of the prefix as an implicit dereference is
coupled with a "use" of the packages in which the designated type and its
coverings are declared. In this implicit dereference case, the package where the
access type itself is declared is irrelevant.

!example

Here is an example of use of the "object.operation" syntax:

package P is
    type T is tagged ...
    -- implicit declaration of T'Class
    procedure Prim(X : in out T);
    procedure Classwide(X : in out T'Class; Y : Integer);
end P;

with P;
package P2 is
    type T2 is new P.T with ...
    -- implicit declaration of T2'Class
    -- implicit declaration of Prim(X : in out T2);
    procedure Prim2(X : in out T2; B : Boolean);
    function Prim3(X : T2) return Float;
end P2;

with P2;
procedure Main is
    Obj : P2.T2;
    CObj : P2.T2'Class := ...
begin
    Obj.Prim;         -- call on inherited primitive
    Obj.Prim2(True);  -- call on primitive
    CObj.Prim;        -- dispatching call
    Obj.Classwide(Y => 77);  -- call on classwide op
    if CObj.Prim3 > 33.5 then  -- dispatching call on primitive function
        ...
    end if;
end Main;

Here is an example using a prefix that is of an access type.

with P2;
package P3 is
    type T3 is new P2.T2 with ...
    procedure Prim4(A : access T3; C : Character);
      -- a primitive of T3 using an access param
end P3;

with P3;
package P4 is
    type AT3 is access all T3'Class;
    procedure APrim5(Q : AT3; R : Integer);
      -- a primitive of AT3 (not of T3)
end P4;

with P4;
procedure AccMain is
    Ptr : P4.AT4 := new ...;
begin
    Ptr.Prim;             -- Implicit dereference, equivalent to Ptr.all.Prim
    Ptr.Prim4(C => 'x');  -- No implicit dereference; "P3" package
"use"ed
                          --  because desig type declared there
    Ptr.Aprim5(R => 13);  -- No implicit dereference; "P4" package
"use"ed
                          --  because AT4 declared there
    if Ptr."="(null) then -- "=" declared in P4 so can be called this
way also
        ...
    end if;
end AccMain;


!discussion

This AI grew out of an issue identified by Erhard Ploedereder and his graduate
students, where it made OOP awkward to always identify (or "use") the specific
package in which a dispatching operation was declared, particularly when the
operation was inherited, and hence only implicitly declared in that package.
Furthermore, the rules for calling classwide operations and primitive operations
were significantly different, where the classwide operation was not inherited,
and hence "remains" in the original package where it was declared, whereas
the primitive operations were inherited, and hence got carried along into
the package where the type was declared.  The tendency was to "use" every
package that might conceivably have an operation of interest, which can
significantly add to the confusion.

We considered an Object'Operation(...) syntax, but that was felt to introduce
possible conflicts with implementation-dependent attributes.  Also, the "."
notation had the additional nice feature that a primitive function could be used
to  effectively provide a "read only" component, with the familiar "." syntax.
Also, using the "." notation allows primitives defined outside a protected or a
task type to be called in the same "obj.operation" notation used for entries and
protected subprograms.  This unifies these two kinds of operations, which from a
user perspective are both "fundamental" operations of the synchronizing types.

We considered only making primitive operations visible, but there are situations
where an abstraction uses a classwide operation very much like a primitive
operation. For example in Claw, whether a given operation is classwide or
primitive is not particularly relevant to how it is used (though of course it is
relevant to how it is handled in type extensions).  The "package"-oriented
approach, as opposed to the "primitive"-oriented approach, may also fit more
cleanly into existing overload resolution algorithms, since it has more in common
with the way "use" visibility works currently.  Of course, any guess of
implementation burden is hard to make, since compilers have so many different
strategies.

The rules for access types are a bit convoluted, but they seem necessary given
the importance of access parameters.

We talk about "covering" types rather than "ancestor" types for two reasons.
One is that it is only operations on class-wide types that are being imported from
packages other than the one in which the type itself is declared.  Second,
the notion of "covers" will presumably generalize better if we adopt the notion
of abstract interfaces (see the multiple inheritance Amendment AI).

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

From: Alexander Kopilovitch
Sent: Monday, June  9, 2003 10:09 AM

!summary

An alternative syntax is proposed here for AI-252. This alternative uses
new attribute instead of extension for meaning of dot-qualification, which is
currently proposed in AI-252. This text assumes the context of current AI-252.

!proposal

Let us introduce new attribute Method, which always must be followed by dot
and operation, i.e.

  Object'Method.Operation

for example:

  type T is ... ;
  function F(A1 : T; A2 : Integer) return Integer;
  procedure P(A1: T);
  ...

  X : T;
  Actual_A2 : Integer;
  F_Result : Integer;
  ...

  F_Result := X'Method.F(Actual_A2);
  ...
  X'Method.P;

Conceptually, the attribute Method returns a record -- the table of all
relevant methods; some analogy with C++ code is present here, although the
major difference is obvious: contents of this table depends not only on the
object's type, but on surround and visibility rules also.

Basic visibility rules for Operation may stay as they are stated currently
in AI-252, but with additional option: a programmer can explicitly list all
visible packages for a particular subtype using appropriate "for...use"
statement:

  for Subtype'Method use Package1 [, Package2, ..., PackageN];

Such explicit statement overrides basic visibility rules for all
Object'Method.Operation expressions where Object belongs to Subtype.

Further, with this approach we may easily provide a denotation for the
component-on-demand abstraction, that is, unified notation for an externally
immutable view of a data component and a function; all we need for this is
another attribute Property, which should be used the same way:

  Object'Property.Function_Or_DataField

for example, for both

  type T is ... ;
  function F(P : T) return Integer;

and

  type T is record
     F : Integer;
     ...
  end record;

we can write:

  X : T;
  R : Integer;
  ...

  R := X'Property.F;

Arguments for a function prefixed by the Property attribute naturally
correspond indices for array data component.

Further, arrays (that are indexed by controlling object type) likewise may be
used in conjunction with the Property attribute  (in accordance with analogy
between arrays and functions, supported in Ada). For example:

  type String_Index is new Integer;
  S : String(1..10);
  I : String_Index;
  ...
  ... I'Property.S ...

Even multi-dimensional arrays are permitted here. For example:

  type Cities is (Edinburgh, Glasgow, London, Paris, New_York);
  type Distances is array (Cities, Cities) of Float;
  Km_To : Distances := ...;
  ...
  ... Edinburgh'Property.Km_To(Paris) ...

As for arrays of arrays, only outer array may be used, that is, the subscripts
for inner arrays cannot appear. For example:

  type Table_Index is new Integer;
  type Table_Line is String(1..50);
  Table : array (Table_Index range 1..10) of Table_Line;
  I : Table_Index;
  ...
  ... I'Property.Table ...     -- legal
  ...
  ... I'Property.Table(1) ...  -- illegal


!discussion

Object.Operation syntax seems as acceptable compromise in a case when
there is a controlling object. Although even then the Operation does not
belong to the Object (as it belongs to a package), it is reasonable to claim
that conceptually, the status of being controlling temporary gives the Object
some additional rights over all its operations, and in particular, extends
visibility rules for the Object.

But in many cases there are no controlling objects, and in those cases this
Object.Operation syntax will act against proper expression and understanding
of program design and logic. Moverover, as this Object.Operation style
potentially conflicts with package-orientation, which is fundamental feature
of Ada 95, and as this Object.Operation notation is compulsory in most of
today's mainstream languages, there is real possibility of massive and
disordered mixture of those design styles if this Object.Operation notation
appear in Ada.

There is also anxiety about possible confusion with component notation.
As Robert I. Eachus recently wrote in comp.lang.ada newsgroup
(message-id <3ED056CB.8000200@attbi.com> ):
"I really don't like the idea of allowing the Object.Operation format to
Ada.  Yeah, the compilers can usually figure it out understand it. But
the potential confusion with component notation is going to confuse
experienced Ada users.  (And eventually other O-O language users when
the actually run into component references."

An alternative proposed here attains main purpose of AI-252, and it does not
contest AI's propositions concerning all things except Object.Operation
syntax. At the same time it establishes a deterrent for unjustified use of
the feature (by extra wording), makes the expression of programmer's intent
more explicit, and additionally, provides finer control over visibility and
over interchangeability between operation and data field.

The level of uniformity achieved with the notation proposed here is even
higher than with dot-notation proposed in current AI-252 (because the arrays
are included) without sacrificing traditional features and natural ways for
expressing specific intentions.

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

From: Craig Carey
Sent: Wednesday, June 11, 2003  3:47 AM

The AI-00252 maybe does not go far enough: it is not about putting ","s
after types.

Here is an example that would occur when creating Ada bindings to C++
*.h files. Two unsatisfactory details are that "Cpp.A.A" & "Cpp.A.Value"
are to be used instead of "A.A" and "A.Value".

package Cpp is

   package A is
      type A_Rec is tagged limited null record;
      subtype A is A_Rec'Class;
      function Value return A;
   end A;

   package B is
      subtype A is Cpp.A.A;
      procedure F (T : A := A.Value);  -- Not legal, the
                        --  A in "A.Value" is a subtype
   end B;

end Cpp;

If the "subtype A is Cpp.A.A;" is replaced with "use A;" then
the error shifts to the "A" in the "A :=", with the problem now
being that it can't see the type but thinks that that A is a
package.

If both "subtype A is Cpp.A.A;" and "use A;" are used, then the
error returns back to the "A.Value" with the ObjectAda compiler
saying that the "A" in "A.Value" is the type "A" and not the
package "A".

The language could be improved to better resolve overlaps of
subtype names and package names. (Maybe AI-00252 could consider
this (it might eventually consider dots after tokens naming
subtypes)).


At 2003\06\09 19:09 +0400 Monday, Alexandre E. Kopilovitch wrote:
>!summary
>
...

That proposal suggested two keywords ("Method", and "Property").
That is general and there I doubt arguments for that would show
up. AI-00252 said 0 special tokens and a "'" is best.

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

From: Craig Carey
Sent: Wednesday, June 11, 2003  8:07 AM

Ada could have a 'class' feature. It can be implemented in that way,
for example:

------------------------------------------------------------
package Cpp is
   type Y is tagged limited null record;

   package A is      -- The "<>" means that this is a class
      type A_Rec is new Y with   -- (or "is tagged")
         record
            N : Integer;
         end record;

      subtype <> is A_Rec'Class;         --  Glue this subtype into whole package
      type A_Ptr is access constant A;   --  this "A" is the "<>" type

      Static  : aliased A_Rec;
      function Value return A;
   end A;

   Var   : A.A_Ptr := A.Static'Access;

   package B is
      procedure F (T : A := A.Value);  -- Legal now
   end B;

end Cpp;
------------------------------------------------------------

In general, statements like this:

   subtype X is X.Y;

just spill out the contents of record Y into the larger surrounding
X record.
The word "renames" is an alternative to "subtype".

By this means, abstract multiple inheritance could be implemented without
a use of the tagged type. Instead of dispatching when procedures are
called, the programmer explicitly specifies the subrecord by gluing on
extra text after a dot.

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

From: Tucker Taft
Sent: Saturday, September 27, 2003  4:11 PM

Here is an update to AI 252.  Not much change (Randy
already incorporated some of the changes from Toulouse).
I added a rule to disallow object.op if "op" is the name
of a visible component of "object."  I also added
a paragraph to 6.3.1 to include object.op as an example
of a subprogram with an intrinsic convention.
Finally, I changed "object" to "object or value" to
be consistent with other paragraphs in 4.1.3, and to cover the
weird cases where the prefix is not officially an
"object" (e.g. my_array_type(tagged_array)(2) is a
"value" rather than an "object" -- this distinction is
pretty silly at this point).

[This is version /05 - ED.]

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

From: Pascal Leroy
Sent: Friday, June 25, 2004  2:55 PM

AI 252 seems to make the following program legal:

    type T is tagged null record;
    function H (Y : access T) return Integer;

    X : T;

    C : Integer := X.H; -- Legal?

Notice that we have managed to pass to H an access value designating a
non-aliased object.  I realize that tagged types are passed by reference,
but it seems surprising that the Object.Operation notation would give you
the capability to pass X as an actual for an anonymous access type in
circumstances where X'Access would be illegal.

Was this intended?  The proposal section of the AI seems to indicate that
the above call should be legal only if X is aliased, but I see no rule to
that effect in the wording section.

If the AI needs to be amended, this should be a legality rule, not a name
resolution rule.

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

From: Randy Brukardt
Sent: Friday, June 25, 2004  5:50 PM

It's certainly hard to say from the AI. Moreover, it's hard to say from our
other thinking (for instance, that "T" and "access T" be essentially equivalent
for tagged incomplete types).

Argubly, its the requirement of "aliased" on tagged objects that is unnecessary
(since the type is already by-reference and implicitly aliased). So an argument
could be made to drop that altogether.

But in tha absence of that, I agree that there should be a rule; the clear
intent is that the prefix form not be different from the normal form.

> If the AI needs to be amended, this should be a legality rule, not a name
> resolution rule.

I agree; a resolution rule would be awful for this (requiring different sets of
operations to be considered depending on the aliased state). (Indeed,
resolution rules shouls be added only when absolutely necessary - legality
rules are usually better, because the cause of the error is easier to report
accurately and thus fix.)

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

From: Pascal Leroy
Sent: Monday, June 28, 2004  2:24 AM

> Argubly, its the requirement of "aliased" on tagged objects
> that is unnecessary (since the type is already by-reference
> and implicitly aliased).
> So an argument could be made to drop that altogether.

I see you point, but I think it would be a bad idea, and it would weaken
the safety of Ada.  If I declare a non-aliased object of a
tagged/task/protected type, I know for a fact that it won't be altered by
accesses through access values.  That helps me assert the correctness of
my program.  If all such objects become aliased, then it becomes very much
like C, where I always have to worry that a pointer operation could wreak
havoc behind my back.  So we should not confuse aliased (a logical
property at the source level) and by-reference (a physical property at the
implementation level).

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

From: Randy Brukardt
Sent: Monday, June 28, 2004  1:08 PM

Humm. When the object is passed as a parameter, that allows the implementation
to take an Unchecked_Access of it and keep it. (That's precisely what Claw does
- and it can modify the object "behind your back", if something happens in
Windows that requires it [such as closing the parent window]. No havoc, I
hope.) So I don't think there is any such property in Ada 95.

It's true that if the type is private (not tagged private), the "havoc" can
occur only within the abstraction. But that would still be true (if not visibly
tagged, then not aliased, either). Otherwise, the only protection is on
directly taking 'Access of the object (but formals are all right), and that
doesn't seem to be a worthwhile restriction for correctness.

The property you mention is a useful one, it just doesn't exist for tagged
types in Ada 95. And that's not a huge problem, because tagged types are
generally used in tight abstractions, where the only way to modify an object is
to use the abstraction. In this case, nothing bad is likely to happen even if
someone does keep an access behind your back.

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

From: Pascal Leroy
Sent: Tuesday, June 29, 2004  12:06 PM

> It's true that if the type is private (not tagged private),
> the "havoc" can occur only within the abstraction.

Now that you mention this, it is clear that making tagged objects aliased
by default would be privacy breaking.

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

From: Randy Brukardt
Sent: Tuesday, June 29, 2004  12:15 PM

Huh? An object would only have the properties that the current view of its type
has, so certainly an object of a non-tagged private type would not be aliased,
even if the full type is tagged. That's already true for the magic parameter
rule (if the type isn't tagged, you can't take 'Access of it, even if you could
have done so knowing the full type). All I was suggesting to do was to extend
that rule to cover all known-to-be-tagged objects.

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

From: Tucker Taft
Sent: Wednesday, July 14, 2004  12:43 PM

It was my original intent that an "implicit" 'Access would only
be permitted if the object were explicitly declared "aliased."

In general I think it is still wise to require an explicit
"aliased" on tagged object declarations to permit use
of explicit 'Access, even though we make tagged formals implicitly
aliased.

On the other hand, I could be convinced that allowing an implicit 'Access
in the Object.Operation notation should not require an
explicit "aliased", since that will almost certainly be a source
of confusion for the user, and doesn't add a lot of safety.

In other words, I am on the fence about this one.

If pushed for a decision, I guess I favor not requiring
the "aliased" for this implicit 'Access.

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

From: Pascal Leroy
Sent: Thursday, July 29, 2004  11:49 AM

If pushed for a decision, I would favor requiring "aliased" for the
implicit Access.  That's because it makes it easier to explain that this
is "merely" a syntactic transformation.  I am opposed to the notion of
adding special semantics to the Obj.Op notation.

An alternative would be to say that "aliased" is not required for objects
of by-reference types, and do that for both implicit and explicit Access.
But that looks like a bigger change.

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

From: Tucker Taft
Sent: Thursday, July 29, 2004  12:12 PM

I am happy to follow Pascal's view on this, because as I said
I am very much on the fence about it.  That is, stick with the
pure syntactic transformation, and require that the prefix be
aliased.  Note that formal tagged parameters are implicitly aliased,
so you wouldn't have to worry when using a formal parameter as
the prefix.  That is probably a somewhat more common case than having
a local object.

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

From: Randy Brukardt
Sent: Thursday, July 29, 2004  7:43 PM

While I would prefer to completely eliminate the requirement for aliased on
by-reference types, the middle position is a bit uncomfortable. So I'll
go along with this resolution.

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

From: Pascal Leroy
Sent: Thursday, August 12, 2004  4:57 AM

Steve ran into a contract model problem with AI 252 as currently written.

3.9.2(8) has a rule that prevents mixing statically and dynamically tagged
controlled operands.  It is unclear how to check this rule in generic bodies
with the Obj.Op notation.  Consider:

	package Pack is
	    type T is tagged ...;
	    procedure Prim (U, V : T);
	end;

	X : Pack.T'Class := ...;
	Y : Pack.T;

	generic
	    with procedure Formal1 (W : Pack.T);
	    with procedure Formal2 (W : Pack.T);
	procedure Gen;

	procedure Gen is
	begin
	    Formal1 (Y);
	    Formal2 (X);
	end;

	procedure Inst is Gen (Formal1 => X.Prim, Formal2 => Y.Prim);

The calls to Formal1 and Formal2 in the instance body violate the no-mixing
rule.

We seem to have two options:

1 - Disallow the instantiation, i.e. say that you cannot pass Obj.Op as a
generic actual.  That seems like a significant loss of functionality to me.  If
we believe that it is convenient to avoid having to name the package when
calling a subprogram, that's true when instantiation a generic too.

2 - Assume the best on generics, recheck on the specification of
instantiations, and do a run-time check in generic bodies.  If the check fails
it will look quite mysterious to users, but then it's not going to fail very
often because this situation is somewhat of a pathology.

Whatever we decide, we should adopt a consistent position for subprogram
renamings.

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

From: Randy Brukardt
Sent: Thursday, August 26, 2004  9:30 PM

I'd think that a better fix would be:

3 - Follow the standard rules for legality (which say that this is only checked
in a generic spec.) and then let the standard tag checking rules do the job in
any calls not covered. That makes more sense than a mysterious runtime legality
rule check. It does require some mods to 3.9.2(15), to say that any dynamically
tagged operands are checked to see that they have the tag T. But that is
consistent with the rule 3.9.2(16) and *much* more palatable than a special
runtime legality check.

Wait a minute. A formal subprogram is never a dispatching operation (AI-200
reaffirms this). Thus, 3.9.2(8) never applies to a call on a formal subprogram
as in the example above. That's a good thing!

Whatever happens for a mixed static/dynamic non-dispatching call should happen
here. (I think that the dynamic operand is effectively truncated to the
specific type. That's ugly in my view, but whatever.) This case doesn't seem to
be a problem.

A rename can be a primitive operation, but a prefix call needs an object, and
the object will freeze the type, making the further declaration of primitive
operations illegal. So there is no problem there, either. So no change to the
standard or the AI is needed.

Have I missed something?

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

From: Pascal Leroy
Sent: Friday, August 27, 2004  1:41 AM

> Wait a minute. A formal subprogram is never a dispatching
> operation (AI-200 reaffirms this). Thus, 3.9.2(8) never
> applies to a call on a formal subprogram as in the example
> above. That's a good thing!
>
> Whatever happens for a mixed static/dynamic non-dispatching
> call should happen here. (I think that the dynamic operand is
> effectively truncated to the specific type. That's ugly in my
> view, but whatever.) This case doesn't seem to be a problem.

Yes, you're absolutely right.  No problem in this case.

> A rename can be a primitive operation, but a prefix call
> needs an object, and the object will freeze the type, making
> the further declaration of primitive operations illegal. So
> there is no problem there, either. So no change to the
> standard or the AI is needed.

This is not exactly true, because the prefix of the call can be an access
value, and that won't freeze the (tagged) type.  But it still the case
that for renamings all the information can be easily obtained by the
compiler: you look at the prefix of the call, and act as if it were a
parameter.  You may have to go through many levels of renamings,
collecting the prefixes as you go, but the processing is not fundamentally
different from what you currently do.  (At least, that's how I have
implemented it.)

So I agree that there is no problem here, but it's still good to record
this discussion in the AI for future reference.

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


Questions? Ask the ACAA Technical Agent