Version 1.4 of ai05s/ai05-0135-1.txt

Unformatted version of ai05s/ai05-0135-1.txt version 1.4
Other versions for file ai05s/ai05-0135-1.txt

!standard 4.1.3(12)          09-02-11 AI05-0135-1/02
!standard 8.4(3)
!standard 8.4(4)
!standard 8.4(8/2)
!standard 8.4(11)
!standard 8.4(18)
!class Amendment 09-01-31
!status work item 09-01-31
!status received 08-12-17
!priority Medium
!difficulty Medium
!subject An extended-scope use clause
!summary
(See proposal.)
!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).
A different, but related, problem has to do with a desire to make more declarations visible via a use_type clause. Given a subtype name (which is often declared in a different declarative region than the first named subtype of the type in question), an existing "use type S;" clause provides use-visibility of the primitive operators of S. It is often the case that more is wanted - use-visibility of other primitive subprograms (notably enumeration literals) and, in the case of a tagged type, subprograms which operate on the corresponding classwide type.
Solving these two problems via two separate mechanisms would introduce too much syntactic complexity. These problems need to be solved via the addition of a single new construct.
!proposal
Define a syntactically new form of use clause by allowing the (optional) reserved word "all", as in
use all Pkg1, Pkg2;
or
use all type T1, T2;
Adding the reserved word "all" gives the use clause the following new effects on visibility:
1) If the use clause occurs in the visible part of a package, then
the items that would be made use-visible by a "normal" use_clause are also made visible as if they were declared in the visible part of the given package. A new form of use visibility, "use-visibility by selection", is defined.
For example,
declare package Pkg1 is X : Integer := 0; end Pkg1;
generic package G is Y : Integer := 0; end G;
package Pkg2 is use all Pkg1; -- makes "Pkg2.X" visible
package I is new G; use all I; -- makes "Pkg2.Y" visible
Z1 : Integer := X + Y; package Inner is X, Y : Float; Z2 : Integer := Pkg2.X = Pkg2.Y; end Inner; end Pkg2;
Ren1 : Integer renames Pkg2.X; Ren2 : Integer renames Pkg2.Y; use Pkg2; Ren3 : Integer renames X; Ren4 : Integer renames Y; begin null; end;
2) In the case of a use_type_clause, the reserved word "all" causes
more subprograms to become use-visible. These include all primitive subprograms of T (not just the primitive operators) and, in the case of a tagged type, the "primitive subprograms" of T'Class (ignoring for purposes of discussion the fact that this notion isn't really defined).
!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.
Modify 8.43-4) to add optional "all" reserved word syntax:
use_package_clause ::= use [all] package_name {, package_name}; use_type_clause ::= use [all] type subtype_mark {, subtype_mark};
Add to the end of 8.4(8/2):
If a use_type_clause includes the reserved word "all", then the following additional declarations are also made potentially use-visible in the same way as is done for the primitive operators of T:
- The visible primitive subprograms of T; - If T is a tagged type declared immediately within a
package_specification, then any visible subprograms that are declared immediately within the same package_specification and operate on T'Class.
Add after 8.4(11) (at the end of the Static Semantics section)
A use_clause which includes the reserved word "all" and which occurs immediately within the visible part of a package is called a reexporting use_clause. The scope of a reexporting use_clause includes the scope of any use_package_clause which names the package immediately enclosing the reexporting use_clause.
At the point of an expanded_name whose prefix denotes a package P (or a rename thereof) the scope of any reexporting use clause which occurs immediately within the visible part of P includes the selector_name of the expanded name. A declaration with the same identifier as that selector_name which is made potentially use-visible in this way is said to be "use-visible by selection" at the point of the selector_name unless
- 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.
The above rules notwithstanding, the scope of a reexporting use_clause never includes any portion of the enclosing package visible part up to and including the use_clause itself.
Add after 8.4(18) (in the Examples section)
generic type T is private; package G is function Make return T; end G;
with G; pragma Elaborate (G); package Pkg1 is type T is null record; package I is new G (T); use all I; end Pkg1;
with Pkg1; package Pkg2 is X : Pkg1.T := Pkg1.Make; use Pkg1; Y : T := Make; end Pkg2;
!discussion
One of the solutions that is currently being discussed for the "declare a private type, use it to instantiate a generic, and export the instance" problem is the "deferred instance body elaboration" proposal.
The original motivation for this "reexporting use_clause" proposal was to make the "deferred instance body elaboration" proposal more attractive by allowing something like
with Some_Container_Generic; package Pkg is type T is private; package I is new Some_Container_Generic (T); use all I; private ... end Pkg;
with Pkg; package Client is X : Pkg.Valise; -- the name Pkg.Valise denotes Pkg.I.Valise; end Client;
That is why the example given in the Example section of the wording involves an instantiation, albeit one that does not involve passing in a non-yet-completed private type as an actual parameter.
The use_type_clause-specific part of the proposal is completely orthogonal to the rest. One could imagine different syntax for this and then having all 4 forms of use_type_clause, as in
use type T; use all type T; use type T with all of interface; -- or whatever use all type T with all of interface;
, but this seems like overkill. The use_type_clause-specific part of this proposal is motivated by a desire to be able to use a subtype name to make more than just the primitive operators of the type use-visible (e.g., enumeration literals).
Consider:
package Pkg1 is type Enum is (Aa, Bb, Cc); function Foo (X : Enum) return Integer; end Pkg;
with Pkg1; package Pkg2 is subtype S is Pkg1.Enum; end Pkg2;
with Pkg2; -- no with of Pkg1 package Pkg3 is use all type Pkg2.S; X : Integer := Foo (Aa); end Pkg3;
The language has no notion of a use_clause (which is not a declaration) being visible or not. The closest thing in the language to visibility for a non-declaration is the streaming attribute availability stuff for limited types, but that's not applicable to use clauses.
This means that we have to be careful in the wording not to open the door for something like
package P is X : Integer;
package Q is Y : Integer renames Q.X; -- illegal use all P; end Q; end P;
and other more convoluted examples.
That's why the wording includes a "notwithstanding" clause. The visible part of Q includes a use all clause which will eventually make the declaration of P.X visible via the name Q.X, but this hasn't happened yet at the point where the name Q.X is being resolved and the wording needs to somehow reflect this.
A "use all" clause affects name resolution of direct names and expanded names.
The legality of
declare package P is X : Integer; end P;
package Q is use all P; end Q;
use Q; Y : Integer renames X; begin null; end;
follows from the rule that the scope of the first use clause includes the scope of the second. This illustrates how the resolution of a direct name may be affected.
The bulk of the wording deals with the case of an expanded name, which is illustrated by the example in the !proposal section.
Note that in resolving an expanded name, we make use of the existing definition of "potentially use-visible", but not that of "use-visible". The definition of "use-visible" does whatever it does in this case, but no language rule depends on the result. The notion of "use visibility by selection" is defined in terms of the set of declarations that are "potentially use-visible", but the set of "use-visible" declarations plays no role here.
Note that the with of a child unit may add a declaration to the visible part of the parent unit and thereby hide a declaration which otherwise would have been use-visible. For example,
package Pkg is Child : Integer; end Pkg;
with Pkg; package Parent is use all Pkg; end Parent;
with Parent; package Client1 is X1 : Integer renames Parent.Child; end Client1;
package Parent.Child is end Parent.Child;
with Parent.Child; package Client2 is package X2 renames Parent.Child; end Client2;
This means that one can never refer to the set of visible declarations declared in a package spec as though it were an immutable set (at least as seen from outside of the package). The wording must always be in terms of the set of declarations that are visible at some particular point in the program. In hindsight this is probably obvious.
!example
(See other sections.)
--!corrigendum 8.4(3)
!ACATS test
Add ACATS tests for this new feature.
!appendix

From: Steve Baird
Date: Wednesday, December 17, 2008  3:30 PM

Tucker Taft wrote in another thread:

 > One thing I think is important is being
 > able to take a type declared within an instantiation
 > and bring it out to the same level as the instantiation:
 >
 >     package T_Vecs is new private Vectors(Element => T, others => private);
 >     type T_Vec is new T_Vecs.Container with null record;
 >

This is one instance of a general problem that comes up in other contexts as
well. We are used to having to introduce a derived type, but this "idiom" is
really a bit of a hack if the only reason for doing this (as opposed to
declaring a subtype) is to avoid having to rename all of the primitive
operations. The derived type is, of course, necessary if we are completing a
private type (completing a private type with a subtype was discussed and
discarded a long time ago).

Within AdaCore there has been some discussion of a generalized renaming
construct of some sort. No consensus regarding syntax has been reached (and it
is not because so many good alternatives have been proposed that the choice is
difficult). For purposes of discussion, I'll go with what Bob Duff describes as
a "string of existing reserved words that more-or-less means the right thing":

    declare renames for <package name>.all;

This construct would be defined to be equivalent to a sequence of rename and
(constraintless) subtype declarations which rename every visible entity exported
from the given package (as seen from the point of the rename declarations). The
simple names of the renames would match those of the renamed entities.

