comp.lang.ada
 help / color / mirror / Atom feed
* Equivalent of dynamic_cast (downcast) for tagged types
@ 2011-01-27 15:46 Maciej Sobczak
  2011-01-27 16:18 ` Dmitry A. Kazakov
  2011-01-27 19:33 ` Equivalent of dynamic_cast (downcast) for tagged types Adam Beneschan
  0 siblings, 2 replies; 25+ messages in thread
From: Maciej Sobczak @ 2011-01-27 15:46 UTC (permalink / raw)


Hi,

Unfortunately I need to HACK some equivalent of dynamic_cast in Ada.

There is a limited tagged type T deriving from Limited_Controlled and
Some_Interface as well.
There is also a procedure like this:

procedure Foo (X : in Some_Interface'Class);

Inside Foo I would like to check if X is indeed T and if so, I would
like to dynamic_cast to it and execute some operation on T. That
operation is in fact declared in Some_Interface anyway, but I would
like to avoid the dispatching call. I would also like to avoid any
potential data copying, so access types are welcome.

In C++ it would look like this:

void foo(some_interface & x)
{
    T * p = dynamic_cast<T*>(&x);
    if (p != NULL)
    {
        // indeed x is of type T
        p->do_something_for_me();
    }
}

If you say that I should not do this because it is not good practice,
then I will quickly answer that GNAT should not randomly call Adjust
on my limited (!) objects.

Thank you for your help.

--
Maciej Sobczak * http://www.inspirel.com



^ permalink raw reply	[flat|nested] 25+ messages in thread

* Re: Equivalent of dynamic_cast (downcast) for tagged types
  2011-01-27 15:46 Equivalent of dynamic_cast (downcast) for tagged types Maciej Sobczak
@ 2011-01-27 16:18 ` Dmitry A. Kazakov
  2011-01-27 17:10   ` J-P. Rosen
                     ` (2 more replies)
  2011-01-27 19:33 ` Equivalent of dynamic_cast (downcast) for tagged types Adam Beneschan
  1 sibling, 3 replies; 25+ messages in thread
From: Dmitry A. Kazakov @ 2011-01-27 16:18 UTC (permalink / raw)


On Thu, 27 Jan 2011 07:46:33 -0800 (PST), Maciej Sobczak wrote:

> Unfortunately I need to HACK some equivalent of dynamic_cast in Ada.
> 
> There is a limited tagged type T deriving from Limited_Controlled and
> Some_Interface as well.
> There is also a procedure like this:
> 
> procedure Foo (X : in Some_Interface'Class);
> 
> Inside Foo I would like to check if X is indeed T and if so, I would
> like to dynamic_cast to it and execute some operation on T. That
> operation is in fact declared in Some_Interface anyway, but I would
> like to avoid the dispatching call. I would also like to avoid any
> potential data copying, so access types are welcome.

You don't need access type, tagged types are by-reference anyway.

> In C++ it would look like this:
> 
> void foo(some_interface & x)
> {
>     T * p = dynamic_cast<T*>(&x);
>     if (p != NULL)
>     {
>         // indeed x is of type T
>         p->do_something_for_me();
>     }
> }

   if X in T'Class then
      Do_Something (T (X));  -- No dispatch
   end if;

The above is a bad idea, because X might be a descendant of T which has
overridden Do_Something. So you might be looking for this

   if X'Tag = T'Tag then -- X is of T
      Do_Something (T (X));  -- No dispatch
   end if;

> If you say that I should not do this because it is not good practice,
> then I will quickly answer that GNAT should not randomly call Adjust
> on my limited (!) objects.

Adjust is not called on limited objects.

-- 
Regards,
Dmitry A. Kazakov
http://www.dmitry-kazakov.de



^ permalink raw reply	[flat|nested] 25+ messages in thread

* Re: Equivalent of dynamic_cast (downcast) for tagged types
  2011-01-27 16:18 ` Dmitry A. Kazakov
@ 2011-01-27 17:10   ` J-P. Rosen
  2011-01-27 17:35     ` Dmitry A. Kazakov
  2011-01-27 22:49     ` Maciej Sobczak
  2011-01-27 17:50   ` Georg Bauhaus
  2011-01-27 22:35   ` Maciej Sobczak
  2 siblings, 2 replies; 25+ messages in thread
From: J-P. Rosen @ 2011-01-27 17:10 UTC (permalink / raw)


Le 27/01/2011 17:18, Dmitry A. Kazakov a �crit :
>    if X'Tag = T'Tag then -- X is of T
>       Do_Something (T (X));  -- No dispatch
>    end if;
> 
or
    if X in T then -- slightly more elegant

>> If you say that I should not do this because it is not good practice,
It's bad practice in C++ because it is unsafe.
I see no objection in Ada, where tags are checked, and I have some
pretty convincing examples of that.

Admitedly, it not very OOrthodox, but OOrthodoxy has been defined by C++
people!


-- 
---------------------------------------------------------
           J-P. Rosen (rosen@adalog.fr)
Adalog a d�m�nag� / Adalog has moved:
2 rue du Docteur Lombard, 92441 Issy-les-Moulineaux CEDEX
Tel: +33 1 45 29 21 52, Fax: +33 1 45 29 25 00



^ permalink raw reply	[flat|nested] 25+ messages in thread

* Re: Equivalent of dynamic_cast (downcast) for tagged types
  2011-01-27 17:10   ` J-P. Rosen
@ 2011-01-27 17:35     ` Dmitry A. Kazakov
  2011-01-27 22:49     ` Maciej Sobczak
  1 sibling, 0 replies; 25+ messages in thread
From: Dmitry A. Kazakov @ 2011-01-27 17:35 UTC (permalink / raw)


On Thu, 27 Jan 2011 18:10:49 +0100, J-P. Rosen wrote:

> I see no objection in Ada, where tags are checked, and I have some
> pretty convincing examples of that.

One objection is that if tags are checked manually in an if-statement
(unfortunately there is no way to use tags or equivalents in the
case-statement, which is a long story to tell), there is a danger that some
alternatives could remain uncovered or handled in an unexpected way.

Arguably the cases, which indeed exist, should rather be addressed by
multiple dispatch and extensible operations.

-- 
Regards,
Dmitry A. Kazakov
http://www.dmitry-kazakov.de



^ permalink raw reply	[flat|nested] 25+ messages in thread

* Re: Equivalent of dynamic_cast (downcast) for tagged types
  2011-01-27 16:18 ` Dmitry A. Kazakov
  2011-01-27 17:10   ` J-P. Rosen
@ 2011-01-27 17:50   ` Georg Bauhaus
  2011-01-27 22:35   ` Maciej Sobczak
  2 siblings, 0 replies; 25+ messages in thread
From: Georg Bauhaus @ 2011-01-27 17:50 UTC (permalink / raw)


On 27.01.11 17:18, Dmitry A. Kazakov wrote:

> The above is a bad idea, because X might be a descendant of T which has
> overridden Do_Something. So you might be looking for this
> 
>    if X'Tag = T'Tag then -- X is of T
>       Do_Something (T (X));  -- No dispatch
>    end if;
> 

The following seems to be working in Pure units, too,
since it does not depend on Ada.Tags:

    procedure Foo (X : in out P.Some_Interface'Class) is
    begin
        declare
            Dynamic_Cast_X : Descendant.T renames Descendant.T(X);
        begin
            Dynamic_Cast_X.Do_Something_For_Me;
        end;
    exception
        when CE:  Constraint_Error =>
            -- X not of type Descendant.T ...
            null;
    end Foo;




^ permalink raw reply	[flat|nested] 25+ messages in thread

* Re: Equivalent of dynamic_cast (downcast) for tagged types
  2011-01-27 15:46 Equivalent of dynamic_cast (downcast) for tagged types Maciej Sobczak
  2011-01-27 16:18 ` Dmitry A. Kazakov
@ 2011-01-27 19:33 ` Adam Beneschan
  1 sibling, 0 replies; 25+ messages in thread
From: Adam Beneschan @ 2011-01-27 19:33 UTC (permalink / raw)


On Jan 27, 7:46 am, Maciej Sobczak <see.my.homep...@gmail.com> wrote:
> Hi,
>
> Unfortunately I need to HACK some equivalent of dynamic_cast in Ada.
>
> There is a limited tagged type T deriving from Limited_Controlled and
> Some_Interface as well.
> There is also a procedure like this:
>
> procedure Foo (X : in Some_Interface'Class);
>
> Inside Foo I would like to check if X is indeed T

   if X in T

> and if so, I would
> like to dynamic_cast to it and execute some operation on T.

     then Execute_Some_Operation_On (T(X));
   end if;

Wonderful, no?  In Ada you get to write pretty much what you were
thinking of doing.  The C++ equivalent is quite elegant, if you
consider the number of ugly punctuation characters and cryptically-
named built-in identifiers to be a measure of elegance.

                             -- Adam



^ permalink raw reply	[flat|nested] 25+ messages in thread

* Re: Equivalent of dynamic_cast (downcast) for tagged types
  2011-01-27 16:18 ` Dmitry A. Kazakov
  2011-01-27 17:10   ` J-P. Rosen
  2011-01-27 17:50   ` Georg Bauhaus
@ 2011-01-27 22:35   ` Maciej Sobczak
  2011-01-28  5:07     ` Yannick Duchêne (Hibou57)
                       ` (2 more replies)
  2 siblings, 3 replies; 25+ messages in thread
From: Maciej Sobczak @ 2011-01-27 22:35 UTC (permalink / raw)


On Jan 27, 5:18 pm, "Dmitry A. Kazakov" <mail...@dmitry-kazakov.de>
wrote:

>    if X in T'Class then
>       Do_Something (T (X));  -- No dispatch
>    end if;
>
> The above is a bad idea, because X might be a descendant of T which has
> overridden Do_Something. So you might be looking for this
>
>    if X'Tag = T'Tag then -- X is of T
>       Do_Something (T (X));  -- No dispatch
>    end if;

Thank you, this makes sense.

> Adjust is not called on limited objects.

Yeah, sure. :-D

http://gcc.gnu.org/bugzilla/show_bug.cgi?id=41845

I have submitted this bug report more than one year ago. Looks like
nobody even bothered to check it.

The bug submission states that Adjust is wrongly recognized as
overriding, which it shouldn't be - but now I have hit something more
interesting.
I got an exception from a place where a regular dispatching operation
on some class-wide type was done. After some debugging and analysis
the exception appeared to come from s-finroo.adb:45, from
System.Finalization_Root.Adjust, which just raises Program_Error. Why
the heck this Adjust was called on my Controlled_Limited type is a
mystery, but that was easy to confirm: I have added the Adjust
operation for my type, just as in the bug report, and instrumented it
and voila - this proved that the Adjust was really *called*.

But that was just the beginning. I could live with a dummy
implementation of Adjust for my limited (!) type, but the real problem
was with the dispatching operation that I wanted to call. This
operation is a function that returns some access value - it is
declared to be abstract in the base interface and implemented in my
type. There is nothing between the abstract declaration and my
implemetation, but my implementation is *not* called (again,
instrumented), but somehow the result of the call is null, which leads
to a disaster few lines later. Looks like GNAT generated some dummy
body for the abstract function and called that instead.

So, to summarise: yes, Adjust is randomly called on Controlled_Limited
types and it even dispatches properly (wow!), and no, my own
operations do not dispatch and instead something dummy gives me
equally dummy results.

This happens in GNAT GPL 2010, but not in older compilers.
It is really demotivating to see that object orientation in its basic
form still doesn't work in GNAT.

But in addition to downcasting, there is an alternative solution which
I have discovered later on, although have not yet tested.
The idea is if I know the actual type (T) in the procedure that
accepts base interface, then apparently I'm working with a closed set
of types, which is actually true. So instead of relying on class-wide
types and dispatching in this particular place I will add another
version of the same operation that accepts T and operates on it. A bit
of code duplication, but with a chance to work around this not-so-
funny GNAT bug.

--
Maciej Sobczak * http://www.inspirel.com



^ permalink raw reply	[flat|nested] 25+ messages in thread

* Re: Equivalent of dynamic_cast (downcast) for tagged types
  2011-01-27 17:10   ` J-P. Rosen
  2011-01-27 17:35     ` Dmitry A. Kazakov
@ 2011-01-27 22:49     ` Maciej Sobczak
  1 sibling, 0 replies; 25+ messages in thread
From: Maciej Sobczak @ 2011-01-27 22:49 UTC (permalink / raw)


On Jan 27, 6:10 pm, "J-P. Rosen" <ro...@adalog.fr> wrote:

> It's bad practice in C++ because it is unsafe.

It is perfectly safe in the form that I have presented.
Another version with references is also perfectly safe, as it does not
even produce any null pointer value and throws an exception - which is
equivalent to what Ada does.

> Admitedly, it not very OOrthodox, but OOrthodoxy has been defined by C++
> people!

Please note that object orientation actually works in C++, whereas it
demonstrably doesn't work with GNAT (see my response to Dmitry for
details) and we are talking about the time gap of some 20 years. I
fully understand your opinion about C++, but in this particular case
it would be very difficult to defend.

OK, now ducking away to move forward my Ada project. ;-)

