From 5b296385298f9ffb708d454ce107bc9a0cdea5de Mon Sep 17 00:00:00 2001 From: Richard Smith Date: Wed, 2 Feb 2022 17:00:35 -0800 Subject: PR45879: Fix assert when constant evaluating union assignment. Consider the form of the first operand of a class assignment not the second operand when implicitly starting the lifetimes of union members. Also add a missing check that the assignment call actually came from a syntactic assignment, not from a direct call to `operator=`. (cherry picked from commit 30baa5d2a450d5e302d8cba3fc7a26a59d4b7ae1) --- clang/lib/AST/ExprConstant.cpp | 12 ++++++++--- clang/test/SemaCXX/constant-expression-cxx2a.cpp | 26 ++++++++++++++++++++++++ 2 files changed, 35 insertions(+), 3 deletions(-) diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp index 9e4088f94015..163109bf7c9f 100644 --- a/clang/lib/AST/ExprConstant.cpp +++ b/clang/lib/AST/ExprConstant.cpp @@ -6124,9 +6124,6 @@ static bool HandleFunctionCall(SourceLocation CallLoc, if (!handleTrivialCopy(Info, MD->getParamDecl(0), Args[0], RHSValue, MD->getParent()->isUnion())) return false; - if (Info.getLangOpts().CPlusPlus20 && MD->isTrivial() && - !HandleUnionActiveMemberChange(Info, Args[0], *This)) - return false; if (!handleAssignment(Info, Args[0], *This, MD->getThisType(), RHSValue)) return false; @@ -7638,6 +7635,15 @@ public: if (!EvaluateObjectArgument(Info, Args[0], ThisVal)) return false; This = &ThisVal; + + // If this is syntactically a simple assignment using a trivial + // assignment operator, start the lifetimes of union members as needed, + // per C++20 [class.union]5. + if (Info.getLangOpts().CPlusPlus20 && OCE && + OCE->getOperator() == OO_Equal && MD->isTrivial() && + !HandleUnionActiveMemberChange(Info, Args[0], ThisVal)) + return false; + Args = Args.slice(1); } else if (MD && MD->isLambdaStaticInvoker()) { // Map the static invoker for the lambda back to the call operator. diff --git a/clang/test/SemaCXX/constant-expression-cxx2a.cpp b/clang/test/SemaCXX/constant-expression-cxx2a.cpp index 88c20c8d1bc6..4aaf6328aaf6 100644 --- a/clang/test/SemaCXX/constant-expression-cxx2a.cpp +++ b/clang/test/SemaCXX/constant-expression-cxx2a.cpp @@ -1447,3 +1447,29 @@ namespace PR48582 { constexpr bool b = [a = S(), b = S()] { return a.p == b.p; }(); static_assert(!b); } + +namespace PR45879 { + struct A { int n; }; + struct B { A a; }; + constexpr A a = (A() = B().a); + + union C { + int n; + A a; + }; + + constexpr bool f() { + C c = {.n = 1}; + c.a = B{2}.a; + return c.a.n == 2; + } + static_assert(f()); + + // Only syntactic assignments change the active union member. + constexpr bool g() { // expected-error {{never produces a constant expression}} + C c = {.n = 1}; + c.a.operator=(B{2}.a); // expected-note 2{{member call on member 'a' of union with active member 'n' is not allowed in a constant expression}} + return c.a.n == 2; + } + static_assert(g()); // expected-error {{constant expression}} expected-note {{in call}} +} -- cgit v1.2.1