Skip to content

Commit cb9b984

Browse files
committed
PR47954 / DR2126: permit temporary objects that are lifetime-extended by
variables that are usable in constant expressions to themselves be usable in constant expressions.
1 parent 6a72635 commit cb9b984

File tree

7 files changed

+100
-11
lines changed

7 files changed

+100
-11
lines changed

clang/include/clang/AST/ExprCXX.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4519,6 +4519,10 @@ class MaterializeTemporaryExpr : public Expr {
45194519
return getValueKind() == VK_LValue;
45204520
}
45214521

4522+
/// Determine whether this temporary object is usable in constant
4523+
/// expressions, as specified in C++20 [expr.const]p4.
4524+
bool isUsableInConstantExpressions(const ASTContext &Context) const;
4525+
45224526
SourceLocation getBeginLoc() const LLVM_READONLY {
45234527
return getSubExpr()->getBeginLoc();
45244528
}

clang/lib/AST/ExprCXX.cpp

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1647,6 +1647,20 @@ void MaterializeTemporaryExpr::setExtendingDecl(ValueDecl *ExtendedBy,
16471647
ES->ManglingNumber = ManglingNumber;
16481648
}
16491649

1650+
bool MaterializeTemporaryExpr::isUsableInConstantExpressions(
1651+
const ASTContext &Context) const {
1652+
// C++20 [expr.const]p4:
1653+
// An object or reference is usable in constant expressions if it is [...]
1654+
// a temporary object of non-volatile const-qualified literal type
1655+
// whose lifetime is extended to that of a variable that is usable
1656+
// in constant expressions
1657+
auto *VD = dyn_cast_or_null<VarDecl>(getExtendingDecl());
1658+
return VD && getType().isConstant(Context) &&
1659+
!getType().isVolatileQualified() &&
1660+
getType()->isLiteralType(Context) &&
1661+
VD->isUsableInConstantExpressions(Context);
1662+
}
1663+
16501664
TypeTraitExpr::TypeTraitExpr(QualType T, SourceLocation Loc, TypeTrait Kind,
16511665
ArrayRef<TypeSourceInfo *> Args,
16521666
SourceLocation RParenLoc, bool Value)

clang/lib/AST/ExprConstant.cpp

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4093,27 +4093,32 @@ static CompleteObject findCompleteObject(EvalInfo &Info, const Expr *E,
40934093
assert(MTE->getStorageDuration() == SD_Static &&
40944094
"should have a frame for a non-global materialized temporary");
40954095

4096-
// Per C++1y [expr.const]p2:
4096+
// C++20 [expr.const]p4: [DR2126]
4097+
// An object or reference is usable in constant expressions if it is
4098+
// - a temporary object of non-volatile const-qualified literal type
4099+
// whose lifetime is extended to that of a variable that is usable
4100+
// in constant expressions
4101+
//
4102+
// C++20 [expr.const]p5:
40974103
// an lvalue-to-rvalue conversion [is not allowed unless it applies to]
4098-
// - a [...] glvalue of integral or enumeration type that refers to
4099-
// a non-volatile const object [...]
4100-
// [...]
4101-
// - a [...] glvalue of literal type that refers to a non-volatile
4102-
// object whose lifetime began within the evaluation of e.
4104+
// - a non-volatile glvalue that refers to an object that is usable
4105+
// in constant expressions, or
4106+
// - a non-volatile glvalue of literal type that refers to a
4107+
// non-volatile object whose lifetime began within the evaluation
4108+
// of E;
41034109
//
41044110
// C++11 misses the 'began within the evaluation of e' check and
41054111
// instead allows all temporaries, including things like:
41064112
// int &&r = 1;
41074113
// int x = ++r;
41084114
// constexpr int k = r;
4109-
// Therefore we use the C++14 rules in C++11 too.
4115+
// Therefore we use the C++14-onwards rules in C++11 too.
41104116
//
41114117
// Note that temporaries whose lifetimes began while evaluating a
41124118
// variable's constructor are not usable while evaluating the
41134119
// corresponding destructor, not even if they're of const-qualified
41144120
// types.
4115-
if (!(BaseType.isConstQualified() &&
4116-
BaseType->isIntegralOrEnumerationType()) &&
4121+
if (!MTE->isUsableInConstantExpressions(Info.Ctx) &&
41174122
!lifetimeStartedInEvaluation(Info, LVal.Base)) {
41184123
if (!IsAccess)
41194124
return CompleteObject(LVal.getLValueBase(), nullptr, BaseType);

clang/test/CXX/drs/dr21xx.cpp

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,40 @@ namespace dr2120 { // dr2120: 7
3232
static_assert(!__is_standard_layout(E), "");
3333
}
3434

35+
namespace dr2126 { // dr2126: 12
36+
#if __cplusplus >= 201103L
37+
struct A { int n; };
38+
39+
const A &a = {1}; // const temporary
40+
A &b = (A &)(const A &)A{1}; // const temporary
41+
A &&c = (A &&)(const A &)A{1}; // const temporary
42+
43+
A &&d = {1}; // non-const temporary expected-note {{here}}
44+
const A &e = (A &)(A &&) A{1}; // non-const temporary expected-note {{here}}
45+
A &&f = (A &&)(A &&) A{1}; // non-const temporary expected-note {{here}}
46+
47+
constexpr const A &g = {1}; // const temporary
48+
constexpr A &&h = {1}; // non-const temporary expected-note {{here}}
49+
50+
struct B { const A &a; };
51+
B i = {{1}}; // extending decl not usable in constant expr expected-note {{here}}
52+
const B j = {{1}}; // extending decl not usable in constant expr expected-note {{here}}
53+
constexpr B k = {{1}}; // extending decl usable in constant expr
54+
55+
static_assert(a.n == 1, "");
56+
static_assert(b.n == 1, "");
57+
static_assert(c.n == 1, "");
58+
static_assert(d.n == 1, ""); // expected-error {{constant}} expected-note {{read of temporary}}
59+
static_assert(e.n == 1, ""); // expected-error {{constant}} expected-note {{read of temporary}}
60+
static_assert(f.n == 1, ""); // expected-error {{constant}} expected-note {{read of temporary}}
61+
static_assert(g.n == 1, "");
62+
static_assert(h.n == 1, ""); // expected-error {{constant}} expected-note {{read of temporary}}
63+
static_assert(i.a.n == 1, ""); // expected-error {{constant}} expected-note {{read of non-constexpr variable}}
64+
static_assert(j.a.n == 1, ""); // expected-error {{constant}} expected-note {{read of temporary}}
65+
static_assert(k.a.n == 1, "");
66+
#endif
67+
}
68+
3569
namespace dr2140 { // dr2140: 9
3670
#if __cplusplus >= 201103L
3771
union U { int a; decltype(nullptr) b; };

clang/test/CodeGenCXX/const-init-cxx11.cpp

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -400,6 +400,22 @@ namespace UnemittedTemporaryDecl {
400400
// CHECK: @_ZN22UnemittedTemporaryDecl4ref2E = constant i32* @_ZGRN22UnemittedTemporaryDecl3refE_
401401
}
402402

403+
namespace DR2126 {
404+
struct A { int &&b; };
405+
constexpr const A &a = {42};
406+
// CHECK: @_ZGRN6DR21261aE0_ = internal global i32 42
407+
// FIXME: This is unused and need not be emitted.
408+
// CHECK: @_ZGRN6DR21261aE_ = internal constant {{.*}} { i32* @_ZGRN6DR21261aE0_ }
409+
// CHECK: @_ZN6DR21261rE = constant i32* @_ZGRN6DR21261aE0_
410+
int &r = a.b;
411+
412+
// Dynamically initialized: the temporary object bound to 'b' could be
413+
// modified (eg, by placement 'new') before the initializer of 's' runs.
414+
constexpr A &&b = {42};
415+
// CHECK: @_ZN6DR21261sE = global i32* null
416+
int &s = b.b;
417+
}
418+
403419
// CHECK: @_ZZN12LocalVarInit3aggEvE1a = internal constant {{.*}} i32 101
404420
// CHECK: @_ZZN12LocalVarInit4ctorEvE1a = internal constant {{.*}} i32 102
405421
// CHECK: @__const._ZN12LocalVarInit8mutable_Ev.a = private unnamed_addr constant {{.*}} i32 103

clang/test/SemaCXX/constant-expression-cxx11.cpp

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -409,12 +409,23 @@ namespace ConstAddedByReference {
409409
const int &r = (0);
410410
constexpr int n = r;
411411

412+
int &&r2 = 0; // expected-note {{created here}}
413+
constexpr int n2 = r2; // expected-error {{constant}} expected-note {{read of temporary}}
414+
412415
struct A { constexpr operator int() const { return 0; }};
413416
struct B { constexpr operator const int() const { return 0; }};
414417
const int &ra = A();
415418
const int &rb = B();
416419
constexpr int na = ra;
417420
constexpr int nb = rb;
421+
422+
struct C { int &&r; };
423+
constexpr C c1 = {1};
424+
constexpr int &c1r = c1.r;
425+
constexpr const C &c2 = {2};
426+
constexpr int &c2r = c2.r;
427+
constexpr C &&c3 = {3}; // expected-note {{created here}}
428+
constexpr int &c3r = c3.r; // expected-error {{constant}} expected-note {{read of temporary}}
418429
}
419430

420431
}
@@ -1843,6 +1854,11 @@ namespace InitializerList {
18431854

18441855
static_assert(*std::initializer_list<int>{1, 2, 3}.begin() == 1, "");
18451856
static_assert(std::initializer_list<int>{1, 2, 3}.begin()[2] == 3, "");
1857+
1858+
namespace DR2126 {
1859+
constexpr std::initializer_list<float> il = {1.0, 2.0, 3.0};
1860+
static_assert(il.begin()[1] == 2.0, "");
1861+
}
18461862
}
18471863

18481864
namespace StmtExpr {

clang/www/cxx_dr_status.html

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12571,7 +12571,7 @@ <h2 id="cxxdr">C++ defect report implementation status</h2>
1257112571
<td><a href="https://wg21.link/cwg2126">2126</a></td>
1257212572
<td>DRWP</td>
1257312573
<td>Lifetime-extended temporaries in constant expressions</td>
12574-
<td class="none" align="center">Unknown</td>
12574+
<td class="unreleased" align="center">Clang 12</td>
1257512575
</tr>
1257612576
<tr class="open" id="2127">
1257712577
<td><a href="https://wg21.link/cwg2127">2127</a></td>
@@ -13843,7 +13843,7 @@ <h2 id="cxxdr">C++ defect report implementation status</h2>
1384313843
<td><a href="https://wg21.link/cwg2338">2338</a></td>
1384413844
<td>DRWP</td>
1384513845
<td>Undefined behavior converting to short enums with fixed underlying types</td>
13846-
<td class="none" align="center">Unknown</td>
13846+
<td class="unreleased" align="center">Clang 12</td>
1384713847
</tr>
1384813848
<tr id="2339">
1384913849
<td><a href="https://wg21.link/cwg2339">2339</a></td>

0 commit comments

Comments
 (0)