--
Maciej Sobczak * http://www.inspirel.com



^ permalink raw reply	[flat|nested] 25+ messages in thread

* Re: Equivalent of dynamic_cast (downcast) for tagged types
  2011-01-27 22:35   ` Maciej Sobczak
@ 2011-01-28  5:07     ` Yannick Duchêne (Hibou57)
  2011-01-28  9:16     ` Dmitry A. Kazakov
  2011-01-28 15:13     ` Maciej Sobczak
  2 siblings, 0 replies; 25+ messages in thread
From: Yannick Duchêne (Hibou57) @ 2011-01-28  5:07 UTC (permalink / raw)


Le Thu, 27 Jan 2011 23:35:28 +0100, Maciej Sobczak  
<see.my.homepage@gmail.com> a écrit:
> http://gcc.gnu.org/bugzilla/show_bug.cgi?id=41845
>
> I have submitted this bug report more than one year ago. Looks like
> nobody even bothered to check it.
Not a bug to me, while I agree this is not expected: this is “not  
portable” instead. However, according to the Ada reference (somewhere at  
the beginning I remember), a compiler with specific added capabilities,  
must 1) inform about this none-standard feature 2) provide an option to  
run the compiler in strict standard mode. I read it two or three years  
ago, and I suppose this apply to standard packages too (it was about  
overall compiler requirements to be OK to be said “an Ada compiler”).

