Packages

Pure program and library units

Todo

Complete section!

Package renaming

We've seen in the Introduction to Ada course that we can rename packages.

Grouping packages

A use-case that we haven't mentioned in that course is that we can apply package renaming to group individual packages into a common hierarchy. For example:

package Driver_M1 is end Driver_M1;
package Driver_M2 is end Driver_M2;
package Drivers with Pure is end Drivers;
with Driver_M1; package Drivers.M1 renames Driver_M1;
with Driver_M2; package Drivers.M2 renames Driver_M2;

Here, we're renaming the Driver_M1 and Driver_M2 packages as child packages of the Drivers package, which is a pure package.

Important

Note that a package that is renamed as a child package cannot refer to information from its (non-renamed) parent. In other words, Driver_M1 (renamed as Drivers.M1) cannot refer to information from the Drivers package. For example:

package Driver_M1 is Counter_2 : Integer := Drivers.Counter; end Driver_M1;
package Drivers is Counter : Integer := 0; end Drivers;
with Driver_M1; package Drivers.M1 renames Driver_M1;

As expected, compilation fails here because Drivers.Counter isn't visible in Driver_M1, even though the renaming (Drivers.M1) creates a virtual hierarchy.

Child of renamed package

Note that we cannot create a child package using a parent package name that was introduced by a renaming. For example, let's say we want to create a child package Ext for the Drivers.M1 package we've seen earlier. We cannot just declare a Drivers.M1.Ext package like this:

package Drivers.M1.Ext is

end Drivers.M1.Ext;

because the parent unit cannot be a renaming. The solution is to actually extend the original (non-renamed) package:

package Driver_M1.Ext is end Driver_M1.Ext;
-- A package called Drivers.M1.Ext is -- automatically available! with Drivers.M1.Ext; procedure Dummy is begin null; end Dummy;

This works fine because any child package of a package P is also a child package of a renamed version of P. (Therefore, because Ext is a child package of Driver_M1, it is also a child package of the renamed Drivers.M1 package.)

Backwards-compatibility via renaming

We can also use renaming to ensure backwards-compatibility when changing the package hierarchy. For example, we could adapt the previous source-code by:

  • converting Driver_M1 and Driver_M2 to child packages of Drivers, and

  • using package renaming to mimic the original names (Driver_M1 and Driver_M2).

This is the adapted code:

package Drivers with Pure is end Drivers;
-- We've converted Driver_M1 to -- Drivers.M1: package Drivers.M1 is end Drivers.M1;
-- We've converted Driver_M2 to -- Drivers.M2: package Drivers.M2 is end Drivers.M2;
-- Original Driver_M1 package still -- available via package renaming: with Drivers.M1; package Driver_M1 renames Drivers.M1;
-- Original Driver_M2 package still -- available via package renaming: with Drivers.M2; package Driver_M2 renames Drivers.M2;

Now, M1 and M2 are actual child packages of Drivers, but their original names are still available. By doing so, we ensure that existing software that makes use of the original packages doesn't break.

Private packages

In this section, we discuss the concept of private packages. However, before we proceed with the discussion, let's recapitulate some important ideas that we've seen earlier.

In the Introduction to Ada course, we've seen that encapsulation plays an important role in modular programming. By using the private part of a package specification, we can disclose some information, but, at the same time, prevent that this information gets accessed where it shouldn't be used directly. Similarly, we've seen that we can use the private part of a package to distinguish between the partial and full view of a data type.

The main application of private packages is to create private child packages, whose purpose is to serve as internal implementation packages within a package hierarchy. By doing so, we can expose the internals to other public child packages, but prevent that external clients can directly access them.

As we'll see next, there are many rules that ensure that internal visibility is enforced for those private child packages. At the same time, the same rules ensure that private packages aren't visible outside of the package hierarchy.

Declaration and usage

We declare private packages by using the private keyword. For example, let's say we have a package named Data_Processing:

package Data_Processing is -- ... end Data_Processing;

We simply write private package to declare a private child package named Calculations:

private package Data_Processing.Calculations is -- ... end Data_Processing.Calculations;

Let's see a complete example:

package Data_Processing is type Data is private; procedure Process (D : in out Data); private type Data is null record; end Data_Processing;
private package Data_Processing.Calculations is procedure Calculate (D : in out Data); end Data_Processing.Calculations;
with Data_Processing.Calculations; use Data_Processing.Calculations; package body Data_Processing is procedure Process (D : in out Data) is begin Calculate (D); end Process; end Data_Processing;
package body Data_Processing.Calculations is procedure Calculate (D : in out Data) is begin -- Dummy implementation... null; end Calculate; end Data_Processing.Calculations;
with Data_Processing; use Data_Processing; procedure Test_Data_Processing is D : Data; begin Process (D); end Test_Data_Processing;

In this example, we refer to the private child package Calculations in the body of the Data_Processing package — by simply writing with Data_Processing.Calculations. After that, we can call the Calculate procedure normally in the Process procedure.

Private sibling packages

We can introduce another private package Advanced_Calculations as a child of Data_Processing and refer to the Calculations package in its specification:

package Data_Processing is type Data is private; procedure Process (D : in out Data); private type Data is null record; end Data_Processing;
private package Data_Processing.Calculations is procedure Calculate (D : in out Data); end Data_Processing.Calculations;
with Data_Processing.Calculations; use Data_Processing.Calculations; private package Data_Processing.Advanced_Calculations is procedure Advanced_Calculate (D : in out Data) renames Calculate; end Data_Processing.Advanced_Calculations;
with Data_Processing.Advanced_Calculations; use Data_Processing.Advanced_Calculations; package body Data_Processing is procedure Process (D : in out Data) is begin Advanced_Calculate (D); end Process; end Data_Processing;
package body Data_Processing.Calculations is procedure Calculate (D : in out Data) is begin -- Dummy implementation... null; end Calculate; end Data_Processing.Calculations;
with Data_Processing; use Data_Processing; procedure Test_Data_Processing is D : Data; begin Process (D); end Test_Data_Processing;

Note that, in the body of the Data_Processing package, we're now referring to the new Advanced_Calculations package instead of the Calculations package.

Referring to a private child package in the specification of another private child package is OK, but we cannot do the same in the specification of a non-private package. For example, let's change the specification of the Advanced_Calculations and make it non-private:

with Data_Processing.Calculations; use Data_Processing.Calculations; package Data_Processing.Advanced_Calculations is procedure Advanced_Calculate (D : in out Data) renames Calculate; end Data_Processing.Advanced_Calculations;

Now, the compilation doesn't work anymore. However, we could still refer to Calculations packages in the body of the Advanced_Calculations package:

package Data_Processing.Advanced_Calculations is procedure Advanced_Calculate (D : in out Data); end Data_Processing.Advanced_Calculations;
with Data_Processing.Calculations; use Data_Processing.Calculations; package body Data_Processing.Advanced_Calculations is procedure Advanced_Calculate (D : in out Data) is begin Calculate (D); end Advanced_Calculate; end Data_Processing.Advanced_Calculations;

This works fine as expected: we can refer to private child packages in the body of another package — as long as both packages belong to the same package tree.

Outside the package tree

While we can use a with-clause of a private child package in the body of the Data_Processing package, we cannot do the same outside the package tree. For example, we cannot refer to it in the Test_Data_Processing procedure:

with Data_Processing; use Data_Processing; with Data_Processing.Calculations; use Data_Processing.Calculations; procedure Test_Data_Processing is D : Data; begin Calculate (D); end Test_Data_Processing;

As expected, we get a compilation error because Calculations is only accessible within the Data_Processing, but not in the Test_Data_Processing procedure.

The same restrictions apply to child packages of private packages. For example, if we implement a child package of the Calculations package — let's name it Calculations.Child —, we cannot refer to it in the Test_Data_Processing procedure:

package Data_Processing.Calculations.Child is procedure Process (D : in out Data); end Data_Processing.Calculations.Child;
package body Data_Processing.Calculations.Child is procedure Process (D : in out Data) is begin Calculate (D); end Process; end Data_Processing.Calculations.Child;
with Data_Processing; use Data_Processing; with Data_Processing.Calculations.Child; use Data_Processing.Calculations.Child; procedure Test_Data_Processing is D : Data; begin Calculate (D); end Test_Data_Processing;

Again, as expected, we get an error because Calculations.Child — being a child of a private package — has the same restricted view as its parent package. Therefore, it cannot be visible in the Test_Data_Processing procedure as well. We'll discuss more about visibility later.

Note that subprograms can also be declared private. We'll see this in another section.

Important

We've discussed package renaming in a previous section. We can rename a package as a private package, too. For example:

package Driver_M1 is end Driver_M1;
package Drivers with Pure is end Drivers;
with Driver_M1; private package Drivers.M1 renames Driver_M1;

Obviously, Drivers.M1 has the same restrictions as any private package:

with Driver_M1; with Drivers.M1; procedure Test_Driver is begin null; end Test_Driver;

As expected, although we can have the Driver_M1 package in a with clause of the Test_Driver procedure, we cannot do the same in the case of the Drivers.M1 package because it is private.

In the Ada Reference Manual

Private with clauses

Definition and usage

A private with clause allows us to refer to a package in the private part of another package. For example, if we want to refer to package P in the private part of Data, we can write private with P:

package P is type T is null record; end P;
private with P; package Data is type T2 is private; private -- Information from P is -- visible here type T2 is new P.T; end Data;
with Data; use Data; procedure Main is A : T2; begin null; end Main;

As you can see in the example, as the information from P is available in the private part of Data, we can derive a new type T2 based on T from P. However, we cannot do the same in the visible part of Data:

private with P; package Data is -- ERROR: information from P -- isn't visible here type T2 is new P.T; end Data;

Also, the information from P is available in the package body. For example, let's declare a Process procedure in the P package and use it in the body of the Data package:

package P is type T is null record; procedure Process (A : T) is null; end P;
private with P; package Data is type T2 is private; procedure Process (A : T2); private -- Information from P is -- visible here type T2 is new P.T; end Data;
package body Data is procedure Process (A : T2) is begin P.Process (P.T (A)); end Process; end Data;
with Data; use Data; procedure Main is A : T2; begin null; end Main;

In the body of the Data, we can access information from the P package — as we do in the P.Process (P.T (A)) statement of the Process procedure.

Referring to private child package

There's one case where using a private with clause is the only way to refer to a package: when we want to refer to a private child package in another child package. For example, here we have a package P and its two child packages: Private_Child and Public_Child:

package P is end P;
private package P.Private_Child is type T is null record; end P.Private_Child;
private with P.Private_Child; package P.Public_Child is type T2 is private; private type T2 is new P.Private_Child.T; end P.Public_Child;
with P.Public_Child; use P.Public_Child; procedure Test_Parent_Child is A : T2; begin null; end Test_Parent_Child;

In this example, we're referring to the P.Private_Child package in the P.Public_Child package. As expected, this works fine. However, using a normal with clause doesn't work in this case:

with P.Private_Child; package P.Public_Child is type T2 is private; private type T2 is new P.Private_Child.T; end P.Public_Child;

This gives an error because the information from the P.Private_Child, being a private child package, cannot be accessed in the public part of another child package. In summary, unless both packages are private packages, it's only possible to access the information from a private package in the private part of a non-private child package.

In the Ada Reference Manual

Limited Visibility

Sometimes, we might face the situation where two packages depend on information from each other. Let's consider a package A that depends on a package B, and vice-versa:

with B; use B; package A is type T1 is record Value : T2; end record; end A;
with A; use A; package B is type T2 is record Value : T1; end record; end B;

Here, we have two mutually dependent types T1 and T2, which are declared in two packages A and B that refer to each other. These with clauses constitute a circular dependency, so the compiler cannot compile either of those packages.

One way to solve this problem is by transforming this circular dependency into a partial dependency. We do this by limiting the visibility — using a limited with clause. To use a limited with clause for a package P, we simply write limited with P.

If a package A has limited visibility to a package B, then all types from package B are visible as if they had been declared as incomplete types. For the specific case of the previous source-code example, this would be the limited visibility to package B from package A's perspective:

package B is

   --  Incomplete type
   type T2;

end B;

As we've seen previously,

  • we cannot declare objects of incomplete types, but we can declare access types and anonymous access objects of incomplete types. Also,

  • we can use anonymous access types to declare mutually dependent types.

Keeping this information in mind, we can now correct the previous code by using limited with clauses for package A and declaring the component of the T1 record using an anonymous access type:

limited with B; package A is type T1 is record Ref : access B.T2; end record; end A;
with A; use A; package B is type T2 is record Value : T1; end record; end B;

As expected, we can now compile the code without issues.

Note that we can also use limited with clauses for both packages. If we do that, we must declare all components using anonymous access types:

limited with B; package A is type T1 is record Ref : access B.T2; end record; end A;
limited with A; package B is type T2 is record Ref : access A.T1; end record; end B;

Now, both packages A and B have limited visibility to each other.

In the Ada Reference Manual

Limited visibility and private with clauses

We can limit the visibility and use private with clauses at the same time. For a package P, we do this by simply writing limited private with P.

Let's reuse the previous source-code example and convert types T1 and T2 to private types:

limited private with B; package A is type T1 is private; private -- Here, we have limited visibility -- of package B type T1 is record Ref : access B.T2; end record; end A;
private with A; package B is type T2 is private; private use A; -- Here, we have full visibility -- of package A type T2 is record Value : T1; end record; end B;

In this updated version of the source-code example, we have not only limited visibility to package B, but also, each package is just visible in the private part of the other package.

Limited visibility and other elements

It's important to mention that the limited visibility we've been discussing so far is restricted to type declarations — which are seen as incomplete types. In fact, when we use a limited with clause, all other declarations have no visibility at all! For example, let's say we have a package Info that declares a constant Zero_Const and a function Zero_Func:

package Info is function Zero_Func return Integer is (0); Zero_Const : constant := 0; end Info;

Also, let's say we want to use the information (from package Info) in package A. If we have limited visibility to package Info, however, this information won't be visible. For example:

limited private with Info; package A is type T1 is private; private type T1 is record V : Integer := Info.Zero_Const; W : Integer := Info.Zero_Func; end record; end A;

As expected, compilation fails because of the limited visibility — as Zero_Const and Zero_Func from the Info package are not visible in the private part of A. (Of course, if we revert to full visibility by simply removing the limited keyword from the example, the code compiles just fine.)

Visibility

In the previous sections, we already discussed visibility from various angles. However, it can be interesting to recapitulate this information with the help of diagrams that illustrate the different parts of a package and its relation with other units.

Automatic visibility

First, let's consider we have a package A, its children (A.G and A.H), and the grandchild A.G.T. As we've seen before, information of a parent package is automatically visible in its children. The following diagrams illustrates this:

allow_mixing

skinparam ArrowColor DarkBlue

namespace A {
   node Public #white
   node Private #lightgray
   node Body #blue

   Private -up--> Public

   Body -up--> Public
   Body -up--> Private
}

namespace A.G #lightyellow {
   node Public #white
   node Private #lightgray
   node Body #blue

   Public -up--> A.Public

   Private -up--> Public
   Private -up--> A.Public
   Private -up--> A.Private

   Body -up--> Public
   Body -up--> Private
   Body ---> A.Public
   Body ---> A.Private
}

namespace A.H {
   node Public #white
   node Private #lightgray
   node Body #blue

   Public -up--> A.Public

   Private -up--> Public
   Private -up--> A.Public
   Private -up--> A.Private

   Body -up--> Public
   Body -up--> Private
   Body ---> A.Public
   Body ---> A.Private
}

namespace A.G.T #white {
   node Public #white
   node Private #lightgray
   node Body #blue

   Public -up--> A.Public
   Public -up--> A.G.Public

   Private -up--> Public
   Private -up--> A.Public
   Private -up--> A.Private
   Private -up--> A.G.Public
   Private -up--> A.G.Private

   Body -up--> Public
   Body -up--> Private
   Body ---> A.Public
   Body ---> A.Private
   Body ---> A.G.Public
   Body ---> A.G.Private
}

Because of this automatic visibility, many with clauses would be redundant in child packages. For example, we don't have to write with A; package A.G is, since the specification of package A is already visible in its child packages.

If we focus on package A.G (highlighted in the figure above), we see that it only has automatic visibility to its parent A, but not its child A.G.T. Also, it doesn't have visibility to its sibling A.H.

With clauses and visibility

In the rest of this section, we discuss all the situations where using with clauses is necessary to access the information of a package. Let's consider this example where we refer to a package B in the specification of a package A (using with B):

allow_mixing

skinparam ArrowColor DarkBlue

namespace B {
   node Public #white
   node Private #lightgray
   node Body #blue

   Private -up--> Public

   Body -up--> Public
   Body -up--> Private
}

namespace A {
   node Public #white
   node Private #lightgray
   node Body #blue

   Public -up--> B.Public #line:DarkGreen;line.bold;text:DarkGreen : with B; package A is

   Private -up--> Public
   Private -up--> B.Public #line:DarkGreen;line.dotted;text:DarkGreen

   Body -up--> Public
   Body -up--> Private
   Body -up--> B.Public #line:DarkGreen;line.dotted;text:DarkGreen
}

As we already know, the information from the public part of package B is visible in the public part of package A. In addition to that, it's also visible in the private part and in the body of package A. This is indicated by the dotted green arrows in the figure above.

Now, let's see the case where we refer to package B in the private part of package A (using private with B):

allow_mixing

skinparam ArrowColor DarkBlue

namespace B {
   node Public #white
   node Private #lightgray
   node Body #blue

   Private -up--> Public

   Body -up--> Public
   Body -up--> Private
}

namespace A {
   node Public #white
   node Private #lightgray
   node Body #blue

   Private -up--> Public
   Private -up-> B.Public #line:DarkGreen;line.bold;text:DarkGreen : private with B; package A is

   Body -up--> Public
   Body -up--> Private
   Body -up--> B.Public #line:DarkGreen;line.dotted;text:DarkGreen
}

Here, the information is visible in the private part of package A, as well as in its body. Finally, let's see the case where we refer to package B in the body of package A:

allow_mixing

skinparam ArrowColor DarkBlue

namespace B {
   node Public #white
   node Private #lightgray
   node Body #blue

   Private -up--> Public

   Body -up--> Public
   Body -up--> Private
}

namespace A {
   node Public #white
   node Private #lightgray
   node Body #blue

   Private -up--> Public

   Body -up--> Public
   Body -up--> Private
   Body -up--> B.Public #line:DarkGreen;line.bold;text:DarkGreen : with B; package body A is
}

Here, the information is only visible in the body of package A.

Circular dependency

Let's return to package A and its descendants. As we've seen in previous sections, we cannot refer to a child package in the specification of its parent package because that would constitute circular dependency. (For example, we cannot write with A.G; package A is.) This situation — which causes a compilation error — is indicated by the red arrows in the figure below:

allow_mixing

skinparam ArrowColor DarkBlue

namespace A {
   node Public #white
   node Private #lightgray
   node Body #blue

   Private -up--> Public

   Body -up--> Public
   Body -up--> Private
}

namespace A.G {
   node Public #white
   node Private #lightgray
   node Body #blue

   Public -up--> A.Public

   Private -up--> Public
   Private -up--> A.Public
   Private -up--> A.Private

   Body -up--> Public
   Body -up--> Private
   Body ---> A.Public
   Body ---> A.Private

   Public x-up- A.Public #line:DarkRed;line.bold;text:DarkRed : with A.G; package A is
   Public x-- A.Private #line:DarkRed;line.bold;text:DarkRed : private with A.G; package A is
   Public <--- A.Body #line:DarkGreen;line.bold;text:DarkGreen : with A.G; package body A is
}

Note that referring to the child package A.G in the body of its parent is perfectly fine.

Private packages

The previous examples of this section only showed public packages. As we've seen before, we cannot refer to private packages outside of a package hierarchy, as we can see in the following example where we try to refer to package A and its descendants in the Test procedure:

allow_mixing

left to right direction
scale 0.75

namespace A {
   node Public #white
   node Private #lightgray
   node Body #blue
}

namespace A.G << private A.G >> #lightgray {
   node Public #white
   node Private #lightgray
   node Body #blue
}

namespace A.H {
   node Public #white
   node Private #lightgray
   node Body #blue
}

namespace A.G.T #white {
   node Public #white
   node Private #lightgray
   node Body #blue
}

node "procedure Test" as Procedure_Test

Procedure_Test -up--> A.Public #line:DarkGreen;line.bold;text:DarkGreen   : with A;
Procedure_Test -up--> A.H.Public #line:DarkGreen;line.bold;text:DarkGreen   : with A.H;
Procedure_Test -up--x A.G.Public #line:DarkRed;line.bold;text:DarkRed : with A.G;
Procedure_Test -up--x A.G.T.Public #line:DarkRed;line.bold;text:DarkRed : with A.G.T;

As indicated by the red arrows, we cannot refer to the private child packages of A in the Test procedure, only the public child packages. Within the package hierarchy itself, we cannot refer to the private package A.G in public sibling packages. For example:

allow_mixing

left to right direction
scale 0.75

namespace A {
   node Public #white
   node Private #lightgray
   node Body #blue
}

namespace A.G << private A.G >> #lightgray {
   node Public #white
   node Private #lightgray
   node Body #blue

   Public <--- A.Body #line:DarkGreen;line.bold;text:DarkGreen : with A.G; package body A is
}

namespace A.H {
   node Public #white
   node Private #lightgray
   node Body #blue

   Public --x A.G.Public #line:DarkRed;line.bold;text:DarkRed : with A.G; package A.H is
   Private ---> A.G.Public #line:DarkGreen;line.bold;text:DarkGreen : private with A.G; package A.H is
   Body ---> A.G.Public #line:DarkGreen;line.bold;text:DarkGreen : with A.G; package body A.H is
}

namespace A.I << private A.I >> #lightgray {
   node Public #white
   node Private #lightgray
   node Body #blue

   Public ---> A.G.Public #line:DarkGreen;line.bold;text:DarkGreen : with A.G; private package A.I is
}

Here, we cannot refer to the private package A.G in the public package A.H — as indicated by the red arrow. However, we can refer to the private package A.G in other private packages, such as A.I — as indicated by the green arrows.

Use type clause

Back in the Introduction to Ada course, we saw that use clauses provide direct visibility — in the scope where they're used — to the content of a package's visible part.

For example, consider this simple procedure:

with Ada.Text_IO; procedure Display_Message is begin Ada.Text_IO.Put_Line ("Hello World!"); end Display_Message;

By adding use Ada.Text_IO to this code, we make the visible part of the Ada.Text_IO package directly visible in the scope of the Display_Message procedure, so we can now just write Put_Line instead of Ada.Text_IO.Put_Line:

with Ada.Text_IO; use Ada.Text_IO; procedure Display_Message is begin Put_Line ("Hello World!"); end Display_Message;

In this section, we discuss another example of use clauses. In addition, we introduce two specific forms of use clauses: use type and use all type.

In the Ada Reference Manual

Another use clause example

Let's now consider a simple package called Points, which contains the declaration of the Point type and two primitive: an Init function and an addition operator.

package Points is type Point is private; function Init return Point; function "+" (P : Point; I : Integer) return Point; private type Point is record X, Y : Integer; end record; function Init return Point is (0, 0); function "+" (P : Point; I : Integer) return Point is (P.X + I, P.Y + I); end Points;

We can implement a simple procedure that makes use of this package:

