Skip to content

Commit ad18665

Browse files
committed
PR45087: Fix check for emptiness when determining whether a trivial copy
operation needs to read from its operand.
1 parent de2c586 commit ad18665

File tree

2 files changed

+18
-21
lines changed

2 files changed

+18
-21
lines changed

clang/lib/AST/ExprConstant.cpp

+14-21
Original file line numberDiff line numberDiff line change
@@ -3047,15 +3047,22 @@ static void expandArray(APValue &Array, unsigned Index) {
30473047
/// is trivial. Note that this is never true for a union type with fields
30483048
/// (because the copy always "reads" the active member) and always true for
30493049
/// a non-class type.
3050+
static bool isReadByLvalueToRvalueConversion(const CXXRecordDecl *RD);
30503051
static bool isReadByLvalueToRvalueConversion(QualType T) {
30513052
CXXRecordDecl *RD = T->getBaseElementTypeUnsafe()->getAsCXXRecordDecl();
3052-
if (!RD || (RD->isUnion() && !RD->field_empty()))
3053-
return true;
3053+
return !RD || isReadByLvalueToRvalueConversion(RD);
3054+
}
3055+
static bool isReadByLvalueToRvalueConversion(const CXXRecordDecl *RD) {
3056+
// FIXME: A trivial copy of a union copies the object representation, even if
3057+
// the union is empty.
3058+
if (RD->isUnion())
3059+
return !RD->field_empty();
30543060
if (RD->isEmpty())
30553061
return false;
30563062

30573063
for (auto *Field : RD->fields())
3058-
if (isReadByLvalueToRvalueConversion(Field->getType()))
3064+
if (!Field->isUnnamedBitfield() &&
3065+
isReadByLvalueToRvalueConversion(Field->getType()))
30593066
return true;
30603067

30613068
for (auto &BaseSpec : RD->bases())
@@ -5460,22 +5467,6 @@ static bool HandleUnionActiveMemberChange(EvalInfo &Info, const Expr *LHSExpr,
54605467
return true;
54615468
}
54625469

5463-
/// Determine if a class has any fields that might need to be copied by a
5464-
/// trivial copy or move operation.
5465-
static bool hasFields(const CXXRecordDecl *RD) {
5466-
if (!RD || RD->isEmpty())
5467-
return false;
5468-
for (auto *FD : RD->fields()) {
5469-
if (FD->isUnnamedBitfield())
5470-
continue;
5471-
return true;
5472-
}
5473-
for (auto &Base : RD->bases())
5474-
if (hasFields(Base.getType()->getAsCXXRecordDecl()))
5475-
return true;
5476-
return false;
5477-
}
5478-
54795470
namespace {
54805471
typedef SmallVector<APValue, 8> ArgVector;
54815472
}
@@ -5546,7 +5537,8 @@ static bool HandleFunctionCall(SourceLocation CallLoc,
55465537
const CXXMethodDecl *MD = dyn_cast<CXXMethodDecl>(Callee);
55475538
if (MD && MD->isDefaulted() &&
55485539
(MD->getParent()->isUnion() ||
5549-
(MD->isTrivial() && hasFields(MD->getParent())))) {
5540+
(MD->isTrivial() &&
5541+
isReadByLvalueToRvalueConversion(MD->getParent())))) {
55505542
assert(This &&
55515543
(MD->isCopyAssignmentOperator() || MD->isMoveAssignmentOperator()));
55525544
LValue RHS;
@@ -5633,7 +5625,8 @@ static bool HandleConstructorCall(const Expr *E, const LValue &This,
56335625
// actually read them.
56345626
if (Definition->isDefaulted() && Definition->isCopyOrMoveConstructor() &&
56355627
(Definition->getParent()->isUnion() ||
5636-
(Definition->isTrivial() && hasFields(Definition->getParent())))) {
5628+
(Definition->isTrivial() &&
5629+
isReadByLvalueToRvalueConversion(Definition->getParent())))) {
56375630
LValue RHS;
56385631
RHS.setFrom(Info.Ctx, ArgValues[0]);
56395632
return handleLValueToRValueConversion(

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

+4
Original file line numberDiff line numberDiff line change
@@ -600,6 +600,10 @@ namespace CopyCtor {
600600
constexpr B c = b;
601601
static_assert(c.arr[2] == 3, "");
602602
static_assert(c.arr[7] == 0, "");
603+
604+
// OK: the copy ctor for X doesn't read any members.
605+
struct X { struct Y {} y; } x1;
606+
constexpr X x2 = x1;
603607
}
604608

605609
constexpr int selfref[2][2][2] = {

0 commit comments

Comments
 (0)