But this was not your main topic :p

-- 
Si les chats miaulent et font autant de vocalises bizarres, c’est pas pour  
les chiens.

“I am fluent in ASCII” [Warren 2010]



^ permalink raw reply	[flat|nested] 25+ messages in thread

* Re: Equivalent of dynamic_cast (downcast) for tagged types
  2011-01-27 22:35   ` Maciej Sobczak
  2011-01-28  5:07     ` Yannick Duchêne (Hibou57)
@ 2011-01-28  9:16     ` Dmitry A. Kazakov
  2011-01-28 13:11       ` AdaMagica
  2011-01-28 16:44       ` Adam Beneschan
  2011-01-28 15:13     ` Maciej Sobczak
  2 siblings, 2 replies; 25+ messages in thread
From: Dmitry A. Kazakov @ 2011-01-28  9:16 UTC (permalink / raw)


On Thu, 27 Jan 2011 14:35:28 -0800 (PST), Maciej Sobczak wrote:

> I have submitted this bug report more than one year ago. Looks like
> nobody even bothered to check it.

Well, I am not a language lawyer to tell if the behavior (public overriding
of a private primitive operation) is legal.

> The bug submission states that Adjust is wrongly recognized as
> overriding, which it shouldn't be - but now I have hit something more
> interesting.

It must be overriding because Adjust is a primitive operation. Whether the
compiler should allow it to become publicly declared as an overriding is
another question.

This is a general language design problem, also reappearing in multiple
inheritance. Until addressed it will pop up in different places on
different occasions.

> Why
> the heck this Adjust was called on my Controlled_Limited type is a
> mystery, but that was easy to confirm: I have added the Adjust
> operation for my type, just as in the bug report, and instrumented it
> and voila - this proved that the Adjust was really *called*.

Maybe the implementation is using the private operation internally. Once
you have overridden it, you broke the thing.

I agree that the problem must be addressed, by handling MI-like cases
properly. But it is a long story.

Another long story is that constructor, destructor, ":=" must be built-in
primitive operations without Initialize, Finalize, Adjust kludges. AdaCore
would not come to the idea of providing Adjust for limited types if there
were proper constructors.

> But in addition to downcasting, there is an alternative solution which
> I have discovered later on, although have not yet tested.
> The idea is if I know the actual type (T) in the procedure that
> accepts base interface, then apparently I'm working with a closed set
> of types, which is actually true. So instead of relying on class-wide
> types and dispatching in this particular place I will add another
> version of the same operation that accepts T and operates on it.

BTW, this is another language design issue. What you want here is
dispatching to a narrower class rather than to the leaf. I know no OO
language which has this. Arguably many cases of downcast could be
eliminated if the application would not forced either to dispatch
"prematurely," or not to dispatch at all.

-- 
Regards,
Dmitry A. Kazakov
http://www.dmitry-kazakov.de



^ permalink raw reply	[flat|nested] 25+ messages in thread

* Re: Equivalent of dynamic_cast (downcast) for tagged types
  2011-01-28  9:16     ` Dmitry A. Kazakov
@ 2011-01-28 13:11       ` AdaMagica
  2011-01-28 14:13         ` Dmitry A. Kazakov
  2011-01-28 16:44       ` Adam Beneschan
  1 sibling, 1 reply; 25+ messages in thread
From: AdaMagica @ 2011-01-28 13:11 UTC (permalink / raw)


On 28 Jan., 10:16, "Dmitry A. Kazakov" <mail...@dmitry-kazakov.de>
wrote:
> On Thu, 27 Jan 2011 14:35:28 -0800 (PST), Maciej Sobczak wrote:
> > I have submitted this bug report more than one year ago. Looks like
> > nobody even bothered to check it.
>
> Well, I am not a language lawyer to tell if the behavior (public overriding
> of a private primitive operation) is legal.
>
> > The bug submission states that Adjust is wrongly recognized as
> > overriding, which it shouldn't be - but now I have hit something more
> > interesting.
>
> It must be overriding because Adjust is a primitive operation. Whether the
> compiler should allow it to become publicly declared as an overriding is
> another question.

The public specification of Ada.Finalization.Limited_Controlled has no
Adjust operation, so there is no primitive operation with that name
which you can override, full stop. It's irrelevant if there is some
internal one deeply burrowed in the compiler's implementation.



^ permalink raw reply	[flat|nested] 25+ messages in thread

