Open keinflue opened 1 month ago
In the case of result objects, the most recent possible resolution in CWG 2868 would go the other direction and make my example guaranteed ill-formed. Even with that resolution, the issue remains for parameter passing.
The intent of CWG2868 is to make b.p
an invalid pointer value (because the storage of the original "A" object has ended). We generally want constant evaluation to behave like runtime evaluation as much as possible, so your example is rightfully ill-formed (bad result of constant evaluation) under CWG2868.
Could you formulate an example for parameter passing, please?
I suppose the situation is less severe in the case of parameter passing. The best I can come with at the moment would be
#include<iostream>
struct A {
A* p;
constexpr A() : p(this) {}
};
constexpr bool f(A a) { return &a == a.p; }
int main() {
constexpr bool b1 = f({});
bool b2 = f({});
std::cout << b1 << b2 << "\n";
}
All four outputs 00
, 01
, 10
and 11
are currently permitted. GCC outputs 00
, i.e. it introduces a temporary at compile time and run time. Clang and MSVC output 10
, i.e. they introduce the temporary only at run time. (https://godbolt.org/z/cMTTEzcbb)
We generally want constant evaluation to behave like runtime evaluation
IMO we cannot eliminate all inconsistencies here, because of we want to allow some types meeting some conditions to be passed and returned in registers, while the concret conditions depend on platforms.
If we want to go for the current approach for CWG2868, perhaps we should say "If the function call is performed in the evaluation of a constant expression ([expr.const]), such a temporary object is always created in argument passing." in [class.temporary] p3.
May be off-topic. Let me check what's happening before & after CWG2868...
return
statement with the "objectness" detected, we have to make the prvalue to be materialized to a temporary object. And then the result object of the function call (which can be temporary or non-temporary) is considered trivially copied/moved from that temporary object.reinterpret_cast
in P2434R1 possibly defeats the intent of CWG2868. Since
i. when materialization is directly performed in the return slot, the address of the return slot can be recorded as an integer by reinterpret_cast
, and then,
ii. after returning, on common platforms, the integer can be reinterpret_cast
'd back to a pointer which properly points to the result object due to the angelic nondeterminism.I don't know whether this is a concern with current implementations, but if the temporary object is mandatory it may require keeping around double the stack size during constant evaluation. Both the temporary object and the parameter object can be accessed through the pointers a.p
and &a
, respectively, and are alive during the function call. The parameter object at least may also be modified and if that happens, the old state of the temporary object, which may be large, must be kept around.
This should not be a concern at run time because the temporary is not mandatory and doesn't need to be used for large objects.
Btw., [class.temporary]/3 doesn't specify the type of the temporary object. Is it X
or const X
, i.e. can the temporary object be modified?
If we want to do something about parameter objects, we'll likely do the two-step temporaries as for the return value, so that internal pointers become invalid, but memory can otherwise be reused.
Full name of submitter (unless configured in github; will be published with the issue): Benjamin Sch.
Reference (section label): [class.temporary]/3, [expr.const]/1
Issue description:
Resolution of CWG 2022, modified by CWG 2278, makes sure that whether or not an expression is a constant expression doesn't depend on unspecified compiler choices for copy elision.
The resolutions don't cover the following modification of the example in CWG 2022:
Here implementations are still permitted to introduce a temporary object for
f
's result object per [class.temporary]/3, causing initialization ofb
to possibly be ill-formed.Suggested resolution:
Disallow introduction of a temporary during constant expression evaluation with the same wording as used for resolution of the issues mentioned above.
Modify [class.temporary]/3:
Modify [expr.const]/1: