comp.lang.ada
 help / color / mirror / Atom feed
* Type extension and discriminants
@ 2014-10-10 20:55 sbelmont700
  2014-10-10 22:34 ` Randy Brukardt
  0 siblings, 1 reply; 8+ messages in thread
From: sbelmont700 @ 2014-10-10 20:55 UTC (permalink / raw)


Hi,

GNAT is giving me odd errors, strange crashes, and dubious successes, so to be safe I will ask the experts: what are the rules behind extending types with discriminants (specifically unknown) from non-child packages?

Consider T1, declared with unknown discriminants in the public part of package A, and then T2 which extends T1 in package B.  T2 can't see which (if any) discriminants T1 actually has, so it stands to reason T2 must be also declared with unknown discriminants (perhaps implicitly, given 3.7~26: "if derived, such a type has the same sort of discriminants (known, unknown, or none) as its parent (or ancestor) type").  Consequently, any constructor function that builds a T2 would need to use an extension aggregate, specifying one of T1's constructor functions to build the unknown parts.  This is what I would assume, but GNAT crashes when trying to compile such constructs.

However, if T2 tries to add discriminants, it would fall into the trap of needing to define T1's discriminants, which it could not since it has no idea if they exist or not.  Seemingly the best it could do is create discriminants for whatever arguments T1's constructor function would need, and use those as arguments in the type declaration?  As in:

type T2 (x : y, a : b) is new T1 (A.T1_Ctor(x)) with ...

Is this generally correct, or are there other esoteric problems that prevents these sort of constructs?

-sb


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

* Re: Type extension and discriminants
  2014-10-10 20:55 Type extension and discriminants sbelmont700
@ 2014-10-10 22:34 ` Randy Brukardt
  2014-10-11 17:45   ` sbelmont700
  0 siblings, 1 reply; 8+ messages in thread
From: Randy Brukardt @ 2014-10-10 22:34 UTC (permalink / raw)


It would have helped a bit had you given an example. I'm presuming you're 
talking about something like:

package P1 is
    type T (<>) is tagged private;
...
end P1;

with P1;
package P2 is
    type NT (???) is new P1.T with private;
....
end P2;

and you want to know how NT is declared.

If you had:
   type NT (D : E) is new P1.T with private;
RM 3.7(12-13) would be violated, so this is illegal.

If you had:
   type NT (D : E) is new P1.T (D) with private;

At least RM 3.7(15) would be violated (and there are probably more), so this 
is also illegal.

As you noted, 3.7(26) says that
    type NT is new P1.T with private;
has unknown discriminants, so this declaration is legal. As you note, you'd 
need an extension aggregate to initialize that. I think that should be legal 
as well (I can't think of a reason off hand that it shouldn't be).

I'd rather have the unknown discriminants explicit, as in:
    type NT(<>) is new P1.T with private;
This would need the same kind of extension aggregate.

My guess is that you've hit some sort of compiler bug (this is a little used 
area in my experience), but it's hard to tell without seeing a complete 
example.

                                          Randy.


<sbelmont700@gmail.com> wrote in message 
news:8d6fe2a6-a8fa-441f-8b35-fc5a744359fb@googlegroups.com...
Hi,

GNAT is giving me odd errors, strange crashes, and dubious successes, so to 
be safe I will ask the experts: what are the rules behind extending types 
with discriminants (specifically unknown) from non-child packages?

Consider T1, declared with unknown discriminants in the public part of 
package A, and then T2 which extends T1 in package B.  T2 can't see which 
(if any) discriminants T1 actually has, so it stands to reason T2 must be 
also declared with unknown discriminants (perhaps implicitly, given 3.7~26: 
"if derived, such a type has the same sort of discriminants (known, unknown, 
or none) as its parent (or ancestor) type").  Consequently, any constructor 
function that builds a T2 would need to use an extension aggregate, 
specifying one of T1's constructor functions to build the unknown parts. 
This is what I would assume, but GNAT crashes when trying to compile such 
constructs.

However, if T2 tries to add discriminants, it would fall into the trap of 
needing to define T1's discriminants, which it could not since it has no 
idea if they exist or not.  Seemingly the best it could do is create 
discriminants for whatever arguments T1's constructor function would need, 
and use those as arguments in the type declaration?  As in:

type T2 (x : y, a : b) is new T1 (A.T1_Ctor(x)) with ...

Is this generally correct, or are there other esoteric problems that 
prevents these sort of constructs?

-sb 



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

* Re: Type extension and discriminants
  2014-10-10 22:34 ` Randy Brukardt