* Re: Equivalent of dynamic_cast (downcast) for tagged types
  2011-01-28 13:11       ` AdaMagica
@ 2011-01-28 14:13         ` Dmitry A. Kazakov
  2011-01-28 23:51           ` Randy Brukardt
  0 siblings, 1 reply; 25+ messages in thread
From: Dmitry A. Kazakov @ 2011-01-28 14:13 UTC (permalink / raw)


On Fri, 28 Jan 2011 05:11:16 -0800 (PST), AdaMagica wrote:

> On 28 Jan., 10:16, "Dmitry A. Kazakov" <mail...@dmitry-kazakov.de>
> wrote:
>> On Thu, 27 Jan 2011 14:35:28 -0800 (PST), Maciej Sobczak wrote:
>>> I have submitted this bug report more than one year ago. Looks like
>>> nobody even bothered to check it.
>>
>> Well, I am not a language lawyer to tell if the behavior (public overriding
>> of a private primitive operation) is legal.
>>
>>> The bug submission states that Adjust is wrongly recognized as
>>> overriding, which it shouldn't be - but now I have hit something more
>>> interesting.
>>
>> It must be overriding because Adjust is a primitive operation. Whether the
>> compiler should allow it to become publicly declared as an overriding is
>> another question.
> 
> The public specification of Ada.Finalization.Limited_Controlled has no
> Adjust operation, so there is no primitive operation with that name
> which you can override, full stop.

It has no *visible* primitive operation.

> It's irrelevant if there is some
> internal one deeply burrowed in the compiler's implementation.

Unfortunately it leaks out. Consider this:

package P is
   type T is tagged private;
private
   type T is tagged null record;
   procedure Foo (X : in out T);
end P;

with P;
package Q is
   type S is new P.T with null record;
   procedure Foo (X : in out S);
end Q;

with Q;
package P.R is
   type U is new Q.S with null record;
   procedure Bar (Object : in out U'Class);
end P.R;

package body P.R is
   procedure Bar (Object : in out U'Class) is
   begin
      Foo (Object); -- Which Foo is called here? Is there one or two?
   end Bar;
end P.R;

This problem can also be observed with components. E.g. P.T may have a
component Baz, declared privately. Q.S may have "another" Baz declared
publicly. P.R.U faces a name conflict which must be resolved in some of two
possible ways: "overloading" vs. "overriding."

-- 
Regards,
Dmitry A. Kazakov
http://www.dmitry-kazakov.de



^ permalink raw reply	[flat|nested] 25+ messages in thread

* Re: Equivalent of dynamic_cast (downcast) for tagged types
  2011-01-27 22:35   ` Maciej Sobczak
  2011-01-28  5:07     ` Yannick Duchêne (Hibou57)
  2011-01-28  9:16     ` Dmitry A. Kazakov
@ 2011-01-28 15:13     ` Maciej Sobczak
  2011-01-28 17:47       ` Robert A Duff
  2 siblings, 1 reply; 25+ messages in thread
From: Maciej Sobczak @ 2011-01-28 15:13 UTC (permalink / raw)


On Jan 27, 11:35 pm, Maciej Sobczak <see.my.homep...@gmail.com> wrote:

> > Adjust is not called on limited objects.
>
> Yeah, sure. :-D

[...]

I have some more news on this.

Previously my observations were that:

> So, to summarise: yes, Adjust is randomly called on Controlled_Limited
> types and it even dispatches properly (wow!), and no, my own
> operations do not dispatch and instead something dummy gives me
> equally dummy results.

Interesting observation #2: my operation is the *third* primitive
operation of Some_Interface.

Interesting observation #3: Adjust is also the *third* primitive
operation of System.Finalization_Root.

Hm...

I have changed the order of declarations in my spec file, so that the
operation in question is now the *second* one.

Guess what? :-)

Now Finalize is called instead of Adjust. Yes, Finalize is the second
primitive operation in System.Finalization_Root.

It looks like the compiler has overwritten one virtual function table
with another one and since the operations have very similar
signatures, what happened is that the wrong operation was executed.

If anybody from AdaCore is reading this, please take the above as a
hint, I believe these observations are enough to show the proper
direction.

> But in addition to downcasting, there is an alternative solution which
> I have discovered later on, although have not yet tested.
> The idea is if I know the actual type (T) in the procedure that
> accepts base interface, then apparently I'm working with a closed set
> of types, which is actually true. So instead of relying on class-wide
> types and dispatching in this particular place I will add another
> version of the same operation that accepts T and operates on it.

It works. I will stick to this solution instead of "if X in T
then ..." as it is fully static instead of dynamic and it offers a
better granularity for later editing - I will just remove that
additional operation without touching the original one when the GNAT
bug is fixed.

Thanks to everybody who offered solution to my original problem.

--
Maciej Sobczak * http://www.inspirel.com



^ permalink raw reply	[flat|nested] 25+ messages in thread

* Re: Equivalent of dynamic_cast (downcast) for tagged types
  2011-01-28  9:16     ` Dmitry A. Kazakov
  2011-01-28 13:11       ` AdaMagica
