diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp --- a/clang/lib/AST/ExprConstant.cpp +++ b/clang/lib/AST/ExprConstant.cpp @@ -5871,7 +5871,7 @@ /// operator whose left-hand side might involve a union member access. If it /// does, implicitly start the lifetime of any accessed union elements per /// C++20 [class.union]5. -static bool HandleUnionActiveMemberChange(EvalInfo &Info, const Expr *LHSExpr, +static bool HandleUnionActiveMemberChange(EvalInfo &Info, const Expr *E, const LValue &LHS) { if (LHS.InvalidBase || LHS.Designator.Invalid) return false; @@ -5879,11 +5879,11 @@ llvm::SmallVector, 4> UnionPathLengths; // C++ [class.union]p5: // define the set S(E) of subexpressions of E as follows: - unsigned PathLength = LHS.Designator.Entries.size(); - for (const Expr *E = LHSExpr; E != nullptr;) { + for (unsigned PathLength = LHS.Designator.Entries.size(); PathLength; + --PathLength) { + SubobjectDesignator::PathEntry PE = LHS.Designator.Entries[PathLength - 1]; // -- If E is of the form A.B, S(E) contains the elements of S(A)... - if (auto *ME = dyn_cast(E)) { - auto *FD = dyn_cast(ME->getMemberDecl()); + if (auto *FD = PE.getAsFieldOrNull()) { // Note that we can't implicitly start the lifetime of a reference, // so we don't need to proceed any further if we reach one. if (!FD || FD->getType()->isReferenceType()) @@ -5900,41 +5900,10 @@ UnionPathLengths.push_back({PathLength - 1, FD}); } - E = ME->getBase(); - --PathLength; - assert(declaresSameEntity( - FD, LHS.Designator.Entries[PathLength].getAsBaseOrMember())); - // -- If E is of the form A[B] and is interpreted as a built-in array // subscripting operator, S(E) is [S(the array operand, if any)]. - } else if (auto *ASE = dyn_cast(E)) { - // Step over an ArrayToPointerDecay implicit cast. - auto *Base = ASE->getBase()->IgnoreImplicit(); - if (!Base->getType()->isArrayType()) - break; - - E = Base; - --PathLength; - - } else if (auto *ICE = dyn_cast(E)) { - // Step over a derived-to-base conversion. - E = ICE->getSubExpr(); - if (ICE->getCastKind() == CK_NoOp) - continue; - if (ICE->getCastKind() != CK_DerivedToBase && - ICE->getCastKind() != CK_UncheckedDerivedToBase) - break; - // Walk path backwards as we walk up from the base to the derived class. - for (const CXXBaseSpecifier *Elt : llvm::reverse(ICE->path())) { - --PathLength; - (void)Elt; - assert( - declaresSameEntity(Elt->getType()->getAsCXXRecordDecl(), - LHS.Designator.Entries[PathLength].getAsBase())); - } - - // -- Otherwise, S(E) is empty. - } else { + // -- Otherwise, S(E) is empty. + } else if (!PE.isArrayIndex() && !PE.isAnyBase()) { break; } } @@ -5946,7 +5915,7 @@ // if modification of X [would access an inactive union member], an object // of the type of X is implicitly created CompleteObject Obj = - findCompleteObject(Info, LHSExpr, AK_Assign, LHS, LHSExpr->getType()); + findCompleteObject(Info, E, AK_Assign, LHS, E->getType()); if (!Obj) return false; for (std::pair LengthAndField : @@ -5958,8 +5927,8 @@ bool DuringInit = Info.isEvaluatingCtorDtor(LHS.Base, D.Entries) == ConstructionPhase::AfterBases; StartLifetimeOfUnionMemberHandler StartLifetime{ - Info, LHSExpr, LengthAndField.second, DuringInit}; - if (!findSubobject(Info, LHSExpr, Obj, D, StartLifetime)) + Info, E, LengthAndField.second, DuringInit}; + if (!findSubobject(Info, E, Obj, D, StartLifetime)) return false; } diff --git a/clang/test/SemaCXX/constant-expression-cxx2a.cpp b/clang/test/SemaCXX/constant-expression-cxx2a.cpp --- a/clang/test/SemaCXX/constant-expression-cxx2a.cpp +++ b/clang/test/SemaCXX/constant-expression-cxx2a.cpp @@ -379,10 +379,10 @@ return *p; // expected-note {{read of uninitialized object}} } static_assert(read_uninitialized() == 0); // expected-error {{constant}} expected-note {{in call}} - constexpr void write_wrong_member_indirect() { // expected-error {{never produces a constant}} + constexpr void write_wrong_member_indirect() { B b = {.b = 1}; int *p = &b.a.y; - *p = 1; // expected-note {{assignment to member 'a' of union with active member 'b'}} + *p = 1; } constexpr int write_uninitialized() { B b = {.b = 1}; @@ -1447,3 +1447,9 @@ constexpr bool b = [a = S(), b = S()] { return a.p == b.p; }(); static_assert(!b); } + +namespace PR45879 { + struct Base { int m; }; + struct Derived : Base {}; + constexpr int k = ((Base{} = Derived{}), 0); +}