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 @@ -1546,6 +1546,7 @@ APValue::LValueBase Base; CharUnits Offset; SubobjectDesignator Designator; + const Expr *LExpr = nullptr; bool IsNullPtr : 1; bool InvalidBase : 1; @@ -1554,6 +1555,7 @@ const CharUnits &getLValueOffset() const { return Offset; } SubobjectDesignator &getLValueDesignator() { return Designator; } const SubobjectDesignator &getLValueDesignator() const { return Designator;} + const Expr *getExpr() const { return LExpr; } bool isNullPointer() const { return IsNullPtr;} unsigned getLValueCallIndex() const { return Base.getCallIndex(); } @@ -1591,6 +1593,8 @@ Offset = CharUnits::fromQuantity(0); InvalidBase = BInvalid; Designator = SubobjectDesignator(getType(B)); + if (const Expr *E = B.dyn_cast()) + LExpr = E; IsNullPtr = false; } @@ -5893,11 +5897,17 @@ /// 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. +/// Returns true if the assignment is OK and it may be processed further. static bool HandleUnionActiveMemberChange(EvalInfo &Info, const Expr *LHSExpr, const LValue &LHS) { if (LHS.InvalidBase || LHS.Designator.Invalid) return false; + // If no LHS expression was specified, assume the LHS cannot be an assignment + // to a union member. + if (!LHSExpr) + return true; + llvm::SmallVector, 4> UnionPathLengths; // C++ [class.union]p5: // define the set S(E) of subexpressions of E as follows: @@ -6101,7 +6111,7 @@ MD->getParent()->isUnion())) return false; if (Info.getLangOpts().CPlusPlus20 && MD->isTrivial() && - !HandleUnionActiveMemberChange(Info, Args[0], *This)) + !HandleUnionActiveMemberChange(Info, This->getExpr(), *This)) return false; if (!handleAssignment(Info, Args[0], *This, MD->getThisType(), RHSValue)) @@ -8058,10 +8068,20 @@ namespace { class LValueExprEvaluator : public LValueExprEvaluatorBase { + friend class LValueExprEvaluatorBase; + friend class ExprEvaluatorBase; + friend class ConstStmtVisitor; + friend class StmtVisitorBase; public: LValueExprEvaluator(EvalInfo &Info, LValue &Result, bool InvalidBaseOK) : LValueExprEvaluatorBaseTy(Info, Result, InvalidBaseOK) {} + bool Evaluate(const Expr *E) { + Result.LExpr = E; + return Visit(E); + } + +protected: bool VisitVarDecl(const Expr *E, const VarDecl *VD); bool VisitUnaryPreIncDec(const UnaryOperator *UO); @@ -8123,7 +8143,7 @@ assert(!E->isValueDependent()); assert(E->isGLValue() || E->getType()->isFunctionType() || E->getType()->isVoidType() || isa(E)); - return LValueExprEvaluator(Info, Result, InvalidBaseOK).Visit(E); + return LValueExprEvaluator(Info, Result, InvalidBaseOK).Evaluate(E); } bool LValueExprEvaluator::VisitDeclRefExpr(const DeclRefExpr *E) { @@ -8424,7 +8444,7 @@ // C++17 onwards require that we evaluate the RHS first. APValue RHS; - if (!Evaluate(RHS, this->Info, CAO->getRHS())) { + if (!::Evaluate(RHS, this->Info, CAO->getRHS())) { if (!Info.noteFailure()) return false; Success = false; @@ -8448,7 +8468,7 @@ // C++17 onwards require that we evaluate the RHS first. APValue NewVal; - if (!Evaluate(NewVal, this->Info, E->getRHS())) { + if (!::Evaluate(NewVal, this->Info, E->getRHS())) { if (!Info.noteFailure()) return false; Success = 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 @@ -1447,3 +1447,22 @@ 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); + +struct sub { + char data; +}; +struct main2 { + constexpr main2() { + member = {}; + } + sub member; +}; +constexpr main2 a{}; +} // namespace PR45879