@ 2011-01-28 16:44       ` Adam Beneschan
  2011-01-28 17:21         ` Dmitry A. Kazakov
  2011-01-28 17:33         ` Adam Beneschan
  1 sibling, 2 replies; 25+ messages in thread
From: Adam Beneschan @ 2011-01-28 16:44 UTC (permalink / raw)


On Jan 28, 1:16 am, "Dmitry A. Kazakov" <mail...@dmitry-kazakov.de>
wrote:
> On Thu, 27 Jan 2011 14:35:28 -0800 (PST), Maciej Sobczak wrote:
> > I have submitted this bug report more than one year ago. Looks like
> > nobody even bothered to check it.
>
> Well, I am not a language lawyer to tell if the behavior (public overriding
> of a private primitive operation) is legal.
>
> > The bug submission states that Adjust is wrongly recognized as
> > overriding, which it shouldn't be - but now I have hit something more
> > interesting.
>
> It must be overriding because Adjust is a primitive operation.

Wrong.

I can't find the language rule right now, but I'm certain that if a
tagged type T has a primitive operation Op declared in the private
part, and another package declares a type extension of T and declares
its own operation Op with the same profile, in a place where the first
Op is not visible, then the new Op is a **new** operation that has no
connection with the Op in the private part of the first package.  This
is what I would expect, since the private part of a package isn't
supposed to have any semantic effect on places that can't see the
private part.

Here's an example:

package Pak1 is
    type T1 is tagged null record;
    procedure Op1 (X : in out T1);
    procedure Test (Param : in out T1'Class);
private
    procedure Op2 (X : in out T1);
end Pak1;

with Ada.Text_IO;  use Text_IO;
package body Pak1 is
    procedure Op1 (X : in out T1) is
    begin
        Put_Line ("in Pak1.Op1");
    end Op1;

    procedure Op2 (X : in out T1) is
    begin
        Put_Line ("in Pak1.Op2");
    end Op2;

    procedure Test (Param : in out T1'Class) is
    begin
        Op1 (Param);
        Op2 (Param);
    end Test;

end Pak1;

with Pak1;  use Pak1;
package Pak2 is
    type T2 is new T1 with null record;
  --overriding                  ILLEGAL!
    procedure Op2 (X : in out T2);
end Pak2;

with Ada.Text_IO;  use Ada.Text_IO;
package body Pak2 is

    procedure Op2 (X : in out T2) is
    begin
        Put_Line ("in Pak2.Op2");
    end Op2;

end Pak2;


with Pak1;
with Pak2;
procedure Test87 is
    Y : Pak2.T2;
begin
    Pak1.Test (Y);
end Test87;

The Op2 defined in Pak2 has no connection with the Op2 defined in the
private part of Pak1.  It is not overriding.  And if Pak1.Op2 is
called as a dispatching operation, it will **not** dispatch to
Pak2.Op2, which is basically a new operation.  I compiled this with
GNAT and got

Pak1.Op1
Pak1.Op2

as expected.  (If you comment out the "private" keyword in Pak1, the
second line becomes Pak2.Op2.)

Furthermore, if the "overriding" line is uncommented, GNAT correctly
rejects the declaration, saying the procedure does not override
anything.

So since it's correctly rejecting the "overriding" in this case, but
not rejecting "overriding" on an Adjust operation of a type derived
from Limited_Controlled, there's definitely something amiss in the
compiler.  My guess is that the compiler handles (Limited_)Controlled
specially and thus doesn't quite go through the same path that it goes
through in my example.  But it's definitely a compiler bug, not a
portability issue.

                                  -- Adam






^ permalink raw reply	[flat|nested] 25+ messages in thread

* Re: Equivalent of dynamic_cast (downcast) for tagged types
  2011-01-28 16:44       ` Adam Beneschan
@ 2011-01-28 17:21         ` Dmitry A. Kazakov
  2011-01-29  0:12           ` Randy Brukardt
  2011-01-28 17:33         ` Adam Beneschan
  1 sibling, 1 reply; 25+ messages in thread
From: Dmitry A. Kazakov @ 2011-01-28 17:21 UTC (permalink / raw)


On Fri, 28 Jan 2011 08:44:08 -0800 (PST), Adam Beneschan wrote:

> On Jan 28, 1:16�am, "Dmitry A. Kazakov" <mail...@dmitry-kazakov.de>
> wrote:

>> It must be overriding because Adjust is a primitive operation.
> 
> Wrong.

[information hiding rationale skipped]

Yes, it is wrong (language design). The operation is primitive even if
invisible in *some* contexts. It means that there might exist other
contexts where two operations would conflict. Furthermore, Ada designers
tried (and expectedly failed) to ensure that "multiple inheritance" always
override. This goal is inconsistent with the information hiding principle,
as you have just pointed out. Now, the can of worms is open and the worms
are crawling far and wide.

P.S. Some silly rules exist to alleviate the mess. Like the rule that an
interface cannot be implemented privately. That does not help. Multiple
inheritance issues must be addressed properly. There is no other way.

-- 
Regards,
Dmitry A. Kazakov
http://www.dmitry-kazakov.de



^ permalink raw reply	[flat|nested] 25+ messages in thread

* Re: Equivalent of dynamic_cast (downcast) for tagged types
  2011-01-28 16:44       ` Adam Beneschan
  2011-01-28 17:21         ` Dmitry A. Kazakov
@ 2011-01-28 17:33         ` Adam Beneschan
  1 sibling, 0 replies; 25+ messages in thread
From: Adam Beneschan @ 2011-01-28 17:33 UTC (permalink / raw)


On Jan 28, 8:44 am, Adam Beneschan <a...@irvine.com> wrote:

> > It must be overriding because Adjust is a primitive operation.
>
> Wrong.
>
> I can't find the language rule right now, but I'm certain that if a
> tagged type T has a primitive operation Op declared in the private
> part, and another package declares a type extension of T and declares
> its own operation Op with the same profile, in a place where the first
> Op is not visible, then the new Op is a **new** operation that has no
> connection with the Op in the private part of the first package.  This
> is what I would expect, since the private part of a package isn't
> supposed to have any semantic effect on places that can't see the
> private part.

Some clarifications:

(1) The above doesn't apply if the "another package" is a child
package.  Thus, in my example, if you change Pak2 to Pak1.Pak2, that
changes the result, because the Op2 in Pak1.Pak2 does override an
inherited operation that is implicitly declared in the private part of
Pak1.Pak2 (even though the private part of Pak1.Pak2 is itself
implicit; it's still there).  But when the package is not a child
package, the inherited operation is not declared at all, so there's no
overriding.

(2) I think the language rules involved are around 3.9.2(20ff); AARM
3.9.2(20.a.3/3) seems to be the most important explanation in this
case.  See also AI05-126.

                                 -- Adam




^ permalink raw reply	[flat|nested] 25+ messages in thread

* Re: Equivalent of dynamic_cast (downcast) for tagged types
  2011-01-28 15:13     ` Maciej Sobczak
@ 2011-01-28 17:47       ` Robert A Duff
  2011-01-28 22:04         ` Maciej Sobczak
  0 siblings, 1 reply; 25+ messages in thread
From: Robert A Duff @ 2011-01-28 17:47 UTC (permalink / raw)


Maciej Sobczak <see.my.homepage@gmail.com> writes:

> If anybody from AdaCore is reading this, please take the above as a
> hint, I believe these observations are enough to show the proper
> direction.

Sorry, but AdaCore does not take bug reports from comp.lang.ada.

AdaCore does accept bug reports from the general public,
and fixes them eventually (if they turn out to be real bugs,
which is often not the case!), but of course this work has
lower priority than work for paying customers.

There seems to be a fairly easy workaround in your case:
Don't declare an Adjust procedure on limited controlled
types.  (I didn't read your bugzilla report; I'm just
going by what you said in this thread.)

- Bob



^ permalink raw reply	[flat|nested] 25+ messages in thread

* Re: Equivalent of dynamic_cast (downcast) for tagged types
  2011-01-28 17:47       ` Robert A Duff
@ 2011-01-28 22:04         ` Maciej Sobczak
  2011-01-30 20:22           ` Stephen Leake
  0 siblings, 1 reply; 25+ messages in thread
From: Maciej Sobczak @ 2011-01-28 22:04 UTC (permalink / raw)


On Jan 28, 6:47 pm, Robert A Duff <bobd...@shell01.TheWorld.com>
wrote:

> Sorry, but AdaCore does not take bug reports from comp.lang.ada.

I knew this will pop up. :-)

