PR47954 / DR2126: permit temporary objects that are lifetime-extended by

variables that are usable in constant expressions to themselves be
usable in constant expressions.
This commit is contained in:
Richard Smith 2020-10-23 14:27:24 -07:00
parent 6a72635881
commit cb9b9842d3
7 changed files with 100 additions and 11 deletions

View File

@ -4519,6 +4519,10 @@ public:
return getValueKind() == VK_LValue;
}
/// Determine whether this temporary object is usable in constant
/// expressions, as specified in C++20 [expr.const]p4.
bool isUsableInConstantExpressions(const ASTContext &Context) const;
SourceLocation getBeginLoc() const LLVM_READONLY {
return getSubExpr()->getBeginLoc();
}

View File

@ -1647,6 +1647,20 @@ void MaterializeTemporaryExpr::setExtendingDecl(ValueDecl *ExtendedBy,
ES->ManglingNumber = ManglingNumber;
}
bool MaterializeTemporaryExpr::isUsableInConstantExpressions(
const ASTContext &Context) const {
// C++20 [expr.const]p4:
// An object or reference is usable in constant expressions if it is [...]
// a temporary object of non-volatile const-qualified literal type
// whose lifetime is extended to that of a variable that is usable
// in constant expressions
auto *VD = dyn_cast_or_null<VarDecl>(getExtendingDecl());
return VD && getType().isConstant(Context) &&
!getType().isVolatileQualified() &&
getType()->isLiteralType(Context) &&
VD->isUsableInConstantExpressions(Context);
}
TypeTraitExpr::TypeTraitExpr(QualType T, SourceLocation Loc, TypeTrait Kind,
ArrayRef<TypeSourceInfo *> Args,
SourceLocation RParenLoc, bool Value)

View File

@ -4093,27 +4093,32 @@ static CompleteObject findCompleteObject(EvalInfo &Info, const Expr *E,
assert(MTE->getStorageDuration() == SD_Static &&
"should have a frame for a non-global materialized temporary");
// Per C++1y [expr.const]p2:
// C++20 [expr.const]p4: [DR2126]
// An object or reference is usable in constant expressions if it is
// - a temporary object of non-volatile const-qualified literal type
// whose lifetime is extended to that of a variable that is usable
// in constant expressions
//
// C++20 [expr.const]p5:
// an lvalue-to-rvalue conversion [is not allowed unless it applies to]
// - a [...] glvalue of integral or enumeration type that refers to
// a non-volatile const object [...]
// [...]
// - a [...] glvalue of literal type that refers to a non-volatile
// object whose lifetime began within the evaluation of e.
// - a non-volatile glvalue that refers to an object that is usable
// in constant expressions, or
// - a non-volatile glvalue of literal type that refers to a
// non-volatile object whose lifetime began within the evaluation
// of E;
//
// C++11 misses the 'began within the evaluation of e' check and
// instead allows all temporaries, including things like:
// int &&r = 1;
// int x = ++r;
// constexpr int k = r;
// Therefore we use the C++14 rules in C++11 too.
// Therefore we use the C++14-onwards rules in C++11 too.
//
// Note that temporaries whose lifetimes began while evaluating a
// variable's constructor are not usable while evaluating the
// corresponding destructor, not even if they're of const-qualified
// types.
if (!(BaseType.isConstQualified() &&
BaseType->isIntegralOrEnumerationType()) &&
if (!MTE->isUsableInConstantExpressions(Info.Ctx) &&
!lifetimeStartedInEvaluation(Info, LVal.Base)) {
if (!IsAccess)
return CompleteObject(LVal.getLValueBase(), nullptr, BaseType);

View File

@ -32,6 +32,40 @@ namespace dr2120 { // dr2120: 7
static_assert(!__is_standard_layout(E), "");
}
namespace dr2126 { // dr2126: 12
#if __cplusplus >= 201103L
struct A { int n; };
const A &a = {1}; // const temporary
A &b = (A &)(const A &)A{1}; // const temporary
A &&c = (A &&)(const A &)A{1}; // const temporary
A &&d = {1}; // non-const temporary expected-note {{here}}
const A &e = (A &)(A &&) A{1}; // non-const temporary expected-note {{here}}
A &&f = (A &&)(A &&) A{1}; // non-const temporary expected-note {{here}}
constexpr const A &g = {1}; // const temporary
constexpr A &&h = {1}; // non-const temporary expected-note {{here}}
struct B { const A &a; };
B i = {{1}}; // extending decl not usable in constant expr expected-note {{here}}
const B j = {{1}}; // extending decl not usable in constant expr expected-note {{here}}
constexpr B k = {{1}}; // extending decl usable in constant expr
static_assert(a.n == 1, "");
static_assert(b.n == 1, "");
static_assert(c.n == 1, "");
static_assert(d.n == 1, ""); // expected-error {{constant}} expected-note {{read of temporary}}
static_assert(e.n == 1, ""); // expected-error {{constant}} expected-note {{read of temporary}}
static_assert(f.n == 1, ""); // expected-error {{constant}} expected-note {{read of temporary}}
static_assert(g.n == 1, "");
static_assert(h.n == 1, ""); // expected-error {{constant}} expected-note {{read of temporary}}
static_assert(i.a.n == 1, ""); // expected-error {{constant}} expected-note {{read of non-constexpr variable}}
static_assert(j.a.n == 1, ""); // expected-error {{constant}} expected-note {{read of temporary}}
static_assert(k.a.n == 1, "");
#endif
}
namespace dr2140 { // dr2140: 9
#if __cplusplus >= 201103L
union U { int a; decltype(nullptr) b; };

View File

@ -400,6 +400,22 @@ namespace UnemittedTemporaryDecl {
// CHECK: @_ZN22UnemittedTemporaryDecl4ref2E = constant i32* @_ZGRN22UnemittedTemporaryDecl3refE_
}
namespace DR2126 {
struct A { int &&b; };
constexpr const A &a = {42};
// CHECK: @_ZGRN6DR21261aE0_ = internal global i32 42
// FIXME: This is unused and need not be emitted.
// CHECK: @_ZGRN6DR21261aE_ = internal constant {{.*}} { i32* @_ZGRN6DR21261aE0_ }
// CHECK: @_ZN6DR21261rE = constant i32* @_ZGRN6DR21261aE0_
int &r = a.b;
// Dynamically initialized: the temporary object bound to 'b' could be
// modified (eg, by placement 'new') before the initializer of 's' runs.
constexpr A &&b = {42};
// CHECK: @_ZN6DR21261sE = global i32* null
int &s = b.b;
}
// CHECK: @_ZZN12LocalVarInit3aggEvE1a = internal constant {{.*}} i32 101
// CHECK: @_ZZN12LocalVarInit4ctorEvE1a = internal constant {{.*}} i32 102
// CHECK: @__const._ZN12LocalVarInit8mutable_Ev.a = private unnamed_addr constant {{.*}} i32 103

View File

@ -409,12 +409,23 @@ namespace ConstAddedByReference {
const int &r = (0);
constexpr int n = r;
int &&r2 = 0; // expected-note {{created here}}
constexpr int n2 = r2; // expected-error {{constant}} expected-note {{read of temporary}}
struct A { constexpr operator int() const { return 0; }};
struct B { constexpr operator const int() const { return 0; }};
const int &ra = A();
const int &rb = B();
constexpr int na = ra;
constexpr int nb = rb;
struct C { int &&r; };
constexpr C c1 = {1};
constexpr int &c1r = c1.r;
constexpr const C &c2 = {2};
constexpr int &c2r = c2.r;
constexpr C &&c3 = {3}; // expected-note {{created here}}
constexpr int &c3r = c3.r; // expected-error {{constant}} expected-note {{read of temporary}}
}
}
@ -1843,6 +1854,11 @@ namespace InitializerList {
static_assert(*std::initializer_list<int>{1, 2, 3}.begin() == 1, "");
static_assert(std::initializer_list<int>{1, 2, 3}.begin()[2] == 3, "");
namespace DR2126 {
constexpr std::initializer_list<float> il = {1.0, 2.0, 3.0};
static_assert(il.begin()[1] == 2.0, "");
}
}
namespace StmtExpr {

View File

@ -12571,7 +12571,7 @@ and <I>POD class</I></td>
<td><a href="https://wg21.link/cwg2126">2126</a></td>
<td>DRWP</td>
<td>Lifetime-extended temporaries in constant expressions</td>
<td class="none" align="center">Unknown</td>
<td class="unreleased" align="center">Clang 12</td>
</tr>
<tr class="open" id="2127">
<td><a href="https://wg21.link/cwg2127">2127</a></td>
@ -13843,7 +13843,7 @@ and <I>POD class</I></td>
<td><a href="https://wg21.link/cwg2338">2338</a></td>
<td>DRWP</td>
<td>Undefined behavior converting to short enums with fixed underlying types</td>
<td class="none" align="center">Unknown</td>
<td class="unreleased" align="center">Clang 12</td>
</tr>
<tr id="2339">
<td><a href="https://wg21.link/cwg2339">2339</a></td>