From ad18665e377824fd545ca81117b4953e60e2823c Mon Sep 17 00:00:00 2001 From: Richard Smith Date: Tue, 3 Mar 2020 15:56:08 -0800 Subject: [PATCH] PR45087: Fix check for emptiness when determining whether a trivial copy operation needs to read from its operand. --- clang/lib/AST/ExprConstant.cpp | 35 ++++++++----------- .../SemaCXX/constant-expression-cxx11.cpp | 4 +++ 2 files changed, 18 insertions(+), 21 deletions(-) diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp index 9a31b64eedda..1028ab96bfd2 100644 --- a/clang/lib/AST/ExprConstant.cpp +++ b/clang/lib/AST/ExprConstant.cpp @@ -3047,15 +3047,22 @@ static void expandArray(APValue &Array, unsigned Index) { /// is trivial. Note that this is never true for a union type with fields /// (because the copy always "reads" the active member) and always true for /// a non-class type. +static bool isReadByLvalueToRvalueConversion(const CXXRecordDecl *RD); static bool isReadByLvalueToRvalueConversion(QualType T) { CXXRecordDecl *RD = T->getBaseElementTypeUnsafe()->getAsCXXRecordDecl(); - if (!RD || (RD->isUnion() && !RD->field_empty())) - return true; + return !RD || isReadByLvalueToRvalueConversion(RD); +} +static bool isReadByLvalueToRvalueConversion(const CXXRecordDecl *RD) { + // FIXME: A trivial copy of a union copies the object representation, even if + // the union is empty. + if (RD->isUnion()) + return !RD->field_empty(); if (RD->isEmpty()) return false; for (auto *Field : RD->fields()) - if (isReadByLvalueToRvalueConversion(Field->getType())) + if (!Field->isUnnamedBitfield() && + isReadByLvalueToRvalueConversion(Field->getType())) return true; for (auto &BaseSpec : RD->bases()) @@ -5460,22 +5467,6 @@ static bool HandleUnionActiveMemberChange(EvalInfo &Info, const Expr *LHSExpr, return true; } -/// 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 ArgVector; } @@ -5546,7 +5537,8 @@ static bool HandleFunctionCall(SourceLocation CallLoc, const CXXMethodDecl *MD = dyn_cast(Callee); if (MD && MD->isDefaulted() && (MD->getParent()->isUnion() || - (MD->isTrivial() && hasFields(MD->getParent())))) { + (MD->isTrivial() && + isReadByLvalueToRvalueConversion(MD->getParent())))) { assert(This && (MD->isCopyAssignmentOperator() || MD->isMoveAssignmentOperator())); LValue RHS; @@ -5633,7 +5625,8 @@ static bool HandleConstructorCall(const Expr *E, const LValue &This, // actually read them. if (Definition->isDefaulted() && Definition->isCopyOrMoveConstructor() && (Definition->getParent()->isUnion() || - (Definition->isTrivial() && hasFields(Definition->getParent())))) { + (Definition->isTrivial() && + isReadByLvalueToRvalueConversion(Definition->getParent())))) { LValue RHS; RHS.setFrom(Info.Ctx, ArgValues[0]); return handleLValueToRValueConversion( diff --git a/clang/test/SemaCXX/constant-expression-cxx11.cpp b/clang/test/SemaCXX/constant-expression-cxx11.cpp index 44b636a5b808..4c0cf109bee0 100644 --- a/clang/test/SemaCXX/constant-expression-cxx11.cpp +++ b/clang/test/SemaCXX/constant-expression-cxx11.cpp @@ -600,6 +600,10 @@ namespace CopyCtor { constexpr B c = b; static_assert(c.arr[2] == 3, ""); static_assert(c.arr[7] == 0, ""); + + // OK: the copy ctor for X doesn't read any members. + struct X { struct Y {} y; } x1; + constexpr X x2 = x1; } constexpr int selfref[2][2][2] = {