You see - if this happened to me, it might as well happen in the
future to anybody else, including their Most Valuable Customer (tm)
and that future might be next year or next week. Silently calling the
wrong operation is not something that I would like to see in a
critical system and I can imagine that the Most Valuable Customer (tm)
can get really pissed if this happens. It is therefore in the best
interest of AdaCore to have the fix ready *before* that time, so they
can prove their excellent support by reacting instantly. Seems like a
good strategy to me.

I don't expect AdaCore to ship updated GPL version out of their
calendar just because I wrote some rant on comp.lang.ada. Far from it
- I anyway appreciate that I get something for free as in free beer.
But if they are proactive in their care about their own business, they
should do the homework before this problem hits them the hard way.

In other words: I, as a person, can have very low priority for them.
But the BUG in their product must have a very high priority, no matter
who is reporting it.

Last but not least: the only reason why I don't submit a separate
bugzilla report for this is that I do not have the minimal
reproducible test case. But I'm willing to provide all necessary
information and assistance on request.

> There seems to be a fairly easy workaround in your case:
> Don't declare an Adjust procedure on limited controlled
> types.

I didn't - and that's why I got Program_Error from the Adjust
operation that is in s_finroo.adb:45.
I have declared Adjust for my own type only as part of my
investigations and in fact this allowed me to gather some interesting
observations that I described already.

BTW - I *never* declare Adjust in normal code, because I never use non-
limited Controlled types.

--
Maciej Sobczak * http://www.inspirel.com



^ permalink raw reply	[flat|nested] 25+ messages in thread

* Re: Equivalent of dynamic_cast (downcast) for tagged types
  2011-01-28 14:13         ` Dmitry A. Kazakov
@ 2011-01-28 23:51           ` Randy Brukardt
  2011-01-29  0:55             ` Adam Beneschan
  0 siblings, 1 reply; 25+ messages in thread
From: Randy Brukardt @ 2011-01-28 23:51 UTC (permalink / raw)


"Dmitry A. Kazakov" <mailbox@dmitry-kazakov.de> wrote in message 
news:joz7ejpgeda0$.etbrqjysvoke$.dlg@40tude.net...
> On Fri, 28 Jan 2011 05:11:16 -0800 (PST), AdaMagica wrote:
>
>> On 28 Jan., 10:16, "Dmitry A. Kazakov" <mail...@dmitry-kazakov.de>
>> wrote:
>>> On Thu, 27 Jan 2011 14:35:28 -0800 (PST), Maciej Sobczak wrote:
>>>> I have submitted this bug report more than one year ago. Looks like
>>>> nobody even bothered to check it.
>>>
>>> Well, I am not a language lawyer to tell if the behavior (public 
>>> overriding
>>> of a private primitive operation) is legal.
>>>
>>>> The bug submission states that Adjust is wrongly recognized as
>>>> overriding, which it shouldn't be - but now I have hit something more
>>>> interesting.
>>>
>>> It must be overriding because Adjust is a primitive operation. Whether 
>>> the
>>> compiler should allow it to become publicly declared as an overriding is
>>> another question.
>>
>> The public specification of Ada.Finalization.Limited_Controlled has no
>> Adjust operation, so there is no primitive operation with that name
>> which you can override, full stop.
>
> It has no *visible* primitive operation.
>
>> It's irrelevant if there is some
>> internal one deeply burrowed in the compiler's implementation.
>
> Unfortunately it leaks out. Consider this:
>
> package P is
>   type T is tagged private;
> private
>   type T is tagged null record;
>   procedure Foo (X : in out T);
> end P;
>
> with P;
> package Q is
>   type S is new P.T with null record;
>   procedure Foo (X : in out S);
> end Q;
>
> with Q;
> package P.R is
>   type U is new Q.S with null record;
>   procedure Bar (Object : in out U'Class);
> end P.R;
>
> package body P.R is
>   procedure Bar (Object : in out U'Class) is
>   begin
>      Foo (Object); -- Which Foo is called here? Is there one or two?
>   end Bar;
> end P.R;
>
> This problem can also be observed with components. E.g. P.T may have a
> component Baz, declared privately. Q.S may have "another" Baz declared
> publicly. P.R.U faces a name conflict which must be resolved in some of 
> two
> possible ways: "overloading" vs. "overriding."

In both of these of these cases, there are two operations (no overriding 
happens). Either one of the Foos is invisible (in which case you get the 
visible one), or the program is illegal because it is ambiguous.

The same happens for the components, with the exception that the type 
extension is illegal if there is anywhere in the program where two 
components with the same name could be visible. (That's necessary because 
they're not overloadable.)

That's what the *language* says; any particular compiler may have bugs in 
this area. For instance, Janus/Ada has an invariant that all component names 
must be unique. That means an attempt to declare an extension like the one 
in this example will always fail, even when the hidden components never 
would be visible. That's wrong, and needs to get fixed someday, but is not 
much of a priority (the bug is pervasive - the invariant simply is wrong, 
but virtually all of the component processing code -- designed for Ada 83 --  
depends on it, so the fix is very expensive,  the bug is rare in real code, 
and often when it happens the type would have been illegal anyway).

                                       Randy.







^ permalink raw reply	[flat|nested] 25+ messages in thread

* Re: Equivalent of dynamic_cast (downcast) for tagged types
  2011-01-28 17:21         ` Dmitry A. Kazakov
