forked from OSchip/llvm-project
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:
parent
6a72635881
commit
cb9b9842d3
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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; };
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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>
|
||||
|
|
Loading…
Reference in New Issue