!standard 5.5.2(6/3) 13-05-08 AI12-0047-1/06 !class binding interpretation 12-12-01 !status Corrigendum 1-2012 12-12-31 !status WG9 Approved 13-06-14 !status ARG Approved by letter ballot (12-0-2) 13-02-25 !status work item 13-01-07 !status ARG Approved 11-0-0 12-12-08 !status work item 12-12-01 !status received 12-11-12 !priority Medium !difficulty Medium !subject Generalized iterators and discriminant-dependent components !summary The iterable_name or iterator_name of an iterator cannot be a discriminant-dependent subcomponent of a mutable type. The iterable_name or iterator_name of an iterator is not finalized until the loop completes. !question 1) Consider: subtype Index is Integer range 0 .. 100; type Rec (Last : Index := 50) is record F : String (1 .. Last); end record; X : Rec; begin for Char of X.F loop X := (0, ""); Char := '?'; end loop; In this case, the assignment to X makes the array object disappear. This should be illegal, right? (Yes.) 2) The following is defined to be a master (7.6.1(3/2)): ... the evaluation of an expression, function_call, or range that is not part of an enclosing expression, function_call, range, or simple_statement other than a simple_return_statement Consider: for I of Func_Returning_Array_With_Controlled_Elements loop The rule above means that the function call result object gets finalized before beginning the first iteration of the loop. This is not good (especially as the tampering mechanism assumes that this finalization happens when the loop is exited). Should something be changed? (No.) !recommendation (See summary.) !wording Add after 5.5.2(6/3): The iterator_name or iterable_name of an iterator_specification shall not denote a subcomponent that depends on discriminants of an object whose nominal subtype is unconstrained, unless the object is known to be constrained. !discussion Problem #1 is solved by following the lead of renaming. We directly repeat the interesting rule as defining iterators in terms of renaming would drag in other rules that may or may not be appropriate. In particular, it would require that the object can be clobbered inside the loop (with unpredictable results for a container iterator): for E of X loop ... X := ; ... end loop; Ada.Containers has tampering checks to prevent this situation (the assignment would raise Program_Error), but user-defined packages may not. So we do not take this approach. Problem #2 is not a problem at all, as the master of a return object of a function call is that which encloses the function call; whether the function call is a master has no impact on that. (See AARM 7.6.1(3.c/2).) The fact that the function call is a master mainly is important to specify the lifetime of the actual parameters to be short, as in the following example: for E of Func_Returning_Array (Func_Returning_Record_With_Controlled_Components) loop In this case, the result of Func_Returning_Record_With_Controlled_Components is finalized before the loop begins to execute (assuming the parameter to Func_Returning_Array is not aliased), as the call of Func_Returning_Array is its master, while the result of Func_Returning_Array is not finalized until the loop is complete (as the loop statement is the master). This is what we want for generalized iterators, so no change is needed. There is no problem with more complex names, either. Consider: for E of Func.Some_Array_with_Controlled_Components loop An iterator_specification contains an iterator_name or iterable_name; these are not expressions. As such, the entire name is not a master, either (a name is not in the list of the things that can be masters in 7.6.1(3/2)). Thus, in this case, Func.Some_Array_with_Controlled_Components is not a master, so its master is the entire loop statement; thus the master of the result of Func is also the entire loop statement, and the result of Func is not finalized until the iteration completes. This is what we want. Contrast this with the following: for I in 1 .. Func.Some_Integer loop Here, Func.Some_Integer is an expression, this is a master by 7.6.1(3/2) as it is not enclosed by one of the things in the list in that rule. Thus the master of the result of Func is the master that is the Func.Some_Integer, and thus that result is finalized before the loop iterates. Again, this is what we want. !corrigendum 5.5.2(6/3) @dinsa In a container element iterator whose @i@fa has type @i, if the @i@fa denotes a constant or the Variable_Indexing aspect is not specified for @i, then the Constant_Indexing aspect shall be specified for @i. @dinst The @i@fa or @i@fa of an @fa shall not denote a subcomponent that depends on discriminants of an object whose nominal subtype is unconstrained, unless the object is known to be constrained. !ACATS test ACATS B-Tests should be created to test these rules. !ASIS No ASIS impact. !appendix From: Steve Baird Sent: Monday, November 12, 2012 12:35 PM Two issues with generalized iterators. 1) We need to deal with the following example, either by making it illegal (following the example of 8.5.1(5/3)) or by defining its execution to be erroneous (following the example of 6.4.1(18/3)): subtype Index is Integer range 0 .. 100; type Rec (Last : Index := 50) is record F : String (1 .. Last); end record; X : Rec; begin for Char of X.F loop X := (0, ""); Char := '?'; end loop; I think we want tn array component iterator to behave (statically and dynamically - see next item) as though a rename of the array object were introduced. If such a rename would be illegal, then the iterator should also be illegal. We don't want new constructs to introduce new forms of erroneous behavior (and hence new ways of writing unreliable software) if we can avoid it. I haven't thought about whether analogous problems exist for the other generalized loop iteration forms. Opinions? 2) The following is defined to be a master (7.6.1(3/2)): ... the evaluation of an expression, function_call, or range that is not part of an enclosing expression, function_call, range, or simple_statement other than a simple_return_statement This is generally a good thing. In a case like if Function_With_Controlled_Components.Flag then P; else Q; , it means that the function call object is finalized before P or Q is called. This is good. In the example for I in 1 .. Function_Call.Scalar_Field loop we will finalize the function result object before beginning the first iteration of the loop (but after storing away the pre-finalization value of the Scalar_Field component). Again, this is good. However, generationed loop iteration introduces (for the first time, I think) the possibility that a non-scalar expression can be a master. Consider: For I of Func_Returning_Array_With_Controlled_Elements loop As the rules stand today, I believe the function call result object gets finalized before beginning the first iteration of the loop (just like the previous example). This is not good. I think this could be solved by adding the word "elementary" (or perhaps "scalar"?) to the 7.6.1 wording cited above, so that it reads .... the evaluation of an elementary expression, function_call, or range that is not part of an enclosing ... With that rule, the nearest enclosing master for the function call would be the loop statement itself, so the function result object would be finalized after the loop is completed (which is what we want). Opinions? **************************************************************** From: Tucker Taft Sent: Monday, November 12, 2012 1:21 PM > 1) ... I think we want the array > component iterator to behave (statically and dynamically - see next > item) as though a rename of the array object were introduced. > If such a rename would be illegal, then the iterator should also be > illegal. We don't want new constructs to introduce new forms of > erroneous behavior (and hence new ways of writing unreliable > software) if we can avoid it. > > I haven't thought about whether analogous problems exist for the other > generalized loop iteration forms. > > Opinions? Agreed. > 2) The following is defined to be a master (7.6.1(3/2)): > > ... the evaluation of an expression, function_call, or range that is > not part of an enclosing expression, function_call, range, or > simple_statement other than a simple_return_statement > > This is generally a good thing. In a case like > > if Function_With_Controlled_Components.Flag then > P; > else > Q; > > , it means that the function call object is finalized before P or Q is > called. > > This is good. > > In the example > > for I in 1 .. Function_Call.Scalar_Field loop > > we will finalize the function result object before beginning the first > iteration of the loop (but after storing away the pre-finalization > value of the Scalar_Field component). > > Again, this is good. > > However, generationed loop iteration introduces (for the first time, I > think) the possibility that a non-scalar expression can be a master. > > Consider: > > For I of Func_Returning_Array_With_Controlled_Elements loop > > As the rules stand today, I believe the function call result object > gets finalized before beginning the first iteration of the loop (just > like the previous example). > > This is not good. > > I think this could be solved by adding the word "elementary" > (or perhaps "scalar"?) to the 7.6.1 wording cited above, so that it > reads > > .... the evaluation of an elementary expression, function_call, > or range that is not part of an enclosing ... > > With that rule, the nearest enclosing master for the function call > would be the loop statement itself, so the function result object > would be finalized after the loop is completed (which is what we > want). > > Opinions? This makes me somewhat nervous, as it seems like a big change. But perhaps it is OK. There are very few places where non-elementary expressions occur where they are not part of a larger expression, and those may mostly be cases where some kind of implicit rename is taking place. The other solution would be to treat these generalized iterators as implicit renames, since you have already suggested that in part (1). **************************************************************** From: Steve Baird Sent: Monday, November 12, 2012 12:35 PM > The other solution would be to treat these generalized iterators as > implicit renames, since you have already suggested that in part (1). Just fleshing out your suggestion, this approach would also include implicitly defining an enclosing block statement, right? The implicit rename declaration has to live somewhere. ... > I haven't thought about whether analogous problems exist for the other > generalized loop iteration forms. I think we may need to look more closely at this question before deciding between the two approaches (i.e., change the definition of master vs. implicit renames in implicit blocks). Incidentally, typos in previous message: "generationed loop iteration" => "generalized loop iteration" "we want tn array" => "we want an array" **************************************************************** From: Randy Brukardt Sent: Monday, November 12, 2012 6:06 PM Implicit renames might cause issues with user-defined iterators. In particular, your example of: for E of X loop ... X := ; ... end loop; looks like it could be trouble if X is considered a rename. The iteration could end up screwed up. The language-defined containers "solve" this problem with the tampering mechanism, and I think the only problem for arrays would be the example you originally showed (which would be illegal if a rename), so it mostly would be a problem with user-defined iterators. OTOH, if you fixed the problem with masters and implicit temporaries, then the problem (1) would not need to be illegal (Constraint_Error might be raised, though, not sure which is worse). **************************************************************** From: Randy Brukardt Sent: Monday, November 12, 2012 6:53 PM > I haven't thought about whether analogous problems exist for the other > generalized loop iteration forms. Don't they have to? Just wrap the container or iterator object in a variant record, and change the discriminant of the variant within the loop to make the object disappear. Of course this is a pathology (especially for the iterator object case, it's hard to imagine any good reason to put those into a record), but it seems dangerous enough to detect (the only alternative being erroneous execution, which we want to avoid if possible). We can fix this problem easily by adapting 8.5.1(5/3) directly: The iterator_name or iterable_name of a iterator_specification shall not be a subcomponent that depends on discriminants of an object a variable whose nominal subtype is unconstrained, unless the object is known to be constrained. This is simpler than trying to drag in the entire mechanism of renaming (presuming we don't need it anyway for the master problem). I'm not going to waste time now considering the master issue...(no sensible solution jumps out at me). **************************************************************** From: Randy Brukardt Sent: Monday, December 31, 2012 10:26 PM AI12-0047-1 changes the definition of masters to ensure that the object in an iterator like: for E of Func loop ... end loop; is not finalized before the end of the loop. However, I think we missed something in that wording. The problem is that compound statements are not masters, so the completion of the loop does not finalize anything. That means that Func in the above could live too long (until whatever master encloses the loop is finalized). That's a problem as it would mean that the natural implementation of the tampering checks for the containers would leave the containers locks for some arbitrary amount of time after the loop completed. That's surely not the intent, nor is it a good idea. I think that we have to add loop_statement with an iterator_specification to the list of things that can be masters. Thoughts?? P.S. I'm unconvinced that we needed to make the change to "scalar function_calls" in 7.6.1(3/2) that we approved, because that does not apply to the result of the function_call (it is whatever encloses a function call that determines when the function result is finalized, not the master that is the function call. This latter master matters for the parameters of the call, not the result, as is explained in the AARM notes following 7.6.1(3/2). This change is harmless, however, so I'm not going to worry about it further. P.P.S. The 2nd question in the AI is completely bogus because of the misinterpretation noted in the P.S. Should it get rewritten to explain the *real* problem?? **************************************************************** From: Steve Baird Sent: Monday, January 7, 2013 2:25 PM > However, I think we missed something in that wording. The problem is > that compound statements are not masters, so the completion of the > loop does not finalize anything. A compound statement is a master and this "problem" therefore isn't a problem. 7.6.1(3/2): Leaving an execution happens immediately after its completion, except in the case of a master: ...; the execution of a statement; or .... **************************************************************** From: Tucker Taft Sent: Monday, January 7, 2013 3:28 PM > However, I think we missed something in that wording. The problem is > that compound statements are not masters, so the completion of the > loop does not finalize anything. That means that Func in the above > could live too long (until whatever master encloses the loop is finalized). As Steve pointed out, all statements are masters. > ... > P.S. I'm unconvinced that we needed to make the change to "scalar > function_calls" in 7.6.1(3/2) that we approved, because that does not > apply to the result of the function_call (it is whatever encloses a > function call that determines when the function result is finalized, > not the master that is the function call. This latter master matters > for the parameters of the call, not the result, as is explained in the > AARM notes following 7.6.1(3/2). This change is harmless, however, so > I'm not going to worry about it further. I agree. The 2nd question of the AI is a red herring. We all got suckered in by Steve's logic! ;-) There was never any problem, and our fix, though benign, was unnecessary. > P.P.S. The 2nd question in the AI is completely bogus because of the > misinterpretation noted in the P.S. Should it get rewritten to explain > the > *real* problem?? Yes, probably. It is unwise to leave this AI in a state where it is clearly misleading. **************************************************************** From: Steve Baird Sent: Monday, January 7, 2013 6:12 PM >> P.S. I'm unconvinced that we needed to make the change to "scalar >> function_calls" in 7.6.1(3/2) that we approved, because that does not >> apply to the result of the function_call (it is whatever encloses a >> function call that determines when the function result is finalized, >> not the master that is the function call. This latter master matters >> for the parameters of the call, not the result, as is explained in the >> AARM notes following 7.6.1(3/2). This change is harmless, however, so >> I'm not going to worry about it further. > I agree. The 2nd question of the AI is a red herring. We all got > suckered in by Steve's logic! ;-) There was never any problem, and > our fix, though benign, was unnecessary. The AARM wording we are talking about here is: The fact that a function_call is a master does not change the accessibility of the return object denoted by the function_call; that depends on the use of the function_call. I agree that I overlooked this. Now you two have me worrying that the fix I thought was necessary is not benign. If we have for I of Container_Valued_Function (Function_With_Controlled_Result) loop ...; end loop; then does the change we approved change the point at which we finalize the function result object for the call to Function_With_Controlled_Result? Without the change, the enclosing call is a master and so the controlled function result object is finalized before the first iteration of the loop body. With the change, that enclosing call is no longer a master. Doesn't that mean that the controlled function result object is therefore not finalized until the completion of the loop statement? That seems like an undesirable change. The main point here is that we want to avoid requiring implementations to do anything different than whatever they were doing before. Bob Duff wrote (on another thread): > I think this discussion illustrates the point that when we meddle with > RM wording, we're about as likely to break things as to fix things Bob, I have no idea what you might be referring to. **************************************************************** From: Tucker Taft Sent: Monday, January 7, 2013 7:33 PM > Now you two have me worrying that the fix I thought was necessary is > not benign. ... Sounds like we should undo this change. **************************************************************** From: Randy Brukardt Sent: Monday, January 7, 2013 7:42 PM Agreed. It's unnecessary, and might change run-time behavior. No reason for that. **************************************************************** From: Tucker Taft Sent: Friday, February 1, 2013 9:41 AM [Relevant part of a larger message - Editor.] Two editorial comments: In AI12-0047, there are two instances of "a iterator" in the summary which should be "an iterator." **************************************************************** From: Gary Dismukes Sent: Friday, February 1, 2013 1:12 PM [Relevant part of a larger message - Editor.] A nit: in the !subject, hyphenate "discriminant dependent". ****************************************************************