This would allow

     with G;
     package Pkg1 is
       package I is new G ( ... ); -- exports a type I.T;
       declare renames for I.all;
     end Pkg1;

     with Pkg1;
     package Pkg2 is
       X : Pkg1.T;
     end Pkg2;

Typically this construct would occur in a package spec. Thus, the focus of this
construct is very different than that of a use clause. A use clause is
associated with importing whereas this construct is intended primarily for
(re)exporting. The effects of this construct within the scope in which it occurs
would be similar in some ways to that of a use clause, but only because that is
what happens to fall out - that is not an essential part of the design. If it
seemed desirable to emphasize this similarity, one could imagine syntax that
somehow uses the reserved word "use".

Syntax along the lines of
    package <> renames Some_Package;
might emphasize that this is somewhat like declaring a package rename and than
"inlining" it into the enclosing scope. Alternatively, it could be argued that
this syntax makes no sense at all.

There has also been some discussion of a finer-granularity
rename-with-all-the-trimmings construct for a type rather than a package
(somewhat like the relationship between "use" and "use type").
Something like
    subtype S is Some_Package.T with renames; would implicitly declare renames
not only of T, but also of all of its primitive operations which are visible
at this point.

This might be preferable to the package-rename construct because it doesn't
implicitly introduce non-overloadable declarations. In a case like

     type T is ... ;
     type Cursor is ... ;
     package I is new G (...); -- exports a type I.Cursor
     declare renames for I.all;

, the two Cursor declarations would collide. On the other hand, the definition
of "directly visible at this point" might be somewhat more complicated in the
case of a subtype as opposed to a package.

Obviously the type and package rename proposals are orthogonal. Any of the four
combinations (package only, type only, both, neither) could be defined.

Nits:
    1) A named number is "renamed" as
         Foo : constant := The_Package.Foo;

    2) Parameter default values for subprograms are preserved as is
       done for derived subprogram parameters in the case where
       no substitution is needed.

    3) A (desirable) consequence of defining this construct via an
       equivalence rule is that these implicitly declared renames
       are treated as though they were explicitly declared for
       purposes of overload resolution, overriding, etc.

Does this idea seem worth pursuing?

This construct is intended to address one of the deficiencies of the
deferred-instance-body-elaboration proposal relative to the
post-private-visible-part proposal by eliminating the need to declare a derived
type (which introduces freezing problems in some cases with the former
proposal).

I do not want to gratuitously load the language down with new features. In my
opinion, some version of this proposal coupled with the
deferred-instance-body-elaboration proposal would be preferable to the
post-private-visible-part proposal.

Is this issue therefore tied to the "instantiating a generic with a private
type" question, or is it an independent question?

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

From: Tucker Taft
Date: Wednesday, December 17, 2008  4:13 PM

I think the subtype-oriented construct is simpler to deal with (because it
avoids the non-overloadables), and matches pretty closely what Dan Eilers and
others have been suggesting for years.

The "subtype S is P.T with renames;" actually seems pretty intuitive.
Presumably it should work in combination with others, so that there is no
complaint if multiple "renames" occur for the same operation, producing only one
actual rename:

    subtype S1 is P.T1 with renames;
    subtype S2 is P.T2 with renames;

This also addresses the complaints about "use type" which only makes operators
visible:

    subtype Enum is P.Enum with renames;

would make all of the enumeration literals for P.Enum directly visible.

I suspect you are introducing a wicked Beaujolais effect, by the way... ;-)  It
is unclear whether we would want to solve that.  Perhaps the solution is that if
by doing this you end up with a non-overloadable being hidden by an
overloadable, then both are hidden -- essentially the use-clause cancellation
rules.

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

From: Steve Baird
Date: Wednesday, December 17, 2008  5:57 PM

...
> The "subtype S is P.T with renames;" actually seems pretty intuitive.
> Presumably it should work in combination with others, so that there is
> no complaint if multiple "renames" occur for the same operation,
> producing only one actual rename:
>
>    subtype S1 is P.T1 with renames;
>    subtype S2 is P.T2 with renames;
>

There go those simple "equivalence rule" semantics ...
Somehow I'm not surprised.

> This also addresses the complaints about "use type" which only makes
> operators visible:
>
>    subtype Enum is P.Enum with renames;
>
> would make all of the enumeration literals for P.Enum directly
> visible.
>
> I suspect you are introducing a wicked Beaujolais effect, by the
> way... ;-)

You are talking about a situation where adding or deleting the "with renames"
takes you from one legal interpretation to another? It's easy to see how the
resolution of a name could change within the declarative region where the
subtype declaration occurs. Outside of that region, I'm not so sure.

> It is unclear whether we would want to solve that.

Agreed. If you have an uplevel reference and then someone adds a hiding
declaration to an intermediate scope, nobody is surprised when the resolution of
the name changes. How is this any different than a situation like

    package Pkg is
       type T is null record;
      -- function F (X : T := (null record)) return Integer;
    end Pkg;

    package body Pkg is ... ;

    F : constant Integer := 17;
  begin
    declare
       type D is new Pkg.T;
       X : constant Integer := F;
    begin
       null;
    end;

where uncommenting the declaration of the function F changes the resolution of
the use of F in the declare block?

> Perhaps the solution is that if by doing this
> you end up with a non-overloadable being hidden by an overloadable,
> then both are hidden -- essentially the use-clause cancellation rules.

I'd like to see a plausible example where this rule would
prevent something bad from happening.

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

From: Bob Duff
Date: Wednesday, December 17, 2008  6:35 PM

> Does this idea seem worth pursuing?

I've wanted something like this for years.

> I do not want to gratuitously load the language down with new features.
> In my opinion, some version of this proposal coupled with the
> deferred-instance-body-elaboration proposal would be preferable to the
> post-private-visible-part proposal.

I tend to agree.

> Is this issue therefore tied to the "instantiating a generic with a
> private type" question, or is it an independent question?

Independent.

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

From: Bob Duff
Date: Wednesday, December 17, 2008  6:39 PM

> I think the subtype-oriented construct is simpler to deal with
> (because it avoids the non-overloadables), ...

In my opinion, the subtype-oriented one doesn't really solve the problem. I much
prefer the package-oriented version.  This issue is all about visibility, and
has nothing to do with types.

I want to reexport all the stuff in that package from this package. That
includes exceptions, generics, etc -- not just operations (primitive ones?) of
some type.

I think it can be done without Beaujolais effects.  If there's a name clash, all
decls are hidden (or don't get renamed, or some such).

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

From: Bob Duff
Date: Wednesday, December 17, 2008  6:52 PM

> Agreed. If you have an uplevel reference and then someone adds a
> hiding declaration to an intermediate scope, nobody is surprised when
> the resolution of the name changes.

Well, I won't claim to be surprised, but I am annoyed. I consider this to be a
Beaujolais-like effect. In the case of subunits and child units, it's
particularly bad, because it crosses source-file boundaries.

My hobby language doesn't have that sort of implicit hiding, by the way.

Ichbiah came up with a quite clever way of avoiding Beaujolais effects for use
clauses.  I'm a little surprised he didn't apply this idea elsewhere.

>...How is
> this any different than a situation like
>
>     package Pkg is
>        type T is null record;
>       -- function F (X : T := (null record)) return Integer;
>     end Pkg;
>
>     package body Pkg is ... ;
>
>     F : constant Integer := 17;
>   begin
>     declare
>        type D is new Pkg.T;
>        X : constant Integer := F;
>     begin
>        null;
>     end;
>
> where uncommenting the declaration of the function F changes the
> resolution of the use of F in the declare block?

This case is even worse, because there's no F in sight to hide the outer one.
It seems very bad for an implicit decl to hide an explicit one.

This is exactly the same problem that Pascal's 'with' statement has been
criticized for.  When you say, "with SomeRecord do...", a component declared off
in the far boondocks hides a local variable.

I think for the above, if F is uncommented, _both_ F's should be hidden, making
the program illegal, thus avoiding the Beaujolais-like effect.

> Perhaps the solution is that if by doing this
> > you end up with a non-overloadable being hidden by an overloadable,
> > then both are hidden -- essentially the use-clause cancellation
> > rules.
> >
>
> I'd like to see a plausible example where this rule would prevent
> something bad from happening.

In my opinion, you have shown such an example above.  ;-) Just replace type D
with this new package-reexporting gizmo.

By the way, why would you ever want to use this new gizmo in a nested block or
procedure?  Isn't essentially the same as a use clause in that case?

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

From: Randy Brukardt
Date: Wednesday, December 17, 2008  7:47 PM

> >...How is this any different than a situation like
> >
> >     package Pkg is
> >        type T is null record;
> >       -- function F (X : T := (null record)) return Integer;
> >     end Pkg;
> >
> >     package body Pkg is ... ;
> >
> >     F : constant Integer := 17;
> >   begin
> >     declare
> >        type D is new Pkg.T;
> >        X : constant Integer := F;
> >     begin
> >        null;
> >     end;
> >
> > where uncommenting the declaration of the function F changes the
> > resolution of the use of F in the declare block?
>
> This case is even worse, because there's no F in sight to hide the
> outer one.
> It seems very bad for an implicit decl to hide an explicit one.