@ 2014-10-11 17:45   ` sbelmont700
  2014-10-11 18:44     ` Simon Wright
                       ` (2 more replies)
  0 siblings, 3 replies; 8+ messages in thread
From: sbelmont700 @ 2014-10-11 17:45 UTC (permalink / raw)


On Friday, October 10, 2014 6:34:54 PM UTC-4, Randy Brukardt wrote:
> It would have helped a bit had you given an example.

Thank you for your response.  An example follows:

package A is
   type T1 (<>) is tagged limited private;
   function T1_Ctor return T1;
private
   type T1 (I_Ptr : access Integer) is tagged limited null record;
end A;


package body A is
   function T1_Ctor return T1 is
   begin
      return (I_Ptr => null);
   end T1_Ctor;
end A;


with A;
package B is
   type T2 (<>) is new A.T1 with private;
   function T2_Ctor return T2;
private
  type T2 is new A.T1 with null record;
end B;

package body B is
   function T2_Ctor return T2 is
   begin
      return T2'(A.T1_Ctor with null record);
   end T2_Ctor;
  
end B;

This code as written makes GNAT go Blammo when compiling b.adb {GPL 2014 (20140331) (i686-pc-mingw32) GCC error: in gnat_to_gnu_entity, at ada/gcc-interface/decl.c:289 | Error detected at b.ads:11:8}, so clearly there is a bug in there somewhere, but more often than not crashes come from parsing bad code on my end, so I just assume it's me.  Then when playing around I realized that it would accept T2 without the explicit (<>), and was doubtful as to whether it had to be there (I prefer it as well), or if it was just implicitly declared from T1 (like 'limited'), which was followed by my realization that I didn't even know if this should be valid to begin with.

Moreover, changing T1 from an access discriminant (e.g. just 'Integer' instead of 'access Integer'), makes it compile fine, but removing all the discriminants from T1 (but leaving it unknown in the public part) causes an odd compiler error (uninitialized unconstrained allocation not allowed / qualified expression required) on the full type declaration of T2, which seems like at least the wrong error message, if not an error itself (it would seem just as legal to have no discriminants as access or named ones).

Making T1 fully public fixes everything.

The stated approach seems legal, at least in concept, but the variety of crashes and errors made me suspect.  I will be submitting this as a bug report at the very least for the hard crash, and probably for the error messages as well.

Thank you again for your continued helpful responses.

-sb




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

* Re: Type extension and discriminants
  2014-10-11 17:45   ` sbelmont700
@ 2014-10-11 18:44     ` Simon Wright
  2014-10-11 18:52     ` Dmitry A. Kazakov
  2014-10-12 16:27     ` AdaMagica
  2 siblings, 0 replies; 8+ messages in thread
From: Simon Wright @ 2014-10-11 18:44 UTC (permalink / raw)


sbelmont700@gmail.com writes:

> This code as written makes GNAT go Blammo when compiling b.adb {GPL
> 2014 (20140331) (i686-pc-mingw32) GCC error: in gnat_to_gnu_entity, at
> ada/gcc-interface/decl.c:289 | Error detected at b.ads:11:8}, so
> clearly there is a bug in there somewhere, but more often than not
> crashes come from parsing bad code on my end, so I just assume it's
> me.

Whenever GNAT does this (produce a bug box: I like "goes Blammo"!) it's
a bug in GNAT, whether or not there's an error in your code.

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

* Re: Type extension and discriminants
  2014-10-11 17:45   ` sbelmont700
  2014-10-11 18:44     ` Simon Wright
@ 2014-10-11 18:52     ` Dmitry A. Kazakov
  2014-10-11 21:09       ` sbelmont700
  2014-10-12 16:27     ` AdaMagica
  2 siblings, 1 reply; 8+ messages in thread
