!standard 4.06 (12) 02-05-14 AI95-00246/05 !standard 4.06 (09) !class binding interpretation 00-10-31 !status Amendment 200Y 02-05-14 !status ARG Approved 6-0-0 02-02-12 !status work item 00-10-31 !status received 00-10-31 !priority Medium !difficulty Medium !qualifier Error !subject View conversions between arrays of a by-reference type !summary A value conversion between two limited arrays that do not have a common ancestor is illegal. A view conversion between two arrays that do not have a common ancestor is illegal if their component type has a subcomponent of a by-reference type or a private type (which might turn out to be a by-reference type). !question Consider the following example: type T is limited null record; -- by-reference type type A1 is array (1..2) of T; for A1'Component_Size use 8; type A2 is array (1..2) of T; for A2'Component_Size use 32; procedure P (X : in out A1) is begin ... end P; Y : A2 := ...; ... P (A1 (Y)); -- A view conversion, passed by-reference. Inside of P, it seems legitimate for the generated code to assume that the component size is 8, e.g. in indexing the array. However, the view conversion will pass an array with a different component size. This is very inefficient to implement. Is this the intent? (No.) !recommendation (See summary.) !wording (See corrigendum.) !discussion Clearly, there is no practical way to generate code for the view conversion. Since the type in question is by-reference, the implementation cannot make a copy in order to get the appropriate representation. This issue is wider than just view conversions. Consider: procedure R (X : in A1) is begin ... end R; R (A1 (Y)); -- A value conversion, passed by-reference. In this case, we have a value conversion. Again, the implementation cannot make a copy in order to get the appropriate representation, because the components are limited by-reference. From a language design perspective, this occurs for all limited types. However, the only limited types which are not by-reference are limited private types that are completed with a non-limited type -- these have a copy operation and thus do not pose an implementation problem. It might appear that this sort of problem would occur for other untagged composite conversions. (Such conversions must be between derived types.) However, 13.1(10) prevents these sorts of problems for type-related aspects of by-reference derived types. So we don't need a rule to handle them. AARM 13.1(10.b) explains that this rule exists precisely to prevent this sort of problem. The model for subtype-related attributes is that implementations do not allow the specification of 'Size and/or 'Alignment values that would cause problems accessing reference parameters. Thus, no rule is needed to handle this case. Therefore, the only problems occur for conversions between unrelated array types. For both problems, the program needs to be made illegal. This could be accomplished by making the representation clauses illegal, or by making the conversion illegal. Making representation items on (array) by-reference types illegal is not very attractive. Such a rule would prevent the use of convention pragmas along with other items. Moreover, array conversions between unrelated by-reference types is rare (and view conversions of such types even rarer). Thus, making the representation items illegal would eliminate a potentially useful capability in order to eliminate a problem with a rarely used feature. Therefore, we make the conversions illegal. The rules we've adopted are In an array value conversion, if the target type and the operand type do not have a common ancestor, then neither type shall be limited. In an array view conversion, if the target type and the operand type do not have a common ancestor, then the operand subtype shall not have a tagged, private, limited, or volatile subcomponent. These rules have the benefit of not breaking privateness of private types, nor need to define an "assume-the-worst" rule for generics. This holds to the contract model; the legality of a conversion does not depend on the full definition of a type. It means that effectively we're using an assume-the-worst rule for all private types. In addition, these rules can be checked before freezing. This is important for the first rule (value conversions can happen in default expressions, and we do not want to have to delay legality checking). It is not important for the second rule (untagged view conversions cannot happen before freezing, as long as IN OUT parameters are not allowed on functions). Note that these rules have considerable overlap: both rules are triggered for the example in the question. However, each rule covers some cases that the other does not: the first covers value conversions of limited types, and the second covers view conversions of non-limited by-reference types. Alternative rules were considered: In an array conversion, if the target type and the operand type do not have a common ancestor and are limited, then neither type may have a representation item. In addition to the normal places that legality rules apply, this also applies in the private part of a generic unit. This rule is checked in an assume-the-worst manner in generic bodies. In an array view conversion, if the target type and the operand type do not have a common ancestor and are a by-reference type, then neither type may have a representation item. In addition to the normal places that legality rules apply, this also applies in the private part of a generic unit. This rule is checked in an assume-the-worst manner in generic bodies. These rules were rejected mainly because they cannot be checked until both the target and operand types are frozen. (As we saw previously, conversions can happen before freezing in default expressions.) In addition, these rules require breaking privateness (the representation items may be on the full type). Finally, these rules don't insure implementable behavior: there is no requirement for an implementation to select the same representation for similar type declarations. !corrigendum 4.6(9) @drepl If the target type is an array type, then the operand type shall be an array type. Further: @dby If the target type is an array type, then the operand type shall be an array type. The target type and operation type shall have a common ancestor, or: !corrigendum 4.6(12) @drepl @xbullet @dby @xbullet !corrigendum 4.6(12.1) @drepl @xbullet @dby @xbullet @xbullet !ACATS test Create a B-Test which checks that the new rules are enforced. Include a check inside a generic unit. !appendix From: Pascal Leroy [pleroy@RATIONAL.COM] Sent: Wednesday, October 18, 2000 7:04 AM > If there is a problem in the RM mandating rep clauses that we are not > sure should be accepted in all compilers, then we should fix this. > It is preemptive evidence of such a situation if a major vendor is > not implementing something that is required by the RM. We have encountered many problems with chapter 13. Let me just take one example that has nothing to do with generics. We reject any non-confirming Component_Size clause on an array of a by-reference type. Why? Because we do not know how to implement view conversion of arrays in this case. Well, we know how to implement it, but that would be insanely inefficient. I suppose that you would see this as a violation of the recommended level of support, but sorry, I cannot feel guilty. And yes, I should have opened AIs for these issues, but given the attention that AIs have gotten lately, I cannot get too motivated... > Pascal's point of view is essentially saying that implementors are > completely free to ignore RM C.2(2) and that is going MUCH too far. Not at all. Pascal is saying that the recommended level of support in chapter 13 is subject to the "impossible or impractical" rule of RM95 1.1.3(4). Of course, the "impossible or impractical" rule is a can of worms that can lead to endless discussions; that's why I am inclined to let the market decide. **************************************************************** From: Robert Dewar Sent: Wednesday, October 18, 2000 7:15 AM <> Can you give a brief example here of where this would cause inefficiency? <> That's an abuse of 1.1.3(4). (I can speak with some historical authority here, since this is just AI 325 :-) If there is ever a case where something would be impsosible or impractical for all targets for a reasonable implementation approach, or for all implementation approaches we can think of, then 1.1.3(4) definitely does NOT apply. This is specigfically intended for target dependent or other special situations. If we find a case where there is NO reasonable implementation, then it is wrong to appeal to 1.1.3(4), instead we should identify this as a bug that should be fixed. I actually think that fixing problems of this kind is a whole lot more useful than many of the other esoteric AI's the ARG has worked on :-) And by the way, I do not think it is the case that AI325 or 1.1.3(4) is a can of worms. We really have seen almost no cases of attempted abuse of this quite reasonable rule (analogous to the general interruption rule in Algol68). But it is important to be vigilant and avoid abuse, and the claim that a feature in the language is fundamentally too inefficient to implement and therefore 1.1.3(4) applies is an abuse :-) All large real programs are full of representation clauses, if we wave our hands and say rep clauses are completely implementation dependent, we are saying goodbye to portability on too large a scale. As the person who helped design and implement the ACVC 1.10 tests that finally demanded some level of portability in rep clauses, this is something that I feel strongly about. And after all, it is clear that the 95 RM feel strongly too, since it put a lot of effort into trying to clearly define this. (Erhard, I trust these messages are more in the style of what you think should happen on this list :-) **************************************************************** From: Pascal Leroy Sent: Wednesday, October 18, 2000 7:47 AM Here is an example: type T is limited null record; -- by-reference type A1 is array (1..2) of T; for A1'Component_Size use 8; type A2 is array (1..2) of T; for A2'Component_Size use 32; procedure P (X : A1) is begin ... -- Here it seems legitimate for the generated code -- to assume that the component size is 8, e.g. in indexing -- the array. end; Y : A2 := ...; ... P (A1 (Y)); -- A view conversion, passed by-reference. Y has 32-bit components, but the generated code for P assumes that its parameter has 8-bit components, so it's unlikely to work. The only way out of this conundrum would be to pass to P (as an extra parameter or as some kind of dope stored with the array) the actual component size, and have the generated code use that value. It would of course be complex and costly, not to mention that it would impose a distributed overhead to deal with the unlikely case where someone plays games with the component size. The ironic twist is that this overhead would be required to comply with (Robert's interpretation of) annex C, but precisely users of annex C is likely to be demanding in terms of performance... **************************************************************** From: Robert Dewar Sent: Wednesday, October 18, 2000 8:00 AM The extra parameter is really out of the question, so I agree this is a hole that needs plugging. <> Well I am not sure there is any interpretation issue here. If I understand this right, this is clearly a mistake. This is quite analogous to the exceptions that have to be made for aliased objects to ensure that pointers to them are interpretable. I tried running this exact example: with Text_IO; use Text_IO; procedure a is type T is limited record x : integer := 3; end record; type A1 is array (1..2) of T; for A1'Component_Size use 8; type A2 is array (1..2) of T; for A2'Component_Size use 32; procedure P (X : A1) is begin for J in X'Range loop Put_Line (X (J).x'Img); end loop; end; Y : A2; begin P (A1 (Y)); -- A view conversion, passed by-reference. end; ----- GNAT rejects the rep clauses, although the message is not exactly clear :-) :-) a.adb:23:07: left hand of assignment must not be limited type this complains about the call, so it is not really a rejection of the rep clause, but a plain bug. I agree there is no obvious implementation here, I think we have to change GNAT to reject the rep clause as you suggest. But I am surprised you do not think this is something that needs fixing in the language! **************************************************************** From: Pascal Leroy Sent: Wednesday, October 18, 2000 8:40 AM > But I am surprised you do not think this is something that needs fixing > in the language! Well, you're right. I guess I have taken the detestable habit of thinking that I could fix chapter 13 "by myself" :-) Note that this is not a single hole, it's an entire family of holes. First, there is a similar problem with alignment. If you write alignment clauses that specify 4-byte alignment for A2 and 1-byte alignment for A2, then it is fine for the generated code of P to take advantage of the fact that its parameter is 4-byte aligned, thereby resulting in better code. However, when P is passed Y, which is only 1-byte aligned, bad things happen (at least on some processors). The only implementation that I can think of would have to look at the least significant bits of the address of its parameter, but again that would be unreasonable. Then, there are additional problems with other types and with objects: 1 - Untagged derived types: again there are issues with view conversions, so the size and alignment of the parent and the derived type must agree. 2 - Record types: a component may be of a by-reference type, in which case a component clause for that component must be confirming in terms of size and alignment. 3 - Objects of a by-reference type: the object must fulfill the promises made by the type, so the alignment of an object must be stricter than that of the type and the size must be confirming. I realize that the above statements are rather vague, and would need to be clarified with example. I'm only dumping the problems I remember. There may be others. In essence, by-reference types that can be view-converted to each other must obey some type of "contract", in the sense that they must all have more-or-less the same representation. Similarly, objects must comply with the "contract" for the type. **************************************************************** From: Robert A Duff Sent: Wednesday, October 18, 2000 9:43 AM > Well, you're right. I guess I have taken the detestable habit of thinking > that I could fix chapter 13 "by myself" :-) I tried that once ("fix" chap 13). Robert jumped all over me for it, too. ;-) > 2 - Record types: a component may be of a by-reference type, in which case a > component clause for that component must be confirming in terms of size and > alignment. Isn't this one covered by 13.1(10)? See also AARM para 10.b. 13.1(24-24.c) also seems relevant to this issue in general. **************************************************************** From: Robert Dewar Sent: Wednesday, October 18, 2000 9:51 AM As I say, this seems very reminiscent of the exceptions we need to make in the aliased case, and the reasons are very much analogous. I think all of these probably fall into a single issue, since it is basically one issue (the notion of "contract" for a type is a useful one here). **************************************************************** From: Pascal Leroy Sent: Wednesday, October 18, 2000 10:01 AM > > 2 - Record types: a component may be of a by-reference type, in which case > > a component clause for that component must be confirming in terms of size > > and alignment. > > Isn't this one covered by 13.1(10)? See also AARM para 10.b. I don't understand. 13.1(10) doesn't have anything to say about records having a component of a by-reference type. Could you explain what you mean? As for AARM 13.1(10.b), it claims that subtype-specific changes of representation are cheap, and I believe that's the fallacy. If you have generated code that assumes 4-byte alignment, passing a parameter which is only 1-byte aligned is not going to be cheap. > 13.1(24-24.c) also seems relevant to this issue in general. It is relevant, but it is insufficiently stringent. It's not only that the component must be addressable, it must be addressable in a way which is consistent with all the view conversions in which it can participate. As I said, there is a contract issue here; addressability is only one item in the contract. **************************************************************** From: Robert A Duff Sent: Wednesday, October 18, 2000 10:52 AM > I don't understand. 13.1(10) doesn't have anything to say about records > having a component of a by-reference type. Could you explain what you mean? I don't know. Maybe I quoted the wrong item on your list, or maybe I was just confused. Anyway, you've certainly made the point that there are some problems in this area. > As for AARM 13.1(10.b), it claims that subtype-specific changes of > representation are cheap, and I believe that's the fallacy. No, that's not talking about by-reference. It's claiming that (by-copy) conversions with subtype-specific changes are cheap, which is true. Note that that part is talking about the Ada 83 rule ("The reason ... *was* ...") (emphasis added). And Ada 83 didn't have by-reference types. The problem here is that we just didn't think of these cases where viewish type conversions and by-ref types conspire to make implementation infeasible. As Robert points out, this is related to the interactions between packing and aliased components and so forth. **************************************************************** From: Jean-Pierre Rosen Sent: Wednesday, October 18, 2000 11:47 AM > I don't understand. 13.1(10) doesn't have anything to say about records > having a component of a by-reference type. Could you explain what you mean? A record with a subcomponent of a by-reference type is a by-reference type. 6.2(8) **************************************************************** From: Tucker Taft Sent: Thursday, October 19, 2000 12:03 PM Pascal Leroy wrote: > I don't understand. 13.1(10) doesn't have anything to say about records > having a component of a by-reference type. Could you explain what you mean? If a record has a subcomponent of a by-reference type, then the record as a whole is a by-reference type (6.2(8)). However, I agree that 13.1(10) is only talking about derived types. You are talking about problems with specifying *any* representation aspects for components (or for types) of a by-reference type. **************************************************************** From: Robert Dewar Sent: Wednesday, October 18, 2000 11:04 AM <<> As for AARM 13.1(10.b), it claims that subtype-specific changes of > representation are cheap, and I believe that's the fallacy.>> Bob said <> That's got to be wrong! The whole model here is that subtype specific changes of representation must be cheap for by reference or the whole mechanism of calling subprograms with reference parameters breaks if there are derived types. Most certainly the understanding is that subtype-specific changes of representation are cheap. That should be true because a) for size, in the case of things you would pass by reference, size can do nothing more than add or subtract padding space at the end. b) for alignment, you do not allow specification of alignments that are smaller than usual in a way that would cause trouble (at least we don't :-) So I think Pascal is right on the description of the change, wrong that it is a fallacy. **************************************************************** From: Erhard Ploedereder Sent: Wednesday, October 18, 2000 12:43 PM > type A1 is array (1..2) of T; > for A1'Component_Size use 8; > type A2 is array (1..2) of T; > for A2'Component_Size use 32; > Y : A2; > begin > P (A1 (Y)); -- A view conversion, passed by-reference. > end; Seems to me that this cannot possibly be a view conversion (this would be semantic madness). If legal at all, this MUST BE a value conversion by any sensible language semantics (and I am not talking just Ada). Is there really no rule in the RM, that view conversions cannot convert between representationally different untagged subtypes ??? I looked and couldn't find any. If that's true, THAT'S A BIG HOLE. 13.6. clearly should apply only to value conversions. What remains to be answered is whether a) the rule of what constitutes a view conversion -- 4.6(5) -- needs to be amended for the second case with "and the source and target subtype have equal representation (see 13)", and copy-in/copy_out conversions prescribed otherwise, illegal for by-reference types and IN OUT generic formal objects. b) the rule for view conversions stays as is and a view conversion among untagged types of different representation is made illegal. Compatibility arguments (with Ada83) favor alternative a) . Simplicity favors alternative b). The generic formal type problem I would solve by an illegality rule. **************************************************************** From: Robert Dewar Sent: Wednesday, October 18, 2000 1:01 PM <> I agree, but really if it is not a view conversion, then the conversion should not be allowed at all in this context, given that it is a by reference type. I would make this conversion illegal in this context. **************************************************************** From: Randy Brukardt Sent: Wednesday, October 18, 2000 2:22 PM We have to be careful here, given that tagged types are all by-reference, and ordinary conversions are value conversions. There is not any (current) rule prohibiting value conversions of by-reference types, and we have to be careful if we introduce one to avoid a serious incompatibility. **************************************************************** From: Erhard Ploedereder Sent: Wednesday, October 18, 2000 1:28 PM > I agree, but really if it is not a view conversion, then the conversion > should not be allowed at all in this context, given that it is a by > reference type. I would make this conversion illegal in this context. Indeed. And that's what I was saying, too. But the problem is not only with by-reference types, but a more general problem (with the standard) even if T is not limited. The RM right now doesn't seem to allow to implement such a call situation by value conversions at all for non-limited parameter types (and that would be an incompatibility with Ada83). You cannot possibly blame it on the rep.specs and make them illegal somehow. It's the view conversion semantics that's at fault. (Plus, it's yet another nail in the coffin of shared generics because of generic formal IN OUT objects, whose semantics prescribe a view conversion.) **************************************************************** From: Erhard Ploedereder Sent: Wednesday, October 18, 2000 1:38 PM Postscript: > The RM right now doesn't seem to allow to implement such a call situation by > value conversions at all for non-limited parameter types (and that would be > an incompatibility with Ada83). Sorry, I take that back. (It's been a long day.) 6.2 is of course the place. But the issue on generic IN OUT formal objects remains, I believe. (Sure, it's easily implemented matching the intent, except for shared generics. But the standard mumbles about a view conversion there and that just ain't right.) **************************************************************** From: Randy Brukardt Sent: Wednesday, October 18, 2000 11:59 AM Since Pascal seems unwilling to initiate the AI, I'll do it, in order that this e-mail exchange has a place to be stored for future reference. Let me try to clarify the issue a bit (or perhaps muddy the waters some more). It seems that the problem occurs only if the type has aliased or by-reference subcomponents (or is such a type). Clearly, if that is not true, we simply have to use copy-in, copy-out for parameter passing in the case that the representations don't match in some significant (to the generated code) way. That is already mandated for elementary types, and is not a big deal for other types. (As I understand it, the reason that doing such conversions implictily is frowned about is accuracy concerns, but that does not apply here, as the conversions in question are explicit.) (Based on the error message Robert reported, it seems pretty clear that that is exactly what GNAT is doing.) However, in the case of aliased or by-reference subcomponents, we cannot copy the item without violating the properties of the component. Thus, if we have a type T that contains aliased or by-reference subcomponents, then for all types that T is view convertible to, the value of any representation property significant to the generated code must be the same. What we need to do, therefore, is amend the "Recommended Level of Support" for representation items to insure such cases are excluded. (After all, these recommendations are required by Annex C.) We don't want to insist that implementations not support this (because implementations are always allowed to go beyond the recommended level of support), but we must not mandate it. Is this a correct summary? I've purposely left the question as to what representation properties are affected blank, because I wanted to summarize the big issue first. **************************************************************** From: Pascal Leroy Sent: Wednesday, October 18, 2000 2:55 PM I agree with the above summary (well, it's rather vague so I'll need to see the details, but the general direction is fine). **************************************************************** From: Tucker Taft Sent: Wednesday, October 18, 2000 2:57 PM > However, in the case of aliased or by-reference subcomponents, we cannot > copy the item without violating the properties of the component. Thus, if we > have a type T that contains aliased or by-reference subcomponents, then for > all types that T is view convertible to, the value of any representation > property significant to the generated code must be the same. An alternative is to disallow view conversions in certain cases. Remember that for untagged types, view conversions only appear when an actual is a conversion, and the mode is [in] out. This is not a highly used feature of the language, I would guess. Disallowing its use for by-reference types (even if they happen to have the same representation) might not be a major incompatibility. Disallowing certain rep clauses on by-reference types might be a bigger incompatibility. **************************************************************** From: Robert Dewar Sent: Wednesday, October 18, 2000 1:55 PM <> I am concerned that prohibiting useful rep clauses for by reference types just so that such conversions can work is far too draconian. I would prefer a solution that just makes the conversions invalid. **************************************************************** From: Pascal Leroy Sent: Wednesday, October 18, 2000 3:01 PM Nobody talked about "prohibiting useful rep clauses". The point is to "not mandate them" if the implementation doesn't know how to support them efficiently. Considering that view conversions occur everywhere in OOP (because of conversions to the parent type) I would be extremely concerned that changing anything in this area would break existing code... **************************************************************** From: Robert Dewar Sent: Wednesday, October 18, 2000 3:10 PM Well it seems like you want to solve this by disallowing the rep clause. I would prefer to be more permissive by allowing the rep clauses, but then disallowing the bogus conversion. You can't just take the position that I can allow the rep clauses if and only if I allow the conversion because a) that makes the old mistake of saying you can't offer A, only A and B, but you can offer neither b) that *does* amount to prohibiting useful rep clauses in practice if not in theory. **************************************************************** From: Robert Dewar Sent: Wednesday, October 18, 2000 2:00 PM Actually our attempt to find out what a view conversion was proved a bit surprising, it seems like a lot of conversions are defined as view conversions in a surprising manner, but perhaps we are missing something. **************************************************************** From: Pascal Leroy Sent: Wednesday, October 18, 2000 2:52 PM > > type A1 is array (1..2) of T; > > for A1'Component_Size use 8; > > > type A2 is array (1..2) of T; > > for A2'Component_Size use 32; > > > Y : A2; > > > begin > > P (A1 (Y)); -- A view conversion, passed by-reference. > > end; > > Seems to me that this cannot possibly be a view conversion (this would > be semantic madness). > > If legal at all, this MUST BE a value conversion by any sensible > language semantics (and I am not talking just Ada). Well, it cannot be a value conversion in the general case, because T could be one of these things that you cannot copy (eg a record with a self-referential access component). > Is there really no rule in the RM, that view conversions cannot convert > between representationally different untagged subtypes ??? I looked and > couldn't find any. If that's true, THAT'S A BIG HOLE. There is a meta-rule of Ada that the static semantics don't depend on the representation aspects, just on the logical properties of types (this rule was very prominently stated in the rationale for Ada 83). Hopefully you are not suggesting to break it?! We have had a similar discussion circa AD 1995 about the meaning of "statically matching subtypes", and as far as I recall, Robert was arguing that subtypes don't match if they have different representations. He lost the argument at the time, but beware, he's fighting back :-) I entirely disagree with your proposed fix. The component size clause is really what's causing trouble, an implementation must be allowed to reject it, period. Changing the rules about view conversions (and generic in out) at this point doesn't make sense to me. **************************************************************** From: Robert Dewar Sent: Wednesday, October 18, 2000 3:20 PM Perhaps we should allow rejection, but in practice Pascal is trying to *insist* that it be rejected (his point that you can allow it if you figure out how is bogus, since we know there is no good implementation model that would allow this conversion). Once again, Pascal is saying 1. My view is that you are allowed to accept the rep clause ONLY if you also allow the conversion. But you can reject both as we have chosen to do. 2. Even though an implementation that accepts the rep clauses byut disallows the conversion is clearly more powerful than one that allows neither, such an implementation should not be tolerated. I really dislike this "nothing is better than something" attitude, especially when the something is clearly useful. **************************************************************** From: Stephen Michell Sent: Wednesday, October 18, 2000 2:15 PM Seems to me that this problem is just other examples of AI-168. AI-168 started with Arrays, but the underlying issue is the disconnect between objects with representations different than one would expect from it's type as expressed in, say, a subprogram parameter. **************************************************************** From: Randy Brukardt Sent: Wednesday, October 18, 2000 3:43 PM Steve said: > Seems to me that this problem is just other examples of AI-168. AI-168 started > with Arrays, but the underlying issue is the disconnect between objects with > representations different than one would expect from it's type as expressed > in, say, a subprogram parameter. AI-168 is in the corrigendum, and the solution to the array problem was to disallow certain view conversions. Pascal said: > There is a meta-rule of Ada that the static semantics don't depend on > the representation aspects, just on the logical properties of types > (this rule was very prominently stated in the rationale for Ada 83). > Hopefully you are not suggesting to break it?! This seems to be a powerful argument for Pascal's position, except that he then undermines it... > We have had a similar discussion circa AD 1995 about the meaning of > "statically matching subtypes", and as far as I recall, > Robert was arguing that subtypes don't match if they have different > representations. He lost the argument at the time, but beware, he's > fighting back :-) I remember the argument, but I would be very wary of claiming who won or lost. This is AI-108, which is still open. That is, it never really got resolved. Indeed, its pretty much impossible to implement pointer conversions if static matching does not imply identical representations (that's why those things require static matching in the first place, because one of the UI teams (mine) reported problems with 'Access on arrays with non-matching constraints (such as unconstrained array parameters). **************************************************************** From: Pascal Leroy Sent: Thursday, October 19, 2000 3:23 AM > Seems to me that this problem is just other examples of AI-168. AI-168 started > with Arrays, but the underlying issue is the disconnect between objects with > representations different than one would expect from it's type as expressed in , > say, a subprogram parameter. That's related, but not identical. AI-168 had to do with aliased-ness of components, which is a logical property of the type, so we just fixed it by adding a new legality rule. This didn't create contract model problems because the aliased-ness of the components is part of the contract. And it didn't require to look at the representation. **************************************************************** From: Robert Dewar Sent: Wednesday, October 18, 2000 4:04 PM <> Right, as I said earlier, to me the issue of ensuring that you can deal with what's at the end of an access value is *VERY* similar to the issue of ensuring that you can deal with a reference parameter. Indeed in most implementations these will be *identical* issues. **************************************************************** From: Pascal Leroy Sent: Thursday, October 19, 2000 4:14 AM > I remember the argument, but I would be very wary of claiming who won or > lost. This is AI-108, which is still open. I believe you mean 109. > That is, it never really got > resolved. Indeed, its pretty much impossible to implement pointer > conversions if static matching does not imply identical representations But precisely there is no problem because you cannot have representation clauses for non-first subtypes (even for subtype-specific aspects). This is what triggered the discussion at the time: Robert wanted to be able to specify Size for non-first subtypes, and the ARG said no-no (even though AI-109 is still opened, it doesn't mention specifying Size for non-first subtype). I believe that Robert went and implemented a Subtype_Size attribute in his compiler because he thought that was useful. **************************************************************** From: Robert Dewar Sent: Thursday, October 19, 2000 8:23 AM Wrong viewpoint, our customers needed this and we responded (well I guess you could make that the definition of what we consider useful, which would be fair). We needed this so that we can duplicate the effect of the Size clause in Ada 83 if necessary where it is too painful to modify the code to assume the Ada 95 default size scheme. We implemented two new attributes Value_Size and Object_Size which are indeed applicable to subtypes. **************************************************************** From: Robert Dewar Sent: Wednesday, October 18, 2000 3:17 PM <> Why not just say that such conversions are not permitted if they would cause a change in representation. That's clearly the spirit of conversions of non-scalar objects in such positions anyway, and I agree with Randy, this would be a negligible impact restriction (indeed neither Rational nor GNAT allows this construction, for different reasons, and certainly we have never had the "bug" reported that we do not allow them, so they cannot be that common :-) **************************************************************** From: Pascal Leroy Sent: Thursday, October 19, 2000 3:26 AM > An alternative is to disallow view conversions in certain > cases. Remember that for untagged types, view conversions > only appear when an actual is a conversion, and the mode > is [in] out. This is not a highly used feature of > the language, I would guess. Disallowing its use > for by-reference types (even if they happen to have the > same representation) might not be a major incompatibility. > Disallowing certain rep clauses on by-reference types might > be a bigger incompatibility. I can live with this rule, as long as the provision "even if they happen to have the same representation" is kept (because then we don't have to look at the representation). **************************************************************** From: Robert Dewar Sent: Thursday, October 19, 2000 7:50 AM Well we already have semantics that depend on whether representation clauses are present, contrary to Pascal's supposed invariant: 9.10 1 If two different objects, including nonoverlapping parts of the same object, are independently addressable, they can be manipulated concurrently by two different tasks without synchronization. Normally, any two nonoverlapping objects are independently addressable. However, if packing, record layout, or Component_Size is specified for a given composite object, then it is implementation defined whether or not two nonoverlapping parts of that composite object are independently addressable. So I think the proper condition, quite consistent with the above viewpoint is that the view conversion should be permitted if no representation clauses are given for either item, but are forbidden if representation clauses are given for either item. It is unnecessarily restrictive to forbid it in the case where there are no rep clauses, and as Erhard says, no point in providing yet another reason to avoid the LIMITED keyword. One could also allow it in the following situations: 1. Derived type which inherits all its representation aspects from the parent type. 2. Two types for which identical sets of rep clauses are given For *SURE* we cannot permit it in the following case 3. Rep clause given for one type, which just happens to confirm the default representation and therefore "is the same as" the representation for the other type. There definitely IS an invariant that the front end of an Ada compiler does not need to know the layout of composite types, and that's something we definitely cannot break (GNAT for example absolutely depends on that, because layout is done deep in the backend, long after all legality checks of this kind are complete) So if we go the route of allowing certain view conversions to be illegal, then we need to discuss which. Note that if we do the following 1. Allow implementations to reject rep clauses on limited types as Pascal thinks is reasonable. 2. Allow implementations to reject conversions if and only if there is a change of representation known to the front end (points 1 and 2 above, but NOT 3), or more conservatively point 1 alone. Then everyone can live with this. Pascal does not have to make any changes in the Rational compiler, Robert does not have to make any changes in the GNAT compiler (though the error message could be more informative :-) At this stage in the game, I think you can assume that the major Ada vendors have put into their compilers the features that their customers really need, including issues of portability, since we are all faced with customers moving from one compiler to another who worry about portability issues (in fact their worry goes far beyond the minimal RM requirements, you can tell a customer -- sorry this feature from compiler X is not in the RM, so you are wrong to expect it in GNAT, but the real market does not work that way, we have all been subject to pressure to implement all kinds of implementation dependent stuff in our compilers for portability purposes, e.g. all the DEC Ada attributes and pragmas in GNAT. This means that placing new requirements on vendors to pass new ACATS tests that test new changes or interpretations in the language at this stage will almost always force vendors to spend time in inappropropriate ways. a) I think it would be silly to make Rational waste time implementing rep clauses that their customers do not need. b) I think it would be silly to make GNAT waste time (and cause its customers incompatibilities) by disallowing rep clauses that it now allows. Therefore we should look for solutions that require neither, but still make reasonable semantic sense. When you initially design a language where there are no implementations, you can always do things completely cleanly. Once there are implementations you compromise, especially on non-important things. I have NEVER argued that I think the rep clauses in question MUST be accepted by an Ada 95 compiler. I have argued that I think the RM currently says they should be accepted. This discussion started if you remember over the question of whether it is necessary to take the trouble to worry about this at the ARG level. These discussions clearly show that it is. Anyway going back to points a and b above, I think it is important here to do nothing that causes unnecessary disturbance. One way of doing this is to do absolutely nothing here, and forget we ever noticed this issue. That makes me uncomfortable, but I could live with it. My preferred solution is to try to fix the RM with minimal effort so that both a) and b) points are taken into full account. We still do not have input from other compilers, Tuck, what's the Intermetrics story here? **************************************************************** From: Pascal Leroy Sent: Thursday, October 19, 2000 8:39 AM It is true that this paragraph (9.10(1)) is a Static Semantics rule, but it doesn't seem to have any bearing on Legality Rules. What I am really concerned about are legality rules, ie cases where a construct (which is not a representation item) is legal or not depending on representation choices. > One could also allow it in the following situations: > > 1. Derived type which inherits all its representation aspects from the > parent type. > > 2. Two types for which identical sets of rep clauses are given These are interesting suggestions, and certainly more acceptable than "when the representation happens to be the same by sheer luck". Two issues come to mind, though: 1 - What do you do in generics, where you don't know whether or not the formal types have representation clauses? I suppose you have to recheck in the specification and assume the worst in the body. 2 - I wonder if a view conversion can happen before the freezing point of either type. If that's possible, then we'll need a special rule, as it could be that the set of representation clauses is not known when the view conversion occurs. Of course, whatever fix we do to the view conversion side with will require violating the privacy of private types. That's sobbering, but then that's because the notion of by-reference types itself violates the privacy. Sigh. > 2. Allow implementations to reject conversions if and only if there is > a change of representation known to the front end (points 1 and 2 above, > but NOT 3), or more conservatively point 1 alone. If we wanted to go that way, it would be "require implementations to reject conversions", not "allow". The last thing we want is implementation-dependent legality rules. > One way of doing > this is to do absolutely nothing here, and forget we ever noticed > this issue. > > That makes me uncomfortable, but I could live with it. Me too. **************************************************************** From: Robert Dewar Sent: Thursday, October 19, 2000 9:03 AM <> Please assume that everyone agrees with this <> Please assume that everyone agrees with this :-) **************************************************************** From: Erhard Ploedereder Sent: Thursday, October 19, 2000 4:51 PM > 2 - I wonder if a view conversion can happen before the freezing point of > either type. If that's possible, then we'll need a special rule, as it > could be that the set of representation clauses is not known when the view > conversion occurs. Looks like that is indeed possible because of default expressions. E.g., something totally harebrained like: package P is type A is array ... -- by reference AObj: A; end P; ... use P; type B is array ... -- by reference function F(Y: in out B) return Integer; type C(Discr: Integer := F(B(AObj))) is record .... -- B not frozen here pragma Pack(B); **************************************************************** From: Robert Dewar Sent: Wednesday, October 18, 2000 3:29 PM <> Value conversions are just fine, that's the *proper* way to achieve change of representation! The rule I would propose again is simply this. View conversions are not permitted if there is a change of representation involved. **************************************************************** From: Randy Brukardt Sent: Wednesday, October 18, 2000 3:50 PM I think *that* would be a serious incompatibility with Ada 83, because you would be disallowing any conversion in an IN OUT parameter where there was a change of representation. Note that such IN OUT parameters can be passed by copy/result as long as the type in question is not a by-reference type. (Parameters passed by copy-result still are technically view conversions.) I think the rule has to be: View conversions are not permitted for untagged by-reference types if there is a change of representation involved. (Because those don't have the option of falling back to copy semantics.) Then, of course, we have to figure out what "change of representation" means. :-) (13.6 never defines that as a term, even though it's the title of the clause.) **************************************************************** From: Robert Dewar Sent: Wednesday, October 18, 2000 4:06 PM Yes, indeed, that's what I meant, but did not say clearly. Change of representation simply means that one or more representation attributes differ for the two types. I don't think that's so hard to define. **************************************************************** From: Robert Dewar Sent: Wednesday, October 18, 2000 3:28 PM <> View conversions where there is a change of representation certainly don't appear in a compiler which forbids this from happening, so this is simply a bogus argument. Disallowing the rep clauses might certainly break existing code in the GNAT case, but not in the Rational case. But disallowing view conversions where there is a change of representation cannot break either GNAT or Rational code, since neither compiler allows this. (for interest what does the intermetrics front end do here?) I am certainly not strongly arguing that a compiler be forced to accept these rep clauses (I really don't care if compilers other than GNAT do or do not accept particular rep clauses, because in practice, we find that most real apps have to go far beyond the rather pathetic minimal rep clause requirements of the RM. This means that whatever the ARG decides in this area, GNAT will accept many rep clauses not required by the RM, and which therefore may or may not be accepted by other compilers. With my purity-of-language hat on, I think that if there are cases of rep clauses mandated by the RM that are not accepted by some compiler, then EITHER the compiler OR the RN should be fixed (I really don't care too much which). But the issue here is quite different, Pascal is arguing that other compilers be forced to disallow programs that they currently allow. That definitely could break existing code. So my recommendation here is that a compiler not be forced to accept rep clauses on by-reference types. This avoids someone creating a test for this, and Rational wasting time implementing what they see as a useless feature. It is very damaging when ARG rulings have this effect. But also, a compiler should be allowed to accept the rep clauses and reject the conversion (otherwise GNAT has the same complaint, someone will make a test, and actually the effect is worse, because we don't know how to implement this in an upwards compatible manner). So Pascal, how does that sound to you? Does that make sense? There may be people who feel that the rep clauses must be accepted if we change things so that the conversion can be rejected as illegal, but I am not in favor of that approach. **************************************************************** From: Randy Brukardt Sent: Wednesday, October 18, 2000 4:13 PM > There may be people who feel that the rep clauses must be accepted if > we change things so that the conversion can be rejected as illegal, > but I am not in favor of that approach. Having *my* purity of language hat on, I can't see why we should make two changes to the language when one will fix the problem. It clear that if we want to disallow the conversions, that requires an RM change. Similarly, disallowing the rep. clauses requires an RM change (if Annex C is in force, and otherwise nothing is required so there is no problem). I don't see a reason to make two unrelated RM changes to fix one problem. The issue of a test is an interesting one; if we change the view conversion rule, by all means we ought to have test(s). But such a test would be applicable only to an implementation claiming conformance to Annex C (at least, we could only require it there). There certainly is nothing to test for an rep. clause rule changes (I think the ACATS mainly tests for required Annex C support; you usually can't test that something is not supported, because it could be in most cases). **************************************************************** From: Pascal Leroy Sent: Thursday, October 19, 2000 4:07 AM > So my recommendation here is that a compiler not be forced to accept > rep clauses on by-reference types. This avoids someone creating a > test for this, and Rational wasting time implementing what they > see as a useless feature. It is very damaging when ARG rulings have > this effect. > > But also, a compiler should be allowed to accept the rep clauses and > reject the conversion (otherwise GNAT has the same complaint, someone > will make a test, and actually the effect is worse, because we don't > know how to implement this in an upwards compatible manner). > > So Pascal, how does that sound to you? Does that make sense? I agree with Randy that having two different changes to the language to plug one hole is ugly. Moreover, your proposal destroys portability in a big way. It's one thing to have representation clauses that fail to port from one compiler to another (users won't be too surprised) but it's another thing to have view conversions that fail to port from one compiler to another. I would also want more details on how rejecting the conversion is supposed to play with generics. As you can imagine, my preference goes to rejecting the rep clause. My second choice is Tuck's proposal, i.e. reject view conversions (of nasty types) regardless of the representation. **************************************************************** From: Robert Dewar Sent: Thursday, October 19, 2000 8:16 AM I think we all agree that we don't want the issue of whether conversions are legal or not to be implementation dependent. That is another, strong and convincing reason, not to allow behavior 3. from my previous note: 3. Rep clause given for one type, which just happens to confirm the default representation and therefore "is the same as" the representation for the other type. (here we are talking about conditions that affect the legality of the conversion). So that is not arguable (we have to argue about the rules, but no one I believe is pushing for implementation dependent rules). It is one thing to have critical runtime semantics be implementation dependent as in the shared variable case [although I think allowing impl dependent behavior there instead of saying it was erroneous was a mistake even in that context], but it is quite another to allow impl dependent legality conditions [we have examples that depend on representations already, e.g. when the tag on a case is Integer'Last, but let's not make more! Now Pascal says "I agree that having two different changes to plug one hole is ugly" Maybe, but if you insist on one change only here, then I think it has to be the illegality of the conversion (and you leave the rule intact that requires the rep clauses to be accepted). Why? Because I think at this stage of the game it is far too contentious to suddenly invent a change to the language that makes previously legal and working programs illegal by making a useful construct illegal. Note that in the case of the "bad" conversions, we can be pretty sure that althougth theoretically from the RM these are legal, no one can be using them, since no one knows how on earth they might be implemented. So insisting on one change only pretty much has to result in being forced to accept the rep clauses. Of course in my opinon, you ARE accepted to /// sorry /// required to accept these. I think it is silly to force anyone to accept these, but perhaps in practice we can just restrain Randy from making a counter-productive test in this area, and the status quo can continue of ignoring this requirement, or deciding that it is not a requirement without discussing it. Incidentally, we need to be very clear about whether new tests should appear in any particular area. Test writers tend to get into the frame of mind that if they can possibly see that failure to write a test MIGHT allow two compilers to do things differently, then of COURSE a test must be written. I definitely see Randy operating in this mode, much as all previous writers of ACVC tests have done. It is a natural view point, but it is potentially destructive. For example, it might well be that whatever the outcome of this discussion is, then the best thing is simply not to write a test. Of course if we change things the way I recommend (the "two different changes" solution), then no tests will come to mind :-) Please note that I am not criticizing Randy here, it's his job to write tests, or at least part of his job, and so he will do it, unless there are clear guidelines. **************************************************************** From: Randy Brukardt Sent: Wednesday, October 18, 2000 4:17 PM Tuck, Pascal, anyone else: <> Is there a contract model issue with this rule? I wonder how one would determine this for a generic formal type. **************************************************************** From: Gary Dismukes Sent: Wednesday, October 18, 2000 7:07 PM I was wondering the same thing, but there doesn't seem to be a way to create a contract model problem using private types (at least not that I've found so far ;-). However, in the case of generic formal types, I can think of one case that seems to be a problem, namely for a generic formal array type a by-reference component type, where there's a specification of Component_Size on the actual array type and another array type inside the generic with a differing Component_Size (basically the same as Pascal's example). Again, view conversions won't work. It seems that the worst case has to be assumed (i.e., that a change of representation will occur) and the view conversions made illegal. This is a little nasty since the restriction would have to be applied even in cases where the type within the generic doesn't have a specified representation. **************************************************************** From: Pascal Leroy Sent: Thursday, October 19, 2000 3:46 AM I believe you can do many evil things with untagged derived types. Look at the following example, which is a variation on the alignment example I sent in a previous message: type T1 is limited record I : Integer; end record; for T1'Alignment use 4; type T2 is new T1; for T2'Alignment use 1; procedure P (X1 : T1) is ... end; generic type F1 is limited private; type F2 is new F1; with procedure P (X1 : F1); package G is ... end; package body G is X2 : F2; begin P (F1 (X2)); -- Legal? end; package I is new G (F1 => T1, F2 => T2, P => P); If the code for P assumes that the component I is 4-byte aligned, then in the instantiation we have a problem, because X2 is not necessarily 4-byte aligned. So it seems to me that in generics you would have to disallow any conversion that might be a view conversion of by-reference types. In general, any rule that requires knowledge of the representation will wreak havoc in generics. Either it will kill the contract model, or it will have to be checked in an assume-the-worst manner, and that is bound to cause existing code to become invalid. **************************************************************** From: Robert Dewar Sent: Thursday, October 19, 2000 7:55 AM Here is what GNAT says: Compiling: k.ads (source file time stamp: 2000-10-18 08:55:04) 8. for T2'Alignment use 1; | >>> alignment for "T2" must be at least 4 10 lines: 1 error I frankly don't see how a compiler can operate if it allows the code that Pascal quotes on a machine with strict alignment, how on earth would you avoid assuming the worst when the record was passed by reference (yes of course this particular record could be passed by value, but we can make a larger example). **************************************************************** From: Pascal Leroy Sent: Thursday, October 19, 2000 8:50 AM What we do in the non-by-reference case is to generate a properly aligned copy at the call site. This is part of the "change of representation" that takes place during the conversion. Of course, we cannot do that for by-reference types. As you can imagine, we don't do that just for the fun of it, we have customers who care. **************************************************************** From: Robert Dewar Sent: Thursday, October 19, 2000 9:07 AM OK, that makes sense and indeed we do the same thing (the message I quoted to you for that nasty conversion comes precisely from the internal attempt by the compiler to do this :-) **************************************************************** From: Pascal Leroy Sent: Thursday, October 19, 2000 3:17 AM > That's got to be wrong! The whole model here is that subtype specific > changes of representation must be cheap for by reference or the whole > mechanism of calling subprograms with reference parameters breaks if > there are derived types. Most certainly the understanding is that > subtype-specific changes of representation are cheap. That should be > true because > > a) for size, in the case of things you would pass by reference, size can > do nothing more than add or subtract padding space at the end. > > b) for alignment, you do not allow specification of alignments that are > smaller than usual in a way that would cause trouble (at least we don't :-) > > So I think Pascal is right on the description of the change, wrong that it > is a fallacy. I am not sure what you are saying, so let me try to clarify what I believe is wrong with subtype-specific aspects (well, I will focus on Alignment for now). Consider the following variation of my original example: type T is limited record C : Integer; end record; -- by-reference type A1 is array (1..2) of T; for A1'Alignment use 4; type A2 is array (1..2) of T; for A2'Alignment use 1; procedure P (X : A1) is begin ... -- Here it seems legitimate for the generated code -- to assume that the array and its components are -- 4-byte aligned. end; Y : A2 := ...; ... P (A1 (Y)); -- A view conversion, passed by-reference. Take for instance the Alpha processor. It is legitimate for a compiler to generate in the body of P the instructions that assume that data is aligned (e.g. LDQ). But for the view conversion to work, the compiler would have to use the instructions that don't assume any particular alignment (e.g. LDQ_U), or else alignment traps will happen (which would either cause the program to die, or cause it to slow down by a factor of 1E6). Are you saying in your item (b) above that you reject the alignment clause for A2 because the alignment is too small? Fine with me, that's what we do too. Or are you saying that there is really no problem in this case? **************************************************************** From: Robert Dewar Sent: Thursday, October 19, 2000 7:35 AM In your example, you are supposing that the compiler will permit less than the standard alignment for the record. Here is the output when you compile your example with GNAT: 8. for A2'Alignment use 1; | >>> alignment for "A2" must be at least 4 The RM specifically says: 31 An implementation need not support specified Alignments for combinations of Sizes and Alignments that cannot be easily loaded and stored by available machine instructions. This is *exactly* intended to address the situation where you are on a machine with strict alignment requirements where an alignment value, like the one above, would cause efficiency problems. Note also that once again, in your example, the difficulty is caused specifically by the kind of conversion that we wish to make illegal. Yes, a compiler can get itself into trouble by supporting inefficient alignments and trying to allow view conversions when representations differ, but we all agree that there is a problem with these conversions in this context anyway. That does not detract from the general idea that when you pass arguments by reference there are no expensive implicit conversions from subtype specific attributes (horrible term incidentally, calculated to confuse, and successful in that aim -- many of our users assume that subtype specific attributes should be able to be specified for subtypes -- silly them :-) **************************************************************** From: Robert A Duff Sent: Thursday, October 19, 2000 8:01 AM When the term was invented, you *were* able to do that. We added the rule you don't like at the last minute. **************************************************************** From: Robert Dewar Sent: Thursday, October 19, 2000 8:55 AM Yes, I know that well, and it leaves the language in a mess, because you can no longer influence the size behavior of subtypes, whereas in practice you could in Ada 83 (because subtypes had the same size behavior as the base types in most Ada 83 compilers). **************************************************************** From: Pascal Leroy [pleroy@RATIONAL.COM] Sent: Thursday, October 19, 2000 7:56 AM > 8. for A2'Alignment use 1; > | > >>> alignment for "A2" must be at least 4 > > The RM specifically says: > > 31 An implementation need not support specified Alignments for > combinations of Sizes and Alignments that cannot be easily loaded > and stored by available machine instructions. Well, I'm perfectly happy with GNAT rejecting the above alignment clause, in fact we do the same. On the other hand, in the example that I was mentioning (Alpha) it's a strech to say that you cannot easily load a 4-byte quantity which is not 4-byte aligned. In fact there is an instruction for that purpose. On other architectures, granted, this may be impractical. Evidently the decision to accept or reject the clause has to be highly target-dependent. Note that in the case of Alpha, there is no reason to reject the above alignment clause for a non-by-reference type. But then again, I am not interesting in legislating the level of support of representation clauses. **************************************************************** From: Robert Dewar Sent: Thursday, October 19, 2000 8:54 AM Well easily is deliberately a vague word here, allowing an implementation dependent decision. I would say that the degradation in code quality by having to assume worst case alignment for all by-reference parameters definitely qualifies as violating "easily". The penalty is quite considerable (try measuing this on late model alpha's and you will see what I mean). On the other hand, on an architecture like the ia32 where unaligned accesses are handled automatically at the instruction level, there is no reason to reject this alignment clause (I think we still do, but it is hard to justify in that case). **************************************************************** From: Erhard Ploedereder Sent: Wednesday, October 18, 2000 8:28 PM Robert writes: > I am concerned that prohibiting useful rep clauses for by reference types > just so that such conversions can work is far too draconian. I would > prefer a solution that just makes the conversions invalid. I second that, quite emphatically. Such a restriction would be really bad news and yet another cause why people would omit "limited" from their vocabulary, because any record with a limited component could no longer be rep-spec'ed. Pascal wrote: > I entirely disagree with your proposed fix. The component size clause is > really what's causing trouble, an implementation must be allowed to reject > it, period. For the generic example, I agree. And so does the RM : 13.1(23) or do you see this differently ? On the non-generic example, which is actually of a quite different nature, I disagree for the reason above (and some reasons below). Pascal continues: > Considering that view conversions occur everywhere in OOP (because of > conversions to the parent type) I would be extremely concerned that changing > anything in this area would break existing code... We're not touching view conversions of tagged types. Luckily, there is no way to change representation characteristics during tagged type derivation, or standard implementation models for OOP would come to a screeching halt. So, no need to be concerned there, since there isn't a problem there to be solved. Tuck writes: > Why not just say that such conversions (view conversions of untagged > by-reference types) are not permitted if they > would cause a change in representation. That's clearly the spirit > of conversions of non-scalar objects in such positions anyway, and > I agree with Randy, this would be a negligible impact restriction That fine with me (with the qualification that I put in, which I believe you meant), but it clearly goes against Pascal's argument that static semantics mustn't depend on representation aspects. Robert proposes: > So my recommendation here is that a compiler not be forced to accept > rep clauses on by-reference types. This avoids someone creating a test... A wise compromise. :-) Depends on how strongly we believe in Pascal's meta-argument vs. introducing an implementation-dependency. (You can beat me into it, but it takes some beating..:-) -------- Meta-rumblings of a semanticist.... I am sufficiently worried about the narrowness of the path we're walking by the position that a "typed view" is merely a logic view, not also a physical view. Only a very careful crafting of the rules for where view-conversions can and cannot occur prevents disaster. For example, I went off to figure out whether I can have a view-conversion as actual to a generic formal (untagged) IN OUT object. I guess I cannot, because the actual isn't termed an "actual parameter", but a "generic actual parameter", so that 4.6(5) doesn't apply. This is such a narrow distinction. Or am I wrong ? In that case, I have absolutely no way of understanding the semantics of generic formal objects anymore (and this has nothing to do with by-reference types), when the actual is a view-conversion. End of Example. On the OOP side, the meaning of a view conversion is very clear: an assertion that I want the object treated as if it were of this view, with a language guarantee that the object actually has all the representational and operational properties that the view promises. (It may have more, but not less.) These semantics compose nicely with the semantics of all the constructs where the view conversions can conceivably occur. I really have a problem mapping this understanding to the meaning (or find any other meaning) of "view conversion" in the following example: X: Float; procedure foo(Y:Integer); foo(Integer(X)); This is a view conversion by the RM. What does it mean ? "Logically X is now an Integer" ??? The only reason that this will work at all is because it will be immediately followed by an implicit conversion to Integer for passing the parameter. But semantically, this is rubbish, because the implicit conversion takes something that it "view"s as Integer and makes it Integer. Converting an Integer to an Integer and assigning it is a bit copy operation in my book, not some involved change in representation. So the semantics do not compose at all. For the original example with the two arrays (assume that they are non-limited), the language semantics allow passing the array by reference, applying yet another view conversion to the type of the formal. Now, we know that this will not (sensibly) work at the implementation level, but why are we getting into this situation at all ? This interpretation should not be possible in the first place (and it wouldn't, if a conversion between types of conflicting representation were always a value-conversion). P.S. Sorry this got longer than intended. **************************************************************** From: Pascal Leroy Sent: Thursday, October 19, 2000 4:00 AM > Such a restriction would be really bad news and yet another cause why people > would omit "limited" from their vocabulary, because any record with a > limited component could no longer be rep-spec'ed. Have you seen many people using "limited", lately :-) > > The component size clause is really what's causing trouble, an > > implementation must be allowed to reject it, period. > > For the generic example, I agree. And so does the RM : 13.1(23) > or do you see this differently ? Right, 13.1(23) gives permission to reject the clause in a generic. > Tuck writes: > > Why not just say that such conversions (view conversions of untagged > > by-reference types) are not permitted if they > > would cause a change in representation. That's clearly the spirit > > of conversions of non-scalar objects in such positions anyway, and > > I agree with Randy, this would be a negligible impact restriction > > That fine with me (with the qualification that I put in, which I > believe you meant), but it clearly goes against Pascal's argument that > static semantics mustn't depend on representation aspects. The only message from Tuck that I saw on this topic was that he wanted to disallow view conversions of untagged by-reference types _even_if_the_representation_is_the_same_. Did I miss something? I can live with Tuck's proposal, I cannot live with yours. > Depends on how strongly we believe in Pascal's > meta-argument vs. introducing an implementation-dependency. > (You can beat me into it, but it takes some beating..:-) It's not only an implementation-dependency. As I indicated earlier, this causes trouble in generics. You have two options there: either you say "assume the worst", and I believe this is going to create terrible incompatibilities. Or you say "we were not really serious about this generic contract model thing" but then this language is no longer Ada. I must add that it can also create implementation difficulties. A compiler may very well check the legality rules _before_ deciding on the representation of types. If legality rules depend on the representation, such a compiler would have a hard time. This is not just speculation: our compiler used to work like that until fairly recently; it turns out that we did rearchitecturing in this area to accommodate some customer requests, but for all I know there might be compilers out there which do it that way. (As I recall the old Alsys technology worked that way, but it's irrelevant now.) **************************************************************** From: Robert Dewar Sent: Thursday, October 19, 2000 8:17 AM <> Yes, we see people using it fairly frequently, I am surprised that you would think otherwise, perhaps it is because we have more "go-go" Ada 95 enthusiasts. (I could claim it is because of the wonderful capabilities we provide including the ability to use rep clauses on such types, but I don't think this would be valid in real life, although I am sure there are some examples where this is a factor). **************************************************************** From: Tucker Taft Sent: Thursday, October 19, 2000 11:31 AM Erhard Ploedereder wrote: > ... > Tuck writes: > > Why not just say that such conversions (view conversions of untagged > > by-reference types) are not permitted if they > > would cause a change in representation. That's clearly the spirit > > of conversions of non-scalar objects in such positions anyway, and > > I agree with Randy, this would be a negligible impact restriction I didn't write the above. I'm not sure who did. I suggested to make certain view conversions illegal regardless of whether representations matched. > ... > For example, I went off to figure out whether I can have a view-conversion > as actual to a generic formal (untagged) IN OUT object. I guess I cannot, > because the actual isn't termed an "actual parameter", but a "generic actual > parameter", so that 4.6(5) doesn't apply. This is such a narrow distinction. > Or am I wrong ? In that case, I have absolutely no way of understanding the > semantics of generic formal objects anymore (and this has nothing to do with > by-reference types), when the actual is a view-conversion. Remember that a generic formal IN OUT object is equivalent to a rename, but with the added requirement that the renamed object be updatable. Conversions of untagged objects appearing in a rename are *value* conversions, and hence are not updatable. So this implies that a conversion may not be applied for a generic formal actual object of mode IN OUT. > End of Example. > > On the OOP side, the meaning of a view conversion is very clear: an > assertion that I want the object treated as if it were of this view, > with a language guarantee that the object actually has all the > representational and operational properties that the view promises. > (It may have more, but not less.) These semantics compose nicely with > the semantics of all the constructs where the view conversions can > conceivably occur. > > I really have a problem mapping this understanding to the meaning (or find > any other meaning) of "view conversion" in the following example: > > X: Float; > procedure foo(Y:Integer); > > foo(Integer(X)); > > This is a view conversion by the RM. What does it mean ? This is *not* a view conversion. For untagged types, conversions are only view conversions if they are an actual parameter and the formal is of mode [in] out. This formal is of mode "in". Now if you changed this to mode in out, perhaps your question still applies. The critical thing about a view conversion is that you can store a value back into the underlying operand via the conversion. > ... "Logically X is now > an Integer" ??? The only reason that this will work at all is because it > will be immediately followed by an implicit conversion to Integer for > passing the parameter. But semantically, this is rubbish, because the > implicit conversion takes something that it "view"s as Integer and makes it > Integer. Converting an Integer to an Integer and assigning it is a bit copy > operation in my book, not some involved change in representation. So the > semantics do not compose at all. > > For the original example with the two arrays (assume that they are > non-limited), the language semantics allow passing the array by reference, > applying yet another view conversion to the type of the formal. Now, we know > that this will not (sensibly) work at the implementation level, but why are > we getting into this situation at all ? This interpretation should not be > possible in the first place (and it wouldn't, if a conversion between types > of conflicting representation were always a value-conversion). It may have been a mistake to use "view" conversion for the untagged case at all. Remember the key point is that you can store back into it. However, if the parameter must be passed by reference, then the requirements are much more stringent, namely that the view conversion have an *address* that you can pass. Clearly a view conversion that might involve a representation change does not have an address. Hence, I would say that any conversion of an untagged by-reference type where a change of representation is *possible* is necessarily a value conversion, because we can't guarantee that the conversion has a meaningful address. Now as far as I can see, the only problem is with arrays, because 13.1(10) eliminates the problem for non-arrays. So the rule would be that any conversion of a by-reference array type to a different array type (as opposed to simply a different *sub*type) would be a value conversion. An unfortunate aspect of this rule is that whether an array is by-reference is not semantically visible. We could say if the array *might* be by-reference (i.e. it has any visibly by-reference subcomponent, or any private subcomponent), then converting it to a different type is a value conversion. **************************************************************** From: Pascal Leroy Sent: Thursday, October 19, 2000 3:04 PM > Hence, I would say > that any conversion of an untagged by-reference type > where a change of representation is *possible* > is necessarily a value conversion, because we can't guarantee > that the conversion has a meaningful address. > Now as far as I can see, the only problem is with arrays, > because 13.1(10) eliminates the problem for non-arrays. 13.1(10) eliminates the problems created by _type_related_ representation items for non-arrays. I have shown an example with 'Alignment which I believe has the same problem, and applies to non-array types. Or do you think that my example didn't cause trouble? > An unfortunate aspect of this rule is that whether an > array is by-reference is not semantically visible. I appreciate your effort to save the privacy of private types, but it's hopeless, because we already have legality rules that depend on whether or not a type is by-reference (see "allow pass by copy" in C.6(12)). **************************************************************** From: Tucker Taft Sent: Thursday, October 19, 2000 12:12 PM Robert Dewar wrote: > > Actually our attempt to find out what a view conversion was proved a bit > surprising, it seems like a lot of conversions are defined as view > conversions in a surprising manner, but perhaps we are missing something. The only conversions for untagged types that are view conversions are conversions of actual parameters, when the formal is [IN] OUT. That is a pretty rare case in my experience. On the other hand, for tagged types, most conversions are view conversions. **************************************************************** From: Robert Dewar Sent: Thursday, October 19, 2000 2:07 PM I find it surprising that a conversion of an integer to a float can be regarded as a view conversion. I had the image of a view conversion as a situation where you look at the same bits through different glasses, but as often occurs, the actuality does not match the intuition, and indeed at this stage I have no good intuition as to what a view conversion is. **************************************************************** From: Randy Brukardt Sent: Tuesday, October 31, 2000 3:15 PM I think that the best way to look at a view conversion is that it is an L-Value (using the C terminology): something that can be assigned into (implicitly by parameter passing; or, for tagged types, explicitly). I don't think that is *too* hard to visualize; although the name doesn't exactly connote that... **************************************************************** From: Erhard Ploedereder Sent: Thursday, October 19, 2000 12:03 PM for the sake of the argument, Pascal, would you find a position acceptable in which THE EXISTENCE of a rep clause is part of the contract of the type (much like the existence of an "aliased" keyword for the contract of an object) so the rule would be one in which the existence, not the nature, of the rep-clause makes an explicit view-conversion illegal (for untagged by-reference types) ? **************************************************************** From: Robert Dewar Sent: Thursday, October 19, 2000 2:06 PM Sounds reasonable to me, and is quite compatible with the treatment in the shared variable case. **************************************************************** From: Pascal Leroy Sent: Thursday, October 19, 2000 2:56 PM I suppose I could live with that (which is essentially Robert's proposal if I understand correctly). I am not too enthusiastic because: 1 - prior to the freezing point, God only knows what the representation clauses will be, 2 - this violates private parts (representation clauses could be in private parts), 3 - in generics you'd have to assume-the-worst anyway. But at least, that doesn't deface the language. And by the way, don't we also need to include representation pragmas in this discussion? Take my original array example, and say that the two arrays have different conventions. Assume furthermore that these conventions actually result in different layouts (e.g. row-major vs. column major). We would want to prevent a view conversion in that case, wouldn't we? (Damn, I have just undermined my own position: I couldn't get too excited about component size clauses or alignment clauses on by-reference array types. But I must admit, before Robert jumps in, that forbidding convention Fortran on a by-reference array type doesn't seem like the right thing to do...) **************************************************************** From: Erhard Ploedereder Sent: Thursday, October 19, 2000 5:54 PM Surely. The AI should talk about rep. items, not rep. clauses. Convention is a rep. item. **************************************************************** From: Robert Dewar Sent: Friday, October 20, 2000 10:43 AM And so is pack :-) **************************************************************** From: Tucker Taft Sent: Thursday, October 19, 2000 12:34 PM Randy Brukardt wrote: > > Tuck, Pascal, anyone else: > > < there is a change of representation involved.>> > > Is there a contract model issue with this rule? I wonder how one would > determine this for a generic formal type. As I proposed elsewhere, I believe this only needs to apply to conversions to a *different* type of arrays that *might* be by-reference, which means they have a subcomponent that is "visibly" by-reference, or is private. **************************************************************** From: Randy Brukardt Sent: Thursday, October 19, 2000 2:19 PM A) This doesn't answer the question. B) It doesn't make any sense: Some of Pascal's examples use 'Alignment, which is clearly not covered by 13.1(10). **************************************************************** From: Randy Brukardt Sent: Thursday, October 19, 2000 12:45 PM > For example, it might well be that whatever the outcome of this > discussion is, then the best thing is simply not to write a test. > Of course if we change things the way I recommend (the "two > different changes" solution), then no tests will come to mind :-) If we adopt a legality rule like the one I proposed in another message, then it should be tested. Especially the "assume-the-worst" part, which would have an impact on users. It seems that the danger of the test is more with collateral issues (the representation clauses) than with the actual objective [Check that the new view conversion rule is checked]. That could be worked around by allowing the test to be NA if the compiler rejects the clauses, without any Annex C requirement. Note that with the currently proposed rule, we only need to give a confirming item to trigger the rule: Type A is limited record... Type B is new A; For B'Alignment use A'Alignment; So there isn't a lot of reason to reject the clause; so we ought to be able to create tests without making Rational do anything to their handling of rep. clauses. (Indeed, a test for the generic body part of the rule could be constructed without any rep. clauses at all.) Anyway, if there is a concern about compatibility of making some view conversions illegal, then I think we cannot adopt the rule at all. If we adopt a legality rule and specifically say we'll never test it, we're simply bringing a lot of implementation-dependence into an area where there needs to be none. So, I think that if we are unwilling to test the legality rule, we shouldn't adopt it in the first place; that is, label the AI as "No Action". **************************************************************** From: Randy Brukardt Sent: Thursday, October 19, 2000 12:12 PM OK, let me summarize the hundred or so messages that Pascal and Robert have pumped out overnight. :-) We want a rule making the view conversion illegal when the representation *might* differ, testable only by the front-end. It seems that rep. clauses on the parent type are irrelevant; the derived type will have the specified representation unless it tries to change it. So the problem only comes from specifying the representation on the derived type (whether or not it is different). Let me propose a rule. (No I haven't tried to word this carefully.) A view conversion is illegal for an untagged by-reference type in the following case. Let A be the type which is the ancestor of both the target and operand types. Then: * If there is no such type, then the conversion is illegal. * If the operand type is not A, then the operand type may not have any representation items. * Similarly, if the target type is not A, then the target type may not have any representation items. In addition to the usual places where legality rules apply, this also applies to the private part of generic packages. This is checked in an assume-the-worst manner in generic bodies. (The first bullet can only happen for arrays. If the types are different, we have to forget it, because we can't say anything useful about the representations of the arrays.) (The last two sentences are necessary to avoid contract model problems.) I worry somewhat about the compatibility implications of the assume-the-worst rule, but I don't think we have an alternative. (Any generic limited private type could be an untagged by-reference type, so this would disallow all view conversions of such a type in a generic body. Luckily, the only non-limited by-reference types are tagged types, so this rule would not apply to generic private types.) Comments? The description of the rule got a bit complex; we could just disallow representation items on either the target type or operand type, but that seems like it would make some useful programs illegal (ones where there is no implementation problem). I'll send a separate message on testing this. **************************************************************** From: Erhard Ploedereder Sent: Thursday, October 19, 2000 5:00 PM You're forbidding the following currently legal case, for which there is no reason to complain: type A is array(1..10) of T; type B is array(1..10) of T; view-converting back and forth between the two is fine. I'll post a simpler rule in a few moments so you can shoot at it, too. :-) **************************************************************** From: Erhard Ploedereder Sent: Thursday, October 19, 2000 5:42 PM I've tried for my own sake to summarize what has been proposed as solutions so far with the most salient arguments. I may have missed some, but it may help others also to find their way in the jungle.. Here goes... --------------------------------------- Solution 1: Rep. items are forbidden for untagged by-reference types or their component types. Pro: solves the implementation problem of view conversions, is a simple rule, and avoids portability issues Con: is a moderate-to-serious incompatibility for existing code; prohibits a useful capability for the sake of avoiding problems with view conversions, which may be absent in the user code. Solution 2: Rep. items may be rejected for untagged by-reference types or their component types. Pro: highest flexibility to implementers Con: does not solve the implementation problem of view conversions for those that do support the rep. items, and thus is really no different from solution 1, which then has to become the default choice of implementers, unless combined with one of solutions 3 or 4. Solution 3: View conversions between untagged by-reference types that have different representation are illegal. Pro: solves the view conversion problems, retaining maximum functionality Con: makes legality rules dependent on implementation characteristics of types, which would upset several implementations profoundly. Also, the legality rule would essentially become implementation-dependent. Has a generic contract problem for the legality of view conversions inside generics, if formal generic types are involved. The problem seems to be limited to the case, where a formal generic array type is involved. May have a freezing problem. Solution 4: View conversions between untagged by-reference types are illegal, if a rep. item applies to one but not the other type. (Note that this allows conversions among derived types, when only the parent has a rep. item. The precise wording would have to say something like: "...illegal if a representational aspect was specified by a rep. item applied to one type, but not inherited by the other type") Pro: solves the view conversion problems. Con: still a legality rule dependent on representational type properties (but not on their nature, which seems much more acceptable) Has the same generic contract problem as solution 3. May have a freezing problem. **************************************************************** From: Tucker Taft Sent: Thursday, October 19, 2000 5:57 PM What happened to 5) View conversions are prohibited between two different array types if their component type *might* be of a by-reference type (i.e. there is a private or visibly by-reference subcomponent). That's the one I like. **************************************************************** From: Pascal Leroy Sent: Friday, October 20, 2000 2:43 AM That's the one I like too, except that I believe the problem also exists for non-array types. **************************************************************** From: Erhard Ploedereder Sent: Thursday, October 19, 2000 6:10 PM Good point. Consider it added. -) (You noticed my gradually emerging understanding that records and derived types in general aren't a problem, thanks to 13.1(10), so I misunderstood this to apply only to the avoidance of the generic contract problem as opposed to being an elegant general rule. How do we codify *might* ? ) **************************************************************** From: Tucker Taft Sent: Thursday, October 19, 2000 10:38 PM That was codified by my "i.e." clause. In other words, view conversions are prohibited between two different array types (that share no common ancestor type) if they have a subcomponent that is private, or a subcomponent of a visibly by-reference type. **************************************************************** From: Erhard Ploedereder Sent: Thursday, October 19, 2000 5:08 PM Hmmm. what about generic Type T is limited private; Type NT is new T; package blah is O: T; procedure P(X: in out NT); -- this time I remembered the "in out" :-) ....P(NT(O))... This I can certainly instantiate with a by-reference record type and a type derived from it with different representation items. Can I not ? **************************************************************** From: Erhard Ploedereder Sent: Thursday, October 19, 2000 5:14 PM No, I cannot. 13.1(10). **************************************************************** From: Tucker Taft Sent: Thursday, October 19, 2000 5:53 PM > This I can certainly instantiate with a by-reference record type and > a type derived from it with different representation items. Can I not? There is no such thing, because 13.1(10) disallows such a combination. **************************************************************** From: Pascal Leroy Sent: Friday, October 20, 2000 2:43 AM Randy and I have been trying to draw your attention to the fact that 13.1(10) only covers type-related representation items, not subtype-specific ones. Maybe we wrong, but then we would like to be educated, rather than ignored :-) **************************************************************** From: Randy Brukardt Sent: Thursday, October 19, 2000 7:25 PM > There is no such thing, because 13.1(10) disallows such > a combination. No it doesn't. It disallowed TYPE-related items, but it doesn't disallow SUBTYPE-related items. (How many times do Pascal and I have to say that???) If T and NT have alignment clauses or size clauses, you certainly can get exactly this problem. Thus, Tuck's solution is junk. (Am I talking into the void here? I've sent notes along this line three times this afternoon...) **************************************************************** From: Tucker Taft Sent: Thursday, October 19, 2000 11:21 PM Hey, watch your language (only Robert is allowed to say "junk" on the ARG list ;-). I think we have heard you and Pascal mention subtype-related items, but some of us continue to ignore that problem, because the RM already allows the implementation to be very stingy in what it accepts. Clearly, the implementation will be asking for trouble if it allows alignment clauses that are smaller than the default for by-reference types. Because of this, I don't see any need to add rules to protect against subtype-specific representation clauses. It is only the type-related ones that are a problem. > (Am I talking into the void here? I've sent notes along this line three > times this afternoon...) No, but some of us don't believe there is any problem with subtype-specific rep clauses, since implementation are not forced to support much of anything for composite types. I believe that Robert has indicated this several times as well, so maybe we all need hearing aids ;o) **************************************************************** From: Robert Dewar Sent: Friday, October 20, 2000 10:36 AM <> Indeed, this distinction is definitely deliberate, as I said before, the assumption is that subtype related attributes (size and alignment) do not affect the ability to reference something via a pointer. These peculiar view conversions are what cause the trouble, but they are to do with component size clauses etc anyway, so I am confused. Alignment clauses and size clauses on Pascal's example do not raise issues as far as I am concerned (GNAT handles these cases fine). **************************************************************** From: Randy Brukardt Sent: Wednesday, November 1, 2000 12:54 PM I don't see how that could work for 'Object_Size (which is subtype-specifiable, at least in GNAT). Wouldn't something like the following cause problems? type Something is limited record A : Short_Integer; end record; for Something'Object_Size use 32; type Something_Else is new Something; for Something_Else'Object_Size use 16; A : Something_Else; procedure P (L : in out Something) is begin L := ...; -- Copies 32 bits(?) end P; P (Something(A)); -- A is 16-bits. Assume Short_Integer is a 16-bit integer type. Inside of P, the compiler "knows" that L has 32-bits (some of these are padding bits). Thus, when selecting instructions, it "knows" that it can write 32-bits. On the Intel processors, it is cheaper to copy 32-bits than 16-bits, so the compiler *could* (it wouldn't have to) use a 32-bit instruction rather than a 16-bit instruction to do the copy. But, since the *real* object L only uses 16-bits, we could overwrite something else. So, is this a problem? If not, why not? (As the one who got the short stick, and who's going to try to write up a consistent model for 'Object_Size, preferably one that is compatible with what GNAT does, I'd like to know about any gotcha's before I trip over them.) **************************************************************** From: Robert Dewar Sent: Wednesday, November 01, 2000 2:07 PM When Object_Size is used to extend the size allocated for a non-scalar object, it simply creates padding at the end. There is no reasonable definition of what these bits *should* be, so the most reasonable implementation is simply to leave them unset, and not mess with them, otherwise you will find you have to generate inefficient code. Your example shows that (reminding us of other discussions recently), it is not just a matter of inefficient code, but in fact if you unwisely decide to copy all the bits you will get into trouble. So you had better not do this! The obvious and efficient implementation for your example is to copy only 16-bits. Yes, it might be more efficient to copy 32 bits, but it would be wrong. Now I see what the issue here is, and why you thought there was a problem, there isn't! **************************************************************** From: Robert Dewar Sent: Friday, October 20, 2000 10:36 AM <> Exactly, I 100% agree with Randy here [Editor's note: This was Tucker, not Randy.] (say, Erhard, is this better, all this technical discussion ?? :-) **************************************************************** From: Gary Dismukes Sent: Thursday, October 19, 2000 1:37 PM Pascal replied: > I believe you can do many evil things with untagged derived types. Look at > the following example, which is a variation on the alignment example I sent > in a previous message: I was referring specifically to the case of nongeneric private types when I said I didn't see contract model problems. I agree there are problems with generic cases (as in the example I went on to cite with formal arrays, as well as the example you give in your message, and there are probably a number of others). > In general, any rule that requires knowledge of the representation will > wreak havoc in generics. Either it will kill the contract model, or it will > have to be checked in an assume-the-worst manner, and that is bound to cause > existing code to become invalid. Yes, we're in agreement. The rule restricting view conversions needs to be applied generally within generic bodies, assuming the worst about formals, which creates potential incompatibilities. **************************************************************** From: Robert Dewar Sent: Friday, October 20, 2000 10:44 AM <<5) View conversions are prohibited between two different array types if their component type *might* be of a by-reference type (i.e. there is a private or visibly by-reference subcomponent).>> I think that's fine. In practice these kind of conversions will be quite rare anyway, and restricted like this, even rarer, and in any case, te great majority of conversions we restrict in this manner are ones that won't work anyway in any existing implementation. Tuck, perhaps I missed it, but did you ever say what the Intermetrics front end (I guess it is the Averstar Front end this year) does in this csae? **************************************************************** From: Tucker Taft Sent: Friday, October 20, 2000 2:14 PM On the case I just tried, we happily do copy-in/copy-out, which seems clearly wrong, since the array components might be task objects, etc. So clearly compilers are already doing rather different things with these "view" conversions. Making the construct illegal will perhaps be doing people a favor. The fact that we have outlawed certain other cases of array-to-array view conversions makes me feel better about simply growing that set a bit to accommodate other similar cases. **************************************************************** From: Robert Dewar Sent: Friday, October 20, 2000 10:37 AM <<> 5) View conversions are prohibited between two > different array types if their component type *might* be > of a by-reference type (i.e. there is a private > or visibly by-reference subcomponent).>> Hmmm! If view conversion meant what it intuitively means (conversions that have to be performed by taking a different view), we could say this more easily :-) **************************************************************** From: Randy Brukardt Sent: Friday, October 20, 2000 3:14 PM > No, but some of us don't believe there is any problem with > subtype-specific rep clauses, since implementation are not > forced to support much of anything for composite types. > I believe that Robert has indicated this several times > as well, so maybe we all need hearing aids ;o) OK, but I thought *his* argument was junk (oops, said it again!), but was too worn out to bother with arguing it. The logical conclusion of his argument is that 'Alignment is only useful on base types, and probably only on elementary base types. (Based on his description, I would expect that the only 'Alignment that you could give in GNAT is 4. Kinda defeats the purpose of the attribute, don't you think?) Anyway, I simply can't get worked up enough about alignment to care. The Intel processors that I work on simply don't have alignment issues that matter to anything except performance, so the problem doesn't arise. **************************************************************** From: Robert Dewar Sent: Friday, October 20, 2000 4:58 PM <> Where did you get that strange idea, of course you can give *larger* alignments, as required by the RM (since of course larger alignments cannot affect ease of access). <> What's the Gack about here? Of *course* you have to only allow rep items that don't cause trouble, that's what rep items are about, if they cause unacceptable trouble, you must reject them. <> useful only on base types? what kind of nonsense is that? YOu most certainly cannmot use alignment clauses on subtypes anyway? I just don't know what you are talking about here! <> A fatal weakness in attitude, because of course alignmment is *really* critical. Ada 83 had a horribly weak attitude to alignment, Ada 95 is better but there are still lots of lurking issues with alignment, in terms of what to allow and what not to allow. **************************************************************** From: Randy Brukardt Sent: Tuesday, October 31, 2000 3:50 PM > Where did you get that strange idea, of course you can give *larger* > alignments, as required by the RM (since of course larger alignments > cannot affect ease of access). But in actual practice there aren't any larger alignments. On most machines, the only sensible alignments are 1, 2, and 4; and most composite types will end up with an alignment of 4 (because you can't give an alignment less than that of any component). > What's the Gack about here? Of *course* you have to only allow rep items > that don't cause trouble, that's what rep items are about, if they cause > unacceptable trouble, you must reject them. Great, then I don't need to worry about bit packed arrays. :-) Seriously, most rep. clauses cause a lot of trouble. How do you define "unacceptable" trouble? Certainly, I would consider supporting generic formal array types that might be bit packed "unacceptable" trouble; but the language doesn't really give me the option to ignore them. > Useful only on base types? what kind of nonsense is that? You most certainly > cannot use alignment clauses on subtypes anyway? I just don't know what > you are talking about here! "Base" type as opposed to derived types. I'm afraid I lapsed into Janus/Ada compiler-speak here. (Tucker and Bob appropriated many of the terms that we were already using internally in Janus/Ada; it being pointless to try to change the names of dozens of functions and rewrite hundreds of comments to change the terminology, I've just had to try to keep the two sets straight. > < Intel processors that I work on simply don't have alignment issues that > matter to anything except performance, so the problem doesn't arise.>> > > A fatal weakness in attitude, because of course alignment is *really* > critical. Ada 83 had a horribly weak attitude to alignment, Ada 95 > is better but there are still lots of lurking issues with alignment, > in terms of what to allow and what not to allow. The main problem that I see with Alignment in Ada 95 is that it really seems to be aimed at cases where the alignment is required. The definition isn't very useful for machines where only performance is involved (i.e. the Intel IA32). There, you really want to be able to specify an alignment to use by default, but you want to be override it to allow packing, etc. (We assume that the user cares more about space than performance in that case; I figure that we ought to let the user make the decision...) Janus/Ada actually has two alignments, the "recommended" alignment, and the "required" alignment. The "Required" alignment is the same as the Ada 95 one; the "recommended" one is similar, but doesn't have the rules requiring the same or larger alignment on composite types. For instance, it would be nice if integer types had an alignment of 4, because that would provide the best performance for stand-alone objects and array components. But we don't want to force all record types to have that alignment, because it would make packing impossible (especially important when the record is used in an array). 13.3(26) prevents giving a smaller alignment for the Ada 95 alignment. Anyway, this is off-topic. **************************************************************** From: Robert Dewar Sent: Tuesday, October 31, 2000 6:19 PM <> Very peculliar .. the expected implementation here is that you respect the efficiency-required alignments (and the efficiency gain is large, we are not talking about 386 and 486's here) in the default case, but if a record is packed, then for sure you allow unaligned stuff if the cost is not too high. **************************************************************** From: Randy Brukardt Sent: Tuesday, October 31, 2000 6:48 PM Well, that is what I'd prefer, but it is clearly disallowed by 13.3(26). Do you mean that you ignore this rule (I doubt that), or are you using some sort of "soft" alignment? (The rule says that it applies to "specified" alignments; I suppose that if you treat "specified" alignments differently than default ones you can do what you say. But that is precisely what I was discussing; we're storing the default ("recommended") and specified ("required") alignments separately, but I suppose other implementations are possible.) It is clear that 13.3(26) makes the following illegal: type Some_Int is range 0 .. 2**20; for Some_Int'Alignment use 4; type Some_Record is record Comp : Some_Int; end record; for Some_Record use record Comp at 0 range 0 .. 23; end Some_Record; for Some_Record'Size use 24; for Some_Record'Alignment use 1; -- Nope, illegal by 13.3(26). -- Use Some_Record in an array. Despite the fact that there is no implementation problem with this. The space waste can be considerable in a case like this. I'd like a way to specify a non-binding alignment, which is not subject to 13.3(26). I doubt it is worth making a formal proposal, however (there aren't many machines that it would matter on). **************************************************************** From: Robert Dewar Sent: Friday, October 20, 2000 10:50 AM <> Lots of things potentially impact users. The above is what I mean when I talk about those who write tests getting into the mode of wanting to write a test whenever they can :-) :-) The mere fact that something has a potential impact on users does NOT mean that something should be tested. We always need to discuss how great the potential impact is. At this stage in the game, things that have only been discovered recently are suspect from this point of view anyway (i.e. it is unlikely that they have wide significant impact). Also we have to look at what kind of impact is involved. For example, if one compiler accepts a rep clause and another does not, big deal, I would not test for this, since you have lots of legitimate cases like this regardless of the tests. This is a case most often better handled by the market place. **************************************************************** From: Randy Brukardt Sent: Friday, October 20, 2000 3:14 PM Let's summarize the state of the AI that I haven't written yet. (But it has hundreds of comments!). The consensus rule to use is: View conversions are illegal for a conversion between two arrays that do not have a common ancestor if their component type is has a private or visibly by-reference subcomponent. Reasons: The rule is necessary because two different array types could have different component sizes, yet a by-reference type cannot be passed by copy. The "private or visibly by-reference subcomponent" rule essentially means "might be by-reference". We need to use "might" because one of these conversions can happen before the type is frozen; we can't tell before the type is frozen if it is by-reference, but we need to be able to determine the legality of the conversion. This rule has the advantage of not breaking privateness (it uses assume-the-worst for private types of all kinds) and does not require any knowledge about representation items. 13.1(10) prevents these sorts of problems for type-related aspects of by-reference derived types. So we don't need a rule to handle them. AARM 13.1(10.b) explains that this rule exists precisely to prevent this sort of problem. We are telling implementors to beware of possible problems with subtype-specific attributes and derived types. They ought to disallow any rep. items that would cause such problems. (Gack!) Potential problems: The rule as written includes all tagged types. Is that intentional, or an oversight? (Earlier, we only considered untagged types for these rules.) Do this rule need an assume-the-worst statement for generics? (I don't think so.) What about generic derived types? Could the actual be by-reference when the formal type does not indicate it? (Humm, maybe not, I can't think of a case.) Since this rule applies to generic formal private types (at least, it better, or we have contract model issues), it could make existing programs illegal that do not actually have a problem. This seems unlikely. In a generic body, it seems very unlikely (arrays of generic formal types seem unlikely, two such types in the same body seem even more unlikely, and conversions between them seem even more unlikely); but it might happen for a private type whose full type is not by-reference. Since users could run into the rule, it ought to be tested (and remember again that rep. items are not needed to trigger the rule). Note that Erhard's example of a view conversion before freezing is bogus, because it uses a function with an IN OUT parameter. I'd like to see those for other (OOP) reasons, but unless we're seriously considering that as an amendment, I don't do think we have an example of a freezing problem. Indeed, I don't think you can use an untagged view conversion in a default expression, because they only occur in IN OUT parameters; which cannot appear in a function, and procedures can't appear in an expression. Does anyone have a *legal* example of a freezing problem? **************************************************************** From: Tucker Taft Sent: Friday, October 20, 2000 5:47 PM > The rule as written includes all tagged types. Is that intentional, or an > oversight? (Earlier, we only considered untagged types for these rules.) Arrays are never tagged, so you mean it includes all array-of-tagged, I presume. Yes I suppose it does, but array-of-tagged are even rarer than other kinds of array-of-by-ref I would guess. > Do this rule need an assume-the-worst statement for generics? (I don't think > so.) Private seems to cover it. > What about generic derived types? Could the actual be by-reference when the > formal type does not indicate it? (Humm, maybe not, I can't think of a > case.) No, all types in the same derivation class are by-reference, or not by-reference. **************************************************************** From: Jean-Pierre Rosen Sent: Saturday, October 21, 2000 4:38 AM > Note that Erhard's example of a view conversion before freezing is bogus, > because it uses a function with an IN OUT parameter. I'd like to see those > for other (OOP) reasons, but unless we're seriously considering that as an > amendment, I don't do think we have an example of a freezing problem. > Indeed, I don't think you can use an untagged view conversion in a default > expression, because they only occur in IN OUT parameters; which cannot > appear in a function, and procedures can't appear in an expression. Does > anyone have a *legal* example of a freezing problem? > At long last, we have a *technical* reason for not allowing in out parameters in functions! (sorry, couldn't resist :-) **************************************************************** From: Randy Brukardt Sent: Wednesday, November 01, 2000 7:58 PM I'm finishing up writing the AI on the recent discussion, and it strikes me that the proposed rule potentially is more incompatible with existing code than necessary. I'm not sure that this is worth worrying about (the sort of view conversion in question is likely to be very rare), but I wanted to point out the issue and a possible solution. The rule that Tucker proposed and everyone agreed to is: (I've recast it to fit into the existing list of rules in 4.6(9-12.1): In a view conversion, if the target type and the operand type do not have a common ancestor, then the component subtype shall not have a private or visibly by-reference subcomponent. The problem I have with this rule is that it casts a rather wide net: private types are common, and the vast majority of them are not by-reference types. For instance, the following example is illegal by the proposed rule: package P is type Priv is private; type A1 is array (1..2) of P; type A2 is array (1..2) of P; procedure Proc (X : in out A1); private type Priv is null record; end P; with P; procedure PP is Y : P.A2 := ...; ... P.Proc (P.A1 (Y)); -- A view conversion, illegal by the proposed rule. end PP; This sort of thing is not going to be common, but there doesn't seem to be much reason to make it illegal: there certainly is no implementation problem as long as the full type isn't by-reference. The primary reason for this wording is to avoid breaking privateness, and to avoid problems with freezing. Pascal has noted that the standard already contains at least one legality rule that breaks privateness for by-reference types (C.6(12)), so it certainly seems harmless to add another. The freezing case is more interesting. The issue is that the rule (whatever it is) must be able to be determined at the point at which a view conversion can occur. If that can happen before the type is frozen, then the rule cannot depend on whether the type is really by reference (because the full definition may not have been seen yet). However, I believe that it is impossible for an untagged view conversion to occur before either of the types that uses it is frozen. (Array types are always untagged). Untagged view conversions can only occur as [IN] OUT actual parameters. [IN] OUT parameters can only occur in procedures. Procedure calls can only occur in bodies. Bodies are always freezing. Therefore, no unfrozen type can exist at the point of an untagged view conversion. (Of course, this would break down if [IN] OUT parameters were added to functions, but this seems unlikely.) A secondary reason for the wording is to cover generic contract problems. The wording above simply "assumes the worst" everywhere in a generic. Considering how unlikely triggering this rule is, that probably is best. Therefore, there seems to be no reason to not simply say "by-reference component" in the rule above, and to say "generic formal private type" to cover the generic case. Humm. The original rule does not seem to handle a visible type derived from a private type. That unfortunately messes up the wording further. (Is there some reason that this is not necessary?) Anyway, I'm proposing the following rule instead of the one original proposed by Tucker. Are there any problems/objections to this rule? Is the compatibility issue worth worrying about? In a view conversion, if the target type and the operand type do not have a common ancestor, then the component subtype shall not have a subcomponent that has a by-reference type, generic formal private type, or a type derived from a generic formal private type. **************************************************************** From: Tucker Taft Sent: Thursday, November 02, 2000 8:00 AM > The rule that Tucker proposed and everyone agreed to is: (I've recast it to > fit into the existing list of rules in 4.6(9-12.1): > > In a view conversion, if the target type and the operand type do not have a > common ancestor, then the component subtype shall not have a private or > visibly by-reference subcomponent. For those who haven't memorized the RM paragraph numbers, note that this is in the section on legality rules for *array* type conversions. So even though the above does not mention arrays explicitly, it implicitly is only disallowing certain array view conversions. > This sort of thing is not going to be common, but there doesn't seem to be > much reason to make it illegal: there certainly is no implementation problem > as long as the full type isn't by-reference. But suppose that during maintenance it became by-reference. That would create an annoying ripple effect. > > The primary reason for this wording is to avoid breaking privateness, and to > avoid problems with freezing. Pascal has noted that the standard already > contains at least one legality rule that breaks privateness for by-reference > types (C.6(12)), so it certainly seems harmless to add another. I certainly don't agree that we want to make the slope slipperier! I would rather try to fix C.6(12). For example, perhaps if a private type is volatile or atomic, that fact needs to be exposed in the visible part. (I'm not sure whether that is addressing Pascal's concern. I'm just guessing.) > ...> > Humm. The original rule does not seem to handle a visible type derived from > a private type. That unfortunately messes up the wording further. (Is there > some reason that this is not necessary?) Isn't an untagged type derived from a private type still private? I mean, if it isn't a private type, what kind of type is it? > Anyway, I'm proposing the following rule instead of the one original > proposed by Tucker. Are there any problems/objections to this rule? Is the > compatibility issue worth worrying about? > > In a view conversion, if the target type and the operand type do not have a > common ancestor, then the component subtype shall not have a subcomponent > that has a by-reference type, generic formal private type, or a type derived > from a generic formal private type. I don't like breaking privateness for a rare feature like array view conversions. **************************************************************** From: Robert A Duff Sent: Thursday, November 02, 2000 8:37 AM So our Guardian of Private Contract Models is away. He might not find just one more hole to be "harmless". ;-) I don't like breaking the contract model, either, even if it's already broken in some other case. **************************************************************** From: Randy Brukardt Sent: Thursday, November 02, 2000 1:44 PM > But suppose that during maintenance it became by-reference. That would > create an annoying ripple effect. True, but it seems rather unlikely to cause a real hardship: certainly the problem will be clear enough. > I certainly don't agree that we want to make the slope slipperier! > I would rather try to fix C.6(12). For example, perhaps if a private > type is volatile or atomic, that fact needs to be exposed in the visible part. > (I'm not sure whether that is addressing Pascal's concern. I'm just > guessing.) To echo Bob and Erhard, I don't think we want to go answering questions that were not asked! Especially because a change to this rule could cause compatibility problems. > > Humm. The original rule does not seem to handle a visible type derived from > > a private type. That unfortunately messes up the wording further. (Is there > > some reason that this is not necessary?) > > Isn't an untagged type derived from a private type still private? I mean, if it > isn't a private type, what kind of type is it? Dunno, I don't think the standard ever answers that question. 3.2(4) says that a private type is a partial view. The only way the standard shows to get one the declaration of the private type (7.3(14)). It seems weird if a derived type represents a partial view (because it couldn't have a completion -- so it would never represent the full type: but you can't declare objects, etc. with a partial view). And it seems weird to represent the full type (because you don't have visibility). In any case, the question needs to be answered in terms of this wording, not necessarily in general. > I don't like breaking privateness for a rare feature like > array view conversions. I'll leave the AI the way I have it (the original Tucker proposal), and we can discuss this possible change at the meeting. I just wanted to make the proposal so the compatibility problem gets considered. I mean, if we assume that array view conversions are rare enough, we could just make them illegal. (No, I'm not seriously proposing that.) But I would expect that the opposition to this change would come from the incompatibility. (Or worse still, we would forbid the ACATS from testing the rule for that reason, giving us an unenforced rule.) **************************************************************** From: Pascal Leroy Sent: Monday, November 06, 2000 6:46 AM > > The primary reason for this wording is to avoid breaking privateness, and to > > avoid problems with freezing. Pascal has noted that the standard already > > contains at least one legality rule that breaks privateness for by-reference > > types (C.6(12)), so it certainly seems harmless to add another. > > I certainly don't agree that we want to make the slope slipperier! > I would rather try to fix C.6(12). For example, perhaps if a private > type is volatile or atomic, that fact needs to be exposed in the visible part. > (I'm not sure whether that is addressing Pascal's concern. I'm just > guessing.) We certainly don't want to add more cases where we are breaking privateness. I agree with Tuck's initial proposal, which is that private types should be considered by-reference for purposes of the new rule about view conversions. I also agree that it would be nice to fix C.6(12), although it's not clear how to do that in an upward-compatible manner. Exposing the volatility/atomicity of a type in the visible part would be great, but what about existing code that assumes that the compiler opens private parts? **************************************************************** From: Randy Brukardt Sent: Monday, November 06, 2000 5:53 PM I was finishing up this AI for the meeting, and I started wondering why value conversions aren't a problem. After all, the components in this case are limited and can't be copied. So, how can you make a value conversion? Here's an example of what I mean (based on the original example): type T is limited null record; -- by-reference type A1 is array (1..2) of T; for A1'Component_Size use 8; type A2 is array (1..2) of T; for A2'Component_Size use 32; procedure P (X : in A1) is begin ... end P; Y : A2 := ...; ... P (A1 (Y)); -- A value conversion, passed by-reference. At first thought, you'd think that the conversion would just have to convert the representation from A2 to A1. But to do that, it has to decrease the space between the components, which means copying them. But the component type is limited: no such copying can be done. (Consider that the type might be a task or protected object.) So, it seems to me that ANY conversion of a (really) limited type where a representation change is needed is impossible. (Such a type has to be a by-reference type, although not all by-reference types fail to have an appropriate assignment operation.) 13.1(10) prevents such types from having a different representation when they are derived. But there is no such rule preventing different representations for unrelated array types (and there cannot be such a rule). So, there is a hole for array conversions. This widens the net somewhat, because the "view" in the proposed rule has to be dropped. I suppose we could make this rule apply only to value conversions of limited types and all view conversions, but that complicates it further. I hope I'm wrong about this, because I really think Tuck's rule without the "view" is really too incompatible to consider. (The advantage of Tuck's rule is that is doesn't consider the actual representation, but of course that throws out many programs that have no implementation problem.) But I don't see why I'm wrong... **************************************************************** From: Tucker Taft Sent: Tuesday, November 07, 2000 8:52 AM Randy Brukardt wrote: > ... > > This widens the net somewhat, because the "view" in the proposed rule has to > be dropped. I suppose we could make this rule apply only to value > conversions of limited types and all view conversions, but that complicates > it further. Be that as it may, that seems like the right rule. Value conversions are useful for non-limited types specifically to do representation conversion (by copying). For limited types, we should disallow all conversions that might require a representation change. This is not just related to arrays. It applies to records as well, since one might be packed, and the other unpacked. > I hope I'm wrong about this, because I really think Tuck's rule without the > "view" is really too incompatible to consider. (The advantage of Tuck's rule > is that is doesn't consider the actual representation, but of course that > throws out many programs that have no implementation problem.) But I don't > see why I'm wrong... You are not wrong. This seems like a much bigger hole, and it has nothing really to do with by-reference or by-copy. Any conversion for limited types that changes representation is bad news, as it requires making a copy. I would handle this as a separate AI, and probably it deserves a separate fix. **************************************************************** From: Robert Dewar Sent: Tuesday, November 07, 2000 9:18 AM Really it is a hole in the language. Change of representation inherently requires conversions, and limited types don't like the conversions. So basically limited types and change of representation are simply incompatible. Perhaps the rules on restriction of rep clauses if primitive subprograms have been defined should apply "ab initio" for limited types (this is too big a change I realize, but would be more consistent). **************************************************************** From: Randy Brukardt Sent: Tuesday, November 07, 2000 12:31 PM Humm, I don't see why you think it is a separate issue. Any uncopyable limited type is in fact a by-reference type, so all of the same issues apply. (The only limited types that aren't by reference are limited private types completed by a non-limited type. The proposed rule covers them, too.) In any case, the issues are closely related (conversions that cannot be performed if there is a representation change). So I don't see why we would want to consider them separately, potentially solving them in different ways. **************************************************************** From: Tucker Taft Sent: Tuesday, November 07, 2000 1:44 PM Good point. I was thinking that all limited types are uncopiable, but there is nothing that promises that. Only by-reference limited types are uncopiable, and because of 13.1(10) and the usual weasel-words about subtype-specific attributes, this puts us back into only needing to worry about unrelated array types. Now I agree in general, but I would still want to limit the restriction to view conversions of possibly by-reference, and value conversions of limited possibly-by-reference. Since the only way to become limited is to either be by-reference or limited private, this comes down to saying that *all* conversions between unrelated limited array types should be disallowed. I personally don't have a problem with that. **************************************************************** From: Randy Brukardt Sent: Tuesday, November 07, 2000 3:49 PM OK, I rewrote the AI to make the rules: In an array conversion, if the target type and the operand type do not have a common ancestor, then neither type shall be limited. In an array view conversion, if the target type and the operand type do not have a common ancestor, then the component subtype shall not have a private or visibly by-reference subcomponent. I haven't posted this yet. I figured I'd wait for all of the good things Tucker is finishing up... (hope I'm not waiting for Godot :-) **************************************************************** From: Robert A Duff Sent: Wednesday, November 08, 2000 9:30 AM > In any case, the issues are closely related (conversions that cannot be > performed if there is a representation change). So I don't see why we would > want to consider them separately, potentially solving them in different > ways. I agree -- keep it as one AI. **************************************************************** From: Randy Brukardt Sent: Thursday, October 18, 2001 3:50 PM The minutes of the Bloomington meeting say: Change first paragraph of the summary to say "value conversion"; add "limited" to the second paragraph. The second change is incorrect: the "view" rule applies to non-limited by-reference types (that is, tagged types) as well as limited ones. Therefore, I did not make that change. **************************************************************** From: Randy Brukardt Sent: Friday, May 24, 2002 8:30 PM At the Cupertino meeting, the wording was changed significantly to make it more readable. Here an excerpt of the minutes of that meeting: Tucker suggests moving the common ancestor wording to 4.6(9), and factoring it out of the following bullets. After further discussion, Randy completes the wording during a break. Later in the meeting, the following wording is proposed to replace 4.9(9-12.1): "If the target type is an array type, then the operand type shall be an array type. The target type and operation type shall have a common ancestor, or: The types shall have the same dimensionality; and Corresponding index types shall be convertible; and The component subtypes shall statically match; and Neither the target type nor the operand type shall be limited; and In a view conversion: the target type and the operand type shall both or neither have aliased components; and the operand type shall not have a tagged, private, limited, or volatile subcomponent." Recently, Tucker has pointed out that the "limited" in the last bullet is redundant (since we've already said that the types are not limited). That change has now been made. ****************************************************************