I agree. This case is a serious bug waiting to happen; it's unfortunate that we
can't make this illegal. Surely we don't want any *more* cases like this.

(This gets to the reason that I dislike derived types as much as I do: you can't
figure out what is getting declared without a fancy tool. It's the reason I hate
use clauses multiplied by 5.)

> This is exactly the same problem that Pascal's 'with'
> statement has been criticized for.  When you say, "with SomeRecord
> do...", a component declared off in the far boondocks hides a local
> variable.
>
> I think for the above, if F is uncommented, _both_ F's should be
> hidden, making the program illegal, thus avoiding the Beaujolais-like
> effect.

Right, although I suspect that the compatibility cost might be too severe to do
it now. For instance, such a rule could very well make instantiating a container
in a nested scope illegal, because of some outer object named "Element" or
"First" or "Last". And I have a lot of objects with names like that...

> > > Perhaps the solution is that if by doing this you end up with a
> > > non-overloadable being hidden by an overloadable, then both are
> > > hidden -- essentially the use-clause cancellation rules.
> > >
> >
> > I'd like to see a plausible example where this rule would prevent
> > something bad from happening.
>
> In my opinion, you have shown such an example above.  ;-) Just replace
> type D with this new package-reexporting gizmo.
>
> By the way, why would you ever want to use this new gizmo in a nested
> block or procedure?  Isn't essentially the same as a use clause in
> that case?

That's interesting: that suggests that this "gizmo" really *is* a use clause --
it's just one that is visible to clients (unlike the normal variety). I wonder
if it would be better to think of it that way (we wouldn't need new visibility
rules then). Something like:
     declare use Pkg;
or (if we were willing to have a new keyword):
     export use Pkg;

and only allow that in a package (or generic package) specification (or just
have it have the same meaning as a usual use clause).

After all, didn't someone say this is all about visibility? Maybe we should say
so (renames are not necessarily about visibility; they're more about
shorthands).

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

From: Steve Baird
Date: Wednesday, December 17, 2008  8:07 PM

> Right, although I suspect that the compatibility cost might be too
> severe to do it now. For instance, such a rule could very well make
> instantiating a container in a nested scope illegal, because of some
> outer object named "Element" or "First" or "Last". And I have a lot of
> objects with names like that...
>

I didn't follow this.
You have

    declare
      Last : Integer;
      package Ii is new Gg; -- exports a function Ii.Last

How does Ii become illegal?

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

From: Randy Brukardt
Date: Wednesday, December 17, 2008  8:45 PM

> > Right, although I suspect that the compatibility cost might be too
> > severe to do it now. For instance, such a rule could very well make
> > instantiating a container in a nested scope illegal, because of some
> > outer object named "Element" or "First" or "Last". And I have a lot
> > of objects with names like that...
> >
>
> I didn't follow this.

I don't follow it either, rereading it. :-)

> You have
>
>     declare
>       Last : Integer;
>       package Ii is new Gg; -- exports a function Ii.Last
>
> How does Ii become illegal?

I was thinking of the derived from contents case, but I botched the explanation
badly.

     declare
       Last : Integer;
       package Ii is new Gg; -- exports a function Ii.Last
       type My_Vector is new Ii.Vector with null record;

This sort of thing is pretty common. (OTOH, I only do it in package
specifications.)

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

From: Steve Baird
Date: Wednesday, December 17, 2008  7:52 PM

> I think for the above, if F is uncommented, _both_ F's should be
> hidden, making the program illegal, thus avoiding the Beaujolais-like effect.
>

It sounds like you don't buy the argument that because Ada already has
constructs with problems that are just as bad as the problems this new construct
would introduce, these new problems are ok.

Fair enough.

>> Perhaps the solution is that if by doing this
>>> you end up with a non-overloadable being hidden by an overloadable,
>>> then both are hidden -- essentially the use-clause cancellation
>>> rules.

It would be odd to introduce a rule like this only for the implicit declarations
associated with this new construct. On the other hand, there wouldn't be any
compatibility issues with such a rule, whereas changing the legality of my
original example would be an incompatible change. Are you arguing for the
incompatible change?

>> I'd like to see a plausible example where this rule would prevent
>> something bad from happening.
>
> In my opinion, you have shown such an example above.  ;-) Just replace
> type D with this new package-reexporting gizmo.

As you point out, any example where this gizmo occurs outside of a package spec
fails the plausibility test. I generally don't like to add language restrictions
that aren't associated with any definitional or implementation issues, so I
would prefer not to see a requirement that this gizmo must occur in a package
spec. Still, such a requirement wouldn't be completely unreasonable.

The example could be modified by adding a package and then we'd have the badness
we are looking for. How bad is this problem?

> By the way, why would you ever want to use this new gizmo in a nested
> block or procedure?  Isn't essentially the same as a use clause in
> that case?

This seems like an advantage of the "subtype S is P.T with renames;" proposal.
It is more generally useful because its semantics differ from those of "use
type" within the scope where the renaming occurs.

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

From: Tucker Taft
Date: Wednesday, December 17, 2008  8:17 PM

> This seems like an advantage of the "subtype S is P.T with renames;"
> proposal. It is more generally useful because its semantics differ
> from those of "use type" within the scope where the renaming occurs.

That was my point.  If you really want the entire package to be visible, then a
"use" clause in the clients isn't terribly painful.  But if you just want the
operations of a single type to be visible, then we don't have any such thing,
except type derivation.  This provides a lighter weight version of type
derivation, which also happens to avoid some of the difficulties of type
derivation, such as the situation where the new derived type is incompatible
with the other things declared in the package, such as generics.  And also the
nasty case where there are two types declared in the package, and deriving from
both produces a useless muddle of operations.

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

From: Steve Baird
Date: Wednesday, December 17, 2008  8:46 PM

Agreed,
I left out an "As Tuck observed,".

It is a little odd that these renaming things (package or type versions) differ
from use clauses within the scope where they are declared with respect to hiding
of declarations from enclosing scopes.

     package P is
        X : Float;
     end P;

     with P;
     procedure P_Client is
       X : Integer;
       type D is new Integer;
       Y : D;
     begin
       declare
         -- use P;
         -- declare renames for P.all;
       begin
         Y := D (X);

If the "use P;" is uncommented, then the operand of the type conversion is still
the Integer-valued X. If the "declare renames for P.all;" is uncommented
instead, then the operand is the Float-valued X.

Perhaps this is an error and the new construct(s) should be defined to be more
consistent with use clauses in this respect. The simplicity of the "it's just
like a bunch of renames" approach is appealing, but it looks like it has
problems.

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

From: Randy Brukardt
Date: Wednesday, December 17, 2008  8:56 PM

> That was my point.  If you really want the entire package to be
> visible, then a "use" clause in the clients isn't terribly painful.

While I understand your point, this argument doesn't fly. Besides the fact that
some people don't like use clauses (one could make the argument that such people
wouldn't use this new construct anyway, for similar reasons), it breaks the
abstraction. If the package is supposed to be exporting a vector of some type,
telling users that "oh, by the way, you'll need to add a use clause for some
subpackage with a random name everywhere that you use our package". And the
reason is because 'it isn't terribly painful'! That could be a lot of extra use
clauses (or a lot of extra and unneeded visibility).

> But if you just want the operations of a single type to be visible,
> then we don't have any such thing, except type derivation.  This
> provides a lighter weight version of type derivation, which also
> happens to avoid some of the difficulties of type derivation, such as
> the situation where the new derived type is incompatible with the
> other things declared in the package, such as generics.

I have sympathy for this position as well. I think *both* of these things are
problems, and *both* deserve solution.

Bob's point is that if your packages are structured well, the two things are
pretty equivalent. But losing exceptions and generics and deferred constants is
annoying. (We had trouble with that in Claw, as iterator generics don't get
inherited by type extensions. They have to be redefined, which is tough to do.)

> And also the nasty case where there are two types declared in the
> package,

> and deriving from both produces a useless muddle of operations.

I don't think it is worth trying to solve this problem; it would be a nasty set
of visibility exceptions. Indeed, this is the best argument against the subtype
case, because I don't even want to think about this (I can't imagine a simple
way to do this in our compiler; all of these operations are declared in the
symbol table, and this would be a case where we're supposed to somehow allow
what is usually illegal. No thanks.)

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

From: Tucker Taft
Date: Wednesday, December 17, 2008  9:11 PM

> While I understand your point, this argument doesn't fly. Besides the
> fact that some people don't like use clauses (one could make the
> argument that such people wouldn't use this new construct anyway, for
> similar reasons), it breaks the abstraction. If the package is
> supposed to be exporting a vector of some type, telling users that
> "oh, by the way, you'll need to add a use clause for some subpackage
> with a random name everywhere that you use our package". And the
> reason is because 'it isn't terribly painful'! That could be a lot of extra
> use clauses (or a lot of extra and unneeded visibility).

I'm afraid I am beginning to lose you.  You may have to give an example.  I
don't know what you mean by "extra and unneeded" visibility.

If you are allergic to use clauses, then you would reference the things that are
*not* "brought out" by using the original subpackage name as a prefix.  If you
like use clauses, then you would do a "use" for the subpackage, presuming you
had already done a "use" for the enclosing package, if you want direct
visibility on generics, etc.  If the construct "brought out" everything from the
subpackage in the first place, then a "use" clause on the enclosing package
would give you direct visibility on all of these things, but that would be
exactly the same set of things you would get by doing the two "use" clauses in
the first scenario.  So I don't see any "extra or unneeded" visibility. It is
exactly the same set of things that are directly visible in the two cases.

> ...
> And also the nasty case where there are two types declared in the
> package,
>
>> and deriving from both produces a useless muddle of operations.
>
> I don't think it is worth trying to solve this problem; it would be a
> nasty set of visibility exceptions. Indeed, this is the best argument
> against the subtype case, because I don't even want to think about
> this (I can't imagine a simple way to do this in our compiler; all of
> these operations are declared in the symbol table, and this would be a
> case where we're supposed to somehow allow what is usually illegal. No
> thanks.)

I don't follow your point here either.  Again perhaps an example would clarify.
I was merely suggesting that if you did two of these "subtype...with renames"
(is that something like "friends ... with benefits"? ;-), you wouldn't get any
complaints, because any shared operations would only be effectively renamed once
into the scope.  That is,

    subtype S1 is P.T1 with renames;
    subtype S2 is P.T2 with renames;

would not complain if there is an operation in P that is a primitive of both T1
and T2.  You wouldn't get two renames of it that somehow clobber each other.

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

From: Randy Brukardt
Date: Wednesday, December 17, 2008 10:40 PM

...
> I'm afraid I am beginning to lose you.  You may have to give an
> example.  I don't know what you mean by "extra and unneeded"
> visibility.
>
> If you are allergic to use clauses, then you would reference the
> things that are *not* "brought out" by using the original subpackage
> name as a prefix.  If you like use clauses, then you would do a "use"
> for the subpackage, presuming you had already done a "use" for the
> enclosing package, if you want direct visibility on generics, etc.  If
> the construct "brought out" everything from the subpackage in the
> first place, then a "use" clause on the enclosing package would give
> you direct visibility on all of these things, but that would be
> exactly the same set of things you would get by doing the two "use"
> clauses in the first scenario.  So I don't see any "extra or unneeded"
> visibility.
> It is exactly the same set of things that are directly visible in the
> two cases.

You aren't making any sense at all. "everywhere" is a lot of places; if you
limit the scope of use clauses, you're going to have to put them in every place
that you would otherwise use a single use clause. (I typically use them, if I
use them at all, within blocks or subprograms.) Or (because you have a bunch of
them rather than just one) you just have to give up and put them at the top
level, but that is giving you "extra and unneeded" visibility.

Having to write two things in place of one logical thing might not be "terribly
painful" if you only have to do that in one place. If you have to do it in
dozens, it gets messy.

To see what I'm talking about, imagine that you have the following library
package that should be logically viewed as one item:

    package Pack is
        type T is ...;
        -- Operations on T.
        -- Now, define "file of T" as part of this package:
        package T_IO is new Ada.Sequential_IO (T);
        -- declare use T_IO; -- Or whatever syntax you want for a package visibility.
        -- subtype File_Type is T_IO.File_Type with renames; -- Or whatever syntax you want for subtype visibility.
    end Pack;

(I used Sequential_IO here because it has exceptions, but if you don't like
that, imagine any package that declares its own exceptions.) Note that T_IO is a
junk name that exists solely because Ada requires one here; we just want these
entities to be available. That same is potentially true of the subtype name --
we probably don't want to give it a different name.

    with Pack;
    procedure Something is
        A : Pack.T_IO.File_Type; -- Full name. Definitely breaks the abstraction of "file of T" being part of Pack.
        use Pack;
        B : T_IO.File_Type; -- Still breaks the abstraction.
        use Pack, Pack.T_IO;
        C : File_Type; -- The declaration is OK now, but the *use clause* breaks the abstraction.
    begin
        ...
    exception
        when Use_Error => ... -- Only works with the third use clause.
    end;

-- Now let's imagine that we use the package renaming scheme:
    with Pack;
    procedure Something is
        A : Pack.File_Type; -- Full name. Abstraction is fine.
        use Pack;
        B : File_Type; -- Abstraction is fine here, too.
    begin
        ...
    exception
        when Use_Error => ... -- Abstraction is fine here, three.
    end;

-- But with the subtype renaming scheme, it isn't quite as perfect:
    with Pack;
    procedure Something is
        A : Pack.File_Type; -- Full name of subtype. Abstraction is fine.
        use Pack;
        B : File_Type; -- Abstraction is fine here, too.
    begin
        ...
    exception
        when Use_Error => ... -- Nope, no good. Have to go back to T_IO
                              -- as in T_IO.Use_Error. Grrr.
    end;

...
> I don't follow your point here either.  Again perhaps an example would
> clarify.  I was merely suggesting that if you did two of these
> "subtype...with renames" (is that something like "friends...
> with benefits"? ;-), you wouldn't get any complaints, because any
> shared operations would only be effectively renamed once into the
> scope.  That is,
>
>     subtype S1 is P.T1 with renames;
>     subtype S2 is P.T2 with renames;
>
> would not complain if there is an operation in P that is a primitive
> of both T1 and T2.  You wouldn't get two renames of it that somehow
> clobber each other.

What the heck is a "shared operation" in language terms?? And how would you
implement such a thing? You can't just search for homographs, as they could come
up in other ways. What happens if someone writes:

     type T1 is new P.T1;
     subtype S2 is P.T2 with renames;

I would expect that you'd still get the mess of crossover operations; how would
the compiler tell that they're conflicting?

Do you really mean that the presence of some other declaration pages away
changes what's declared here? Is there any way to implement that without going
back through all of the previously generated symbols and trying to eliminate
extra ones? (We don't do that for anything else, so doing that would be weird
and expensive.) There are thousands of such symbols in OOP programs (such as
children of Claw), that's not going to be cheap. (And doing it the other way, by
name is no better, because there are often thousands of the various operator
symbols in a program.)

The package idea has no such problems; it's nothing but an inherited use clause
which means it's unlikely that there are many semantic issues with it.

Besides, I don't see much reason to encourage giant messy packages (even if I
write them from time-to-time). If you stick to one type to one package, the two
ideas are equivalent, but the package one is easier to describe and doesn't have
funny cases.

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

From: Tucker Taft
Date: Wednesday, December 17, 2008 11:38 PM

> What the heck is a "shared operation" in language terms?? And how
> would you implement such a thing? You can't just search for
> homographs, as they could come up in other ways.

I didn't mean anything sophisticated here.  All I meant was that if you used the
"subtype ... with renames" twice from the same package in the same scope, it
wouldn't end up creating two renames for the same operation.  By a "shared"
operation I mean one that is a primitive on both types.


> ...
> What happens if someone writes:
>
>      type T1 is new P.T1;
>      subtype S2 is P.T2 with renames;
>
> I would expect that you'd still get the mess of crossover operations;
> how would the compiler tell that they're conflicting?

Yes, this would still produce a bit of a mess.  I wasn't suggesting to fix this.
Merely to say that if we provide this "subtype ... with renames," it shouldn't
cause conflicts with other subtype-with-renames from the same package which are
given in the same scope.

> Do you really mean that the presence of some other declaration pages
> away changes what's declared here? ...

No.

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

From: Jean-Pierre Rosen
Date: Thursday, December 18, 2008  3:03 AM

> That's interesting: that suggests that this "gizmo" really *is* a use
> clause
> -- it's just one that is visible to clients (unlike the normal
> variety). I wonder if it would be better to think of it that way (we
> wouldn't need new visibility rules then). Something like:
>      declare use Pkg;
> or (if we were willing to have a new keyword):
>      export use Pkg;
>
> and only allow that in a package (or generic package) specification
> (or just have it have the same meaning as a usual use clause).
>

Alternatively, why not:
    pragma Inline (Pkg);

which would declare everything from the package directly in the enclosing
construct. Could be applied to an instantiation, too....

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

From: Ed Schonberg
Date: Thursday, December 17, 2008  4:13 PM

This is confusing, it might mean that all subprograms within the package are to
be inlined. However, the point is that we would want to associate this with  an
instance, so why not label the instance accordingly? Randy does not want some
rule that declares that the new renaming construct should only appear in a
restricted context, and I tend to agree.  So let's use some new syntax on the
instantiation itself:

     package V is new open G (X);   -- your favorite new keyword here.

Indicating that all the entities in G are available in the enclosing context,
with the same name. It seems confusing to relate this to a use-clause:  the
use-clause affects  locally the visibility of entities declared elsewhere, while
here we want to create exportable views of those entities.

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

From: Bob Duff
Date: Thursday, December 18, 2008  8:47 AM

> That was my point.  If you really want the entire package to be
> visible, then a "use" clause in the clients isn't terribly painful.

I think it is somewhat painful.

I agree with what Randy said on this point.

You haven't specified exactly what declarations this subtype-based idea brings
out.  By analogy with type derivation, I assume you mean primitive operations of
the type.  But surely you want class-wide ops as well.

You really want everything related to the type.  For example, you want
exceptions that are raised by primitive operations of the type.

My point is that "everything related to the type" is "everything in the package
visible part" -- presuming the package is properly designed.

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

From: Bob Duff
Date: Wednesday, December 17, 2008  8:47 AM

> ...Are you arguing > for the incompatible change?

I am not.

I guess (after seeing Randy's point of view) that I am saying we don't want this
new feature to have the same problems of confusing hiding.  And thinking of it
as a "transitive use clause" is the right direction in that regard.

One possible syntax is "use Pack_Name.all;".

> As you point out, any example where this gizmo occurs outside of a
> package spec fails the plausibility test. I generally don't like to
> add language restrictions that aren't associated with any definitional
> or implementation issues, so I would prefer not to see a requirement
> that this gizmo must occur in a package spec.
> Still, such a requirement wouldn't be completely unreasonable.

I agree 100% with the above wishy-washy opinion.  ;-)

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

From: Bob Duff
Date: Thursday, December 18, 2008  9:37 AM

>   So let's use some new syntax on the instantiation
> itself:
>
>      package V is new open G (X);   -- your favorite new keyword here.

I think the new gizmo Steve has proposed is not specific to instances.

As I see it, it would allow you have an abstraction that is viewed as a single
"thing" by clients, but is structured as many packages internally.  The idea is
that the client doesn't need to know about the many packages -- it just refers
to one package that collects it all together.

Some of those packages might be instances.  Some might be physically nested,
others children.  You might rearrange their structure from time to time, while
presenting an unchanging/compatible view to clients.

There might be some "interesting" (in a good way) interactions with
limited-with's, but I'm too busy to think about that right now...

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

From: Bob Duff
Date: Thursday, December 18, 2008  9:38 AM

>      package V is new open G (X);   -- your favorite new keyword here.

If that keyword is reserved, Text_IO becomes illegal.  ;-)

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

From: Tucker Taft
Date: Thursday, December 18, 2008  12:27 PM

I think we should make it a "use" clause, but with some kind of extra
indication.  I don't like ".all" as that automatically conjures up thoughts of
access values, which seems completely unrelated to the situation.  I could see:

    use P with renames;

and presumably:

    use type T with renames; -- makes operators transitively visible

If there is interest in having direct visibility on all primitive operations
(and perhaps class-wide as well, though that would take some more thought), then
probably we should just define a new kind of "use" clause, such as:

    use all type T [with renames];
       -- makes all primitives (and classwides) directly visible,
       -- transitively so if "with renames" included.

One interesting question is whether the primitives of a tagged type remain
dispatching after such a transitive use clause.  Clearly they do with a normal
use clause, but they don't with a normal rename.

Another possible syntax:

    use P for all;

That is the "use P" is effectively exported "for all" contexts where the direct
components of P are visible.

Or:

     use and declare P;

     use P and declare;

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

From: Steve Baird
Date: Thursday, December 18, 2008 12:38 PM

>     package V is new open G (X);   -- your favorite new keyword here.

My gut feeling is that restricting this new mechanism to instances does not seem
like a good idea. Ditto for the general idea of associating this new visibility
property with the declaration of the entity itself (as opposed to introducing a
separate rename/use-clause-like construct). If pressed, I will try to justify
this position.

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

From: Bob Duff
Date: Friday, December 19, 2008 10:12 AM

OK, I'd like to hear your justification.  I mean, for the part starting
"Ditto..." -- I definitely agree with the previous part, but I'm not sure about
the "Ditto...".

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

From: Steve Baird
Date: Friday, December 19, 2008  4:42 PM

I was thinking about the case of a package spec consisting of nothing but
expressionless renames, constraintless subtypes, named numbers initialized to
other named numbers, etc.

This, for example, might reflect a (static) implementation choice, as in

    with Task_Safe_Foo;
    package Foo is
        procedure Bar renames Task_Safe_Foo.Bar;
        ...;
    end Foo;

or a restricted subset of a more general interface

     with Foo;
     package ReadOnly_Foo is
        subtype T is Foo.T;
        function Bar_Selector (X : T) return Integer renames
          Foo.Bar_Selector;
        -- no mention here of the corresponding procedure
        -- for assigning to the Bar attribute of a Foo.T
     end ReadOnly_Foo;

or whatever.

Suppose that we want to rename something like Ed's example,

    package V is new open G (X);   -- your favorite new keyword here.

Choices include:

    1) A rename loses this special visibility property (just as a rename
       of a primitive operation can't be called via a dispatching call).
       This will make problems either for whoever has to write/maintain
       the ReadOnly_Foo spec (because they would have to declare
       explicit renames corresponding to those that are implicitly
       declared in Foo) or for the clients (if these explicit renames
       are omitted from the ReadOnly_Foo spec).
       I would view this as a bad thing.

    2) A rename preserves this special visibility property.
       This means we have lost the ability to generate a "simple"
       rename in the case where that is all we want.
       This makes me nervous.
       This would also confuse the semantic equivalence between
       renames and actual-to-formal bindings for instantiations.
       Finally, consider a generic package that exports a rename
       of a formal instance;  I suppose the set of declarations that
       are exported depends on the actual parameter.
       This all seems a bit complicated.

    3) The rename itself gets new optional syntax so that the choice can
       go either way:

           package P renames Q [and all in P with out exception];

       This pretty well takes us back to where we started. If we have
       to support this case, then why do we need anything else?

As another point, consider the interaction with more nested packages.
With the rename/use-clause approach, the following is straightforward:

    package Pkg is
       package Inner is
          package I is new G;
       end Inner;

       use or rename or somehow reexport Inner.I;
    end Pkg;

It is not at all clear how to accomplish the same thing using the

      package I is new G and it glows in the dark;

approach of including the specification of the special visibility property in
the declaration of the package itself. The significance of this is, of course,
open to debate.

Does any of this seem convincing?

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

From: Bob Duff
Date: Friday, December 19, 2008 10:10 AM

> I think we should make it a "use" clause, but with some kind of extra
> indication.  I don't like ".all"
> as that automatically conjures up thoughts of access values, which
> seems completely unrelated to the situation.

Shrug.  That doesn't bother me.

By the way, I'm a huge fan of compatibility, but I'm not allergic to new
reserved words -- that's just about the least harmful kind of incompatibility.

>...I could see:
>
>     use P with renames;
>
> and presumably:
>
>     use type T with renames; -- makes operators transitively visible

Yes, the use-type version seems useful, now that you mention it.

The above syntax seems tolerable, but "use all [type] X;"
seems nicer (not meaning what you say below).

> If there is interest in having direct visibility on all primitive
> operations (and perhaps class-wide as well, though that would take
> some more thought),

I don't see any use in that.  A typical package spec has 1 type, 15 subprograms,
1 exception, and 1 generic.  Why would I want to import all but 2 of those
things (or all but 4, if you leave out the class-wide ops)?

> One interesting question is whether the primitives of a tagged type
> remain dispatching after such a transitive use clause.

The correct answer is "yes".

>...  Clearly they do with a normal use clause,  but they don't with a
>normal rename.

> Another possible syntax:
>
>     use P for all;
>
> That is the "use P" is effectively exported "for all" contexts where
> the direct components of P are visible.
>
> Or:
>
>      use and declare P;
>
>      use P and declare;

All of the above tolerable.

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

From: Ed Schonberg
Date: Friday, December 19, 2008  4:19 PM

> The above syntax seems tolerable, but "use all [type] X;"
> seems nicer (not meaning what you say below).

I also find "use all type P.T"  clear and concise. But I'm confused about the
semantics of the construct. A use clause makes the entities potentially visible
in the current context, nothing else. I thought that we wanted this construct to
also create a local entity that is visible outside of the current package.
That's certainly the effect of the  null derivation that we are trying to
replace.  I don't see how this new-fangled use-clause achieves this purpose.

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

From: Randy Brukardt
Date: Friday, December 19, 2008  4:53 PM

The idea is that the use clause is effective in clients of the package as well,
such that:

    package P is
       ...
       package Vect is new Ada.Containers.Vectors (Some_Type);
       use all Vect;
    end P;

    with P;
    procedure One is
        A : P.Vector; -- The selection lets us see entities that are
                      -- use-all-visible in P, as well as real declarations.
    begin
        null;
    end One;

    with P; use P;
    procedure Two is
        A : Vector; -- The "use P" also makes things that are use-all-visible in P visible.
    begin
        null;
    end Two;

And other rules (in particular conflicting declarations) are handled as they are
for use clauses.

This is a pretty mimimal change to the language (how minimal to compilers I
don't know and haven't thought about). The only thing that is truly new here is
the selection followed by a use-all-visible item (the other way of course
already happens). I recall that we had to work hard to disallow transitive
use-clauses -- I think it is more natural for them to be transitive (depending
on how you implement them), so I don't expect that part to be hard.

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

From: Ed Schonberg
Date: Friday, December 19, 2008  5:08 PM

OK, that's a very clear formulation, the "all" marks the use clause as
transitive.  I don't think the implementation is too complex, and the
description has the great virtue of conciseness.  I suppose that "use all" can
be a context item as well?

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

From: Tucker Taft
Date: Friday, December 19, 2008  5:28 PM

> I also find "use all type P.T"  clear and concise. But I'm confused
> about the semantics of the construct. A use clause makes the entities
> potentially visible in the current context, nothing else. I thought
> that we wanted this construct to also create a local entity that is
> visible outside of the current package.  That's certainly the effect
> of the null derivation that we are trying to replace.  I don't see how
> this new-fangled use-clause achieves this purpose.

I think we have two separate threads going here, and I am afraid I am
responsible for the confusion.  I have seen a desire for some kind of "use"
clause that makes enumeration literals directly visible.  "use type" only makes
operators visible, so I was suggesting something like "use all type" to pick up
enumeration literals, and any other primitive subprograms.

A relatively independent thread is one about creating "transitive" use clauses,
or "reexporting" or whatever you want to call them.  They work sort of like a
bunch of renames, but I think we were concluding that they may need to have some
special rules to deal with Beaujolais effects.  One suggestion was "use P with
renames" for a transitive "use" package clause.  Once you establish this as
merely a special "tweak" on a "use" clause, then it makes sense to generalize it
to any kind of use clause, such as "use type P.T with renames;"

I have confused the situation by talking about both at the same time.  Sorry
about that.  Bob added a bit to the confusion by expressing a preference for the
syntax "use all type P.T" to mean what I suggested to be "use type P.T with
renames."  I don't understand why "all" communicates transitivity to Bob, so
I'll have to let him explain that.

So we have two relatively separate ideas:

    Add a new kind of "use" clause oriented toward
    getting direct visibility on enumeration literals
    (and other primitives).

    Define a syntax that can be used with any kind of
    "use" clause to make it transitive, so that the
    entities made directly visible at the point of
    the use clause are also visible in the extended
    scope of the enclosing package spec (a transitive
    use clause would presumably be no different from
    a non-transitive one when not in a package spec).

I don't know if that clarifies or confuses, perhaps both?  ... ;-)

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

From: Tucker Taft
Date: Friday, December 19, 2008  5:30 PM

I don't find "use all" an intuitive syntax for transitivity.  I could understand
"all use" perhaps... ;-)

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

From: Bob Duff
Date: Friday, December 19, 2008  5:31 PM

...
> And other rules (in particular conflicting declarations) are handled
> as they are for use clauses.

Yes, that matches my understanding of the idea.

But I think the wording will be tricky to get right.

So there needs to be an implicit decl of Vector in P, so P.Vector makes sense.
But the "conflicting declarations" issue is interesting.

I think you want to be able to say:

    package P is
       ...
       package Int_Vect is new Ada.Containers.Vectors (Integer);
       use all Int_Vect;
       subtype Int_Vector is Int_Vect.Vector;

       package Boolean_Vect is new Ada.Containers.Vectors (Boolean);
       use all Boolean_Vect;
       subtype Boolean_Vector is Boolean_Vect.Vector;

    end P;

and then P.Vector in clients should be illegal.
But package P itself should be OK.
And P.Append is just overloaded.

> This is a pretty mimimal change to the language (how minimal to
> compilers I don't know and haven't thought about). The only thing that
> is truly new here is the selection followed by a use-all-visible item
> (the other way of course already happens). I recall that we had to
> work hard to disallow transitive use-clauses -- I think it is more
> natural for them to be transitive (depending on how you implement
> them), so I don't expect that part to be hard.

And in message that came in while I was composing the above, Ed wrote:

>   I suppose that "use
> all" can be a context item as well?

Sure.  Why not?

Are there "interesting" interactions with "limited with"?

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

From: Steve Baird
Date: Friday, December 19, 2008  9:32 PM

> Are there "interesting" interactions with "limited with"?

I would think these new constructs would be ignored in a limited view, just a
use clauses and renames are already ignored.

The case where the construct does not occur in a limited view but references a
limited view is more interesting. In this example

    package P is
       type T is  ... ;
       procedure Foo;
       package Inner is
         type TT is ... ;
         procedure Bax;
       end Inner;
       package I is new G;
    end P;

    limited with P;
    package Q is
      use all P; -- whatever the syntax is
    end Q;

the use clause only allows clients of Q to name Q.T and Q.Inner, right?
It doesn't matter whether the client can see a non-limited view of P.

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

From: Randy Brukardt
Date: Friday, December 19, 2008  9:54 PM

Ummm, use_clauses are illegal for limited views (8.4(5/2)). I'd surely expect
that property to carry over, and thus the "use all" would be illegal here. So
that's not very interesting, either.

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

From: Steve Baird
Date: Saturday, December 20, 2008  1:56 AM

Good point.

I guess I'm still thinking of these things as being like renames, not use
clauses. The use clause model is looking better and better.

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

From: Brad Moore
Date: Sunday, December 21, 2008  1:06 PM

A very interesting thread. I like the overall intent. I see this being useful as
it is a problem I have run into often enough.

I am wondering it there are some other cases worth considering. In particular, I
am thinking it would be nice to be able to use this renaming concept to simplify
access to a protected object or a task object exported from a generic.

For example, suppose I have;

generic
   type F is private;
package G is

   protected P  is
      procedure Foo;
      function Bar returns Integer;
   private
      X : F;
   end Driver;
end G;


with G;
package Pkg1 is
   type T is null record;
   package I is new G (T);
   declare renames for I.P; -- Or whatever syntax works best end Pkg1;

with Pkg1;
package Pkg2 is
 Pkg1.Foo;   -- Really calling Pkg1.I.P.Foo
end Pkg2;

I didn't recall seeing the ability to do this in the thread so far.
Please accept my apology if this is already covered.

This is actually something I could have used to enhance the example I just
provided in my first email under the subject of private generic actuals.

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

From: Brad Moore
Date: Friday, January 9, 2009  12:29 AM

This email actually applies to both AI05-0074 and this thread.

One of the problems that AI05-0074 is trying to solve is being able to
instantiate a generic in the visible part of a package using a private type.

The quoted example is:

"generic
   type Elem is private;
   with function Hash(El : Elem) return Integer; package Hashed_Sets is

   type Set is private;
   function Union(Left, Right : Set) return Set;
   ...

   package Signature is new Set_Signature(Elem, Set); private
   type Set is record ... end record;
end Hashed_Sets;

The problem is that we can't do the instantiation of Set_Signature where we
would want to do so, because the instantiation freezes the type "Set"
prematurely."

The AI states that the there is a workaround in Ada 2005, which is stated as;

"Ada 2005 provides solutions to these problems, but they are not ideal. For the
first example, making the instantiation a child unit solves the problem.
However, this is annoying, because accessing the instance requires an extra with
and instantiation (since children of generics must be generic):

generic
   type Elem is private;
   with function Hash (El : Elem) return Integer; package Hashed_Sets is
   type Set is private;
   function Union(Left, Right : Set) return Set;
   ...
private
   type Set is record ... end record;
end Hashed_Sets;

generic
package.Hashed_Sets.Signature is
   package The_Signature is new Set_Signature(Elem, Set); end Hashed_Sets.Signature;

A user of Hashed_Sets must with and instantiate Hashed_Sets.Signature in order
to access the instance. "

It seems to me that a better work around does exist, which involves the use of
nested packages.

generic
   type Elem is private;
   with function Hash (El : Elem) return Integer; package Hashed_Sets is
   package Sets is
      type Set is private;
      function Union(Left, Right : Set) return Set;
   private
      type Set is record ... end record;
   end Sets;

   type Set is new Sets.Set;

   package The_Signature is new Set_Signature(Elem, Set);

end Hashed_Sets;

No messy child packages to deal with. It all happens in one place. This is
somewhat like the end private alternative, in that a second visible part occurs
after the end of a private section, except that its available today in Ada 2005.

This should work if it is possible to derive a new type from the private type.
If you can't do the derivation, for example because the type is a protected
type, then you can declare a subtype instead, but then you run into the renaming
issue. eg

generic
   type Elem is private;
   with function Hash (El : Elem) return Integer; package Hashed_Sets is
   package Sets is
      type Set is private;
      function Union(Left, Right : Set) return Set;
   private
      type Set is record ... end record;
   end Sets;

   subtype Set is Sets.Set;
   function Union (Left, Right : Set) return Set renames Sets.Union;
   ...

   package The_Signature is new Set_Signature(Elem, Set);

end Hashed_Sets;

Now here is where this email thread kicks in. Hopefully all of the renames could
be replaced by one of these new super-duper rename clauses. This sort of feels
to me like a better work around to the problem than a solution, but others may
feel differently.

Comments?

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

From: Brad Moore
Date: Tuesday, January 20, 2009 10:39 PM

> I think you want to be able to say:
>
>    package P is
>       ...
>       package Int_Vect is new Ada.Containers.Vectors (Integer);
>       use all Int_Vect;
>       subtype Int_Vector is Int_Vect.Vector;
>
>       package Boolean_Vect is new Ada.Containers.Vectors (Boolean);
>       use all Boolean_Vect;
>       subtype Boolean_Vector is Boolean_Vect.Vector;
>
>    end P;
>
> and then P.Vector in clients should be illegal.
> But package P itself should be OK.
> And P.Append is just overloaded.