@ 2011-01-29  0:12           ` Randy Brukardt
  2011-01-29  8:47             ` Dmitry A. Kazakov
  0 siblings, 1 reply; 25+ messages in thread
From: Randy Brukardt @ 2011-01-29  0:12 UTC (permalink / raw)


"Dmitry A. Kazakov" <mailbox@dmitry-kazakov.de> wrote in message 
news:1phnecgw6ckgv.1vm12emlufu5b.dlg@40tude.net...
...
> Yes, it is wrong (language design). The operation is primitive even if
> invisible in *some* contexts. It means that there might exist other
> contexts where two operations would conflict. Furthermore, Ada designers
> tried (and expectedly failed) to ensure that "multiple inheritance" always
> override.

I have no idea as to what you are talking about here. The inheritance rules 
haven't changed significantly since Ada 95 (there is a tweak in Ada 2012 
which will help alievate this problem when it is unintentional). And 
multiple inheritance has nothing whatsoever to do with it. IMHO, MI is a 
large cannon whose primary purpose is to blow off limbs. :-) Don't do it.

Remember that a lot more goes into what is overriding than just the names of 
operations; it also depends on the profiles and visibility. Just thinking 
about names always gets you into trouble when thinking about Ada.

And, regardless of whether there is a language design flaw, there is no 
chance that this would change. It would break too much existing code. 
Moreover, I think that what you are talking about is actively harmful. Claw, 
for instance, uses the current rules to have implementation operations that 
are dispatching, but that clients cannot override in any circumstances. (If 
they did, Claw would likely fall over in a heap, causing a debugging and 
support nightmare.)

                                         Randy.





^ permalink raw reply	[flat|nested] 25+ messages in thread

* Re: Equivalent of dynamic_cast (downcast) for tagged types
  2011-01-28 23:51           ` Randy Brukardt
@ 2011-01-29  0:55             ` Adam Beneschan
  0 siblings, 0 replies; 25+ messages in thread
From: Adam Beneschan @ 2011-01-29  0:55 UTC (permalink / raw)


On Jan 28, 3:51 pm, "Randy Brukardt" <ra...@rrsoftware.com> wrote:

> In both of these of these cases, there are two operations (no overriding
> happens). Either one of the Foos is invisible (in which case you get the
> visible one), or the program is illegal because it is ambiguous.

Technically, I don't think you can get an ambiguous program from two
subprograms with the same name and same profile.  Either one overrides
the other, as you say; or, by 8.3(12.2/2), all of them will be hidden
from all visibility, and then none of the Foos is visible.  Although I
guess that really isn't that much different from calling it
"ambiguous".

                                 -- Adam



^ permalink raw reply	[flat|nested] 25+ messages in thread

* Re: Equivalent of dynamic_cast (downcast) for tagged types
  2011-01-29  0:12           ` Randy Brukardt
@ 2011-01-29  8:47             ` Dmitry A. Kazakov
  0 siblings, 0 replies; 25+ messages in thread
From: Dmitry A. Kazakov @ 2011-01-29  8:47 UTC (permalink / raw)


On Fri, 28 Jan 2011 18:12:20 -0600, Randy Brukardt wrote:

> "Dmitry A. Kazakov" <mailbox@dmitry-kazakov.de> wrote in message 
> news:1phnecgw6ckgv.1vm12emlufu5b.dlg@40tude.net...
> ...
>> Yes, it is wrong (language design). The operation is primitive even if
>> invisible in *some* contexts. It means that there might exist other
>> contexts where two operations would conflict. Furthermore, Ada designers
>> tried (and expectedly failed) to ensure that "multiple inheritance" always
>> override.
> 
> I have no idea as to what you are talking about here.

I mean that the issues, MI is customary accused of, are common and
unavoidable with or without MI.

> IMHO, MI is a 
> large cannon whose primary purpose is to blow off limbs.

Nope, MI is an important software development tool for refactoring and
reuse. My current project's size could be reduced in a third, if Ada had
MI.

> Remember that a lot more goes into what is overriding than just the names of 
> operations; it also depends on the profiles and visibility.

But the core issue is always same: the choice between overloading vs.
overriding.

> And, regardless of whether there is a language design flaw, there is no 
> chance that this would change. It would break too much existing code. 

I don't think so, because the means to control the choice are just absent.
Clearly there should be some syntax additions to specify the behavior. So
far the only one is the keyword "overriding."

Also there must be means to resolve unavoidable conflicts. Each overloaded
item should have a unique name. Furthermore, the primitive operations
should have names (presently the names of their type-specific instances are
used in an ambiguous manner).

> Moreover, I think that what you are talking about is actively harmful. Claw, 
> for instance, uses the current rules to have implementation operations that 
> are dispatching, but that clients cannot override in any circumstances. (If 
> they did, Claw would likely fall over in a heap, causing a debugging and 
> support nightmare.)

I am talking about the reality, the reality hurts, I know (:-)). BTW, I am
not proposing to change the current language behavior. I want new language
means to be able to *describe* this behavior explicitly and same means to
describe the alternative behavior where I wanted.

-- 
Regards,
Dmitry A. Kazakov
http://www.dmitry-kazakov.de



^ permalink raw reply	[flat|nested] 25+ messages in thread

* Re: Equivalent of dynamic_cast (downcast) for tagged types
  2011-01-28 22:04         ` Maciej Sobczak
@ 2011-01-30 20:22           ` Stephen Leake
  2011-01-31  9:04             ` AdaCore, user community and communication channels Maciej Sobczak
  0 siblings, 1 reply; 25+ messages in thread
From: Stephen Leake @ 2011-01-30 20:22 UTC (permalink / raw)


Maciej Sobczak <see.my.homepage@gmail.com> writes:

> On Jan 28, 6:47 pm, Robert A Duff <bobd...@shell01.TheWorld.com>
> wrote:
>
>> Sorry, but AdaCore does not take bug reports from comp.lang.ada.
>
> I knew this will pop up. :-)
>
> You see - if this happened to me, it might as well happen in the
> future to anybody else, including their Most Valuable Customer (tm)
> and that future might be next year or next week. 

This is true.

> Silently calling the wrong operation is not something that I would
> like to see in a critical system and I can imagine that the Most
> Valuable Customer (tm) can get really pissed if this happens. It is
> therefore in the best interest of AdaCore to have the fix ready
> *before* that time, so they can prove their excellent support by
> reacting instantly. Seems like a good strategy to me.

Yes. But the point is, how are they to become aware of the bug?

Think of all the places bugs might be mentioned; here, on your personal
blog, on some university student mailing list, on a Debian bug list,
etc.

AdaCore cannot possibly monitor all possible places bugs might be
mentioned.

What they can do is provide one place to report bugs to them, and make
that as easy to use as possible. That is sending an email to
report@gnat.com.

Why have you not done that?

I can think of one reason; this is not mentioned on
libre.adacore.com/libre. Searching for "report bug" there turned up a
reference to reporting bugs in GtkAda, but not for other AdaCore tools.

So I'm reporting a bug in the website, via an unrecognized channel;
chance of success?
 
> Last but not least: the only reason why I don't submit a separate
> bugzilla report for this is that I do not have the minimal
> reproducible test case. But I'm willing to provide all necessary
> information and assistance on request.

AdaCore does not take bugs from bugzilla either.

-- 
-- Stephe



^ permalink raw reply	[flat|nested] 25+ messages in thread

* AdaCore, user community and communication channels
  2011-01-30 20:22           ` Stephen Leake
