Home > Enterprise >  Is there a way to verify two generic formals are the same type if one is incomplete?
Is there a way to verify two generic formals are the same type if one is incomplete?

Time:10-02

Given a generic parent package:

generic
    type T(<>) is tagged;
package Parent is
    -- Stuff
end Parent;

is there a way in a child package to ensure a type passed into the child as a generic formal is the same type as (or even a descendant of) Parent.T? For example, consider the generic child package:

generic
    type T(<>) is new Base with private;
package Parent.Child is
    -- stuff   
end Parent.Child;

where Base is some base tagged type. You can use the following as a placeholder:

type Base is tagged limited null record;

I'm looking for a way either compile time or runtime to verify inside of Parent.Child that Parent.Child.T is the same as Parent.T (or even if Parent.Child.T is a descendent of Parent.T.

NOTE: I am trying to use the parent child package relationship because it allows Child to see into the private section of Parent.

Naively I tried something runtime based like:

package body Child is

    -- other stuff

begin
    if Child.T not in Parent.T then
        raise Storage_Error with "Invalid type passed to child package";
    end if;
end Child;

but that just results in a GNAT error:

premature usage of incomplete type "T"

because Parent.T is incomplete. The intent here is to create an automatic memory management framework that can be used with incomplete types, so the parent package provides the majority of the functionality while the child package can be instantiated later and add functionality that requires the full type information (like construction/deallocation). You could then do declarations like:

type Test is tagged;
package B is new Parent(Test);

type Test is new Base with record
    Thing : -- Some type from Parent;
end record;

package M is new B.Child(Test);

Any suggestions on how to verify that Parent.T is the same or a descendant of Parent.Child.T? Compile time versions would be preferred but even runtime checking will work.

CodePudding user response:

What about forcing them to have the same base?

generic
   type T (<>) is tagged private;
package Parent is
end Parent;

generic
   type T (<>) is new Parent.T with private;
package Parent.Child is
end Parent.Child;

with Parent.Child;
package User is
   type Base is tagged null record;
   package P is new Parent (T => Base);

   --  this is OK
   type Extension is new Base with null record;
   package PC is new P.Child (T => Extension);

   --  this fails
   type Wrong is tagged null record;
   package PF is new P.Child (T => Wrong);
end User;

The error message (using -gnatl) is

>>> expect type derived from "T" in instantiation
>>> instantiation abandoned

CodePudding user response:

If you don't need the parent/child relationship of the generics, you can do something like this:

foo.ads

generic
   type T(<>) is tagged;
package Foo is

end Foo;

bar.ads

with Foo;

generic
   type T(<>) is tagged private;
   with package Foo_Instance is new Foo(T); --package parameter
package Bar is
 
end Bar;

This way, the complete type must exactly match the incomplete type, ie. it cannot be a type extension, thus:

with Foo;
with Bar;

package Baz is

   type Base is tagged;
   package Base_Foo is new Foo(Base);
   
   
   type Base is tagged null record;
   package Base_Bar is new Bar(Base, Base_Foo);
   
   
   type Extension is new Base with null record;
   package Extension_Bar is new Bar(Extension, Base_Foo); -- fails!

end Baz;
  • Related