CVS difference for acs/ac-00147.txt
--- acs/ac-00147.txt 2007/10/25 06:08:48 1.1
+++ acs/ac-00147.txt 2008/07/13 01:04:00 1.2
@@ -116,3 +116,620 @@
****************************************************************
+!topic Ada 2005 makes limited private idiom illegal
+!reference 7.5
+!from Adam Beneschan 08-05-21
+!discussion
+
+
+I've run into a problem where an idiom I found useful in Ada 95 is
+no longer allowed in Ada 2005.
+
+Suppose you have a package whose purpose is to open a communication
+channel to another computer, using Unix sockets:
+
+ package Socket_Communication is
+ type Channel is limited private;
+ function Open_Channel (Computer_Name : String) return Channel;
+ ... other operations
+ private
+ type Channel is record -- not limited
+ ...
+ end record;
+ ... other definitions
+ end Socket_Communication;
+
+(Note that the full type is not limited, so there wouldn't have been any
+return-by-reference issues in Ada 95.)
+
+Later, you want to write a different communication package for some more
+specialized purpose---say a package that connects to a specified central
+warehouse to get information on the current inventory. In the abstract,
+you might want the visible part of the package spec to say something like:
+
+ package Warehouses is
+ type Warehouse_Connection is limited private;
+ function Open_Connection (Warehouse_Number : Integer)
+ return Warehouse_Connection;
+
+For the implementation, let's say you decide to implement it using
+Socket_Communication. Reasonable enough, right? In general, a
+Warehouse_Connection may need to carry additional information besides
+the Socket_Communication info, but let's say it doesn't. It seems
+natural to want to implement it as:
+
+ with Socket_Communication;
+ package Warehouses is
+ type Warehouse_Connection is limited private;
+ function Open_Connection (Warehouse_Number : Integer)
+ return Warehouse_Connection;
+ ...
+ private
+ type Warehouse_Connection is new Socket_Communication.Channel;
+ ...
+
+and then implement Open_Connection as
+
+ function Open_Connection (Warehouse_Number : Integer)
+ return Warehouse_Connection is
+ Name : constant String :=
+ Warehouse_Computer_Name_Of (Warehouse_Number);
+ begin
+ return Warehouse_Connection
+ (Socket_Communication.Open_Channel (Name));
+ end Open_Connection;
+
+Seems simple enough, right? BZZZZZZZT! It isn't legal. It was legal
+in Ada 95, but no more, because:
+
+(1) only the partial view of Socket_Communication.Channel is
+ available, and that type has the word "limited" in its definition;
+ therefore Socket_Communication.Channel is limited by 7.5(4);
+
+(2) Warehouse_Connection is derived from Socket_Communication.Channel,
+ and is therefore limited by 7.5(6.1);
+
+(3) 7.5(2.1) and 7.5(2.8) apply, and since a type conversion isn't
+ listed as one of the possible expressions that can appear in a
+ return statement, it isn't legal.
+
+I don't know what the solution is, or if one is needed. I haven't
+studied things enough to figure out whether type conversions, or certain
+kinds of type conversions, could be allowed in 7.5(2.1).
+
+I actually ran into this while trying to implement both Ada.Text_IO and
+Ada.Wide_Text_IO, putting all the interesting code in Wide_Text_IO and
+implementing Text_IO mostly by making calls to Wide_Text_IO.
+(File_Type is limited private in both packages, so code I had gotten
+working for Ada 95 no longer compiles, for the language-defined functions
+that return File_Type.) But I think the problem is more general than that.
+I'm not sure how serious it is; the problem can be worked around by making
+the full view of Warehouse_Connection a (nonlimited) record with a
+Socket_Communication.Channel component, but it seems a bit hokey to have
+to do this.
+
+****************************************************************
+
+From: Adam Beneschan
+Sent: Wednesday, May 21, 2008 11:14 AM
+
+> I actually ran into this while trying to implement both Ada.Text_IO
+> and Ada.Wide_Text_IO, putting all the interesting code in Wide_Text_IO
+> and implementing Text_IO mostly by making calls to Wide_Text_IO.
+> (File_Type is limited private in both packages, so code I had gotten
+> working for Ada 95 no longer compiles, for the language-defined
+> functions that return File_Type.) But I think the problem is more
+> general than that. I'm not sure how serious it is; the problem can be
+> worked around by making the full view of Warehouse_Connection a
+> (nonlimited) record with a Socket_Communication.Channel component, but
+> it seems a bit hokey to have to do this.
+
+I must be asleep---the record would still be limited, but you could at
+least write an aggregate in the return statement. It does seem strange
+that you would have to do that. It seems that if you can say
+
+ type T2 is record
+ X : T1; -- T1 is limited
+ end record;
+
+ return T2' (X => Some_Func_Call(...));
+
+you ought to be able to say
+
+ type T2 is new T1;
+
+ return T2 (Some_Func_Call (...));
+
+I don't know whether all type conversions ought to be allowable by 7.5(2.1),
+but at the least it should be OK to allow untagged type conversions where
+the target type is derived from the operand type.
+
+****************************************************************
+
+From: Tucker Taft
+Sent: Wednesday, May 21, 2008 11:43 AM
+
+If it is declared limited, then that means from the outside, you aren't allowed
+to copy it. The Ada 95 rules implied privacy breakage.
+
+In Ada 2005, function return always involves the creation of a new object, either
+by copying an old one (which isn't allowed if it is limited), or by creating a
+new object with an aggregate or an extended return statement.
+
+If you want some clients to be able to copy it, then you need to make it
+non-limited, or move those clients that want to take advantage of its underlying
+non-limitedness into child packages.
+
+On the other hand, you have illustrated a case where you use non-tagged type
+derivation, and it might be argued that a type conversion between non-tagged
+derived types need not create a new object, presuming there weren't any
+rep-clauses applied to the derived type.
+
+On the other other hand, it seems that either these types are very closely
+related, in which case it would make sense for the derived type to be declared
+in a child package, or they are pretty much unrelated, in which case you could
+define the new type by wrapping the old type in a record with only one component,
+as you suggested.
+
+Or you could make the types tagged, in which case you can use an extension
+aggregate in the return statement.
+
+****************************************************************
+
+From: Robert A. Duff
+Sent: Wednesday, May 21, 2008 4:43 PM
+
+> If it is declared limited, then that means from the outside, you
+> aren't allowed to copy it. The Ada 95 rules implied privacy breakage.
+
+Really? How do the Ada 95 rules break privacy?
+
+An alternative rule would be to allow return-by-copy (e.g. return of a global
+object) if the type is not a build-in-place type. That would break privacy,
+which is evil. But the existing Ada 2005 rules break compatibility, which is
+even more evil. I think ARG made the wrong trade-off between breaking privacy
+and breaking compatibility in this case.
+
+Experiments with AdaCore's bug database show that almost all of the violations
+of the new Ada 2005 rule (return expression must be a function call or
+aggregate) in existing Ada 95 code are the full-type-non-limited case.
+That is, return-by-reference is quite rare in Ada 95, so disallowing that would
+have been much more compatible. (I suspect some of the return-by-ref cases are
+accidental!)
+
+I guess things get more interesting with generic formal limited private types
+-- some instances use return-by-ref, and some use return-by-copy!
+
+> In Ada 2005, function return always involves the creation of a new
+> object, either by copying an old one (which isn't allowed if it is
+> limited), or by creating a new object with an aggregate or an extended
+> return statement.
+
+It doesn't have to be extended. You can say "return F(...)", which is equivalent
+to "return R : constant T := F(...);".
+
+P.S. Build-in-place is a very cool feature. Too bad Ichbiah didn't think of
+it in the first place!
+
+****************************************************************
+
+From: Adam Beneschan
+Sent: Wednesday, May 21, 2008 5:01 PM
+
+> > If it is declared limited, then that means from the outside, you
+> > aren't allowed to copy it. The Ada 95 rules implied privacy
+> > breakage.
+>
+> Really? How do the Ada 95 rules break privacy?
+
+That's an interesting question, since the return-by-reference rules in
+Ada 95 are mostly in Dynamic Semantics, and someone (was it you?) pointed
+out to me some time ago that the "privacy breaking" thing doesn't apply to
+what happens at run-time, i.e. dynamic semantics.
+However, a quick textual search of the Ada 95 AARM did find one place where
+return-by-reference was referred to in Static Semantics, in 3.10.2, having
+to do with the accessibility level of a return object.
+Since any detailed analysis of 3.10.2 causes the exception Migraine_Error
+to be raised, I haven't tried to construct an example in detail.
+
+But in a case like this:
+
+ package Pak1 is
+ type T is limited private;
+ private
+ type T is ???;
+ end Pak1;
+
+ with Pak1;
+ package Pak2 is
+ type T2 is limited private;
+ private
+ type T2 is new Pak1.T;
+ end Pak1;
+
+the rules for determining whether T2 is a return-by-reference type
+(RM95 6.5(11-16)) do seem to imply that one needs to look in the private part
+of Pak1 to answer the question. And since it seems possible (thanks to RM95
+3.10.2(10)) to create a case where the compiler must determine whether a
+function result type is return-by-reference in order to determine whether
+a program is legal, I guess that does mean there's a privacy breakage.
+That may be the sort of thing Tuck was referring to.
+
+****************************************************************
+
+From: Tucker Taft
+Sent: Wednesday, May 21, 2008 10:36 PM
+
+>> If it is declared limited, then that means from the outside, you
+>> aren't allowed to copy it. The Ada 95 rules implied privacy
+>> breakage.
+>
+> Really? How do the Ada 95 rules break privacy?
+
+They break privacy because whether or not you can return a local object
+of the private type is determined by whether the full type is or is
+not a return-by-reference type.
+
+****************************************************************
+
+From: Randy Brukardt
+Sent: Wednesday, June 11, 2008 11:39 PM
+
+> On the other hand, you have illustrated a case where you use
+> non-tagged type derivation, and it might be argued that a type
+> conversion between non-tagged derived types need not create a new
+> object, presuming there weren't any rep-clauses applied to the derived
+> type.
+
+We discussed this issue (allowing a few forms of type conversion for
+build-in-place types) here last July. While that discussion was
+inconclusive, the discussion about whether to make it an AI brought up
+some additional issues which I annotated onto the AC (AC-147). Since
+probably no one here read them, I'll attach them here in an effort to
+lay this idea to rest:
+
+"It is clear that only a few kinds of type conversions would be safe
+in build-in-place contexts. Surely, we couldn't allow anything that
+could change representation or discard portions of a type. That suggests
+that allowing [only safe] kinds of conversions could be confusing for
+the user, as the rules would appear rather arbitrary. Thus, I don't
+think this is a good idea."
+
+The previous discussion suggested allowing conversion to a class-wide type;
+this one suggests allowing non-tagged derived type that don't have
+representation clauses. We could also probably allow conversions between
+a null extension and it's parent. And that's it. I can't imagine a
+much weirder set of rules, and it seems dubious that they would help
+in many real situations (and depending on rep. clauses could break
+privacy, as the rep. clauses can be given in the private part) so I
+still don't think this is a good idea.
+
+****************************************************************
+
+From: Gary Dismukes
+Sent: Thursday, May 22, 2008 12:26 AM
+
+I agree with your assessment Randy. I don't think it makes sense to
+allow conversions in build-in-place contexts. The privacy-breaking issue
+in particular is a nonstarter, and allowing just the few cases where
+there couldn't be a problem doesn't seem worth it.
+
+****************************************************************
+
+From: Adam Beneschan
+Sent: Thursday, June 19, 2008 4:48 PM
+
+I'm not convinced this is the same issue at all. The example I started
+with dealt with a type T2 derived from a limited private type T, and
+the problem of writing a function to return a value of type T2.
+
+This isn't a build-in-place issue at all, because the build-in-place
+rule for function results (7.5(8.1)) only applies to explicitly limited
+record types (and tasks and protected types), *not* limited private
+types (see AARM 7.5(9.a)).
+
+As for representation clauses: it appears that the language may allow
+representation items on types derived from private types, as long as the
+private type isn't by-reference, which you have to look at the full view
+of the type to determine (13.1(10)). If this is true, that a
+representation item may be allowed on a type derived from a private type,
+then there appears to be a privacy breakage in the language rules.
+If it's not allowed, though, then the representation clause shouldn't be
+an issue for the type conversion problem I asked about.
+
+****************************************************************
+
+From: Randy Brukardt
+Sent: Saturday, June 28, 2008 4:16 PM
+
+...
+> This isn't a build-in-place issue at all, because the build-in-place
+> rule for function results (7.5(8.1)) only applies to explicitly
+> limited record types (and tasks and protected types), *not* limited
+> private types (see AARM 7.5(9.a)).
+
+True, but for the purposes of legality rules they're treated like
+build-in-place types in order to avoid breaking privacy. After all,
+whether or not a limited private type is build-in-place depends solely
+on the declaration of the full type. That's why the legality rules
+apply to all limited types.
+
+Essentially, these legality rules are "assume-the-worst" when it comes
+to limited private types, because otherwise it would be easy for a
+client to have an implied dependency on the form of the declaration
+of the full type. We don't want clients to become illegal if someone
+adds a protected component, for instance.
+
+> As for representation clauses: it appears that the language may allow
+> representation items on types derived from private types, as long as
+> the private type isn't by-reference, which you have to look at the
+> full view of the type to determine (13.1(10)). If this is true, that
+> a representation item may be allowed on a type derived from a private
+> type, then there appears to be a privacy breakage in the language
+> rules. If it's not allowed, though, then the representation clause
+> shouldn't be an issue for the type conversion problem I asked about.
+
+Representation items generally don't have any effect on legality rules
+(other than for other rep. items). There are a couple of exceptions but
+we really don't want to be adding any more, because rep. items generally
+can be hidden and thus such dependencies break privacy.
+
+You seem to be implying that this sort of conversion should be allowed
+because the full parent type (and derived type) in question don't prevent
+it. But that's backwards to the usual Ada model, where the clients can't
+be made illegal by maintenance on the parent type. I don't think we want
+to be changing that.
+
+****************************************************************
+
+From: Adam Beneschan
+Sent: Wednesday, July 2, 2008 10:20 AM
+
+...
+> Representation items generally don't have any effect on legality rules
+> (other than for other rep. items). There are a couple of exceptions
+> but we really don't want to be adding any more, because rep. items
+> generally can be hidden and thus such dependencies break privacy.
+>
+> You seem to be implying that this sort of conversion should be allowed
+> because the full parent type (and derived type) in question don't
+> prevent it. But that's backwards to the usual Ada model, where the
+> clients can't be made illegal by maintenance on the parent type. I
+> don't think we want to be changing that.
+
+I'm having trouble following this and understanding why it argues
+against any point I was making, and perhaps that's because I didn't make
+myself clear enough last time. Let me try again...
+
+Suppose you write:
+
+ package Pak1 is
+ type T1 is limited private;
+ private
+ type T1 is record
+ F1 : Character;
+ F2 : Integer;
+ end record;
+ end Pak1;
+
+ with Pak1;
+ package Pak2 is
+ type T2 is new Pak1.T1;
+ pragma Pack (T2); -- legal?
+ end Pak2;
+
+I'm not clear on whether the noted rep clause is legal or not. I think
+it is---I don't see any rule that makes this illegal. [Pack is allowed
+on composite subtypes, and 3.2(4.1) says that partial views are composite.]
+
+However, if you changed the full type of T1 to make it "tagged", or "limited",
+or add another component whose type is a tagged record, then T1 becomes a
+by-reference type (even the private view becomes a by-reference view, by
+6.2(9)), and the rep clause is illegal by 13.1(10).
+
+So if your concern is that clients shouldn't be made illegal by maintenance
+on the parent type, it appears that we *already* have a case that violates
+this principle---if the Pack pragma is legal.
+
+However, if I've missed something, and the Pack clause is actually illegal,
+then I don't see how my "proposal" (see below) causes any new privacy
+breakage problems.
+
+I realize that I don't think I ever made a specific proposal here; I
+pointed out a problem and made some general comments. Looking back, some
+of those comments seemed to suggest that maybe type conversions should be
+allowed even where private types aren't involved, and that may have been
+incorrect and may have led to some confusion. So I'm going to try
+something more specific:
+
+I propose changing 7.5(2.1), which currently reads:
+
+ In the following contexts, an expression of a limited type is not
+ permitted unless it is an aggregate, a function_call, or a
+ parenthesized expression or qualified_expression whose operand is
+ permitted by this rule:
+
+to
+
+ In the following contexts, an expression of a limited type is not
+ permitted unless it is an aggregate, a function_call, or a
+ parenthesized expression or qualified_expression whose operand is
+ permitted by this rule, or a type_conversion whose operand is
+ permitted by this rule, if the type_conversion is between a
+ derived type and an ancestor of the derived type, where the
+ ancestor is a limited untagged partial view:
+
+or something to that effect.
+
+I've narrowed this down to cases where a type is derived from an untagged
+limited private type. If representation clauses on types derived from
+private types are illegal, then I think one of the main objections, that
+such a type conversion could require a copy of a limited object due to a
+representation change, would not apply. If it's possible to come up with
+a case involving a *chain* of derived types where the type conversion would
+still involve a representation change, perhaps "ancestor" could be changed
+to "parent" in the above.
+
+Anyway, assuming that rep clauses such as the above are illegal, then
+I don't see any privacy breakage concerns with this proposal.
+
+My reasons in support of the proposal:
+
+(1) It eliminates a backward incompatibility---a case where code that
+ was legal in Ada 95 is illegal in Ada 2005.
+
+(2) The code that Ada 2005 made illegal actually came up in practice.
+
+(3) Although some workarounds have been mentioned, such as this in my
+ original example:
+
+ package Pak1 is
+ type T1 is limited private; ...
+
+ package Pak2 is
+ type T2 is limited private;
+ private
+ type T2 is record
+ The_Data : Pak1.T1;
+ end record; ...
+
+ the original case, where the full view of T2 was a derived type:
+
+ package Pak2 is
+ type T2 is limited private;
+ private
+ type T2 is new Pak1.T1; ...
+
+ is not illegal, and it's possible to implement all of Pak2 in that
+ manner. But later, if it is desired to add a new operation to
+ Pak2 that is a function that returns T2, it's only then that the
+ Pak2 implementor finds that a derived type won't work, and that he
+ therefore needs to redefine T2 and fix all uses of it in the body
+ of T2, which is annoying.
+
+It may also be desirable to modify 13.1(10) to prevent rep clauses on types
+derived from private types, to eliminate the possible privacy breakage that
+comes from making the legality depend on whether the parent type is a
+by-reference type (assuming there isn't some other rule somewhere else that
+already prevents this).
+
+However, if the privacy breakage in 13.1(10) is considered acceptable, then
+I can see how my proposed change to 7.5(2.1) could make things worse from
+a privacy breakage standpoint.
+
+Anyway, I hope this is clearer.
+
+****************************************************************
+
+From: Randy Brukardt
+Sent: Wednesday, July 2, 2008 3:59 PM
+
+> I'm not clear on whether the noted rep clause is legal or not. I
+> think it is---I don't see any rule that makes this illegal. [Pack is
+> allowed on composite subtypes, and
+> 3.2(4.1) says that partial views are composite.]
+
+The ARG cannot agree on much when it comes to pragma Pack (other than
+that we don't want to discuss it anymore), so there can be no definitive
+answer.
+
+However, attribute clauses for 'Size and 'Alignment would be allowed here
+(although a particular implementation could reject them if it wished).
+
+> However, if you changed the full type of T1 to make it "tagged", or
+> "limited", or add another component whose type is a tagged record,
+> then T1 becomes a by-reference type (even the private view becomes a
+> by-reference view, by 6.2(9)), and the rep clause is illegal by
+> 13.1(10).
+>
+> So if your concern is that clients shouldn't be made illegal by
+> maintenance on the parent type, it appears that we
+> *already* have a case that violates this principle---if the Pack
+> pragma is legal.
+
+I though that is what I said: there are some exceptions for representation
+items that break the privacy model, but we surely don't want to be adding
+any more.
+
+Remember that the following is legal:
+
+ with Pak1;
+ package Pak3 is
+ type T3 is new Pak1.T1;
+ private
+ for T3'Alignment use <some-expr>;
+ end Pak3;
+
+and thus the clients of T3 do not even know if there is a representation
+item on the type.
+
+...
+> I propose changing 7.5(2.1), which currently reads:
+>
+> In the following contexts, an expression of a limited type is not
+> permitted unless it is an aggregate, a function_call, or a
+> parenthesized expression or qualified_expression whose operand is
+> permitted by this rule:
+>
+> to
+>
+> In the following contexts, an expression of a limited type is not
+> permitted unless it is an aggregate, a function_call, or a
+> parenthesized expression or qualified_expression whose operand is
+> permitted by this rule, or a type_conversion whose operand is
+> permitted by this rule, if the type_conversion is between a
+> derived type and an ancestor of the derived type, where the
+> ancestor is a limited untagged partial view:
+>
+> or something to that effect.
+
+This is exactly the extremely narrow (and complex) exception for type conversions
+that pretty much everyone has agreed that we don't want. Especially as it would
+have to break privacy in order to include a rule disallowing a type like T3.
+Also recall that the full type for T1 could easily be immutably limited, and
+then the build-in-place rules would be triggered. A value type conversion is
+considered a copying operation, after all, and trying to redefine this case
+as a view conversion doesn't seem worth the effort.
+
+> I've narrowed this down to cases where a type is derived from an
+> untagged limited private type. If representation clauses on types
+> derived from private types are illegal, then I think one of the main
+> objections, that such a type conversion could require a copy of a
+> limited object due to a representation change, would not apply. If
+> it's possible to come up with a case involving a *chain* of derived
+> types where the type conversion would still involve a representation
+> change, perhaps "ancestor" could be changed to "parent" in the above.
+>
+> Anyway, assuming that rep clauses such as the above are illegal, then
+> I don't see any privacy breakage concerns with this proposal.
+
+They're legal, although an implementation could reject them for their own
+reasons (I haven't been able to think of any case covered by the Recommended
+Level of Support).
+
+...
+> However, if the privacy breakage in 13.1(10) is considered acceptable,
+> then I can see how my proposed change to 7.5(2.1) could make things
+> worse from a privacy breakage standpoint.
+
+I don't think the privacy breakage is acceptable per-se (if it was up to me,
+I would do the entire set of representation items differently), but eliminating
+it could be a significant incompatibility and surely would be a disincentive
+to using private types. I don't think we want such disincentives. And even
+with such a rule change, we'd still need the complex and narrow rule you
+proposed, which at best looks like a wart and at worst looks like something
+added to appease someone.
+
+Indeed the rule reminds me of the speed limit signs in Illinois. When you reach
+the Illinois border, you first see a large "Speed Limit 65" sign. But then
+shortly afterwards you see "Speed Limit 55 <many lines of tiny text>".
+There is never enough time to read the tiny text as you drive by at highway
+speeds. It starts out "Trucks over nnn GW...", but the last few lines could
+hide almost anything. I always tell people that the last time is ", and green
+cars on Tuesdays." (which is relevant because I now drive a dark green
+car.) I doubt that most drivers have actually read the whole list, meaning
+that they have no way to know if it applies to them. I don't think we need
+rules like that in Ada.
+
+****************************************************************
+
Questions? Ask the ACAA Technical Agent