From: Peter C. Chapin on

I'm using GNAT GPL 2007. Consider the following packages. The
specifications and bodies are each in their own files, as usual.

----> parent.ads <----

package Parent is
-- Needed so this package requires/allows a body.
procedure Dummy;
end Parent;

----> parent-child.ads <----

package Parent.Child is
procedure Print_Stuff;
end Parent.Child;

----> parent.adb <----

with Parent.Child;
package body Parent is
procedure Dummy is
begin
null;
end;
begin
Parent.Child.Print_Stuff; -- Note: invoking child here.
end Parent;

----> parent-child.adb <----

with Ada.Text_IO;
package body Parent.Child is
procedure Print_Stuff is
begin
Ada.Text_IO.Put_Line("Printing stuff in package Parent.Child");
end Print_Stuff;
end Parent.Child;

My main procedure just does a "with Parent" but otherwise does nothing
(it contains only a null statement). Note that the elaboration code in
package Parent is calling a subprogram in package Parent.Child. With the
-gnatwl option, GNAT tells me

warning: implicit pragma Elaborate_All for "Parent.Child" generated

when compiling parent.adb. This does not surprise me and seems fine.
However, when trying to build the executable I get:

error: elaboration circularity detected
info: "parent (body)" must be elaborated before "parent (body)"
info: reason: implicit Elaborate_All in unit "parent (body)"
info: recompile "parent (body)" with -gnatwl for full details
info: "parent (body)"
info: must be elaborated along with its spec:
info: "parent (spec)"
info: which is withed by:
info: "parent.child (spec)"
info: which is withed by:
info: "parent (body)"

gnatmake: *** bind failed.

I don't understand where the circularity is coming from. Isn't the
following elaborate order acceptable:

parent (spec)
parent.child (spec)
parent.child (body)
parent (body)

How is it the case that parent (body) must be elaborated before parent
(body)? I've tried tinkering around with some explicit elaboration
related pragmas to control the order of elaboration, but GNAT insists
there is circularity regardless. I'm left with the impression that all
my attempts to control the elaboration order are fruitless.

Note my actual program involves a task in the parent that is trying to
use subprograms in the child. However, the difficulties I'm having
appear to be unrelated to tasking.

Thanks in advance for any thoughts you might have.

Peter
From: Samuel Tardieu on
>>>>> "Peter" == Peter C Chapin <pchapin(a)sover.net> writes:

Peter> I don't understand where the circularity is coming from. Isn't
Peter> the following elaborate order acceptable:

No. pragma Elaborate_All is transitive, and forces the elaboration of
Parent body (since Parent.Child has an implicit dependency on Parent)
before... Parent body elaboration. Hence the circular elaboration
order.

But you can fix the situation by providing GNAT with the necessary
elaboration information:

- put a "pragma Elaborate (Parent.Child)" in Parent body to indicate
that Parent elaboration requires Parent.Child to be elaborated; this
pragma isn't transitive, so you have to ensure that subprograms of
Parent.Child called during the elaboration of Parent do not
themselves require that other packages be elaborated first, or add
the required pragma;

- put a "pragma Elaborate_Body" in Parent.Child spec if you know
that this package is likely to be called during the elaboration of
other packages; this requires that the body be elaborated just
after the spec.

Using only the "pragma Elaborate_Body" will not be enough for GNAT, so
the "pragma Elaborate" is required. That is because GNAT is
overcautious and adds implicit "pragma Elaborate_All" unless you
provide explicit "pragma Elaborate".

Note that having GNAT be overcautious by default is actually a good
thing: I spent three hours two weeks ago debugging a tricky
elaboration problem in a software written for an embedded system. The
original author incorrectly used "pragma Elaborate". Had he done
nothing, GNAT would have warned him about the dangerous situation.

I hope this clarifies things.

Sam
--
Samuel Tardieu -- sam(a)rfc1149.net -- http://www.rfc1149.net/
From: Robert A Duff on
"Peter C. Chapin" <pchapin(a)sover.net> writes:

> I'm using GNAT GPL 2007. Consider the following packages. The
> specifications and bodies are each in their own files, as usual.
>
> ----> parent.ads <----
>
> package Parent is
> -- Needed so this package requires/allows a body.
> procedure Dummy;
> end Parent;
>
> ----> parent-child.ads <----
>
> package Parent.Child is
> procedure Print_Stuff;
> end Parent.Child;
>
> ----> parent.adb <----
>
> with Parent.Child;
> package body Parent is
> procedure Dummy is
> begin
> null;
> end;
> begin
> Parent.Child.Print_Stuff; -- Note: invoking child here.
> end Parent;
>
> ----> parent-child.adb <----
>
> with Ada.Text_IO;
> package body Parent.Child is
> procedure Print_Stuff is
> begin
> Ada.Text_IO.Put_Line("Printing stuff in package Parent.Child");
> end Print_Stuff;
> end Parent.Child;
>
> My main procedure just does a "with Parent" but otherwise does nothing
> (it contains only a null statement). Note that the elaboration code in
> package Parent is calling a subprogram in package Parent.Child. With the
> -gnatwl option, GNAT tells me
>
> warning: implicit pragma Elaborate_All for "Parent.Child" generated

This pragma (on Parent body) means Parent.Child, and it's body, and
everything they depend on, and their bodies, and so on, must all be
elaborated before Parent body. One of those is Parent body --
hence the cycle.

If you write pragma Elaborate, I think GNAT will not generate the
implicit pragma Elab_All. Pragma Elaborate(Parent.Child) on Parent body
means elaborate the body of Parent.Child before Parent body, but it's
not transitive.

Note that the default GNAT rules are stricter than standard Ada.
To get the standard Ada rules, use -gnatE. But it's not a good
idea -- the stricter rules are beneficial. For your program,
if you use the standard rules, it is implementation dependent
whether or not you get Program_Error. That's bad language design!

The stricter rules are conservative, and modular -- when compiling
Parent, it sees that you're calling Parent.Child, and assumes the worst
WITHOUT looking at Parent.Child body. For example, it assumes that
Parent.Child might call Dummy, causing a real cycle.

Pragma Elaborate is somewhat evil, since it breaks this modularity
(it requires one package body to "know" what's in another package
body).

I suggest you read the section in the GNAT docs about elaboration.
It explains all this stuff in great detail.

>... I'm left with the impression that all
> my attempts to control the elaboration order are fruitless.

Well, they're not fruitless, but elab cycles are indeed frustrating.
I find the error messages (from all compilers I've tried, not just
GNAT) to be confusing. And every time you add or delete one
of those pragmas you have to recompile a whole bunch of stuff.

> Note my actual program involves a task in the parent that is trying to
> use subprograms in the child. However, the difficulties I'm having
> appear to be unrelated to tasking.

Don't be too sure. Tasks get activated "early", and can easily cause
elab cycles. Look at the docs for details.

- Bob
From: Robert A Duff on
Samuel Tardieu <sam(a)rfc1149.net> writes:

> No. pragma Elaborate_All is transitive, and forces the elaboration of
> Parent body (since Parent.Child has an implicit dependency on Parent)

Yes, it has a dependence. But why do you call it "implicit"?

- Bob
From: Samuel Tardieu on
>>>>> "Robert" == Robert A Duff <bobduff(a)shell01.TheWorld.com> writes:

Robert> Samuel Tardieu <sam(a)rfc1149.net> writes:
>> No. pragma Elaborate_All is transitive, and forces the elaboration
>> of Parent body (since Parent.Child has an implicit dependency on
>> Parent)

Robert> Yes, it has a dependence. But why do you call it "implicit"?

Because it is not spelt explicitely using a with clause or an
Elaborate/Elaborate_All pragma. And if it's not explicit, it must be
implicit.

One could say that when you write "package Parent.Child", you
explicitely spell "Parent" so this is an explicit dependency
declaration. However, for me it is a declaration of the "Parent.Child"
package, which implicitely creates a semantic dependency on Parent.

Now, I think we are both nitpicking here, as I don't think the RM says
anything about "explicit" vs. "implicit" dependencies, it only
consider "static dependences" and "elaboration dependences".

Btw, I never noticed the use of "dependence" vs. "dependency"
before. Is there a difference in English?

Sam
--
Samuel Tardieu -- sam(a)rfc1149.net -- http://www.rfc1149.net/