AI22-0017-1

!standard 4.5.9(10/5)                                    23-04-27  AI22-0017-1/03

!class ramification 23-03-17

!status Corrigendum 1-2022  23-03-30

!status WG9 Approved 23-06-13

!status ARG Approved 12-0-0  23-03-30

!status work item 21-11-12

!status received 21-04-08

!priority Low

!difficulty Easy

!subject Objects declared in declare expressions may be long-lived

!summary

Clarify that an object declared within a declare_expression may outlive the evaluation of the expression. This may be surprising for folks who incorrectly think that a declare expression is equivalent to declaring a function and then calling it.

!issue

In many cases, the evaluation of a declare expression is not a master. In such cases, the lifetimes of objects declared within the declare expression may extend well beyond the completion of the evaluation of the declare expression. This seems to be inconsistent with what many users expect, so an AARM note clarifying this point is added.

!recommendation

This is just a clarification. No changes are being made.

!wording

Add after 4.5.9(10/5) [at the end of the Dynamic semantics section]

AARM Ramification:

If the evaluation of a declare_expression is not a master (see 7.6.1) then objects declared therein may still exist (and have pending finalization) after the evaluation of the declare_expression has completed. This is consistent with the handling of other objects created by subexpressions, such as those created by the evaluation of aggregates or function calls.

!discussion

The original motivation for this AI was the requirement that objects created by repeated evaluations of a declare_expression as part of the evaluation of an enclosing expression persist until the entire expression is evaluated. For example, in an aggregate like (1..10 => (declare X : constant Some_Controlled_Type := F begin X.C)), none of the 10 X objects could be finalized until all 10 had been evaluated. Eventually, it was recognized that lifetimes of repeatedly evaluated subexpressions was a problem that existed in many existing cases (going back to Ada 95) and was not specific to declare_expressions. It was split out into AI22-0040-1 and addressed for all expressions.

After some false starts, we realized that the model for Ada has always been that there is a single master for an (entire) expression. This means that temporaries created for inner function calls have to persist until the expression as a whole has been evaluated. The declare expression case is no different, and should be handled consistently.

We note that AI22-0040-1 makes some exceptions to this general rule, but the general rule remains. This rule was adopted in part because creating/destroying a master probably has a runtime cost in any case where it is possible that finalization (or task waiting!) is needed. And there are many cases where it is not possible to determine that finalization is not needed. If every expression construct was a potential master, a lot of additional and unnecessary overhead could be created. Moreover, changing the general rule could have a significant effect on existing implementations (which do not have to create masters in the middle of expressions).

!example

In the following example, the finalization of X takes place after, not before, the call to the procedure Proc:

  Proc (Boolean'(declare X : constant Some_Controlled_Type := ...;
          begin True));

!ACATS test

No ACATS test is needed for a ramification, although it would be possible to write one based on the example given above.

!appendix