First I have a question, then I will describe a problem that I can see, then I
have a suggestion for how it might be possible to address/improve the situation.

First the question:

Will it be possible to complete an incomplete type with a "use all"
statement?

e.g.

   -- A generic implementing a protected interface
   private with Protected_Queue;

   package Pkg is
      type T is private;
   private
      type Queue_Type;
      type T is
        record
          Queue : access Queue_Type;
        end record;
      package I is new Protected_Queue (T);
      use all I;
      --  Queue_Type is exported from package I, and completes the
      --  incomplete type declaration.

      -- Can't derive a new type here, i.e.,
      -- type Queue_Type is new I.Queue with null record;
      -- ERROR Cannot derive a new type from a protected type or
      -- interface.
   end Pkg;

Without this capability this is a problem without any workaround that I can see.
You cannot derive from a synchronized tagged type, for example. You cannot use a
subtype, because the incomplete type needs a completing type declaration, not a
subtype declaration. Yet this seems like something users could run into easily
enough. Suppose that type T has a task component that writes to the queue. Some
sort of concurrency support is needed. One option might be to allow synchronized
tagged types to be extended, but I don't feel that is the correct solution to
this particular problem.

We don't want to have to derive a new type here, we just want to be able to use
the one we created with the instantiation.

Assuming the use all can complete the incomplete type, this solves the problem
above, but now suppose there are two difference instantiations of the same
generic package declared and referenced in type T. The name collisions would be
a problem, and you wouldn't be able to have two incomplete type declarations
with the same name. Once again, subtype declarations wouldn't help you here. The
problem once again becomes unsolvable.

   -- A generic implementing a protected interface
   -- Similar to Ada.Containers.Hashed_Maps, but a protected type
   private with Protected_Hashed_Map;

   package Pkg is
      type T is private;
   private
      type Map;  -- Need an incomplete type for the Integer Map type
      type Map;  -- Need an incomplete type for the float Map type
                 -- Cant use the same name for both!
      type T is
        record
          Integer_Map : access Map_Type;
          Float_Map : access Map_Type;
        end record;
      package I is new Protected_Hashed_Map
         (Key => T, Element => Integer, ...);
      use all I;
      package J is new Protected_Hashed_Map
        (Key => T, Element => Float, ...);
      use all J;
      -- Broken
   end Pkg;

Both of these examples seems to want something different than what the "use all"
statement would provide here. The use all enabled us to get rid of having to
derive a new type. That's an improvement. But we still are being forced to
export the entire instantiated packages into the main package. Don't get me
wrong, the use all clause is a great idea, and I can see it having many uses,
but in this particular case it seems to be overkill for what we are trying to
achieve. All we really want here is to just be able to use the types that have
been instantiated.

Instead of trying to bring the water to the horse, what if we instead tried to
bring the horse to the water?

The idea here is to be able to declare what I would call a scoped incomplete
type, for lack of a better term. The incomplete type declaration could
optionally include package name qualifiers, which indicates that the declaration
will be completed by a type declaration in a matching named scope.

eg. Something like;

incomplete_type_declaration
  ::= TYPE {[defining_program_unit_name].}defining_identifier
           [discriminant_part] [IS TAGGED];

Then the above example could be fixed to look like;

 -- A Map abstraction implementing a protected interface
   with Protected_Hashed_Map;

   package Pkg is
      type T is private;
   private
      type I.Map;  -- Need an incomplete type for the Integer Map type
      type J.Map;  -- Need an incomplete type for the float Map type
                 -- Now we have a unique name for both incomplete types
      type T is
        record
          Integer_Map : access I.Map;
          Float_Map : access J.Map;
        end record;
      package I is new Protected_Hashed_Map
        (Key => T, Element => Integer, ...);
      package J is new Protected_Hashed_Map
        (Key => T, Element => Float, ...);
      -- I.MAP is completed by the I instantiation
      -- J.MAP is completed by the J instantiation
   end Pkg;

In this case, there would be no need to use a "use all" statement.

I don't see this as a replacement for use all, just as an extra tool that can be
used in addition to the "use all" tool. If such a tool existed I would have also
used it in the first example with the protected queue rather than use a use all
statement.

Does this idea seem like it would be worth pursuing?
Maybe if this feature existed, we wouldn't need to worry about having to have
the use all feature be able to complete an incomplete type declaration.

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

From: Brad Moore
Date: Tuesday, January 20, 2009  11:02 PM

...
> First the question:
>
> Will it be possible to complete an incomplete type with a "use all"
> statement?

A use clause doesn't complete anything, and the model as I see it is that this
is just a super use clause. So I surely would not expect it to complete
anything.

...
>    -- A generic implementing a protected interface
>    private with Protected_Queue;
>
>    package Pkg is
>       type T is private;
>    private
>       type Queue_Type;
>       type T is
>         record
>           Queue : access Queue_Type;
>         end record;
>       package I is new Protected_Queue (T);
>       use all I;
>       --  Queue_Type is exported from package I, and completes the
>       --  incomplete type declaration.
>
>       -- Can't derive a new type here, i.e.,
>       -- type Queue_Type is new I.Queue with null record;
>       -- ERROR Cannot derive a new type from a protected type or
>       -- interface.
>    end Pkg;
>
> Without this capability this is a problem without any workaround that
> I can see. You cannot derive from a synchronized tagged type, for
> example.
> You cannot use a subtype, because the incomplete type needs a
> completing type declaration, not a subtype declaration.