with Points; use Points; procedure Show_Point is P : Point; begin P := Init; P := P + 1; end Show_Point;

Here, we have a use clause, so we have direct visibility to the content of Points's visible part.

Visibility and Readability

In certain situations, however, we might want to avoid the use clause. If that's the case, we can rewrite the previous implementation by removing the use clause and specifying the Points package in the prefixed form:

with Points; procedure Show_Point is P : Points.Point; begin P := Points.Init; P := Points."+" (P, 1); end Show_Point;

Although this code is correct, it might be difficult to read, as we have to specify the package whenever we're referring to a type or a subprogram from that package. Even worse: we now have to write operators in the prefixed form — such as Points."+" (P, 1).

use type

As a compromise, we can have direct visibility to the operators of a certain type. We do this by using a use clause in the form use type. This allows us to simplify the previous example:

with Points; procedure Show_Point is use type Points.Point; P : Points.Point; begin P := Points.Init; P := P + 1; end Show_Point;

Note that use type just gives us direct visibility to the operators of a certain type, but not other primitives. For this reason, we still have to write Points.Init in the code example.

use all type

If we want to have direct visibility to all primitives of a certain type (and not just its operators), we need to write a use clause in the form use all type. This allows us to simplify the previous example even further:

with Points; procedure Show_Point is use all type Points.Point; P : Points.Point; begin P := Init; P := P + 1; end Show_Point;

Now, we've removed the prefix from all operations on the P variable.

Use clauses and naming conflicts

Visibility issues may arise when we have multiple use clauses. For instance, we might have types with the same name declared in multiple packages. This constitutes a naming conflict; in this case, the types become hidden — so they're not directly visible anymore, even if we have a use clause.

In the Ada Reference Manual

Code example

Let's start with a code example. First, we declare and implement a generic procedure that shows the value of a Complex object:

with Ada.Numerics.Generic_Complex_Types; generic with package Complex_Types is new Ada.Numerics.Generic_Complex_Types (<>); procedure Show_Any_Complex (Msg : String; Val : Complex_Types.Complex);
with Ada.Text_IO; with Ada.Text_IO.Complex_IO; procedure Show_Any_Complex (Msg : String; Val : Complex_Types.Complex) is package Complex_Float_Types_IO is new Ada.Text_IO.Complex_IO (Complex_Types); use Complex_Float_Types_IO; use Ada.Text_IO; begin Put (Msg & " "); Put (Val); New_Line; end Show_Any_Complex;

Then, we implement a test procedure where we declare the Complex_Float_Types package as an instance of the Generic_Complex_Types package:

with Ada.Numerics; use Ada.Numerics; with Ada.Numerics.Generic_Complex_Types; with Show_Any_Complex; procedure Show_Use is package Complex_Float_Types is new Ada.Numerics.Generic_Complex_Types (Real => Float); use Complex_Float_Types; procedure Show_Complex_Float is new Show_Any_Complex (Complex_Float_Types); C, D, X : Complex; begin C := Compose_From_Polar (3.0, Pi / 2.0); D := Compose_From_Polar (5.0, Pi / 2.0); X := C + D; Show_Complex_Float ("C:", C); Show_Complex_Float ("D:", D); Show_Complex_Float ("X:", X); end Show_Use;

In this example, we declare variables of the Complex type, initialize them and use them in operations. Note that we have direct visibility to the package instance because we've added a simple use clause after the package instantiation — see use Complex_Float_Types in the example.

Naming conflict

Now, let's add the declaration of the Complex_Long_Float_Types package — a second instantiation of the Generic_Complex_Types package — to the code example:

