Version 1.1 of ai05s/ai05-0135-2.txt

Unformatted version of ai05s/ai05-0135-2.txt version 1.1
Other versions for file ai05s/ai05-0135-2.txt

!standard 4.1.3(12)          10-01-07 AI05-0135-2/01
!standard 7.1(2)
!standard 7.1(3)
!standard 7.1(4)
!standard 7.1(5/2)
!standard 7.1(7)
!standard 8.3(25)
!standard 8.4(5/2)
!standard 8.4(16)
!standard 8.5.3(3)
!standard 8.5.3(4)
!standard 10.1.1(12.2/3)
!standard 12.3(2/2)
!standard 12.3(18)
!class Amendment 10-01-07
!status work item 10-01-07
!status received 09-11-07
!priority Medium
!difficulty Medium
!subject "Integrated" nested packages
!summary
Add "package <P> is ..." to allow integrated packages and package renames.
!problem
One common idiom is to write a derived type to take a type declared within an instantiation (or some other unit) and bring it out to the same level as the instantiation (or into the new unit):
package T_Vecs is new Vectors(Element => T); type T_Vec is new T_Vecs.Container with null record;
This is often done to bring the type and its operations to this location. But often a new, distinct type is not what is wanted. Moreover, some related entities (classwide operations, exceptions, generic operations) don't get exported this way, so additional renaming or declarations are required to completely make the instance transparent.
This use of a derived type is really something of a kludge. What is really wanted here is a way to make all of the declarations declared in the instantiation visible as if they were declared at the same level as the instantiation (as opposed to within it).
Consider a package which needs to export both a private type and an instance of one of the container generics for that type.
Currently, this requires something like
package Pkg is package Inner is type T is private; private ... end Inner;
package I is new Some_Container_Generic (Inner.T); end Pkg;
What is wanted here is a way to make all of the declarations visible in Pkg.Inner (and perhaps Pkg.I; that's less clear) visible as if they were declared immediately within Pkg. This special case of the problem alone has been viewed as being sufficiently important to merit considering major changes in the language (see the 4 alternative versions of AI05-0074).
An unwanted nested package may also be introduced when a user wishes to declare a non-primitive operation for a tagged type, as in
package Pkg is type T is tagged ... ;
package Inner is function Not_Primitive (X : T) return T; end Inner; end Pkg;
As in the previous example, what is wanted here is a way to make all of the declarations visible in Pkg.Inner visible as if they were declared immediately within Pkg.
!proposal
A package or package rename which is declared immediately within the visible part of an enclosing package may be declared with angle brackets enclosing the first occurence of the package identifier. This indicates that the package is to be "integrated" into its parent; this means that clients of the parent have the ability to name the constructs declared within the integrated package as though they were declared within the enclosing package.
One scenario where this construct may be useful is in declaring a package that exports both a private type and a container generic instance for that type without requiring that clients deal with nested packages. This can be accomplished using this new construct as follows:
with Some_Container_Generic; package Pkg is package <Inner> is type T is private; private ...; end Inner;
package <I> is new Some_Container_Generic (T); -- exports type Valise end Pkg;
with Pkg; package Client is X : Pkg.T; Y : Pkg.Valise; use Pkg; Xx : T; Yy : Valise; end Client;
!wording
Modify 4.1.3(12) as follows
The selector_name shall resolve to denote a declaration that occurs immediately within the declarative region of the package or enclosing construct (the declaration shall be visible at the place of the expanded name - see 8.3) {, or to denote a declaration that is use-visible by selection at the point of the selector_name (see 8.4)}. The expanded name denotes that declaration.
Replace 7.1(3) to allow optional "< package_id >" syntax
integrated_package_identification ::= < defining_identifier >
package_identification := defining_program_unit_name | integrated_package_identification
package_specification ::= package package_identification is {basic_declarative_item} [private {basic_declarative_item}] end [[parent_unit_name] . identifier ]
Modify 7.1(4):
If an identifier or parent_unit_name.identifier appears at the end of a package_specification, then this sequence of lexical elements shall repeat the defining_program_unit_name {or, in the case of an integrated_package_identification, the defining_identifier}.
Add after 7.1(5/2) (i.e. append to the end of the Legality section):
The declaration of an integrated package or package renaming (see 8.5.3) shall occur immediately within the visible part of an enclosing package or generic package. The package_identification of a generic_package_declaration shall not consist of an integrated_package_identification. The package_identification of a package_declaration of a library_unit_declaration (or of a package_renaming_declaration of a library_unit_renaming_declaration) shall not consist of an integrated_package_identification.
Add after 7.1(7) (i.e. append to the end of the Static Semantics section):
If a package's package_identification consists of an integrated_package_identification, then the package is said to be "integrated" (see 8.4).
[Move the Legality section of 7.1 so that it follows, rather than precedes, the Static Semantics section. This is because the legality rule added above refers to "an integrated package" and this term is defined in the Static Semantics section.]
Add after 8.3(25) (still in the Name Resolution Rules section):
A name shall not resolve to denote an integrated package or package renaming outside of the immediate scope of that declaration.
AARM note: This rule means that an integrated package or package renaming is, in effect, anonymous outside of its immediate scope. However, it remains visible. This peculiar distinction (i.e., visible but unnamable) is important because visibility of the declarations declared within an integrated package may depend on the visibility of the package (see 8.4). Because this is a name resolution rule, the following example is legal:
declare package P1 is package X is end X; end P1;
package P2 is package <X> is end X; end P2;
use P1; use P2;
package Y renames X; -- unambiguously renames P1.X begin null; end;
Add after 8.4(11) (i.e. append to the end of the Static Semantics section)
At any point where an integrated package or package renaming is either potentially use-visible or directly visible, and where an entity declared immediately within the package or renamed package is visible, the entity is potentially use-visible.
At the point of an expanded_name whose prefix denotes a package P (or a rename thereof) which immediately encloses the visible declaration of an integrated package or package renaming, any visible declaration declared immediately within the integrated package (or, in the case of an integrated package renaming, within the renamed package) is said to be "potentially use-visible by selection" at the point of the selector_name. In addition, if the declaration of an integrated package or package rename is "potentially use-visible by selection" at the point of a selector_name, then so are any visible declarations declared immediately within the package or renamed package. A declaration which is "potentially use-visible by selection" at the point of a selector_name is said to be "use-visible by selection" at that point unless
- the defining name of the declaration is not the same as the
selector_name; or
- a visible homograph of the declaration is declared in P; or - more than one such declaration is potentially use-visible by
selection at the point of the selector_name and at least one of them is not overloadable.
Add after 8.4(16) (in the Examples section)
Example of integrated packages:
generic type T is private; package G is function Make return T; end G;
with G; pragma Elaborate (G); package Pkg1 is package <Inner_Pkg_1> is type T is private; private type T is ... ; end Inner_Pkg_1;
package <Inner_Pkg_2> is new G (T); end Pkg1;
with Pkg1; package Pkg2 is X : Pkg1.T := Pkg1.Make; use Pkg1; Y : T := Make; end Pkg2;
Modify 8.5.3(3) to allow optional "< package_id >" syntax:
package_renaming_declaration
::= package_identification renames package_name;
Add after 8.5.3(4) (i.e. append to the end of the Static Semantics section):
If a package rename's package_identification consists of an integrated_package_identification, then the package renaming is said to be "integrated" (see 8.4).
Modify 10.1.1(12.2/3) to disallow limited views of integrated packages:
For each nested package_declaration directly in the visible part {which does not declare an integrated package}, a declaration of the limited view of that package, with the same defining_program_unit_name.
Modify 12.3(2/2) to allow optional "< package_id >" syntax:
generic_instantiation ::= package package_identification is new generic_package_name [generic_actual_part];
Add after 12.3(18) (i.e. append to the end of the Static Semantics section):
If a package instance's package_identification consists of an integrated_package_identification, then the package instance is said to be "integrated" (see 8.4).
!discussion
Just to summarize, adding angle brackets to a package or package rename declaration has four main effects:
1) It is as though an implicit use_clause were added
immediately after the package declaration, so that visible declarations in the integrated package's spec become potentially use-visible within the enclosing package.
This allows:
package P is package <Q> is X : Integer := 0; end Q; -- use Q; Y : Integer := X; end P;
2) It is as though an implicit use_clause were added immediately
after any use_clause that applies to the enclosing package.
This allows:
package P is package <Q> is X : Integer := 0; end Q; end P;
with P; use P; -- use P.Q; package R is Y : Integer := X; end R;
3) It is as though an implicit use_clause were in effect when
resolving the selector_name of a selected name whose prefix denotes the enclosing package.
This allows:
package P is package <Q> is X : Integer := 0; end Q; end P;
with P; package R is Y : Integer := P. -- use P.Q X; end R;
4) The integrated package cannot be named outside of its immediate
scope (and this is a name resolution rule).
Note that this construct is not defined in terms of implicit use_clauses. This is just an alternative way to view it for expository purposes.
----
#2 above states
It is as though an implicit use_clause were added immediately after any use_clause that applies to the enclosing package.
Note that this informal description applies recursively to the implicit use clauses described in #1 - #3.
This allows:
use System; package P is
package <S_Ren> renames System;
end P;
with P; package Q is package <P_Ren> renames P; end Q;
with Q; use Q; package R is A : Address; end R;
----
The usual use_clause name resolution rules apply. This allows:
package P is package <Q> is X, Y : Integer := 0; end Q; X : Float; end P;
with P; package R is Z : Float renames P.X; -- legal end R:
----
If it is decided that integrated package renames are not wanted, it would be easy to remove them from the proposal. As Erhard has pointed out, there are good reasons to consider this option. It is certainly possible to use integrated package renames to write code that is very difficult to read; this was already true of Ada83 use clauses, but integrated package renames seem to offer substantially greater potential for confusion.
Integrated package renames also make possible a number of odd corner cases:
limited with Foo; package P is package <Bar> renames Foo; end P;
package Outer is package Inner is package <Outer_Rename> renames Outer; end Inner; end Outer;
Finally, integrated package renames are not needed to solve any of the problems that provided the original motivation for introducing integrated packages.
The wording changes need to delete integrated package renames from this AI would be straightforward. These would include:
- in the additions after 7.1(5/2), delete "or package renaming" and
"(or of a package_renaming_declaration
of a library_unit_renaming_declaration)".
- in the additions after 8.3(25), delete "or package renaming" (twice).
- in the additions after 8.4(11), delete "or package renaming" (three
occurences), "or renamed package" (twice), and "(or, in the case of an integrated package renaming, within the renamed package)" .
- eliminate the changes to 8.5.3(3) and the addition after 8.5.3(4).
The only occurences of any form of the word "rename" (e.g., renaming, renamed) remaining in the proposed wording changes would then be the rename declaration in the example in the AARM note after 8.3(25) and the text "(or a rename thereof)" added after 8.4(11).
----
An integrated package may not be named outside of its immediate scope. To understand the motivation for this rule, consider the following example (supplied by Tuck):
Text_IO, Sequential_IO, and Direct_IO each include renames of each exception declared in the package IO_Exceptions. It is a no-brainer to replace those renames with
package <Some_Name> renames Ada.IO_Exceptions;
However, what should "Some_Name" be? Well, most natural would be:
package <IO_Exceptions> renames Ada.IO_Exceptions;
But now suppose you have:
with Ada.IO_Exceptions; with Ada.Text_IO; use Ada; use Text_Io; procedure What_Fun is begin ...; exception when IO_Exceptions.Name_Error => -- we want this to compile ...; end What_Fun;
Tuck observes that this rule should "apply to all integrated packages, not just integrated package renames, since the same problem comes up when you declare an integrated subpackage, or an integrated generic instance. You really don't want the names of the integrated packages cluttering up the namespace when you do a "use" of the enclosing package, but you do want them when trying to disambiguate where you would be using a complete expanded name."
----
We simply disallow limited views of integrated packages (10.1.1). They are like instantiations. Now that we don't allow the normal view of an integrated package to be named outside of its immediate scope, why would we want to allow naming of the limited view of an integrated package? If we were going to go down that road, it would be better to incorporate (integrate?) integration into limited views, allowing something like
package P is package <Q> is type T is ... ; end Q; end P;
limited with P; package R is type Ref is access P.T; -- illegal end R;
and we have decided against that. It seems fine to say that an integrated package, like an instance, simply does not have a limited view.
----
When resolving the name P.Q, where P denotes a package, declarations with names other than "Q" may be potentially use-visible by selection at the point of the selector_name, but they will never be use-visible by selection at that point. This is necessary because a visible integrated package declared directly in P must be potentially use-visible by selection regardless of the name of the package.
This allows:
package P is package <P1> is Aaa : Integer; package <Inner> is function F return Integer; end Inner; end P1;
package <P2> is package <Inner> is Bbb : Integer; function F return Float; end Inner; end P2; end P;
with P; package Q is X : Float := P.F; -- legal end Q;
At the point of the selector_name, 8 declarations are potentially use-visible by selection:
P.P1, P.P1.Inner, P.P1.Inner.F, P.P2, P.P2.Inner, P.P2.Inner.F, P.P1.Aaa, and P.P2.Bbb
Only 2 declarations are use-visible by selection:
P.P1.Inner.F and P.P2.Inner.F
This means that the word "such" in "more than one such declaration is potentially use-visible" is important.
In this example,
package P is package <Q> is X, Y : Integer; end Q; end P;
with P; package R is Z : Integer renames P.X; end R;
both X and Y are potentially use-visible at the point of the selector_name, but the word "such" implies that only declarations named "X" are to be considered. Thus, the example is legal.
----
The angle brackets of an integrated package are not part of the package's identifier.
In this example,
package P is package <Q> is end Q; end P;
, the name of the nested package is "Q", not "<Q>".
This is why angle brackets are not repeated at the end of a package spec, at the start or end of a package body, or for a package body stub.
--!corrigendum 8.4(3)
!ACATS test
Add ACATS tests for this new feature.
!appendix

From: Steve Baird
Sent: Thursday, January 7, 2010  6:35 PM

Here's a new version reflecting feedback from the St. Petersburg meeting and
subsequent discussions with Randy. [This is a new alternative; version /01 of
that alternative - Editor.]

This proposal includes:
    - new angle-bracket "package <P> is" syntax instead of
      "use package P".
    - effective anonymity of an integrated package outside of
      its immediate scope
    - support for easily jettisoning integrated package renames
      from the AI if it is decided that this is what we want

****************************************************************


Questions? Ask the ACAA Technical Agent