Why not put the instantiation first (I'm presuming that we find a way to do
this, it is too fundamental to container usage to not solve it somehow - without
it, we can't have real iterators or container interfaces). Using my limited
instantiation idea, for instance:

    package Pkg is
       type T is private;
    private
       package I is limited new Protected_Queue (T);
       type T is
         record
           Queue : access I.Queue_Type;
         end record;
       package I is new Protected_Queue (T);
    end Pkg;

(I took the use clause out 'cause I don't like them much, but you could leave it
in, of course.)

Whatever solution we come up with ought to be able to do this.

...
> Does this idea seem like it would be worth pursuing?
> Maybe if this feature existed, we wouldn't need to worry about having
> to have the use all feature be able to complete an incomplete type
> declaration.

It doesn't seem necessary, assuming we solve the basic problem. And there are
all kinds of traps waiting if we try to expand the meaning of incomplete types
-- we had a lot of places where ugly cases couldn't happen because everything
has to occur in the same scope.

Your examples here do tell me one thing, however: the workarounds to fixing the
instance problem directly are never going to handle all of the cases. (The
problem is, that all of the instance problem solutions don't handle all of the
cases, either.) We really have to solve the AI05-0074-x problem somehow.

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

From: Brad Moore
Date: Wednesday, January 21, 2009 10:34 AM

> Whatever solution we come up with ought to be able to do this.

I agree.

> Your examples here do tell me one thing, however: the workarounds to
> fixing the instance problem directly are never going to handle all of
> the cases. (The problem is, that all of the instance problem solutions
> don't handle all of the cases, either.) We really have to solve the
> AI05-0074-x problem somehow.

Note also, the second example using the hashed map generic is a bigger problem
than just for synchronized tagged types. It applies to any types. You would have
the same problem if you actually tried to do the same thing with the existing
Ada.Containers.Hashed_Maps container. This second case is really a problem that
appears to have existed all along since the dawn of Ada. Though the fact that it
is being reported now seems to suggest that there are not a lot of cases of
people running into this particular problem. However, the problem is exacerbated
with Ada 2005 since we now have containers such as the Hashed Map container,
making it more likely that people could run into the problem. It may be that in
the past, people just shrugged their shoulders, and redesigned their software to
work some other way.


The private instantiation proposal solves these problems, but it currently has
to straddle the visible/private boundary.

e.g.

   private with Ada.Containers.Hashed_Maps;
   package Pkg is
      type T is private;

      package I is new private Ada.Containers.Hashed_Maps
         (others => private);
      package J is new private Ada.Containers.Hashed_Maps
         (others => private);

   private
      type T is
        record
          Integer_Map : access I.Map;
          Float_Map : access J.Map;
        end record;
      package I is new Ada.Containers.Hashed_Maps
        (Key => T, Element => Integer, ...);
      package J is new Ada.Containers.Hashed_Maps
        (Key => T, Element => Float, ...);
   end Pkg;

I believe my proposal can be reworked fairly easily with only fairly minor
alterations so that it is not restricted to crossing the visible/private
boundary.

The syntax would be modified to change an occurrence of the word "private" to
"limited", as in.


limited_instantiation_declaration ::=
   PACKAGE defining_program_unit_name IS
         NEW LIMITED generic_package_name [deferred_generic_actual_part];

deferred_generic_actual_part ::=
   (deferred_generic_association {, deferred_generic_association})

deferred_generic_association ::=
   [generic_formal_parameter_selector_name =>] explicit_generic_actual_parameter  |  [generic_formal_parameter_selector_name =>] PRIVATE  | others => PRIVATE

The (OTHERS => PRIVATE) syntax would remain intact, but could only be used in
cases where the instantiation straddles the visible/private bounddary.
Otherwise, the instantiation and completion can also occur both in the visible
part, or both in the private part.

e.g.

   private with Ada.Containers.Hashed_Maps;
   package Pkg is
      type T is private;
   private
      package I is new limited Ada.Containers.Hashed_Maps
        (Key => T, Element => Integer, ...);
      package J is new limited Ada.Containers.Hashed_Maps
        (Key => T, Element => Float, ...);
      type T is
        record
          Integer_Map : access I.Map;
          Float_Map : access J.Map;
        end record;
      package I is new Ada.Containers.Hashed_Maps
        (Key => T, Element => Integer, ...);
      package J is new Ada.Containers.Hashed_Maps
        (Key => T, Element => Float, ...);
   end Pkg;

I'm also thinking that a shorthand form of the others=>private syntax might be
allowed where you simply don't have to supply any parameters. This shorthand
form could be used anywhere the others=>private could be used, but in addition
could be used in cases such as this, as in;

   private with Ada.Containers.Hashed_Maps;
   package Pkg is
      type T is private;
   private
      package I is new limited Ada.Containers.Hashed_Maps;
      package J is new limited Ada.Containers.Hashed_Maps;
      type T is
        record
          Integer_Map : access I.Map;
          Float_Map : access J.Map;
        end record;
      package I is new Ada.Containers.Hashed_Maps
        (Key => T, Element => Integer, ...);
      package J is new Ada.Containers.Hashed_Maps
        (Key => T, Element => Float, ...);
   end Pkg;

I am currently reworking my proposal to include some other ideas. Most notably,
I think the May_Be_Partial pragma should allow deriving a new type from a formal
that has the pragma applied within the visible part of the generic spec. (i.e.
Freezing does not occur for type derivations that have a May_Be_Partial pragma
applied.)

generic
  type T is private;
  pragma May_Be_Partial (T);
package Pkg is
  type Y is new T;
end Pkg;

My reasoning is that if the actual for T is a partial type, then it should be OK
to derive a new type from it, because you can derive from a partial type. If the
actual for T isn't a partial type, it should still be OK, because the
May_Be_Partial pragma ensures that there will be no objects or expressions of
that type in the package specification. I think freezing issues can be avoided
in either case. If the pragma is not applied to a formal type, then deriving
from that type will cause freezing to occur, as it does currently in the
standard.

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

From: Steve Baird
Date: Wednesday, January 28, 2009  7:47 PM

>  I think the semantics is pretty clear (a reexporting use clause), but
> surely it would be good to write that up. (Shouldn't be too hard.)


One more corner case to clear up ...

Should we allow a child unit to conflict with a homograph that is made visible
in a package spec via this new construct?

Consider:

     package P is
       type R is record F : Integer; end record;
       X : R;
     end P;

     with P;
     package Q is
         use all P; -- syntax TBD
     end Q;

     package Q.X is -- legal?
         F : Integer;
     end Q.X;

     with Q;
     -- with Q.X;
     procedure Foo is
     begin
         Q.X.F := 17;
     end Foo;

If the declaration of the child unit is legal, then does the name Q.X.F mean two
different things depending on whether the with of Q.X is commented out or not?
This would be particularly confusing in the case where the name and the
context_clause are in different compilation units (e.g., the name Q.X.F is in a
subunit and the context clause of an enclosing unit is modified by
adding/deleting a with of Q.X).

However this ends up being handled, the case of a "sprouted" generic (RM
10.1.1(9/2)) should be treated consistently:

     package P is
       generic
       package G is
       end G;
     end P;

     with P;
     generic
     package Q is
         use all P; -- syntax TBD
     end Q;

     with Q;
     package I1 is new Q;

     generic
     package Q.G is -- legal?
     end Q.X;

     with I1;
     -- with Q.G;
     procedure Foo is
         package I2 is new I1.G;
     begin
         null;
     end Foo;


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

From: Randy Brukardt
Date: Saturday, January 31, 2009  11:26 PM

> One more corner case to clear up ...

Isn't there always? :-)

> Should we allow a child unit to conflict with a homograph that is made
> visible in a package spec via this new construct?
>
> Consider:
>
>      package P is
>        type R is record F : Integer; end record;
>        X : R;
>      end P;
>
>      with P;
>      package Q is
>          use all P; -- syntax TBD
>      end Q;
>
>      package Q.X is -- legal?
>          F : Integer;
>      end Q.X;
>
>      with Q;
>      -- with Q.X;
>      procedure Foo is
>      begin
>          Q.X.F := 17;
>      end Foo;
>
> If the declaration of the child unit is legal, then does the name
> Q.X.F mean two different things depending on whether the with of Q.X
> is commented out or not?
> This would be particularly confusing in the case where the name and
> the context_clause are in different compilation units (e.g., the name
> Q.X.F is in a subunit and the context clause of an enclosing unit is
> modified by adding/deleting a with of Q.X).

Humm. My original thought was that this should work just like use clauses, warts
and all.

After all, you make this sound nasty and original; but it already happens for
use clauses and child units, and has since Ada 95 was introduced. We've
survived. :-)

For example:

     package P is
        type R is record F : Integer; end record;
        X : R;
     end P;

     package Q is
     end Q;

     package Q.X is
        F : Integer;
     end Q.X;

     with P; use P;
     -- with Q.X;
     procedure Q.Z is
     begin
        X.F := 17;
     end Q.Z;

If you add the with clause for Q.X, package X becomes directly visible and hides
the use visible object from P. But the programmer probably never even considered
the idea of calling the package plain X (they've always thought of it as Q.X),
so they're likely to be very confused. All of the same concerns apply about
doing it in another unit (it can also happen when an unrelated unit is converted
to a child).

This case does seem a bit worse, because it doesn't require a child unit to get
in trouble; and it happens in selected notation (as we didn't have use-visible
selected entities before).

I guess the question has to be "how nasty is the rule preventing children?".
That is, how hard is to word, and how hard is it to understand. If it's
relatively simple, perhaps we should simply ban the confusion (we did similar
things with "private with" even though the similar problems with children are
not prevented). But a big mess it isn't worth.

I worry that it might get very messy once you bring "sprouting" into the mix.
I'll trust you to work that out.

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

From: Bob Duff
Date: Monday, February  2, 2009  2:15 PM

> After all, you make this sound nasty and original; but it already 
> happens for use clauses and child units, and has since Ada 95 was introduced.

You can come up with similar surprises even in Ada 83.  And with subunits, you
can make the surprise cross source-file boundaries.  Ada 95 makes it a little
worse, with child units.

>...We've
> survived. :-)

I tend to agree with that attitude.  I don't like it, but as you point out,
we already have similar cases, and we can't fix the whole problem compatibly.

These are really Beaujolais-like effects.  Ichbiah came up with some excellent
visibility rules for use_clauses, and prevented Beaujolais effects (with a few
minor bugs, fixed in Ada 95).  But I wonder why he defined "Beaujolais effect"
purely in terms of use_clauses -- it should be defined more broadly, in terms
of all visibility-related things.

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

From: Robert I. Eachus
Date: Monday, February  2, 2009  6:28 PM

>You can come up with similar surprises even in Ada 83.  And with 
>subunits, you can make the surprise cross source-file boundaries.  Ada 
>95 makes it a little worse, with child units.

Reminds me of a very old gotcha in Ada.  I think I originally wrote it as
part of a test of compiler overload resolution.  Dave Emery, Rich Hilliard and
I extracted one case and ran it against Ada compilers on display at a SIGAda
meeting.  The reaction of almost all who saw their compiler reject it was:
"That must be a compiler bug--I'll write it up."  We then assured the
astonished demonstrator that there was no bug their compiler was right to reject
the program:

procedure Hiding is
   procedure Foo (X: in Integer := 1) is begin null; end; begin
   declare
      procedure Foo is begin null; end;
   begin
      Foo;
   end;
end Hiding;

The outer Foo can be called by providing an explicit parameter, the inner Foo
never can.

Is this a bug in Ada that you can declare a subprogram that can't be called?
Not really, this case is pretty harmless. The case where both subroutines are
declared in the same scope is similar, and again, a surprise at compile time, but
relatively harmless. There are variations using use clauses and/or nested packages,
which were the original purpose of the test, but none of them result in a
subprogram being totally uncallable.

Why bring this up?  The old joke:

"Doctor, Doctor, it hurts when I do this!"
"Well, don't do it then."

There are lots of areas in Ada that may be of great interest to language lawyers,
but which Ada programmers have learned to avoid.  If not because they are bad
software engineering, because any rocks they run into in those shoals will make
them look foolish later.

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

From: Steve Baird
Date: Friday, February  6, 2009  6:18 PM

The attached [Version /01 of the AI] is a first cut at wording for this AI.

The syntax for this construct is still very much up in the air; I just went with
"use all" here in order to have a specific proposal.

Note that the new super-powers for "use all type" are completely orthogonal to the
rest of the proposal. We are using this new reserved word to indicate two new
properties which are really unrelated (other than that they both increase visibility).

The only hard part of separating the two properties would be deciding on syntax.

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

Questions? Ask the ACAA Technical Agent