10.1.2 Context Clauses - With Clauses
Language Design Principles
{
one-pass context_clauses}
The
reader should be able to understand a
context_clause
without looking ahead. Similarly, when compiling a
context_clause,
the compiler should not have to look ahead at subsequent
context_items,
nor at the compilation unit to which the
context_clause
is attached. (We have not completely achieved this.)
{
AI95-00217-06}
{
ripple effect}
A
ripple effect occurs
when the legality of a compilation unit could be affected by adding or
removing an otherwise unneeded
with_clause
on some compilation unit on which the unit depends, directly or indirectly.
We try to avoid ripple effects because they make understanding and maintenance
more difficult. However, ripple effects can occur because of direct visibility
(as in child units); this seems impossible to eliminate. The ripple effect
for
with_clauses
is somewhat similar to the Beaujolais effect (see
8.4)
for
use_clauses,
which we also try to avoid.
Syntax
limited_with_clause ::= limited [
private]
with library_unit_name {,
library_unit_name};
nonlimited_with_clause ::= [
private]
with library_unit_name {,
library_unit_name};
{
AI95-00262-01}
{
private with_clause}
A
with_clause
containing the reserved word
private is called a
private with_clause.
It can be thought of as making items visible only in the private part,
although it really makes items visible everywhere except the visible
part. It can be used both for documentation purposes (to say that a unit
is not used in the visible part), and to allow access to private units
that otherwise would be prohibited.
Name Resolution Rules
Discussion: {
AI95-00262-01}
Suppose a non-private
with_clause
of a public library unit mentions one of its private siblings. (This
is only allowed on the body of the public library unit.) We considered
making the scope of that
with_clause
not include the visible part of the public library unit. (This would
only matter for a
subprogram_body,
since those are the only kinds of body that have a visible part, and
only if the
subprogram_body
completes a
subprogram_declaration,
since otherwise the
with_clause
would be illegal.) We did not put in such a rule for two reasons: (1)
It would complicate the wording of the rules, because we would have to
split each
with_clause
into pieces, in order to correctly handle “
with P, Q;”
where P is public and Q is private. (2) The conformance rules prevent
any problems. It doesn't matter if a type name in the spec of the body
denotes the completion of a
private_type_declaration.
Discussion: With_clauses
control the visibility of declarations or renamings of library units.
Mentioning a root library unit in a
with_clause
makes its declaration directly visible. Mentioning a non-root library
unit makes its declaration visible. See Section 8 for details.
{
AI95-00114-01}
Note that this rule implies that “
with A.B.C;” is
almost equivalent to “
with A, A.B, A.B.C;”. The reason
for making a
with_clause
apply to all the ancestor units is to avoid “visibility holes”
— situations in which an inner program unit is visible while an
outer one is not. Visibility holes would cause semantic complexity and
implementation difficulty. (This is not exactly equivalent because the
latter
with_clause
names A and A.B, while the previous one does not. Whether a unit is “named”
does not have any effect on visibility, however, so it is equivalent
for visibility purposes.))
[Outside its own declarative region, the declaration
or renaming of a library unit can be visible only within the scope of
a
with_clause
that mentions it. The visibility of the declaration or renaming of a
library unit otherwise follows from its placement in the environment.]
Legality Rules
{
AI95-00262-01}
the declaration, body, or subunit of a private descendant of that library
unit;
{
AI95-00220-01}
{
AI95-00262-01}
the body or subunit of a public descendant of that library unit, but
not a subprogram body acting as a subprogram declaration (see
10.1.4);
or
{
AI95-00262-01}
the declaration of a public descendant of that library unit, in which
case the
with_clause
shall include the reserved word
private.
Reason: {
AI95-00262-01}
The purpose of this rule is to prevent a private child from being visible
from outside the subsystem rooted at its parent. A private child can
be semantically depended-on without violating this principle if it is
used in a private
with_clause.
Discussion: This rule violates the one-pass
context_clauses
Language Design Principle. We rationalize this by saying that at least
that Language Design Principle works for legal compilation units.
Example:
package A is
end A;
package A.B is
end A.B;
private package A.B.C is
end A.B.C;
package A.B.C.D is
end A.B.C.D;
with A.B.C; -- (1)
private package A.B.X is
end A.B.X;
package A.B.Y is
end A.B.Y;
with A.B.C; -- (2)
package body A.B.Y is
end A.B.Y;
private with A.B.C; -- (3)
package A.B.Z is
end A.B.Z;
{
AI95-00262-01}
(1) is OK because it's a private child of A.B — it would be illegal
if we made A.B.X a public child of A.B. (2) is OK because it's the body
of a child of A.B. (3) is OK because it's a child of A.B, and it is a
private
with_clause.
It would be illegal to say “
with A.B.C;” on any
library_item
whose name does not start with “A.B”. Note that mentioning
A.B.C.D in a
with_clause
automatically mentions A.B.C as well, so “
with A.B.C.D;”
is illegal in the same places as “
with A.B.C;”.
{
AI95-00262-01}
A
name denoting
a library item that is visible only due to being mentioned in one or
more
with_clauses
that include the reserved word
private shall appear only within:
a private part;
a private descendant of the unit on which one of
these
with_clauses
appear; or
a pragma within a context clause.
Ramification: These rules apply only
if all of the
with_clauses
that mention the name include the reserved word
private. They
do not apply if the name is mentioned in any
with_clause
that does not include
private.
Reason: These rules make the library
item visible anywhere that is not visible outside the subsystem rooted
at the
compilation_unit
having the private
with_clause,
including private parts of packages nested in the visible part, private
parts of child packages, the visible part of private children, and context
clause pragmas like Elaborate_All.
We considered having the scope of a private
with_clause
not include the visible part. However, that rule would mean that moving
a declaration between the visible part and the private part could change
its meaning from one legal interpretation to a different legal interpretation.
For example:
package A is
function B return Integer;
end A;
function B return Integer;
with A;
private with B;
package C is
use A;
V1 : Integer := B; -- (1)
private
V2 : Integer := B; -- (2)
end C;
If we say that library subprogram B is not in
scope in the visible part of C, then the B at (1) resolves to A.B, while
(2) resolves to library unit B. Simply moving a declaration could silently
change its meaning. With the legality rule defined above, the B at (1)
is illegal. If the user really meant A.B, they still can say that.
{
AI95-00217-06}
[A
library_item
mentioned in a
limited_with_clause
shall be the implicit declaration of the limited view of a library package,
not the declaration of a subprogram, generic unit, generic instance,
or a renaming.]
Reason: We have to explicitly disallow
limited with P;
package P is ...
as we can't depend on the semantic dependence
rules to do it for us as with regular withs. This says “named”
and not “mentioned” in order that
limited private with P.Child;
package P is ...
can be used to allow a mutual dependence between
the private part of P and the private child P.Child, which occurs in
interfacing and other problems. Since the child always semantically depends
on the parent, this is the only way such a dependence can be broken.
Reason: This prevents visibility issues,
where whether an entity is an incomplete or full view depends on how
the name of the entity is written. The
limited_with_clause
cannot be useful, as we must have the full view available in the parent
in order for the
use_clause
to be legal.
3 {
AI95-00217-06}
A
library_item
mentioned in a
nonlimited_with_clause
of a compilation unit is visible within the compilation unit and hence
acts just like an ordinary declaration. Thus, within a compilation unit
that mentions its declaration, the name of a library package can be given
in
use_clauses
and can be used to form expanded names, a library subprogram can be called,
and instances of a generic library unit can be declared. If a child of
a parent generic package is mentioned in a
nonlimited_with_clause,
then the corresponding declaration nested within each visible instance
is visible within the compilation unit. Similarly, a
library_item
mentioned in a
limited_with_clause
of a compilation unit is visible within the compilation unit and thus
can be used to form expanded names.
Ramification: The rules given for
with_clauses
are such that the same effect is obtained whether the name of a library
unit is mentioned once or more than once by the applicable
with_clauses,
or even within a given
with_clause.
If a
with_clause
mentions a
library_unit_renaming_declaration,
it only “mentions” the
prefixes
appearing explicitly in the
with_clause
(and the renamed view itself); the
with_clause
is not defined to mention the ancestors of the renamed entity. Thus,
if X renames Y.Z, then “with X;” does not make the declarations
of Y or Z visible. Note that this does not cause the dreaded visibility
holes mentioned above.
Examples
{
AI95-00433-01}
with Ada.Strings.Unbounded;
package Office.Locations
is
type Location
is new Ada.Strings.Unbounded.Unbounded_String;
end Office.Locations;
{
AI95-00433-01}
limited with Office.Departments; --
types are incomplete
private with Office.Locations; --
only visible in private part
package Office.Employees
is
type Employee
is private;
function Dept_Of(Emp : Employee) return access Departments.Department;
procedure Assign_Dept(Emp : in out Employee;
Dept : access Departments.Department);
...
private
type Employee is
record
Dept : access Departments.Department;
Loc : Locations.Location;
...
end record;
end Office.Employees;
limited with Office.Employees;
package Office.Departments is
type Department is private;
function Manager_Of(Dept : Department) return access Employees.Employee;
procedure Assign_Manager(Dept : in out Department;
Mgr : access Employees.Employee);
...
end Office.Departments;
{
AI95-00433-01}
The
limited_with_clause
may be used to support mutually dependent abstractions that are split
across multiple packages. In this case, an employee is assigned to a
department, and a department has a manager who is an employee. If a
with_clause
with the reserved word
private appears on one library unit and
mentions a second library unit, it provides visibility to the second
library unit, but restricts that visibility to the private part and body
of the first unit. The compiler checks that no use is made of the second
unit in the visible part of the first unit.
Extensions to Ada 83
{
extensions to Ada 83}
The
syntax rule for
with_clause
is modified to allow expanded name notation.
Wording Changes from Ada 83
The syntax rule for
context_clause
is modified to more closely reflect the semantics. The Ada 83 syntax
rule implies that the
use_clauses
that appear immediately after a particular
with_clause
are somehow attached to that
with_clause,
which is not true. The new syntax allows a
use_clause
to appear first, but that is prevented by a textual rule that already
exists in Ada 83.
The concept of “scope of a
with_clause”
(which is a region of text) replaces RM83's notion of “apply to”
(a
with_clause
applies to a
library_item)
The visibility rules are interested in a region of text, not in a set
of compilation units.
No need to define “apply to” for
use_clauses.
Their semantics are fully covered by the “scope (of a
use_clause)”
definition in
8.4.
Incompatibilities With Ada 95
{
AI95-00220-01}
{
incompatibilities with Ada 95}
Amendment
Correction: A subprogram body acting as a declaration cannot
with
a private child unit. This would allow public export of types declared
in private child packages, and thus cannot be allowed. This was allowed
by mistake in Ada 95; a subprogram that does this will now be illegal.
Extensions to Ada 95
{
AI95-00217-06}
{
extensions to Ada 95}
limited_with_clauses
are new. They make a limited view of a package visible, where all of
the types in the package are incomplete. They facilitate construction
of mutually recursive types in multiple packages.
{
AI95-00262-01}
{
extensions to Ada 95}
The syntax rules for
with_clause
are modified to allow the reserved word
private. Private
with_clauses
do not allow the use of their library item in the visible part of their
compilation_unit.
They also allow using private units in more locations than in Ada 95.