!standard 3.10.2(29) 07-05-15 AI05-0054-1/01 !standard 13.9.1(13) !class binding interpretation 07-05-15 !status No Action (7-0-1) 08-11-02 !status work item 07-05-15 !status received 07-05-07 !priority Low !difficulty Hard !qualifier Omission !subject Variable views of constant objects !summary *TBD* !question The current instance of a limited type can be used to obtain a variable aliased view of a (limited) constant object. Are runtime checks needed in order to prevent the modification of a constant? !recommendation Add a runtime check to prevent this. !wording Add after 3.10.2(29): If X denotes the current instance of a (limited) type, and A is an access-to-variable type, a check is made that the object denoted by X is not a constant object. Program_Error is raised if this check fails. !discussion Consider the following example: procedure Modify_A_Constant is type Outer; type Inner (Outer_Ref : access Outer) is limited null record; type Outer is limited record Inner_Field : Inner (Outer_Ref => Outer'Access); Integer_Field : Integer; end record; X : constant Outer := (Inner_Field => <>, Integer_Field => 123); begin X.Inner_Field.Outer_Ref.Integer_Field := 0; end Modify_A_Constant; The example is (statically) legal. We don't check 3.10.2(25) when a self-referential access value is created using the name of the current instance of a type because 3.3(16-22) doesn't list the current instance as a constant, so it has to be a variable. At runtime, this situation is covered by 13.9.1(13): The dereference of an access value is erroneous if ... , or if it is an access-to-variable value that designates a constant object. Thus, the first question is whether any action at all is needed. We typically define the execution of a problematic construct to be erroneous only if there is no other reasonable alterative. That does not seem to be the case here. As Pascal Leroy put it (in private correspondence): "Remember that each time we declare that something is erroneous, we fail the user because we weaken the safety and security guarantees that they expect from Ada. We are not in the business of making compiler writers' life easy: we are in the business of designing a safe language." If it is decided that some action is required, then there are issues with the proposed solution that need to be resolved: =1= The proposed wording assumes that the term "constant object" (as opposed to a constant view of an object) is well-defined. Does an allocator of an access-to-constant type yield a value which designates a constant object, or which designates a constant view of a variable object? Presumably the former, but this needs to be clarified. What about the 6 combinations of a return object in an extended return statement in the cases where the function call is being used to initialize in place a constant, a variable, or neither and the return object itself is or is not declared as a constant? What about a default initialized component of an aggregate that is being used to initialize in place a variable, a constant, or neither? Or to initialize of one of the 6 return objects mentioned above? =2= X'Access, where X denotes the current instance of a limited type, can only occur as part of either a per-object constraint or a per-object expression. We concern ourselves here only with the case where X'Access yields a value of an access-to-variable type. If X'Access occurs in a constraint, then this almost means that no constants of the type are possible. There would be no exception raised in the case of a nonexistent component, as in type Outer; type Inner (D : access Outer) is limited null record; type Outer (Has_Inner : Boolean) is limited record case Has_Inner is when False => null; when True => F : Inner (D => Outer'Access); end Has_Inner; X : constant Outer := (Has_Inner => False); -- ok; no exception , but that's the only case that would not result in an exception. Even this is unclear; 3.8(18/2) talks about the point where a per-object expression is evaluated, but the point at which a per-object constraint is elaborated is less clear, particularly in the case of an object with an explicit initial value (see 3.3.1(18/2)). Is a clarification needed? When evaluating a per-object expression, the implementation would need to know whether the object being initialized is a constant object. If, for example, initialization is performed out of line, then this might require passing in an additional boolean parameter. =3= This solution does not address the problems associated with other violations of 3.3(13)'s rule that "all views of a constant object are constant". These include (at least) 1) An implicit call to Initialize or Finalize for a constant object (the Initialize case requires use of a "<>" expression in an aggregate) yields a variable view of the constant object within the call. and 2) An extended_return_statement which includes the reserved word "aliased" but not the reserved word "constant" in the case where the function call is used to initialize a constant object in place (either as required by 7.5(8.1/2) or as allowed by 7.6(21/2)) yields a variable view of the constant object within the statement. Evaluating the Access attribute of such objects to obtain an access-to-variable value which designates a constant object seems safe because the value cannot outlive the variable view of the constant object (in fact, in this case 13.9.1(13) seems to classify safe and reasonable constructs as erroneous). The situation with the Unchecked_Access attribute is more complex. Does it need to be made clear that if a variable view of a constant is used as the prefix of an Unchecked_Access attribute to obtain an access-to-variable value, then the resulting value is valid only for the lifetime of the variable view (which will typically be shorter than the lifetime of the designated object)? --- From the minutes and private discussion of the Paris meeting: Is there a a way to statically make this illegal? That has problems; any such rule would have to be privacy breaking (for instance, making declaring constants of types that contain self-references illegal), or incompatible (making some self-references illegal). There is one potential rule that isn’t either: ban constants of limited types, reverting to Ada 95. But we don’t want to go there. Moreover, static checks could not detect all cases, such as a self-reference created during an Initialize routine. Note that the proposed run-time check doesn't detect this case, either, yet it has the same effect as a direct self-reference. (Indeed, this is how the self-references in Claw work; we didn't have the X'Access of a type implemented when we started Claw.) Such self-references can be perfectly safe (even if they use 'Unchecked_Access), because the Finalize routine can destroy all uses of them (again, this is how Claw works). A solution which made such self-references illegal would not be acceptable (because of incompatibility), and leaving them erroneous seems bad for the reasons given above. It doesn't seem to help to catch this problem in one case and leave a very similar case undetected. (Especially as that would result in a situation much like the redefinition of "=" in Ada 83; you could do it, but only if you jumped through a bunch of hoops. What's the point of that?) !ACATS test !appendix From: Randy Brukardt Sent: Tuesday, May 15, 2007 9:00 PM There is (almost) nothing new here. This issue was noticed early on when allowing limited aggregates, and we explicitly decided to do nothing. The part of AI-287 that starts with "There is one slight oddity..." gives an example very similar to yours. It's ridiculous to bring it up again at this late date. One important reason for this is that this is the Rosen trick, which is widely used (at least by its namesake!). It was needed in Ada 95 to get a variable view of what otherwise would have been a constant view (an in parameter); to try to close this loophole now is way too incompatible. Personally, I think this was a serious bug in Ada 95, but it is way, way too late to plug that. The proposed run-time check is very dangerous. It means that any limited private type could raise Program_Error if declared with the keyword "constant"; it's not something that would be apparent in the specification. For example, the type Generator in Ada.Numerics.Float_Random is sometimes implemented with the Rosen trick. If declared as a constant with this check, it would raise Program_Error. But nothing bad has happened. (I realize this is not a great example, but it would be true for any limited private type implemented with the Rosen trick. That seems scary.) AI-287 essentially says that there are no constant objects of a limited type (at least, you can't assume they are constant unless you know the implementation of the type). The only fix for that is to return to the Ada 95 rule and ban constants of limited types. (Any other rule would have to break privacy, and Mr. Private would start foaming at the mouth... ;-) The only new thing here is the quoted text in 13.9.1(13): The dereference of an access value is erroneous if ... , or if it is an access-to-variable value that designates a constant object. I agree this is a bad thing, but the bad thing is that it doesn't properly take self-referential types into effect. I would just change this to: The dereference of an access value is erroneous if ... , or if it is an access-to-variable value that designates a {nonlimited} constant object. We probably ought to check if there are any other rules that assume constants are actually constant. An alternative to this is to define limited constants to be variable objects, but that seems rather goofy. (It would be safer in most ways, though.) **************************************************************** From: Pascal Leroy Sent: Wednesday, May 16, 2007 2:15 AM > There is (almost) nothing new here. This issue was noticed > early on when allowing limited aggregates, and we explicitly > decided to do nothing. The part of AI-287 that starts with > "There is one slight oddity..." gives an example very similar > to yours. It's ridiculous to bring it up again at this late date. I hadn't realized that, but in retrospect it looks like a mistake to me. Whether it's worth revisiting or not is an interesting question. > For example, the type Generator in Ada.Numerics.Float_Random > is sometimes implemented with the Rosen trick. If declared as > a constant with this check, it would raise Program_Error. But > nothing bad has happened. I disagree here. If you declare a generator as constant, you are seriously confused, because it is clear from the description of the semantics of the package that the generator is changed by a call to Random. So it would only be fair to get a P_E. > AI-287 essentially says that there are no constant objects of > a limited type (at least, you can't assume they are constant > unless you know the implementation of the type). The only fix > for that is to return to the Ada 95 rule and ban constants of > limited types. (Any other rule would have to break privacy, > and Mr. Private would start foaming at the mouth... ;-) Note that nobody is talking about a privacy violation here: a runtime check doesn't violate privacy. OK, there was a hole in Ada 95, but it was rather innocuous. There was nothing that you could do with the Rosen trick and that you could not have done with an access parameter: just change Random to take an access parameter, and you can modify the generator. And passing a constant generator would be (statically) rejected. But in Ada 2005 we have made the hole much bigger: there are now things that you can do with the Rosen trick and that you cannot do with the rest of the language. It certainly won't be obvious to users that they should not trust the word "constant". In the example of AI 287, a naïve programmer might write: if Const.Comp = 123 then Proc (Const); and assume that, after the call to Proc, Const.Comp is still 123. But that is not true, since Proc might have changed Const (and you have no way to tell without looking at the body of Proc). This looks like a safety problem to me. > An alternative to this is to define limited constants to be > variable objects, but that seems rather goofy. (It would be > safer in most ways, though, so maybe it is > preferable.) It's not only goofy, it's also sweeping the problem under the rug. It wouldn't change the fact that users will get confused about "constant" not meaning "constant". **************************************************************** From: Randy Brukardt Sent: Tuesday, May 15, 2007 1:32 PM ... > > For example, the type Generator in Ada.Numerics.Float_Random > > is sometimes implemented with the Rosen trick. If declared as > > a constant with this check, it would raise Program_Error. But > > nothing bad has happened. > > I disagree here. If you declare a generator as constant, you are > seriously confused, because it is clear from the description of the > semantics of the package that the generator is changed by a call to > Random. So it would only be fair to get a P_E. But this problem is not just with a top-level generator, but also any component that is a generator. If you have a large, complex ADT that happens to include a generator component, it might raise Program_Error if declared as a constant. That may not be obvious from the spec. Worse, there is a portability problem: some implementations (like Janus/Ada) don't use the Rosen trick, and they will not raise Program_Error in that situation. So, you could port a perfectly working program from Janus/Ada to your compiler and it would suddenly stop working. That could be especially bad if the constant is declared in a little-used path like an error handler. > > AI-287 essentially says that there are no constant objects of > > a limited type (at least, you can't assume they are constant > > unless you know the implementation of the type). The only fix > > for that is to return to the Ada 95 rule and ban constants of > > limited types. (Any other rule would have to break privacy, > > and Mr. Private would start foaming at the mouth... ;-) > > Note that nobody is talking about a privacy violation here: a runtime > check doesn't violate privacy. A runtime check is simply not acceptable. As I mentioned previously, there would be nothing in the specification to tell the user that declaring a constant is inappropriate. Moreover, when composition occurs, any comments about it could easily get lost. Essentially, while a runtime check doesn't *technically* break privacy, it does break privacy from the user's perspective: they have to know the implementation of something before they can know whether it is OK to declare it as a constant. Finally, a runtime check is a serious distributed overhead in Janus/Ada: we'd have to add a Target_is_a_Constant parameter to all of the thunks in order to detect this (there are no current rules that depend on whether an initialized object is a constant; indeed, the language says that such an object doesn't become constant until after the initialization is finished, so this is a totally new idea). I suppose that we could try to limit this to only limited thunks, but that's not really right if coextensions are allowed (see Steve's AI again). I think only a compile-time check is acceptable. If that means that almost no limited types can be declared constant, that would be OK with me (they are worse than useless, they're actually dangerous as you point out). > OK, there was a hole in Ada 95, but it was rather innocuous. There was > nothing that you could do with the Rosen trick and that you could not have > done with an access parameter: just change Random to take an access > parameter, and you can modify the generator. And passing a constant > generator would be (statically) rejected. > > But in Ada 2005 we have made the hole much bigger: there are now things > that you can do with the Rosen trick and that you cannot do with the rest > of the language. It certainly won't be obvious to users that they should > not trust the word "constant". In the example of AI 287, a naïve > programmer might write: > > if Const.Comp = 123 then > Proc (Const); > > and assume that, after the call to Proc, Const.Comp is still 123. But > that is not true, since Proc might have changed Const (and you have no way > to tell without looking at the body of Proc). This looks like a safety > problem to me. Only if there isn't appropriate education. But I can see your point, which is why I've said previously that allowing "constant" on limited types was a mistake. When I was complaining that a constant isn't equivalent to a function call, I was also thinking about this case. I think we need a static check that applies any time this could happen. Which means constants of limited private types are out (unless we can find some way to get that information into the specification: a pragma perhaps??). > > An alternative to this is to define limited constants to be > > variable objects, but that seems rather goofy. (It would be > > safer in most ways, though, so maybe it is > > preferable.) > > It's not only goofy, it's also sweeping the problem under the rug. It > wouldn't change the fact that users will get confused about "constant" not > meaning "constant". There never has been and never will be a limited type that is really constant. What the heck is a constant protected object or task, anyway? Surely anything in the task can change, and the protected object's queues and locks can change. The only way to get something limited that really is constant is to declare something that could have been non-limited as limited (that is, a limited record), and that isn't that interesting of a case. So the problem is thinking of limited objects as constant in any context. If that is too brain-bending for you, then we simply should get rid of the possibility. (But that really isn't possible, because we surely aren't going to ban "in" parameters.) So I think you need to get over it... ****************************************************************