@ 2011-01-31  9:04             ` Maciej Sobczak
  2011-01-31 10:42               ` Georg Bauhaus
  0 siblings, 1 reply; 25+ messages in thread
From: Maciej Sobczak @ 2011-01-31  9:04 UTC (permalink / raw)


On Jan 30, 9:22 pm, Stephen Leake <stephen_le...@stephe-leake.org>
wrote:

> Yes. But the point is, how are they to become aware of the bug?

This is very simple: AdaCore is the leading provider of Ada-related
products and comp.lang.ada is the most active social website where Ada
is being discussed - and, please fasten your seatbelt, where their
next generation of customers is growing up. If AdaCore does not lurk
here, then there is either something terribly wrong with them or with
this newsgroup - ie. at least one of them has serious identity issues.

> AdaCore cannot possibly monitor all possible places bugs might be
> mentioned.

Of course. They have to set up some borders, where to stop. This
border also defines the "relevancy" of any given forum.

> What they can do is provide one place to report bugs to them, and make
> that as easy to use as possible. That is sending an email to
> rep...@gnat.com.
>
> Why have you not done that?

Because, as already mentioned, I don't have the reduced test case.
Please also note that I have described my problem as a follow-up to my
original question in this thread, in order to explain what happened,
why I wanted to bypass the dynamic dispatch and because I consider the
whole to be technically interesting in general. It was not supposed to
be a bug-report and I have never claimed it is intended to be.
But...

> So I'm reporting a bug in the website, via an unrecognized channel;
> chance of success?

... as already said, if comp.lang.ada is "an unrecognized channel",
there is something wrong.

> AdaCore does not take bugs from bugzilla either.

Then they will take them from their Most Valuable Customer (tm). Next
year or next week. I'm really fine with that and I think I have
already done enough on my side.

--
Maciej Sobczak * http://www.inspirel.com



^ permalink raw reply	[flat|nested] 25+ messages in thread

* Re: AdaCore, user community and communication channels
  2011-01-31  9:04             ` AdaCore, user community and communication channels Maciej Sobczak
@ 2011-01-31 10:42               ` Georg Bauhaus
  0 siblings, 0 replies; 25+ messages in thread
From: Georg Bauhaus @ 2011-01-31 10:42 UTC (permalink / raw)


On 31.01.11 10:04, Maciej Sobczak wrote:
> On Jan 30, 9:22 pm, Stephen Leake <stephen_le...@stephe-leake.org>
> wrote:
> 
>> Yes. But the point is, how are they to become aware of the bug?
> 
> This is very simple: AdaCore is the leading provider of Ada-related
> products and comp.lang.ada is the most active social website where Ada
> is being discussed - and, please fasten your seatbelt, where their
> next generation of customers is growing up. If AdaCore does not lurk
> here, then there is either something terribly wrong with them or with
> this newsgroup - ie. at least one of them has serious identity issues.

Perhaps another means of nurturing future Ada markets should be
added to the picture: the GAP contracts might be creating win-win
situations, without the need for programming students (and AdaCore
staff) to spend time in a public news group.   I'll also speculate
that not all types of programmer personalities would wish to present
themselves in public fora.

If said sets of next generation customers are perceived to be
relatively large, there is only one conclusion considering
comp.lang.ada: AdaCore staff stopping by would be enough, and
should be preferred to spending work hours here.  At least
when seen from the perspective of a marketing agent who compares
Utility(Resource => GAP) and Utility(Resource => comp.lang.ada).





^ permalink raw reply	[flat|nested] 25+ messages in thread

end of thread, other threads:[~2011-01-31 10:42 UTC | newest]

Thread overview: 25+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2011-01-27 15:46 Equivalent of dynamic_cast (downcast) for tagged types Maciej Sobczak
2011-01-27 16:18 ` Dmitry A. Kazakov
2011-01-27 17:10   ` J-P. Rosen
2011-01-27 17:35     ` Dmitry A. Kazakov
2011-01-27 22:49     ` Maciej Sobczak
2011-01-27 17:50   ` Georg Bauhaus
2011-01-27 22:35   ` Maciej Sobczak
2011-01-28  5:07     ` Yannick Duchêne (Hibou57)
2011-01-28  9:16     ` Dmitry A. Kazakov
2011-01-28 13:11       ` AdaMagica
2011-01-28 14:13         ` Dmitry A. Kazakov
2011-01-28 23:51           ` Randy Brukardt
2011-01-29  0:55             ` Adam Beneschan
2011-01-28 16:44       ` Adam Beneschan
2011-01-28 17:21         ` Dmitry A. Kazakov
2011-01-29  0:12           ` Randy Brukardt
2011-01-29  8:47             ` Dmitry A. Kazakov
2011-01-28 17:33         ` Adam Beneschan
2011-01-28 15:13     ` Maciej Sobczak
2011-01-28 17:47       ` Robert A Duff
2011-01-28 22:04         ` Maciej Sobczak
2011-01-30 20:22           ` Stephen Leake
2011-01-31  9:04             ` AdaCore, user community and communication channels Maciej Sobczak
2011-01-31 10:42               ` Georg Bauhaus
2011-01-27 19:33 ` Equivalent of dynamic_cast (downcast) for tagged types Adam Beneschan

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox