Version 1.2 of acs/ac-00009.txt

Unformatted version of acs/ac-00009.txt version 1.2
Other versions for file acs/ac-00009.txt

!standard 4.6 (00)          01-06-01 AC95-00009/01
!class uninteresting 01-06-01
!status received 01-05-17
!subject Dynamic view conversion
!summary
!appendix

!topic Dynamic view conversion
!reference RM95-4.6
!from Michael Gray
!sent Thursday, May 17, 2001, 7:23 AM
!discussion

We had a problem in comparing two tagged records A and B, where the type of
B is an extension of the type of A, and the type of A is an extension of
some Root_Type.

In a general piece of code (i.e. a piece of code written without knowledge
of which descendants of Root_Type exist), we needed to compare the values of
A and B, where the required semantics were to compare the common fields,
i.e. to check whether the fields of A had identical values in B. Although
this seems like quite a reasonable thing to want to do, it seems to be
impossible within the language.

I.e. consider

procedure X (A, B : in Root_Type'Class)
...
  if A = B ...

always delivers false if A and B are of different types.

The problem here is that there is no syntax that allows an object (B) to be
view-converted to the type of another object (A). Something along the lines
of A'Class(B) would be needed. This would then allow a comparison A =
A'Class(B). This would make inherited operations far more useful. In other
words, allow a view conversion where the target view is dynamically
determined rather than statically.

In implementations terms (I guess), to execute the required comparison, it's
'just' necessary to look up the address of the = operation in the dispatch
table (indexed via the tag of A), and to execute it, passing the objects A
and B as parameters .... ?

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

From: Pascal Leroy
Sent: Monday, May 28, 2001 5:25 AM

I'd say that we don't want to add more dynamicity in the language at this stage.
My gut feeling is that defining the proposed construct would be quite hard, and
would probably have a significant impact on implementations.

But more importantly, conversions never change the tag of an object (RM95
3.9(25)) so I don't see how the proposed construct would help: in the comparison
A = A'Class (B), the tags would still be different, and the comparison would
still return False.

Note that while it is possible to write value conversions for tagged types (RM95
4.6(5/1)) the above rule presumably applies to both view conversions and value
conversions.

I have occasionally wanted a conversion-that-changes-the-tag, but not often
enough that I see this as an important addition to the language.  Incidentally,
if such a conversion existed I believe that it could be used to solve your
problem (in conjunction with clever use of dispatching subprograms).

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

From: Christop Grein
Sent: Tuesday, May 29, 2001 5:22 AM

> > I.e. consider
> >
> > procedure X (A, B : in Root_Type'Class)
> > ...
> >   if A = B ...
> >
> > always delivers false if A and B are of different types.
> >

I've also once come into a situation where I wanted to compare the common parts
of tagged types, i.e. I would have needed an operation that, given two objects
of a class, returned the type of the first common ancestor, which you then could
use in a type conversion.

Something like

T'Common_Ancestor (A, B)
     converts A (the first argument) to that type which is the nearest
     common ancestor (going backward) of A and B in the hierachy T
     (for A, B in T).

There must always be a common root (it might however be abstract and this could
present a problem):

              Root
             /    \
            T1     T2
            /\     /\
         T11 T12 T21 T22

A: T11;
B: T12;
Root'Common_Ancestor (A, B) would convert A to T1

A: T11;
B: T2;
Root'Common_Ancestor (A, B) would convert A to Root

Thus one could write

  Compare (Left  => Root'Common_Ancestor (A, B),
           Right => Root'Common_Ancestor (B, A))

where Compare is a dispatching operation. [Problem when the common ancestor is
abstract and no operation exists => Constraint_Error, Program_Error ?]

I have no idea whether and how this could be implemented. I'm even not sure
whether such an operation really could make sense in general. The more I think
about it, the more it seems to be a burden for implementors.

So I returned to plain discriminated records.

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

From: Michael Gray
Sent: Thursday, May 31, 2001 10:53 AM


> I'd say that we don't want to add more dynamicity in the language at this
> stage. My gut feeling is that defining the proposed construct would
> be quite hard, and would probably have a significant impact on
> implementations.

Fair enough. This problem hasn't cropped up for us very frequently; the
email from Christoph Grein ("I've also once come into a situation where I
wanted to compare the common parts of tagged types, ... " also implies that
it's an occasional problem rather than a recurrent one.

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

From: Randy Brukardt
Sent: Wednesday, June 06, 2001 7:31 PM

I was thinking about this again when reviewing AI-260.

The problem in AI-260 is solved with a rather specific feature. The dynamic
view conversion problem was solved with dynamic view conversions. But the
problem in both cases isn't conversion, or reading tags, its really
controlling dispatching.

A solution that included more control over dispatching would probably solve
both problems.

Pascal Leroy said:

> I'd say that we don't want to add more dynamicity in the language at this
> stage. My gut feeling is that defining the proposed construct would be quite
> hard, and would probably have a significant impact on implementations.

That might be true for the exact proposal given, but I think that
controlling dispatching isn't likely to cause problems.

Consider that pseudo-code for the canonical implementation of a dispatching
call looks like:

   declare
       Disp_Tag : Ada.Tags.Tag := <First_Controlling_Parameter>'Tag;
   begin
       for I in <all controlling parameters> loop
           if I'Tag /= Disp_Tag then
               raise Constraint_Error;
           end if;
       end loop;
       Disp_Tab (Slot_Number).all (<parameters>);
   end;

What we need to solve both this problem and the AI-260 one is a way to
dispatch on a user-calculated tag. Such a call would probably have pseudo
code that looks like:

   declare
       Disp_Tag : Ada.Tags.Tag := <some user expression>;
   begin
       for I in <all controlling parameters> loop
           if I'Tag not in Disp_Tag'Class then
               raise Constraint_Error;
           end if;
       end loop;
       Disp_Tab (Slot_Number).all (<parameters>);
   end;

The parameter check in this case is essentially the same as that done for
type conversions, so compilers certainly know how to deal with it.

This suggests that there isn't much of a problem with this as a (runtime)
model. (Tag indeterminate operands would just use the calculated tag, which
is exactly how they are handled in the current model of dispatching).

The problem here is to think of some appropriate syntax for this. Tucker had
suggested a magic generic, but I can't figure out how that would work.

I also thought about using a variation of the proposed Obj.Op(...) calling
to handle this, but that doesn't give us an appropriate way to handle Op.

So, the best I can come up with is a new syntax:

     <Tag_Prefix> ::= <tag_object_name> :
     <function_call> ::= <tag_prefix> <function_name>
         (and so on for the other forms of call).

We'd also want to add a routine to Ada.Tags:

    function Common_Ancestor (A, B : in Ada.Tags.Tag) return Tag;
	-- Returns the common ancestor closest to A and B. Raises Tag_Error
	-- if A and B do not have a common ancestor.

Then, to solve Michael Grey's problem:

    procedure Common_Parts_Equal (A, B : in Root_Type'Class) is
        Ancestor_Tag : Ada.Tags.Tag :=
            Ada.Tags.Common_Ancestor (A'Tag, B'Tag);
            -- This can't raise Tag_Error, as the common ancestor must be
            -- at least Root_Type.
    begin
        return Ancestor_Tag : "=" (A, B);
           -- This would call the appropriate "=" for Ancestor tag, both
           -- operands must therefore have types that are descendants of the
           -- type identified by Ancestor tag.
    end Common_Parts_Equal;

Similarly, to solve the AI-260 problem, we'd override XML'Class'Input with

    function XML_Object_Input
        (Stream : access Ada.Streams.Root_Stream_Type'Class;
         return XML'Class is
        Object_Tag : Ada.Tags.Tag := Read (Stream);
              -- Read from AI-260 example.
    begin
        return Object_Tag : XML'Input (Stream);
           -- Which would call the 'Input routine for the type identified by
           -- object tag.
    end XML_Object_Input;


The only real problem I see with this is the need for new syntax. Perhaps
someone else can figure a way around that?? (Or a better syntax.)

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

From: Pascal Leroy
Sent: Tuesday, June 12, 2001 3:17 AM

While I have some sympathy for supporting the kind of dispatching that Randy
and Michael advocate, I am adamantly opposed to introducing newly (and
disgraceful) syntax for that purpose.

As I mentioned earlier, all you need is a conversion that changes the tag, but
leaves the rest of the record unchanged.  This can be achieved by a new
predefined generic (which could go in Ada.Tags):

    generic
        type Destination is tagged private;
        type Source is abstract new Destination with private;
    function Tag_Conversion (S : Source; T : Ada.Tags.Tag)
               return Destination'Class;

The effect of which is to make a copy of S, replacing the tag with T.  Then the
result of calling (an instantiation of) this function may be used for
dispatching, and the right thing will happen.

> We'd also want to add a routine to Ada.Tags:
>
>     function Common_Ancestor (A, B : in Ada.Tags.Tag) return Tag;
> -- Returns the common ancestor closest to A and B. Raises Tag_Error
> -- if A and B do not have a common ancestor.

Again, I object to Common_Ancestor, which represents one usage pattern among
many others.  I would be willing to add to Ada.Tags a function to return the tag
of the parent:

    function Parent_Tag (T : Ada.Tags.Tag) return Ada.Tag.Tags;

Using this function, it would be straightforward to implement Common_Ancestor by
walking up the derivation tree.  It would also be possible to implement other
usage patterns.  And finally it would be easier to call the parent in
general-purpose code.

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

From: Randy Brukardt
Sent: Tuesday, June 12, 2001 4:52 PM

> The effect of which is to make a copy of S, replacing the tag with T.  Then the
> result of calling (an instantiation of) this function may be used for
> dispatching, and the right thing will happen.

Danger Will Robinson!!!!

When you say "copy", you immediately have two problems: (1) Copying the item
would trigger all of the Adjust rules. But which version of Adjust do you call?
And if you don't call Adjust, then you have an object for which the invariants
don't hold. For instance, Claw Window objects are linked into an active object
list. If that doesn't happen, you'll have problems, especially if the user
passes this result into something that closes the object.

(2) Once you say copy, you've eliminated the possibility to use this on limited
types. That seems like an unacceptable limitation; at least half of the O-O
types I've used are limited. (Once you've given up and gone over to the dark
side of reference semantics, it usually is best to prevent all copying of
objects.)

I assume that you are restricting this somehow to downward conversions, because
clearly we can't allow upward conversions. That would have to be done at
runtime, since the check couldn't be part of a generic.

The problem is exactly one of controlling dispatching; to try to describe it as
something else is likely to bring in additional, unnecessary problems (such as
those above). Note that in my scheme, if the called routine wants to force
redispatching, it will redispatch to the original tag (as normally happens in
Ada 95); while in yours, the called routine will redispatch to the new tag
(which clearly would be different than normal practice).

In any case, I suggested what I did to try to start some discussion in this
topic. Since only you, I, and Michael have commented on it, it obviously isn't
of interest to many. And since my solution is too heavy for some, your solution
does not work, and Tucker hasn't weighed in with the solution to all problems
:-), it probably is best to forget this one altogether.

> ...  I would be willing to add to Ada.Tags a function to return the
> tag of the parent:
>
>     function Parent_Tag (T : Ada.Tags.Tag) return Ada.Tag.Tags;
>
> Using this function, it would be straightforward to implement Common_Ancestor
> by walking up the derivation tree.  It would also be possible to implement
> other usage patterns.

This seems fine. (Although I can't think of any way to implement
Common_Ancestor using it -- but since any implementation of Common_Ancestor
would have to use a parent function of some sort, that's not a problem. The
problem is that walking up the tree alone isn't enough, because you don't know
which operand has to be walked.)

> And finally it would be easier to call the parent in
> general-purpose code.

This seems to be true only if you have syntax to control the dispatching:

     Ada.Tags.Parent_Tag(Object):Me(Object, Args...);

If you have to instantiate a generic to do it, it doesn't help anything at all.
That would be twice as much text as the type conversion that we need now.
(Unless, of course, we add "automatic" instantiations to the language, an idea
which I would oppose on the grounds that "implicit" declarations are nearly
always bad.)

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

From: Christoph Grein
Sent: Monday, June 18, 2001 4:55 AM

> This seems fine. (Although I can't think of any way to implement
> Common_Ancestor  using it -- but since any implementation of Common_Ancestor
> would have to use a parent function of some sort, that's not a problem. The
> problem is that walking up the tree alone isn't enough, because you don't
> know which operand has to be walked.)
>

A further operation in Ada.Tags would be needed like

  function is_Derived_From (Left, Right: Ada.Tags.Tag) return Boolean;

> > And finally it would be easier to call the parent in
> > general-purpose code.
>
> This seems to be true only if you have syntax to control the dispatching:
>
>      Ada.Tags.Parent_Tag(Object):Me(Object, Args...);
>
> If you have to instantiate a generic to do it, it doesn't help anything at
> all. That would be twice as much text as the type conversion that we need
> now. (Unless, of course, we add "automatic" instantiations to the language,
> an idea which I would oppose on the grounds that "implicit" declarations are
> nearly always bad.)

I think this new syntax proposal in its utmost ugliness has no chance in the
eyes of the Ada-powers-that-be.

Are there similar functionalities in Java (IIRC no) or C++ (I don't know)?

The problem I tried to solve was that with discriminated types, an operation
like

  Compare (Left, Right)

can have Left and Right with different discriminants and I can easily compare
the common parts, but a dispatching operation cannot have different tags on Left
and Right, and I have no access to the common parts.

It seems the problem is a mixture of conversion and dispatching control.

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

From: Randy Brukardt
Sent: Tuesday, July 3, 2001 4:14 PM

> > > And finally it would be easier to call the parent in
> > > general-purpose code.
> >
> > This seems to be true only if you have syntax to control the dispatching:
> >
> >      Ada.Tags.Parent_Tag(Object):Me(Object, Args...);
> >
> > If you have to instantiate a generic to do it, it doesn't help anything at
> > all. That would be twice as much text as the type conversion that we need
> > now. (Unless, of course, we add "automatic" instantiations to the language,
> > an idea which I would oppose on the grounds that "implicit" declarations are
> > nearly always bad.)
>
> I think this new syntax proposal in its utmost ugliness has no chance in the
> eyes of the Ada-powers-that-be.

It wasn't so much of a serious proposal, but rather an attempt to put the
problem (and one way to solve it) in front of the community.

> Are there similar functionalities in Java (IIRC no) or C++ (I don't know)?

Me either.

> The problem I tried to solve was that with discriminated
> types, an operation like
>
>   Compare (Left, Right)
>
> can have Left and Right with different discriminants and I can easily compare
> the common parts, but a dispatching operation cannot have different tags on Left
> and Right, and I have no access to the common parts.
>
> It seems the problem is a mixture of conversion and dispatching control.

No, that's the thinking that leads to the paralysis of being unable to do
anything about it. In order to solve this, we have to think a bit outside of
the box.

In Ada 95, conversions are used to control dispatching. So one can think of
this problem as one of dispatching control OR conversion. But both is not
necessary.

If we found a way to control dispatching directly, then the existing (implicit)
conversions present in Ada 95 is sufficient to provide all of the conversions
needed.

An alternative way to look at the problem is purely as an exercise in view
conversions. If we know the common type that we want to compare, we simply
would convert to that type explicitly (this works in Ada 95, and indeed is
how I approached the problem):

    Compare (Base_Type(Left), Base_Type(Right));

which will call the version of Compare for Base_Type.

So an alternative solution would look something like:

    declare
       Common_Tag : Ada.Tags.Tag := Common_Ancestor(Left'Tag, Right'Tag);
           -- Common_Ancestor doesn't have to be predefined.
    begin
       Compare (Magic_Conversion (Left, Common_Tag),
                Magic_Conversion (Right, Common_Tag));
    end;

The problem is defining Magic_Conversion.

One could consider a (magic) generic for this purpose:
    generic
        type Some_Tagged_Classwide (<>) is tagged limited private;
    function Magic_Conversion (Obj : in Some_Tagged_Classwide;
        New_Tag : in Ada.Tags.Tag) return Some_Tagged_Classwide;

which would then be instantiated:
    function My_Magic_Conversion is new Magic_Conversion (Root_Type'Class);

The trouble with this is that this would give us a value conversion. But for
this purpose (where we don't actually want to convert anything, we're just
using that as a shorthand to control dispatching), we really want a view
conversion. Moreover, we don't want to try to copy anything, as that triggers
Adjust, and doesn't work for limited types.

I suppose we could define this generic to be totally magic, doing a view
conversion, but I think I'd rather have syntax in that case.

Note that Pascal's previous comment saying that you need to change the tag is
wrong. You NEVER change the tag in Ada 95, you simply change the tag that is
used for dispatching determination. All we need here is to be able to control
what tag is used for dispatching; we don't need to change the object in any
way.

One reason that I looked at syntax for this is the well-known problem with
calling the parent routine in an overriding of a operation:

    My_Operation (Parent_Type(Object), ....);

which is fragile because we have to explicitly write the parent type's name.
If an additional type is inserted between the child and parent types, all of
the uses of Parent_Type need to be searched out and changed if they are
intended to point at the parent.

One use for the proposed syntax would be to force dispatching to the parent
without naming it:

    Ada.Tags.Parent_Tag (Object'Tag):My_Operation (Object, ...);

We could simplify this further by introducing a 'Parent_Tag attribute:

    Object'Parent_Tag:My_Operation (Object, ...);

---

Anyway, my syntax proposal was really intended to start a discussion. It solves
three problems that have been discussed in various amounts:

1) The AI-260 problem for which the band-aid of 'Tag_Read and 'Tag_Write
attributes were introduced. I'd prefer a more building block approach if
possible.

2) The Compare problem noted by several people.

3) The problem that the call to parent operation usually made in overriding
operations has to explicitly use the name of the parent type.

At this point, since hardly anyone has seemed interested enough to discuss it,
much less propose an alternative, I consider this whole discussion dead.

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

From: Robert Duff
Sent: Wednesday, July 4, 2001 10:23 AM

Christoph Grein wrote:
>   Compare (Left, Right)

I haven't followed this whole discussion, so this might be a non
sequitor, but you can do something like this:

    function Compare(X, Y: T'Class) return Boolean is
    begin
        if X'Tag = Y'Tag then
            return Compare_Method(X, Y); -- dispatching call
        else
            return False;
        end if;
    end Compare;

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

From: Christoph Grein
Sent: Wednesday, July 4, 2001 12:25 AM

> One reason that I looked at syntax for this is the well-known problem with
> calling the parent routine in an overriding of a operation:
>
>     My_Operation (Parent_Type(Object), ....);
>
> which is fragile because we have to explicitly write the parent type's name.
> If an additional type is inserted between the child and parent types, all of
> the uses of Parent_Type need to be searched out and changed if they are
> intended to point at the parent.

I haven't thought of this, but I think this problem deserves a well-designed
solution. This is much like the problem of inadvertant overriding or not
overriding an inherited operation, which is being solved via pragmas.

> One use for the proposed syntax would be to force dispatching to the parent
> without naming it:
>
>     Ada.Tags.Parent_Tag (Object'Tag):My_Operation (Object, ...);
>
> We could simplify this further by introducing a 'Parent_Tag attribute:
>
>     Object'Parent_Tag:My_Operation (Object, ...);

Couldn't this be solved by a new attribute T'Parent_Type:

      My_Operation (T'Parent_Type(Object), ....);

without the need of a new syntax?
Here for every tagged type T, T'Parent_Type denotes the parent type (or, if
T is the root of the hierarchy, denotes T itself). [This is much like
T'Class'Class being the same as T'Class.]

> Anyway, my syntax proposal was really intended to start a discussion. It
> solves three problems that have been discussed in various amounts:
>
> 1) The AI-260 problem for which the band-aid of 'Tag_Read and 'Tag_Write
> attributes were introduced. I'd prefer a more building block approach if
> possible.
>
> 2) The Compare problem noted by several people.
>
> 3) The problem that the call to parent operation usually made in overriding
> operations has to explicitly use the name of the parent type.
>
> At this point, since hardly anyone has seemed interested enough to discuss
> it, much less propose an alternative, I consider this whole discussion dead.

I think it should not be dead for the third problem. This is also a problem
Java (and I gather many other OO languages) suffer from. A solution would be
in the Ada spirit.

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

From: Steve Deller
Sent: Wednesday, July 4, 2001 6:49 PM

Are you proposing a new attribute function that takes an object and returns
an object with the parent tag of the original object?  Or are you proposing
an attribute that denotes a subtype (much like T'Class denotes a subtype),
and intending type conversion to take care of converting an object?  In the
latter case, one could also write:
   x : T ;
   y : T'Parent_Type ;
which would be invalid for the former approach.

Somehow the latter seems more appropriate to me.  It feels like an
appropriate "completion" to the 'Class and 'Tag attributes.

I too like the "building block" approach and it does seem that 'Parent_Type
would be a powerful building block.

The "compare the most common ancestor" is then easy to write, as you point
out:

   function Compare_Common( X, Y : T'Class ) return Boolean is
     Temp : T'Class ;
     Tx : T'Class ;
     Ty : T'Class ;
   begin

     loop

       loop

         if Tx'Tag = Ty'Tag then
            return Compare_Method( Tx, Ty ) ;
         end if ;

         Temp := T'Parent_Type( Tx ) ;
         exit when Tx = Temp ;
         Tx := Temp ;

        end loop ;

        Temp := T'Parent_Type(Py) ;
        exit when Temp'Tag = Ty'Tag ;
        Ty := Temp ;

      end loop ;

      raise Program_Error ; -- no common ancestor found and at least the
root T should be found
   end Compare_Common ;

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

From: Robert Duff
Sent: Thursday, July 5, 2001 9:22 AM

> without the need of a new syntax?
> Here for every tagged type T, T'Parent_Type denotes the parent type (or, if T is
> the root of the hierarchy, denotes T itself). [This is much like T'Class'Class
> being the same as T'Class.]

A Parent_Type attribute was considered during the 9X design.
I don't remember the details, or why it was rejected.

The T'Parent_Type = T case at the root seems fishy to me.

Some other questions that would have to be answered:
A private type does not have a "parent type" -- it has an "ancestor
type".  Does that mean there should be a 'Ancestor_Type?
Or does 'Parent_Type return the ancestor type for a partial view.

If the ancestor of a private type is different from the parent of the
full type, does 'Parent_Type return something different depending
on where it appears in the source code (i.e., whether the full type is
visible)?  Sounds questionable to me...

For a generic formal type, does T'Parent_Type in the instance return
something different from T'Parent_Type in the generic?

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

From: Christoph Grein
Sent: Monday, July 9, 2001 2:12 AM

> Are you proposing a new attribute function that takes an object and returns
> an object with the parent tag of the original object?  Or are you proposing
> an attribute that denotes a subtype (much like T'Class denotes a subtype),
> and intending type conversion to take care of converting an object?  In the
> latter case, one could also write:
>    x : T ;
>    y : T'Parent_Type ;
> which would be invalid for the former approach.

Yes, this was the idea, but see the comments of Robert Duff about the problems
with private types, which only have ancestors.

My feeling would be that you "get what you can see", i.e. for private view
T'Parent_Type is the ancestor, for the full view is the parent. But I'll leave
the discussion to more knowledgable people.

----

The following code [Steve Deller's - ED] would not work since you do not know
where X and Y meet in the hierarchy.
Also initial values for Tx and Ty are missing, I assume you mean X resp. Y. The
declaration of Temp, Tx, Ty is illegal and the code impossible since they must
be initialized and are then constrained.

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

From: Randy Brukardt
Sent: Friday, July 13, 2001 5:07 PM

> ...Or are you proposing an attribute that denotes a subtype (much like
> T'Class denotes a subtype), and intending type conversion to take care
> of converting an object? ...
>
> Somehow the latter seems more appropriate to me.  It feels like an
> appropriate "completion" to the 'Class and 'Tag attributes.

That seems like a truckload of worms to me. (Like a can of worms, only
bigger.) You'd have problems with discriminants, private types, generics,
and gosh knows what else.

> I too like the "building block" approach and it does seem that 'Parent_Type
> would be a powerful building block.
>
> The "compare the most common ancestor" is then easy to write,

Perhaps, but not the way you show it below:

> as you point out:
>
>    function Compare_Common( X, Y : T'Class ) return Boolean is
>      Temp : T'Class ;

Umm, this is illegal in Ada 95. The item needs to be initialized, and the
type can't be changed after the initialization. So you can't solve the
problem this way.

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

From: Michael Yoder
Sent: Friday, July 6, 2001 11:06 AM

It seems to me the principal missing thing is a way to go from a tag to a
type.  Given that, everything else can be done by defining the right
operations on tags (e.g., a function to yield the tag of the parent given
the tag of the type).

Here's an initial suggestion: an attribute 'Type that takes a single
argument which is a tag, and which yields a tagged type.  (Yes, I know this
is a peculiar sort of attribute.)  The prefix must be a tagged type, and the
tag is required to be that of a type in the derivation class of the prefix,
or Tag_Error is raised.

If this type is allowed to be used generally, e.g. to declare an object,
more is needed because of discriminants.  Consider the following:

    declare
       X : T'Type (Tag);
    ...

We must require that the type corresponding to Tag has defaults for all its
discriminants (or an exception is raised).

It is possible that T has discriminants which you want to use; consider
this:

    declare
       X : T'Type (Tag) (Discrim1, Discrim2);
    ...

Here (Discrim1, Discrim2) must be a valid discriminant constraint for the
type T.  Moreover Tag mustn't correspond to a type that replaces T's
discriminants with others, or an exception is raised.

I do not have a good handle on how difficult this would be to implement on
the compiler side, but it seems to me that the run-time side could work
using standard techniques.  (If I'm wrong, by all means disabuse me of this
notion.)

In the above, "an exception" is one of Constraint_Error, Program_Error, or
Tag_Error.  I lean towards Constraint_Error.  Oh, and I'm not seriously
suggesting that the name of the attribute be 'Type, though I suppose it
could be.

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

From: Christoph Grein
Sent: Monday, July 9, 2001 2:47 AM

>     declare
>        X : T'Type (Tag);
>     ...
>
> We must require that the type corresponding to Tag has defaults for all its
> discriminants (or an exception is raised).

And T'Type (Tag) must not be abstract to be consistent with the current
situration that there are never objects of an abstarct type.

>
> It is possible that T has discriminants which you want to use; consider
> this:
>
>     declare
>        X : T'Type (Tag) (Discrim1, Discrim2);
>     ...
>
> Here (Discrim1, Discrim2) must be a valid discriminant constraint for the
> type T.  Moreover Tag mustn't correspond to a type that replaces T's
> discriminants with others, or an exception is raised.

Uh-hu, where does this lead us?

And it seems to me that for the problem of the common ancestor, we would need
more for a nice solution: Tags could be partially ordered:

   function "<=" (Left, Right: Tag) return Boolean;

   Returns True if Right is in the derivation tree of Left, else False.

X: TX;
Y: TY;

X'Tag <= Y'Tag if and only if Y in TX'Class.

Then we could easily walk back in the derivation tree until the tags match.

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

From: Randy Brukardt
Sent: Friday, July 13, 2001 5:33 PM

> Uh-hu, where does this lead us?

Personally, I think it is into an impenetrable morass. Remember that tagged
types can add discriminants when they are derived, and moreover cannot have
initialized discriminants. Thus, you have to get the discriminants on an
object declaration, but with this attribute, there would be no way to know
what they are!

Also, as Christoph noted, if the tag represented an abstract type, you
couldn't declare an object.

I think these would have to be restricted to very specific contexts. One way
would be to declare them indefinite (which would prevent object
declarations, and many sorts of generic matching), but I'm not sure that's
enough.

The real goal here is to get a dynamic view conversion where the type is
identified by a tag value, rather than by name. Going further than that
brings in a truckload of worms. Perhaps we just need to define such an
attribute:

    T'Class'Convert (Obj : in out T'Class; Tag : in Ada.Tags.Tag)

Returns a view conversion of Obj to the type identified by tag. This
attribute is considered dynamically tagged for dispatching purposes. Raises
Constraint_Error if Tag does not identify a type in T'Class, or if the tag
of Obj does not identify a type that is covered by or descended from the
classwide type identified by Tag (this is 4.6(42)). Note: The tag of <Obj>
is unchanged, as in all view conversions.

> And it seems to me that for the problem of the common ancestor, we would
need
> more for a nice solution: Tags could be partially ordered:
>
>    function "<=" (Left, Right: Tag) return Boolean;
>
>    Returns True if Right is in the derivation tree of Left, else False.
>
> X: TX;
> Y: TY;
>
> X'Tag <= Y'Tag if and only if Y in TX'Class.
>
> Then we could easily walk back in the derivation tree until the tags
match.

Right. We still need a way to get the parent tag or something like that. The
above would work, as would my original 'Parent_Tag. (I really think it is a
horrible idea to introduce types that aren't known at compile time; I
suspect that is why the 'Parent_Type was rejected.)

My preference for 'Parent_Tag is that it is illegal on non-derived,
non-tagged types. But making it illegal on non-derived type probably is a
contract model problem. So, I suppose it probably ought to return Null_Tag
on non-derived types (which means we'd have to define that, too). Similarly,
it ought to return the parent tag even for private types (skipping levels of
derivation would cause problems in practice, and would make it worthless for
the "calling the parent" problem. That means the parent tag of the full
type, which should not be a problem to do (this is not a static expression,
and certainly at runtime the parent tag is known. Indeed, Ada
implementations have to store this in the tag already in order to be able to
make the type conversion checks of 4.6(42) - I would expect that this is
precisely what would be returned. If the compiler has the information, why
shouldn't the programmer be able to use it??)

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


Questions? Ask the ACAA Technical Agent