forked from OSchip/llvm-project
Fix bug where a trivial constexpr copy/move operation couldn't copy from an
empty non-constexpr object. Such a copy doesn't break any of the constexpr rules. llvm-svn: 222387
This commit is contained in:
parent
06839a536f
commit
be6dd818fb
|
@ -3657,6 +3657,22 @@ static bool CheckConstexprFunction(EvalInfo &Info, SourceLocation CallLoc,
|
|||
return false;
|
||||
}
|
||||
|
||||
/// Determine if a class has any fields that might need to be copied by a
|
||||
/// trivial copy or move operation.
|
||||
static bool hasFields(const CXXRecordDecl *RD) {
|
||||
if (!RD || RD->isEmpty())
|
||||
return false;
|
||||
for (auto *FD : RD->fields()) {
|
||||
if (FD->isUnnamedBitfield())
|
||||
continue;
|
||||
return true;
|
||||
}
|
||||
for (auto &Base : RD->bases())
|
||||
if (hasFields(Base.getType()->getAsCXXRecordDecl()))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
namespace {
|
||||
typedef SmallVector<APValue, 8> ArgVector;
|
||||
}
|
||||
|
@ -3695,8 +3711,12 @@ static bool HandleFunctionCall(SourceLocation CallLoc,
|
|||
// For a trivial copy or move assignment, perform an APValue copy. This is
|
||||
// essential for unions, where the operations performed by the assignment
|
||||
// operator cannot be represented as statements.
|
||||
//
|
||||
// Skip this for non-union classes with no fields; in that case, the defaulted
|
||||
// copy/move does not actually read the object.
|
||||
const CXXMethodDecl *MD = dyn_cast<CXXMethodDecl>(Callee);
|
||||
if (MD && MD->isDefaulted() && MD->isTrivial()) {
|
||||
if (MD && MD->isDefaulted() && MD->isTrivial() &&
|
||||
(MD->getParent()->isUnion() || hasFields(MD->getParent()))) {
|
||||
assert(This &&
|
||||
(MD->isCopyAssignmentOperator() || MD->isMoveAssignmentOperator()));
|
||||
LValue RHS;
|
||||
|
@ -3753,11 +3773,18 @@ static bool HandleConstructorCall(SourceLocation CallLoc, const LValue &This,
|
|||
}
|
||||
|
||||
// For a trivial copy or move constructor, perform an APValue copy. This is
|
||||
// essential for unions, where the operations performed by the constructor
|
||||
// cannot be represented by ctor-initializers.
|
||||
// essential for unions (or classes with anonymous union members), where the
|
||||
// operations performed by the constructor cannot be represented by
|
||||
// ctor-initializers.
|
||||
//
|
||||
// Skip this for empty non-union classes; we should not perform an
|
||||
// lvalue-to-rvalue conversion on them because their copy constructor does not
|
||||
// actually read them.
|
||||
if (Definition->isDefaulted() &&
|
||||
((Definition->isCopyConstructor() && Definition->isTrivial()) ||
|
||||
(Definition->isMoveConstructor() && Definition->isTrivial()))) {
|
||||
(Definition->isMoveConstructor() && Definition->isTrivial())) &&
|
||||
(Definition->getParent()->isUnion() ||
|
||||
hasFields(Definition->getParent()))) {
|
||||
LValue RHS;
|
||||
RHS.setFrom(Info.Ctx, ArgValues[0]);
|
||||
return handleLValueToRValueConversion(Info, Args[0], Args[0]->getType(),
|
||||
|
|
|
@ -1942,3 +1942,16 @@ namespace PR19010 {
|
|||
void PR21327(int a, int b) {
|
||||
static_assert(&a + 1 != &b, ""); // expected-error {{constant expression}}
|
||||
}
|
||||
|
||||
namespace EmptyClass {
|
||||
struct E1 {} e1;
|
||||
union E2 {} e2; // expected-note {{here}}
|
||||
struct E3 : E1 {} e3;
|
||||
|
||||
// The defaulted copy constructor for an empty class does not read any
|
||||
// members. The defaulted copy constructor for an empty union reads the
|
||||
// object representation.
|
||||
constexpr E1 e1b(e1);
|
||||
constexpr E2 e2b(e2); // expected-error {{constant expression}} expected-note{{read of non-const}} expected-note {{in call}}
|
||||
constexpr E3 e3b(e3);
|
||||
}
|
||||
|
|
|
@ -910,3 +910,31 @@ namespace PR17331 {
|
|||
constexpr int ARR[] = { 1, 2, 3, 4, 5 };
|
||||
static_assert(sum(ARR) == 15, "");
|
||||
}
|
||||
|
||||
namespace EmptyClass {
|
||||
struct E1 {} e1;
|
||||
union E2 {} e2; // expected-note 4{{here}}
|
||||
struct E3 : E1 {} e3;
|
||||
|
||||
template<typename E>
|
||||
constexpr int f(E &a, int kind) {
|
||||
switch (kind) {
|
||||
case 0: { E e(a); return 0; } // expected-note {{read}} expected-note {{in call}}
|
||||
case 1: { E e(static_cast<E&&>(a)); return 0; } // expected-note {{read}} expected-note {{in call}}
|
||||
case 2: { E e; e = a; return 0; } // expected-note {{read}} expected-note {{in call}}
|
||||
case 3: { E e; e = static_cast<E&&>(a); return 0; } // expected-note {{read}} expected-note {{in call}}
|
||||
}
|
||||
}
|
||||
constexpr int test1 = f(e1, 0);
|
||||
constexpr int test2 = f(e2, 0); // expected-error {{constant expression}} expected-note {{in call}}
|
||||
constexpr int test3 = f(e3, 0);
|
||||
constexpr int test4 = f(e1, 1);
|
||||
constexpr int test5 = f(e2, 1); // expected-error {{constant expression}} expected-note {{in call}}
|
||||
constexpr int test6 = f(e3, 1);
|
||||
constexpr int test7 = f(e1, 2);
|
||||
constexpr int test8 = f(e2, 2); // expected-error {{constant expression}} expected-note {{in call}}
|
||||
constexpr int test9 = f(e3, 2);
|
||||
constexpr int testa = f(e1, 3);
|
||||
constexpr int testb = f(e2, 3); // expected-error {{constant expression}} expected-note {{in call}}
|
||||
constexpr int testc = f(e3, 3);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue