summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRichard Smith <richard@metafoo.co.uk>2022-02-02 17:00:35 -0800
committerTom Stellard <tstellar@redhat.com>2022-06-09 13:55:17 -0700
commit5b296385298f9ffb708d454ce107bc9a0cdea5de (patch)
treeee2f03949b26c542eced5ef2e2cb2dce5eb0b9a0
parentdeb573739df926c9b7321d0b05994370074aa53c (diff)
downloadllvm-5b296385298f9ffb708d454ce107bc9a0cdea5de.tar.gz
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)
-rw-r--r--clang/lib/AST/ExprConstant.cpp12
-rw-r--r--clang/test/SemaCXX/constant-expression-cxx2a.cpp26
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}}
+}