Version 1.6 of ais/ai-00412.txt
!standard 3.10.1(2) 05-07-12 AI95-00412/06
!standard 3.10.1(7)
!standard 8.3(20)
!standard 8.5.3(3)
!standard 8.5.3(4)
!standard 10.1.2(8)
!class amendment 05-02-03
!status Amendment 200Y 05-02-28
!status ARG Approved 5-0-5 05-02-12
!status work item 05-02-03
!status received 05-02-03
!priority Medium
!difficulty Medium
!subject Subtypes of incomplete types; renamings of limited views
!summary
Subtype declarations are permitted when the subtype mark is that of an
incomplete type provided they do not have any constraints or a null
exclusion.
Renaming of the limited view of a package is permitted.
The view visible via a rename of a library package (or one of
its visible subpackages) is determined by what kind of with
clause applies at the point of reference.
!problem
The introduction of limited with clauses and the fact that use package
clauses and renamings are not permitted with a limited view can create
long names which cannot be abbreviated since subtype declarations are not
permitted for an incomplete type.
In writing examples for the rationale, we have found some
of these long names tedious, and wonder whether we couldn't
allow the declaration of subtypes, so long as no constraint is
specified. Similarly, we wonder whether we could also permit renamings
of limited views of packages, as this is a common alternative
approach to "use" clauses to provide "shorthand" notations for
entities whose full name is very long.
Even if we didn't permit renaming of limited views, there is a
question of what view of a package is visible via a renaming
declared in a place that has a full view, but referenced in
a place that has only a limited view of a package.
!proposal
To answer the last issue first -- if referenced from within the scope
of a limited with for a package, any renaming of that package that
might occur in some other compilation unit provides the
same limited view. Similarly, if the package has nested packages, then
renames of those also provide limited views when referenced from inside
the scope of the limited with.
We propose to allow declarations of constraint-less subtypes of incomplete
types, and renamings of limited views. These have slightly different
rules, so we will describe them separately.
For an incomplete type other than one implicitly declared in a limited view of
a package, a named subtype denotes the full view of the type where
the full view is visible. For an incomplete type from a limited view
of a package, a named subtype denotes the full view when within the
scope of a non-limited with clause for the enclosing library unit.
Otherwise it denotes the incomplete view. This is intended to mirror the
rules for access-to-incomplete types.
For a renaming of a limited view, the renaming may only be used within
the scope of a limited or non-limited with clause for the enclosing
library unit. If neither kind of with clause for the library
unit containing the viewed package applies to a given place, no
reference to the renaming is permitted. If within the scope
of a with clause, then if it is limited, a limited view is provided,
if the with clause is non-limited, then a full view is provided.
This ensures that only one view of a given package is visible
at a given point.
These rules are intended to ensure that there is never the need
to implicitly produce a full view of a package as a side effect
of referencing a renaming of a limited view or a subtype of an
incomplete type, except within the scope of a non-limited with
clause for the package. Equivalently, after processing the context
clause, for any given library package, no further views will be
needed.
!wording
Add after the paragraphs added by AI-326 to 3.10.1(2):
Similarly, if a subtype_mark denotes a subtype_declaration defining
a subtype of an incomplete view T, the subtype_mark denotes an
incomplete view except under the same two circumstances given above,
in which case it denotes the full view of T.
Add after 3.10.1(6/2) (might as well reuse 7/2 which is now empty):
* as the subtype_mark in the subtype_indication of a subtype_declaration;
the subtype_indication shall not have a null_exclusion or a constraint;
Revise 8.3(20) as follows:
*
The declaration of a library unit (including a
library_unit_renaming_declaration) is hidden from all visibility at
places outside its declarative region that are not within the scope
of a nonlimited_with_clause that mentions it. The {declaration} of
the limited view of a library package is hidden from all visibility
at places that are not within the scope of a limited_with_clause
that mentions it; in addition, the {declaration of the} limited view
is hidden from all visibility within the declarative region of the
package, as well as within the scope of any nonlimited_with_clause
that mentions it. {Where the declaration of the limited view of a
package is visible, any name that denotes the package denotes the
limited viewRedundant:[, including those provided by a package renaming.]}
*
Redundant:[For each declaration or renaming of a generic unit as a
child of some parent generic package, there is a corresponding
declaration nested immediately within each instance of the parent.]
Such a nested declaration is hidden from all visibility except at
places that are within the scope of a with_clause that mentions the
child.
Drop the restriction of 8.5.3(3/2) that requires the
view be nonlimited. Instead, add the following:
If the package_name of a package_renaming_declaration denotes a limited view
of a package P, then a name that denotes the package_renaming_declaration
shall occur only within the immediate scope of the renaming or the
scope of a with_clause that mentions the package P or, if P is a nested
package, the innermost library package enclosing P.
Add after 8.5.3(4):
Redundant:[At places where the declaration of the limited view of the renamed
package is visible, a name that denotes the package_renaming_declaration
denotes a limited view of the package (see 10.1.1).]
AARM Note: Proof: This rule is found in 8.3, Visibility.
Replace the second paragraph added after 10.1.2(8) by AI-217-6 with:
A limited_with_clause shall not appear on a library_unit_body, subunit, or
library_unit_renaming_declaration.
AARM Reason: We don't allow a limited_with_clause on a
library_unit_renaming_declaration because it would be useless. A renaming
could not appear in a limited_with_clause (by the rule above), and a renaming
could not appear in a nonlimited_with_clause (because the name would not be
within the scope of a with_clause denoting the package, see 8.5.3). Nor could
it be a parent of another unit. That doesn't leave anywhere that that
the name of such a renaming could appear.
!discussion
The canonical example of limited with clauses is
limited with Departments;
package Employees is
type Employee is tagged private;
procedure Assign_Employee(E: in out Employee;
D: in out Departments.Department'Class);
type Dept_Ptr is access all Departments.Department'Class;
function Current_Department(E: Employee) return Dept_Ptr;
...
end Employees;
limited with Employees;
package Departments is
type Department is tagged private;
procedure Choose_Manager(D: in out Department;
M: in out Employees.Employee'Class);
...
end Departments;
The names Departments.Department and Employees.Employee are long and
cumbersome. There are two ways of avoiding such long names in general.
One is by adding use clauses and the other is by renaming (or using
subtypes).
In the case of limited with, use package clauses are forbidden. Moreover
use package clauses are often forbidden by projects anyway. In such
circumstances the use of renaming or subtypes is very useful. Thus we
might have
V: Float renames Aeroplane_Data.Current_Velocity;
The use of meaningful package names and variable names is good practice
but when put together they can be overwhelming. The renaming enables an
abbreviation to be used locally but ensures that the full name is still
visible to the human reader. In the case of types, renaming is not
appropriate but a subtype serves an equivalent purpose thus
subtype T is Long_Package_Name.Long_Type_Name;
In the Employees and Departments example we would like to say
subtype Dept is Departments.Department;
and
subtype Empl is Employees.Employee;
This is currently forbidden because the name of an incomplete type is
only permitted in a short list of places by 3.10.1(5/2-10/2).
We therefore add that it can also be used in a subtype declaration.
Clearly however, we know little about the type (except that it might be
tagged) and so constraints and not null are not permitted.
The only other things made visible by a limited with clause are the
names of the package itself and any nested packages. We are also
proposing to allow renaming of such packages.
Because subtype declarations and package renamings can be used to
"re-export" items made visible in a unit via limited with clauses, we
need to be concerned about what are the properties of these re-exported
views.
One concern is about "ripple" effects, by which we mean that the
addition, removal, or change of a with clause on some "distant" member
of the semantic closure of a unit might affect the legality of the given
unit. In particular, we are worried what happens when we change a
"with" clause to a "limited with" clause, or vice-versa.
In the past, we have been concerned primarily about ripple effects
relating to a rule that causes the presence or absence of a particular
unit in the semantic closure to affect the legality of otherwise
unrelated constructs. For example, in Ada 83, the 'Address attribute
was only legal if package System was somewhere within the semantic
closure. This notion was dropped in Ada 95, as it is surprising during
maintenance, and it makes incremental compilation that much harder.
In the case of the "limited with," we tried to avoid having ripple
effects that determined whether the dereference of an
access-to-incomplete type was itself incomplete based on whether there
was a non-limited with clause on the package defining the full type
somewhere in the closure. We ultimately decided the with clause had to
be on the unit itself. But we also allowed incomplete to match complete
in more circumstances.
Nevertheless, we are still left with a semi-ripple effect, in that if
the place where the access type is declared changed its with clause from
a non-limited to a limited with clause, the dereference of an access
value of the type shifts from being non-incomplete to incomplete. This
is a "semi" ripple effect in that there must be an unbroken chain of
references crossing only direct "with" clause dependences back to the
unit whose with clause is changed. This does not seem as bad. This is
more like there being two separate packages, P1 and P2, both defining a
type T, one private and one non-private, and a third package defining an
access-to-T type. If the third package changes its with clause to "with
and use" P2 rather than P1, then the properties of the designated type
of the access-to-T changes, and if we make use of values of that type,
they change from being access-to-non-private-T to access-to-private-T.
That of course is not at all surprising.
Effectively we should realize that changing a "with" clause
to a "limited with" is somewhat like change "with P1" to "with P2."
We are seeing a significantly different set of types and subpackages.
If we reexport entities whose properties derive from declarations
within P1 and P2, or equivalently, within the full or limited
view of a package, then the change in the with clause will
certainly be visible to those who import those entities.
We have tried to isolate this somewhat, by saying that if the
"importing" unit itself has a "with" or "limited with" clause
on the package, then that determines its view of a renaming,
a subtype-of-T, or an access-to-T. If it has neither kind
of with clause, then for subtype-of-T and access-to-T, the
incomplete/access-to-incomplete view is provided. For renaming,
we make neither view available.
For renaming and subtype-of-T, we considered providing an
"implicit" non-limited "with" if needed, but it was pointed out
that there are implementations that rely on the context clause
to determine order of compilation, and having such implicit
dependences on the full view of a package would seriously
undermine that approach. This alternative is considered in
more depth in the following.
A REJECTED ALTERNATIVE
A possible alternative is to say that you always get the full
view, unless you are in the scope of a limited with.
This would probably have friendlier behavior during maintenance, and
during transition to Ada 2005.
Let's take a specific scenario. We have two packages
in Ada 95, P_Pkg and Q_Pkg, which declare T and U,
respectively. We would really like to have P_Pkg
have operations on U, and Q_Pkg have operations on T,
but we get into a cycle, so we play the "class-wide"
type trick, as follows:
package Q_Root_Pkg is
type Q_U_Root is tagged ...
end Q_Root_Pkg;
with Q_Root_Pkg; use Q_Root_Pkg;
package P_Pkg is
type T is tagged ...
procedure Cyclic(X : Q_U_Root'Class);
--
--
--
--
end P_Pkg;
with Q_Root_Pkg; use Q_Root_Pkg;
with P_Pkg;
package Q_Pkg is
package P_Ren renames P_Pkg; --
subtype P_T is P_Ren.T; --
type U is new Q_U_Root with ... --
procedure Fun(Y : P_T);
end Q_Pkg;
Now let's suppose that we have lots of existing code
that depends on Q_Pkg, and makes use of some of the
shorthands it declares:
with Q_Pkg; use Q_Pkg;
procedure Some_Random_Procedure is
A : P_Ren.T; --
B : P_T; --
begin
Fun(A); Fun(B);
end;
Now along comes Ada 2005, and we realize we can change the "with" of P_Pkg
to a "limited with" of P_Pkg, and then
add a "with" of Q_Pkg on P_Pkg, and change the
Cyclic procedure to take a Q_Pkg.U directly, rather than
playing the class-wide type game.
We also have these shorthands declared
in Q_Pkg. We can erase them, but then who
knows how much code is using them. But with this
proposal, we allow shorthands to be
used even in the presence of limited with,
so I'm saved.
Hence, we can leave the shorthands there, change
the "with" of P_Pkg to a "limited with" of P_Pkg,
and fix Cyclic to produce:
with Q_Pkg; use Q_Pkg;
package P_Pkg is
type T is tagged ...
procedure Cyclic(X : U);
--
end P_Pkg;
limited with P_Pkg;
package Q_Pkg is
package P_Ren renames P_Pkg; --
subtype P_T is P_Ren.T; --
type U is tagged ...
procedure Fun(Y : P_T);
end Q_Pkg;
Unfortunately, all the compilation units that were using
the shorthands are now in trouble, depending on what are the
rules relating to the re-export of limited/incomplete views.
There are various choices, and conceivably one could
make different choices for package renaming and subtype
declarations (and deref of access-to-incomplete):
1) In the absence of a "with" or "limited with" clause:
a) you see the same view as was present at the place
where the shorthand was declared;
b) you see the full view
2) In the presence of a "with" clause:
a) you see the same view as at the place where the shorthand
was declared
b) you see the full view
3) In the presence of a "limited with" clause:
a) you see the same view as at the place where the shorthand
was declared
b) you see the limited/incomplete view
We haven't talked much about (1), but for package renaming, we are
proposing 2(b) and 3(b) -- the "local" with clause wins.
For subtypes, we agree that for 3, we want (a), that is the subtype
doesn't "become incomplete". We are proposing that we allow "shorthand"
subtypes of incomplete types, but we haven't really concluded whether we
want 2(a) or 2(b) for them, that is do we want them to "become full"
when viewed from a place that has a full view on the package.
for deref of access-to-incomplete, we seem to have 2(b) and 3(b). when
neither a "with" or a "limited with" is in scope, we seem to treat it as
an incomplete view, but allow it to match the full view.
If you look at it from the transition and maintenance point of view, it
would seem ideal if changing a "with" to a "limited with" really only
affected the compilation units in the scope of the limited with clause.
Any other compilation units are unaffected. This would say that our
rule for deref of access-to-incomplete is not quite right. We shouldn't
require the location of the deref (e.g. procedure P3 in example given
near the top) to add a "with" clause just because the place where the
access type was declared (package P2) changed a "with" to a "limited
with." This would also argue that you see the full view via a shorthand,
unless you are in the scope of a limited with.
An implication of this approach is that you end up with an implicit
semantic dependence on the full view if you dereference an
access-to-incomplete or use a subtype-of-incomplete name outside the
scope of a limited with, and you use the deref or the subtype name in a
way that would be illegal if it were incomplete (e.g. using a deref as a
prefix, or using the subtype to declare an object).
Similarly, if you use a package rename in a context outside of the scope
of a limited with for the renamed package (or its enclosing library
unit), other than in another renaming, you get the full view (and hence
an implicit semantic dependence on the full view).
This approach relates to the choice we made in Ada 95 to create an
implicit semantic dependence on package System when the 'Address
attribute is used. Essentially, we are saying that the clients of a
package that has only a limited view on a package may end up with an
implicit semantic dependence on the full view of the package.
Equivalently, if you need to break circularity, then you need to change
a with clause to a limited with, or add a limited with, on one or more
units that are part of the circularity. But you never have to add a
"with" clause on units outside the circularity.
The problem with this approach is that Ada is an explicit language; implicit
things are kept to a minimum. Implicit dependencies are bad for both
implementers and maintenance programmers. Maintenance programmers are faced
with operations that are mysteriously available, and it may take looking in
a number of units to find their declarations - they could only be found by
tracing back a chain of declarations. Moreover, changes in a unit could
possibly affect some code that does not explicitly mention the unit, which
is likely to be confusing. Implementations are complicated by
implicit dependencies; many implementations find the declarations to load
based on the explicit list of dependencies given in the context clause;
anything that requires items to be made available later could be very hard
to add. The existing case if implicit dependence doesn't suffer from these
problems because Address is a magic attribute anyway, and no one
is giving to be surprised where it is declared. Therefore, we reject any
approach that requires implicit dependencies.
BOTTOM LINE:
We propose to allow subtype-of-incomplete and renaming limited views.
We also want to minimize ripple effects. Finally, we want to preserve
the principle that the context clause determines the semantic
dependences (ignoring language-defined implicit dependences on certain
standard packages).
The net effect is that when you change a with clause of some unit Q from
"with P" to "limited with P," some of the clients of Q may need to add
their own "with P" to remain compilable. This is because Q may now be
exporting access-to-incomplete, subtype-of-incomplete, and
renaming-of-limited, and a "with P" may be needed in the client to turn
these back into access-to-complete, subtype-of-complete, and
renaming-of-full-view. It is the intent that no other changes
to clients would be necessary to survive this shift in Q.
!example
(See discussion.)
!corrigendum 3.10.1(2)
Insert after the paragraph:
incomplete_type_declaration ::= type defining_identifier [discriminant_part];
the new paragraph:
Similarly, if a subtype_mark denotes a subtype_declaration defining
a subtype of an incomplete view T, the subtype_mark denotes an
incomplete view except under the same two circumstances given above,
in which case it denotes the full view of T.
!corrigendum 3.10.1(7)
Replace the paragraph:
- as the subtype_mark defining the subtype of a parameter or
result of an access_to_subprogram_definition;
by:
- as the subtype_mark in the subtype_indication of a
subtype_declaration; the subtype_indication shall not have a
null_exclusion or a constraint;
!corrigendum 8.3(20)
Replace the paragraph:
- The declaration of a library unit (including a
library_unit_renaming_declaration) is hidden from all visibility except at
places that are within its declarative region or within the scope of a
with_clause that mentions it. For each declaration or renaming of a generic
unit as a child of some parent generic package, there is a corresponding
declaration nested immediately within each instance of the parent. Such a
nested declaration is hidden from all visibility except at places that are
within the scope of a with_clause that mentions the child.
by:
- The declaration of a library unit (including a
library_unit_renaming_declaration) is hidden from all visibility at
places outside its declarative region that are not within the scope of a
nonlimited_with_clause that mentions it. The limited view of a library
package is hidden from all visibility at places that are not within the scope
of a limited_with_clause that mentions it; in addition, the limited view
is hidden from all visibility within the declarative region of the package, as
well as within the scope of any nonlimited_with_clause that mentions it.
Where the declaration of the limited view of a
package is visible, any name that denotes the package denotes the
limited view, including those provided by a package renaming.
- For each declaration or renaming of a generic unit as a child of some
parent generic package, there is a corresponding declaration nested immediately
within each instance of the parent. Such a nested declaration is hidden from
all visibility except at places that are within the scope of a with_clause
that mentions the child.
!corrigendum 8.5.3(3)
Insert after the paragraph:
The renamed entity shall be a package.
the new paragraph:
If the package_name of a package_renaming_declaration denotes a
limited view of a package P, then a name that denotes the
package_renaming_declaration shall occur only within the immediate scope
of the renaming or the scope of a with_clause that mentions the package
P or, if P is a nested package, the innermost library package enclosing
P.
!corrigendum 8.5.3(4)
Insert after the paragraph:
A package_renaming_declaration declares a new view of the renamed package.
the new paragraph:
At places where the declaration of the limited view of the renamed package is
visible, a name that denotes the package_renaming_declaration denotes
a limited view of the package (see 10.1.1).
!corrigendum 10.1.2(8)
!comment This is a fake just to force a conflict. The conflict file includes
!comment the change.
@drepl
A @fa<limited_with_clause> shall not appear on a @fa<library_unit_body> or
@fa<subunit>.
@dby
A @fa<limited_with_clause> shall not appear on a @fa<library_unit_body>,
@fa<subunit>, or @fa<library_unit_renaming_declaration>.
!ACATS test
!appendix
*************************************************************
Questions? Ask the ACAA Technical Agent