LambdaBeta
LambdaBeta

Reputation: 1505

Ada elaboration not occurring at all

I have an unusual situation in which elaboration code is simply not being executed at all. This is not an elaboration order issue, but rather an elaboration at all issue.

The problem is that I don't "with" the unit in question whatsoever, yet in theory it should still be accessible, as long as its elaboration occurs.

Of course I could just add a useless "with" for the unit in question, but in my real use case there are a large number of units that I would have to do that with.

My question is if there is any way either in the code, through pragmas, in the gpr project file, or through command-line switches that I could force the compiler to include a file even though it thinks the file isn't referenced?

Here is a minimal working example:

as.ads:

package As is
    type A is tagged null record;
    type Nothing is null record;
    function Create (Ignored : not null access Nothing) return A;
    function Image (From : A) return String;
end As;

as.adb:

package body As is
    function Create (Ignored : not null access Nothing) return A is
        (null record);
    function Image (From : A) return String is ("A");
end As;

finder.ads:

with Ada.Tags;

package Finder is
    procedure Register (Name : String; Tag : Ada.Tags.Tag);
    function Find (Name : String; Default : Ada.Tags.Tag) return Ada.Tags.Tag;
end Finder;

finder.adb:

with Ada.Containers.Indefinite_Vectors;

package body Finder is
    type Name_Tag (Size : Natural) is
        record
            Name : String (1 .. Size);
            To : Ada.Tags.Tag;
        end record;

    package Name_Tag_Vectors is new Ada.Containers.Indefinite_Vectors (Positive, Name_Tag);
    Name_Tags : Name_Tag_Vectors.Vector := Name_Tag_Vectors.Empty_Vector;

    procedure Register (Name : String; Tag : Ada.Tags.Tag) is begin
        Name_Tags.Append ((Name'Length, Name, Tag));
    end Register;

    function Find (Name : String; Default : Ada.Tags.Tag) return Ada.Tags.Tag is begin
        for Tag of Name_Tags loop
            if Tag.Name = Name then
                return Tag.To;
            end if;
        end loop;

        return Default;
    end Find;
end Finder;

bs.ads:

with As;
package Bs is
    type B is new As.A with null record;
    function Create (Ignored : not null access As.Nothing) return B;
    function Image (From : B) return String;
end Bs;

bs.adb:

with Finder;
package body Bs is
    function Create (Ignored : not null access As.Nothing) return B is
        (As.Create (Ignored) with null record);
    function Image (From : B) return String is ("B");
begin
    Finder.Register ("B", B'Tag);
end Bs;

test.adb:

with As; use As;
-- with Bs; -- (uncommenting this line solves my problem, but what if I had the rest of the alphabet?)
with Finder;
with Ada.Tags.Generic_Dispatching_Constructor;
with Ada.Text_IO;

procedure Test is
    function Constructor is new Ada.Tags.Generic_Dispatching_Constructor (
        T => A,
        Parameters => Nothing,
        Constructor => Create);

    Nada : aliased Nothing := (null record);
    What : A'Class := Constructor (Finder.Find ("B", A'Tag), Nada'Access);
begin
    Ada.Text_IO.Put_Line (What.Image);
end Test;

Upvotes: 2

Views: 288

Answers (3)

Zerte
Zerte

Reputation: 1516

Since the package Bs is invisible to your program, so is the type B.

So the next question is: why do you need to register type B if it is not used anywhere?

If an Ada compiler did elaborate all units (packages or standalone subprograms) that are irrelevant to a main program, but are visible through source path, it would become really messy!...

Upvotes: 0

Keith Thompson
Keith Thompson

Reputation: 263227

The compiler thinks your package Bs isn't referenced because it isn't. You don't have a with clause for it, so it's not part of your program.

A simple example:

a.ads

package A is
    procedure Blah;
end A;

a.adb

with Ada.Text_IO;
package body A is
    procedure Blah is begin null; end Blah;
begin
    Ada.Text_IO.Put_Line("Elaborate A");
end A;

b.ads

package B is
    procedure Blah;
end B;

b.adb

with Ada.Text_IO;
package body B is
    procedure Blah is begin null; end Blah;
begin
    Ada.Text_IO.Put_Line("Elaborate B");
end B;

main.adb

with Ada.Text_IO;
with A;
procedure Main is
begin
    Ada.Text_IO.Put_Line("Main");
end Main;

When I run main, it prints

Elaborate A
Main

It doesn't print Elaborate B because that package isn't part of the program; it's just a couple of source files in the same directory.

The obvious solution is to add the with clauses.

I don't know whether there's a less obvious solution. If there is, it's probably compiler-specific. But I'm not sure why a compiler would have a feature that lets you incorporate an otherwise unused package into a program.

Upvotes: 6

Simon Wright
Simon Wright

Reputation: 25491

What I’ve done (e.g. here ff) is to actually reference the units in the main program (with pragma Unreferenced to prevent warnings).

Alternatively, you could have a package e.g. Required_Units with all the necessary withs included, and then with that from the main program.

Even if there was some alternative process, you’d have to tell it what units you need to have included; might as well go with the flow and do it in Ada!

Upvotes: 3

Related Questions