From: Dmitry A. Kazakov @ 2014-10-11 18:52 UTC (permalink / raw)


On Sat, 11 Oct 2014 10:45:55 -0700 (PDT), sbelmont700@gmail.com wrote:

> On Friday, October 10, 2014 6:34:54 PM UTC-4, Randy Brukardt wrote:
>> It would have helped a bit had you given an example.
> 
> Thank you for your response.  An example follows:
> 
> package A is
>    type T1 (<>) is tagged limited private;
>    function T1_Ctor return T1;
> private
>    type T1 (I_Ptr : access Integer) is tagged limited null record;
> end A;
> 
> 
> package body A is
>    function T1_Ctor return T1 is
>    begin
>       return (I_Ptr => null);
>    end T1_Ctor;
> end A;
> 
> with A;
> package B is
>    type T2 (<>) is new A.T1 with private;
>    function T2_Ctor return T2;
> private
>   type T2 is new A.T1 with null record;
> end B;
> 
> package body B is
>    function T2_Ctor return T2 is
>    begin
>       return T2'(A.T1_Ctor with null record);
>    end T2_Ctor;
>   
> end B;

Don't use this pattern, it simply does not work. Even without GNAT bugs.
However all GNAT compilers I know have serious problems with more than one
nested limited return. But if GNAT worked, this pattern will eventually hit
you back.

In short, limited return (constructing function) and limited aggregate are
not suitable for construction of parent objects which private discriminants
and components.

As an alternative consider a child package to derive the type. Also
consider using non-limited handles pointing to the reference counted
limited private implementation objects.

You can use the same interface type for both the handle type and the
implementation type. The implementation of the interface for the handle
type calls the implementation of the limited private implementation type
after dereferencing (delegation).

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

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

* Re: Type extension and discriminants
  2014-10-11 18:52     ` Dmitry A. Kazakov
@ 2014-10-11 21:09       ` sbelmont700
  2014-10-12  6:51         ` Dmitry A. Kazakov
  0 siblings, 1 reply; 8+ messages in thread
From: sbelmont700 @ 2014-10-11 21:09 UTC (permalink / raw)


On Saturday, October 11, 2014 2:53:05 PM UTC-4, Dmitry A. Kazakov wrote:
> 
> Don't use this pattern, it simply does not work. Even without GNAT bugs.
> 
> However all GNAT compilers I know have serious problems with more than one
> 
> nested limited return. But if GNAT worked, this pattern will eventually hit
> 
> you back.
> 

The issue of favoring composition over inheritance is that if T1 has lots of operations, especially those which are unchanged (not overridden) for T2 (and T3, and T4, etc), then you end up with the hassle of writing all these one-line delegation methods.  It's easy enough to refactor this so that T1 and T2 both implement interface I, have T2 contain a reference to a T1 and just pass the calls through, but apart from being a PITA and unnecessary, often the compile-time safety is an advantage, not a drawback; any T2 is forced to use T1's (classwide) methods, and can NOT be reconfigured dynamically.  Obviously this a case-by-case sort of thing, but this is certainly a pattern any purportedly OOP language needs to support.

> 
> 
> In short, limited return (constructing function) and limited aggregate are
> 
> not suitable for construction of parent objects which private discriminants
> 
> and components.
> 

+1(000).  This is especially aggravating, because Ada's access discriminants and pass-by-reference semantics are so totally superior for saving references to other objects without having to resort to the heap.  But the intermixing of "true" discriminated records (i.e. variant records, where the structure actually depends on the discriminant), and "fake" discriminated records that just use them when they need a constant always seems to cause endless headaches.  I just wish I had a better idea of how to do it, because C++ initializer lists are a syntax abomination. :'-(


-sb


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

* Re: Type extension and discriminants
  2014-10-11 21:09       ` sbelmont700
