!standard 3.10.1(2/2) 09-06-06 AI05-0151-1/03 !class Amendment 09-04-29 !status work item 09-04-29 !status received 09-04-29 !priority High !difficulty Hard !subject Allow incomplete types as parameter and result types !summary Make limited views of packages more useful by allowing more use of the incomplete types they make visible. !problem The limited with clause makes incomplete views of types visible. Tagged incomplete types are useful for formal parameters, but most other incomplete types are pretty much useless, except as the designated type of an access type. It would be nice if a type declared in a package named in a limited with clause could be used somehow, if only as a parameter type. !proposal Incomplete types may be used as formal parameter and result types, so long as they are fully defined at the point of call and at the point where the body is defined. Incomplete types may *not* be used for components or objects. For tagged incomplete types, we will continue to permit them as parameters even in calls and in a body, provided the incomplete type comes from a limited view of a package, rather than from an incomplete type declaration. For non-tagged incomplete types, they will not be permitted in calls or in a body. If the parameter or result type was incomplete at the point of declaration, any call or the body must be within the scope of a *non* limited view of the package defining the type. !wording ** TBD ** !discussion This proposal originally focused on a concern that a "limited with" naming a package with named access types forces the use of a lot of explicit conversion and/or anonymous access types in the clients. We originally proposed to allow "incomplete access types" to be used as formal parameters and result types, to avoid the need for all of the conversion. However, after analysis of the issues involved, it became clear that it was possible to generalize, and in some ways simplify, the proposal by allowing the use of *any* incomplete type as a parameter or result type, provided that the full definition was available at the point of call or at the definition of the body of the subprogram. An incomplete type is OK as a parameter or result since, until there is a call, there is no need to know the representation of the type. This is in contrast to the case with components, where an aspect clause would need to be obeyed to ensure that streaming, aliased objects, etc., work as expected. Currently compilers generally do know the representation of the parameters and result at the point of declaration of a subprogram (though not for access-to-subprogram declarations). However, it should be possible to handle the lack of such representation information by having the compiler implicitly create a local access-to-subprogram type at the point of call, and then call through that, using only the address of the subprogram with incomplete types in its declaration, while determining the parameter passing mechanism from the access-to-subprogram type. Note that we have chosen to allow incomplete types as function results, even though we restricted tagged incomplete types to being parameters only. This seems inconsistent. Why should tagged incomplete types be *more* restrictive than incomplete types in general? Well, they shouldn't be, so with this proposal, the only advantage a tagged incomplete type will have is that a call or body *will* be permitted on a subprogram with tagged incomplete parameters, provided they are not controlling parameters. Tagged or untagged incomplete types will be permitted as both parameters and results when declaring a subprogram. The net effect of this proposal is that changing a "with" to a "limited with" will be much less of a disruption for a client package, since it may continue to declare subprograms using the types from the limited-with'ed package. It won't be able to use those types as component types, but access types are frequently an adequate work-around in that case. Generic Formal Incomplete Types? I wonder whether we could have generic formal incomplete types as a way to allow instantiation with a private type before it is completely defined. This might be sufficient for a "signature" generic. !example --!corrigendum 3.10.1(2/2) !ACATS test Add ACATS tests for this new feature. !appendix From: Tucker Taft Sent: Saturday, June 6, 2009 3:58 PM Here is an AI that came out of discussions about access types we have been having over the past couple of months. Basically it allows incomplete types as parameter and result types, so long as at the point of call or the body the types are fully defined. This makes changing a "with" clause to a "limited with" clause *much* less disruptive, since there is no need to change the parameter/result profile of any subprograms declared in the client package just because the types might be incomplete because they are coming from a limited view of a package. [This is version /03 of the AI, the earlier versions were merged into AI05-149 and eliminated here. - ED] **************************************************************** From: Bob Duff Sent: Saturday, June 6, 2009 4:30 PM > Here is an AI that came out of discussions about access types we have > been having over the past couple of months. Basically it allows > incomplete types as parameter and result types, so long as at the > point of call or the body the types are fully defined. Seems like a big improvement to the language. Ideally, access type should be used only when you really want reference semantics. This change moves closer to that ideal. > This proposal originally focused on a concern that a "limited with" > naming a package with named access types forces the use of a lot of > explicit conversion and/or anonymous access types in the clients. We > originally proposed to allow "incomplete access types" to be used as > formal parameters and result types, to avoid the need for all of the > conversion. However, after analysis of the issues involved, it became > clear that it was possible to generalize, and in some ways simplify, > the proposal by allowing the use of *any* limited type as a parameter > or ^^^^^^^ I think you mean "incomplete type from a limited-with'ed package". [Corrected in the posted version /03, Editor.] > result type, provided that the full definition was available at the > point of call or at the definition of the body of the subprogram. > > An incomplete type is OK as a parameter or result since, until there > is a call, there is no need to know the representation of the type. > This is in contrast to the case with components, where an aspect > clause would need to be obeyed to ensure that streaming, aliased > objects, etc., work as expected. > > Currently compilers generally do know the representation of the > parameters and result at the point of declaration of a subprogram > (though not for access-to-subprogram declarations). What's the acc-to-subp case you're referring to? >... However, it should > be possible to handle the lack of such representation information by >having the compiler implicitly create a local access-to-subprogram type >at the point of call, and then call through that, using only the >address of the subprogram with incomplete types in its declaration, >while determining the parameter passing mechanism from the access-to-subprogram type. > > Note that we have chosen to allow incomplete types as function > results, even though we restricted tagged incomplete types to being > parameters only. This seems inconsistent. Why should tagged > incomplete types be > *more* restrictive than incomplete types in general? Well, they > shouldn't be, so with this proposal, the only advantage a tagged > incomplete type will have is that a call or body *will* be permitted > on a subprogram with tagged incomplete parameters, provided they are > not controlling parameters. Tagged or untagged incomplete types will > be permitted as both parameters and results when declaring a subprogram. > > The net effect of this proposal is that changing a "with" to a "limited with" > will be much less of a disruption for a client package, since it may > continue to declare subprograms using the types from the limited-with'ed package. > It won't be able to use those types as component types, but access > types are frequently an adequate work-around in that case. That seems like a secondary benefit. The primary benefit is that when you use "limited with" (in the first place) you don't have to use pointers when you don't want pointer semantics. I'd say that first, then ``In addition, changing a "with" to a "limited with"...''. > Generic Formal Incomplete Types? > > I wonder whether we could have generic formal incomplete types as a > way to allow instantiation with a private type before it is completely > defined. This might be sufficient for a "signature" > generic. Interesting idea. Does the "use package Blah" thing provide a different way to do signature generics? **************************************************************** From: Tucker Taft Sent: Sunday, June 7, 2009 6:15 AM > ... However, after analysis of the issues involved, it became >> clear that it was possible to generalize, and in some ways simplify, >> the proposal by allowing the use of *any* limited type as a parameter >> or > ^^^^^^^ I think you mean > "incomplete type from a limited-with'ed package". Right you are. > ... >> Currently compilers generally do know the representation of the >> parameters and result at the point of declaration of a subprogram >> (though not for access-to-subprogram declarations). > > What's the acc-to-subp case you're referring to? You are allowed to use incomplete types as parameter or result types in an access-to-subprogram type declaration. > ... >> Generic Formal Incomplete Types? >> >> I wonder whether we could have generic formal incomplete types as a >> way to allow instantiation with a private type before it is >> completely defined. This might be sufficient for a "signature" >> generic. > > Interesting idea. > > Does the "use package Blah" thing provide a different way to do > signature generics? Yes, by putting most of the content of a (generic) package into an "integrated" subpackage, you can then follow the integrated subpackage with one or more generic instantiations, which are allowed to use private types defined in the integrated subpackage. **************************************************************** From: Tucker Taft Sent: Saturday, June 6, 2009 3:24 PM I'm trying to figure out what is currently allowed for subprograms with parameters of a tagged incomplete type. Calling a subprogram freezes its profile, and freezing its profile freezes all of its parameter types. What happens when you freeze a tagged incomplete type? Isn't that illegal if the type isn't completely defined? Are incomplete types produced by a limited view of a package "completely defined" everywhere, so that freezing them is OK? I presume that freezing an incomplete type that is defined by an incomplete type declaration is illegal if it happens in a place outside the scope of the full definition. I am going to proceed in my work on AI05-0151 presuming that it is OK to freeze an incomplete type coming from the limited view of a package, and it has no effect, while freezing an incomplete type defined by an incomplete type declaration would be illegal outside the scope of the full type definition. I suspect this question needs a more explicit answer somewhere in the RM, or at least in the AARM. **************************************************************** From: Bob Duff Sent: Saturday, June 6, 2009 4:33 PM ... > I suspect this question needs a more explicit answer somewhere in the > RM, or at least in the AARM. I'm puzzled by this message. The version of AI05-0151-1v3 you just sent says you can't call a subp or give its body, until you have completion. Isn't that obviously necessary? Maybe if you gave an example of what you're asking, as in "Should the following be legal?"... **************************************************************** From: Randy Brukardt Sent: Saturday, June 6, 2009 9:56 PM > I'm trying to figure out what is currently allowed for subprograms > with parameters of a tagged incomplete type. > Calling a subprogram freezes its profile, and freezing its profile > freezes all of its parameter types. What happens when you freeze a > tagged incomplete type? Isn't that illegal if the type isn't > completely defined? 13.14(17): "A type shall be fully defined before it is frozen." So, yes, of course, that is illegal. > Are incomplete types produced by a limited view of a package > "completely defined" everywhere, so that freezing them is OK? > I presume that freezing an incomplete type that is defined by an > incomplete type declaration is illegal if it happens in a place > outside the scope of the full definition. I don't think this matters; either the completion exists (in which case it is fine to freeze it elsewhere) or it does not. I surely would expect that all entities imported by with clauses are considered frozen. That doesn't appear to be an explicit rules, but it is true because the exported entities are all frozen by the end of the spec. Since all views are frozen at once, that surely must include any exported incomplete views in the limited view of the package. QED. :-) And in this case, Robert's rule of absurdity would apply: if tagged limited views imported by with clauses were *not* frozen, it would be impossible to use them in subprogram parameters. Which would make all of the effort to define that moot. That's an absurd result. The AARM notes added by AI05-0017-1 (13.14(3.b-3.b.1/3)) sort of address this topic. but don't explicitly mention limited views. Probably we ought to add an AARM note to mention this meta-rule (probably in the Language Design Principles). Unrelated aside: 13.14(1.j) is misleading, as all of these entities have freezing and get frozen at particular points (for Convention and address clauses at least); I think the point of the note is that we don't use freezing per-se to handle most issues of premature access. Probably ought to be rewritten. > I am going to proceed in my work on AI05-0151 presuming that it is OK > to freeze an incomplete type coming from the limited view of a > package, and it has no effect, while freezing an incomplete type > defined by an incomplete type declaration would be illegal outside the > scope of the full type definition. That would be correct. > I suspect this question needs a more explicit answer somewhere in the > RM, or at least in the AARM. I think an AARM note is enough, since it clearly (!) follows from the existing freezing rules. But I would agree that it isn't obvious. **************************************************************** From: Tucker Taft Sent: Sunday, June 7, 2009 6:21 AM ... > I'm puzzled by this message. The version of AI05-0151-1v3 you just > sent says you can't call a subp or give its body, until you have completion. > Isn't that obviously necessary? The issue is with the *current* rules that allow the use of tagged incomplete types as parameters. Can these be incomplete at the point of call or the body, provided they came from the limited view of a package? > Maybe if you gave an example of what you're asking, as in "Should the > following be legal?"... OK: package P is type T is tagged null record; end P; limited with P; package Q is procedure M(X : P.T); type Acc is access T; end Q; -- NOTE: no "with" of P here package body Q is procedure M(X : P.T) is ... -- legal? Y : Acc; begin M(Y.all); -- legal? end Q; **************************************************************** From: Bob Duff Sent: Sunday, June 7, 2009 8:12 AM OK, I see what you're getting at. Both of the above had better be illegal, right? Y has to be null, but we plugged a hole like that in Ada 83. **************************************************************** From: Tucker Taft Sent: Sunday, June 7, 2009 8:29 AM > Both of the above had better be illegal, right? No, I think our intent was that both of the above would be *legal*, since we know that tagged types are passed by reference, and so there is no problem passing them around even if you don't know anything about them. > Y has to be null, but we plugged a hole like that in Ada 83. I should have thrown in some "..." since my presumption was that Y might have been initialized by calling some function that returned an Acc. That function would presumably be in another package which *does* have a non-limited "with" of P, and can legally do an allocator to create a non-null Acc value. **************************************************************** From: Randy Brukardt Sent: Sunday, June 7, 2009 9:38 PM > > Both of the above had better be illegal, right? > > No, I think our intent was that both of the above would be *legal*, > since we know that tagged types are passed by reference, and so there > is no problem passing them around even if you don't know anything > about them. That is my understanding as well. That is why 3.10(10/2) talks about *prefixes* that are of incomplete views, as opposed to *names* that are of incomplete views. The call cannot be a dispatching call (since we don't know where the tag is), but a simple "pass-through" call is allowed. OTOH, we can't support that for untagged types (because we don't know how they're passed) or for function results (since they are creating a new object, even for tagged types). So it seems that Tucker cannot rely solely on the freezing rules to implement the completeness check here. (That appears to be the difference between tagged and untagged incomplete types in this scheme, along with the use of 'Class: that you can declare bodies and make calls with tagged incomplete parameters/objects, but not with untagged incomplete.) > > Y has to be null, but we plugged a hole like that in Ada 83. > > I should have thrown in some "..." since my presumption was that Y > might have been initialized by calling some function that returned an > Acc. That function would presumably be in another package which > *does* have a non-limited "with" of P, and can legally do an allocator > to create a non-null Acc value. Right, that was the idea. If a package is just passing a tagged object from one package to another, there is no need to know any details about the type, and, as Bob mentioned in another message, we don't want to force using access types unless the reference semantics are actually needed. I'm not sure this really works very well in practice, but it surely was intended to be possible. ****************************************************************