with Ada.Numerics; use Ada.Numerics; with Ada.Numerics.Generic_Complex_Types; with Show_Any_Complex; procedure Show_Use is package Complex_Float_Types is new Ada.Numerics.Generic_Complex_Types (Real => Float); use Complex_Float_Types; package Complex_Long_Float_Types is new Ada.Numerics.Generic_Complex_Types (Real => Long_Float); use Complex_Long_Float_Types; procedure Show_Complex_Float is new Show_Any_Complex (Complex_Float_Types); C, D, X : Complex; -- ^ ERROR: type is hidden! begin C := Compose_From_Polar (3.0, Pi / 2.0); D := Compose_From_Polar (5.0, Pi / 2.0); X := C + D; Show_Complex_Float ("C:", C); Show_Complex_Float ("D:", D); Show_Complex_Float ("X:", X); end Show_Use;

This example doesn't compile because we have direct visibility to both Complex_Float_Types and Complex_Long_Float_Types packages, and both of them declare the Complex type. In this case, the type declaration becomes hidden, as the compiler cannot decide which declaration of Complex it should take.

Circumventing naming conflicts

As we know, a simple fix for this compilation error is to add the package prefix in the variable declaration:

with Ada.Numerics; use Ada.Numerics; with Ada.Numerics.Generic_Complex_Types; with Show_Any_Complex; procedure Show_Use is package Complex_Float_Types is new Ada.Numerics.Generic_Complex_Types (Real => Float); use Complex_Float_Types; package Complex_Long_Float_Types is new Ada.Numerics.Generic_Complex_Types (Real => Long_Float); use Complex_Long_Float_Types; procedure Show_Complex_Float is new Show_Any_Complex (Complex_Float_Types); C, D, X : Complex_Float_Types.Complex; -- ^ SOLVED: package is now specified. begin C := Compose_From_Polar (3.0, Pi / 2.0); D := Compose_From_Polar (5.0, Pi / 2.0); X := C + D; Show_Complex_Float ("C:", C); Show_Complex_Float ("D:", D); Show_Complex_Float ("X:", X); end Show_Use;

Another possibility is to write a use clause in the form use all type:

with Ada.Numerics; use Ada.Numerics; with Ada.Numerics.Generic_Complex_Types; with Show_Any_Complex; procedure Show_Use is package Complex_Float_Types is new Ada.Numerics.Generic_Complex_Types (Real => Float); use all type Complex_Float_Types.Complex; package Complex_Long_Float_Types is new Ada.Numerics.Generic_Complex_Types (Real => Long_Float); use all type Complex_Long_Float_Types.Complex; procedure Show_Complex_Float is new Show_Any_Complex (Complex_Float_Types); C, D, X : Complex_Float_Types.Complex; begin C := Compose_From_Polar (3.0, Pi / 2.0); D := Compose_From_Polar (5.0, Pi / 2.0); X := C + D; Show_Complex_Float ("C:", C); Show_Complex_Float ("D:", D); Show_Complex_Float ("X:", X); end Show_Use;

For the sake of completeness, let's declare and use variables of both Complex types:

with Ada.Numerics; use Ada.Numerics; with Ada.Numerics.Generic_Complex_Types; with Show_Any_Complex; procedure Show_Use is package Complex_Float_Types is new Ada.Numerics.Generic_Complex_Types (Real => Float); use all type Complex_Float_Types.Complex; package Complex_Long_Float_Types is new Ada.Numerics.Generic_Complex_Types (Real => Long_Float); use all type Complex_Long_Float_Types.Complex; procedure Show_Complex_Float is new Show_Any_Complex (Complex_Float_Types); procedure Show_Complex_Long_Float is new Show_Any_Complex (Complex_Long_Float_Types); C, D, X : Complex_Float_Types.Complex; E, F, Y : Complex_Long_Float_Types.Complex; begin C := Compose_From_Polar (3.0, Pi / 2.0); D := Compose_From_Polar (5.0, Pi / 2.0); X := C + D; Show_Complex_Float ("C:", C); Show_Complex_Float ("D:", D); Show_Complex_Float ("X:", X); E := Compose_From_Polar (3.0, Pi / 2.0); F := Compose_From_Polar (5.0, Pi / 2.0); Y := E + F; Show_Complex_Long_Float ("E:", E); Show_Complex_Long_Float ("F:", F); Show_Complex_Long_Float ("Y:", Y); end Show_Use;

As expected, the code compiles correctly.