@ 2014-10-12  6:51         ` Dmitry A. Kazakov
  0 siblings, 0 replies; 8+ messages in thread
From: Dmitry A. Kazakov @ 2014-10-12  6:51 UTC (permalink / raw)


On Sat, 11 Oct 2014 14:09:48 -0700 (PDT), sbelmont700@gmail.com wrote:

[...]
> Obviously this a case-by-case sort of thing, but this is certainly a
> pattern any purportedly OOP language needs to support.

No. The language should support constructors, not this nonsense (I mean
returning limited objects rubbish).

>> In short, limited return (constructing function) and limited aggregate are
>> not suitable for construction of parent objects which private discriminants
>> and components.
> 
> +1(000).  This is especially aggravating, because Ada's access
> discriminants and pass-by-reference semantics are so totally superior for
> saving references to other objects without having to resort to the heap.

Well, access discriminants were invented as a hack, e.g. for having mix-ins
instead of honest multiple inheritance or for circumvention of function's
"in" parameter modes etc.

An access discriminant represent a serious safety problem, in combination
with dynamic accessibility checks, a deadly problem.

> But the intermixing of "true" discriminated records (i.e. variant records,
> where the structure actually depends on the discriminant), and "fake"
> discriminated records that just use them when they need a constant always
> seems to cause endless headaches.

Discriminants are misused in many ways. Having an immutable (constant)
record member is one of them.

> I just wish I had a better idea of how
> to do it, because C++ initializer lists are a syntax abomination. :'-(

The language should have proper abstraction mechanisms. Ada is only halfway
there. Otherwise a discriminant would be a pure type constraint which
implementation would not necessarily be a record member while the type
would not necessarily be a record. Ugly Ada 2012 aspects are somewhat
would-be discriminants.

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

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

* Re: Type extension and discriminants
  2014-10-11 17:45   ` sbelmont700
  2014-10-11 18:44     ` Simon Wright
  2014-10-11 18:52     ` Dmitry A. Kazakov
@ 2014-10-12 16:27     ` AdaMagica
  2 siblings, 0 replies; 8+ messages in thread
From: AdaMagica @ 2014-10-12 16:27 UTC (permalink / raw)


On Saturday, October 11, 2014 7:45:56 PM UTC+2, sbelm...@gmail.com wrote:
> package A is
>    type T1 (<>) is tagged limited private;
>    function T1_Ctor return T1;
> private
>    type T1 (I_Ptr : access Integer) is tagged limited null record;
> end A;
> 
> package body A is
>    function T1_Ctor return T1 is
>    begin
>       return (I_Ptr => null);
>    end T1_Ctor;
> end A;
> 
> with A;
> package B is
>    type T2 (<>) is new A.T1 with private;

I guess the following function is implicitly declared here and must be overridden because it's abstract:
     function T1_Ctor return T2;

>    function T2_Ctor return T2;
> private
>   type T2 is new A.T1 with null record;
> end B;
>
> package body B is
>    function T2_Ctor return T2 is
>    begin
>       return T2'(A.T1_Ctor with null record);
>    end T2_Ctor;
> end B;

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

end of thread, other threads:[~2014-10-12 16:27 UTC | newest]

Thread overview: 8+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2014-10-10 20:55 Type extension and discriminants sbelmont700
2014-10-10 22:34 ` Randy Brukardt
2014-10-11 17:45   ` sbelmont700
2014-10-11 18:44     ` Simon Wright
2014-10-11 18:52     ` Dmitry A. Kazakov
2014-10-11 21:09       ` sbelmont700
2014-10-12  6:51         ` Dmitry A. Kazakov
2014-10-12 16:27     ` AdaMagica

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