cplusplus / CWG

Core Working Group
23 stars 7 forks source link

CWG2849 [class.temporary] "temporary object other than a function parameter object" wording is wrong #490

Closed t3nsor closed 2 months ago

t3nsor commented 5 months ago

Full name of submitter: Brian Bi

Reference (section label): [class.temporary]

Issue description: From Stack Overflow.

The wording "temporary object other than a function parameter object" does not make sense since a function parameter object is never a temporary object ([class.temporary]/1). In the example that was added during CWG deliberation in Kona (2022) to illustrate this exception, the parameter of f2 is not a temporary in the first place, so there is no exception. Note that in the special case where the implementation creates an extra temporary under [class.temporary]/3, that temporary is not the same as the function parameter object, and its lifetime is unobservable.

Suggested resolution:

Strike "other than a function parameter object" from [class.temporary]/7.

Insert a new note before Example 2 in [stmt.ranged]:

[ Note: A function parameter object is not a temporary object ([class.temporary]), even if its lifetime ends at the end of the full-expression containing the call ([expr.call]). — end note ]

frederick-vs-ja commented 5 months ago

There also seems something wrong in [class.temporary] p3.

The current wording says:

[...] implementations are permitted to create a temporary object to hold the function parameter or result object. [...]

which is inconsistent with the next sentence:

[...] The temporary object is constructed from the function argument or return value, respectively, [...]

It seems to me that the part "to hold the function parameter or result object" is redundant and defective, perhaps we should strike it.

t3nsor commented 5 months ago

There also seems something wrong in [class.temporary] p3.

The current wording says:

[...] implementations are permitted to create a temporary object to hold the function parameter or result object. [...]

which is inconsistent with the next sentence:

[...] The temporary object is constructed from the function argument or return value, respectively, [...]

It seems to me that the part "to hold the function parameter or result object" is redundant and defective, perhaps we should strike it.

I think that could be fixed as part of CWG2434 (which probably needs a volunteer for drafting)

jensmaurer commented 5 months ago

One says "function parameter or result object", the other "function argument or return value". Note parameter vs. argument.

jensmaurer commented 5 months ago

If a parameter object is not a temporary object, what is the storage duration related to a function parameter object? (Function parameter objects may be destroyed when the function returns, or at the end of the full-expression. Does the storage outlive the point of destruction, at least in the former case?)

t3nsor commented 5 months ago

If a parameter object is not a temporary object, what is the storage duration related to a function parameter object? (Function parameter objects may be destroyed when the function returns, or at the end of the full-expression. Does the storage outlive the point of destruction, at least in the former case?)

In current wording, parameter objects unambiguously have automatic storage duration under [basic.stc.auto]/1 notwithstanding the special rules in [expr.call]/6 about when they're destroyed.

I don't think we ever explicitly say when storage lifetime ends, except in the case of dynamic storage duration, but that's a different issue.

jensmaurer commented 5 months ago

I don't think we ever explicitly say when storage lifetime ends,

We do:

[basic.stc.auto] p1 says:

The storage for these entities lasts until the block in which they are created exits.

Since parameter objects are created in the caller, that means their storage stays around until the end of the block (and can't be reused by the compiler unless proven otherwise).

t3nsor commented 5 months ago

[basic.stc.auto] p1 says:

The storage for these entities lasts until the block in which they are created exits.

Since parameter objects are created in the caller, that means their storage stays around until the end of the block (and can't be reused by the compiler unless proven otherwise).

That seems unlikely to be what we actually want, right? If a block contains two separate statements that each contain a function call, it seems obvious that the second function call can reuse the space in which the first function call allocated parameter objects.

I think what we want is for the storage lifetime for parameter objects to parallel the (implementation-defined choice of) lifetime, and [stmt.dcl] probably needs to be amended too.

jensmaurer commented 5 months ago

That seems unlikely to be what we actually want, right?

Right. That means we have three issue with "storage duration", at least in the sense of "how long does the storage exist":

jensmaurer commented 5 months ago

CWG2849 for the original issue.

t3nsor commented 5 months ago

If we decide we also want to tackle the wider "storage duration of function parameters" issue then here's a first attempt at some drafting.

Edit [basic.stc.auto]/1, combining it with the note in paragraph 2. (The "end of duration of storage" wording is borrowed from [basic.stc.general].)

Variables that belong to a block or parameter scope and are not explicitly declared static, thread_local, or extern have automatic storage duration. The storage for ~these entities~ such block variables lasts until the block in which they are created exits.
[ Note 1: These block variables are initialized and destroyed as described in [stmt.dcl]. —end note ] It is implementation-defined whether the duration of storage for a parameter ends when the function in which it is defined returns or at the end of the full-expression containing the call.
[ Note 2: Parameters are initialized and destroyed as described in [expr.call]. — end note ]

Edit [class.temporary]/8:

~The destruction of a temporary whose lifetime is not extended beyond the full-expression in which it was created is sequenced before the destruction of every temporary which is constructed earlier in the same full-expression.~ Let x and y each be either a temporary object whose lifetime is not extended, or a function parameter. If the lifetimes of x and y end at the end of the same full-expression, and the initialization of x is sequenced before that of y, then the destruction of y is sequenced before that of x. If the lifetime of two or more temporaries with lifetimes extending beyond the full-expressions in which they were created ends at the same point, these temporaries are destroyed at that point in the reverse order of the completion of their construction. [...]

Edit [expr.call]/6:

[...] ~It is implementation-defined whether the lifetime of a parameter ends when the function in which it is defined returns or at the end of the enclosing full-expression.~ When the duration of storage for one or more parameters ends ([basic.stc.auto]), all such parameters are destroyed in the reverse order of their construction. The initialization and destruction of each parameter occurs within the context of the full-expression ([intro.execution]) where the function call appears.

Edit [stmt.dcl]/2 to clarify that it applies only to block variables, since that seems to be the original intent anyway (e.g., we don't guarantee that function parameters are initialized in declaration order). Since function parameters don't have a declaration-statement, it would make more sense to describe them elsewhere.

A block variable with automatic storage duration ([basic.stc.auto]) is active everywhere in the scope to which it belongs after its init-declarator. Upon each transfer of control (including sequential execution of statements) within a function from point P to point Q, all block variables with automatic storage duration that are active at P and not at Q are destroyed in the reverse order of their construction. Then, all block variables with automatic storage duration that are active at Q but not at P are initialized in declaration order; unless all such variables have vacuous initialization ([basic.life]), the transfer of control shall not be a jump. When a declaration-statement is executed, P and Q are the points immediately before and after it; when a function returns, Q is after its body.

t3nsor commented 5 months ago

I updated the drafting above based on feedback from today's telecon.

jensmaurer commented 5 months ago

CWG2850 for the storage duration of function parameter objects