Skip to content

Commit 30baa5d

Browse files
committed
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=`.
1 parent 4a6c9b5 commit 30baa5d

File tree

2 files changed

+35
-3
lines changed

2 files changed

+35
-3
lines changed

clang/lib/AST/ExprConstant.cpp

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6124,9 +6124,6 @@ static bool HandleFunctionCall(SourceLocation CallLoc,
61246124
if (!handleTrivialCopy(Info, MD->getParamDecl(0), Args[0], RHSValue,
61256125
MD->getParent()->isUnion()))
61266126
return false;
6127-
if (Info.getLangOpts().CPlusPlus20 && MD->isTrivial() &&
6128-
!HandleUnionActiveMemberChange(Info, Args[0], *This))
6129-
return false;
61306127
if (!handleAssignment(Info, Args[0], *This, MD->getThisType(),
61316128
RHSValue))
61326129
return false;
@@ -7638,6 +7635,15 @@ class ExprEvaluatorBase
76387635
if (!EvaluateObjectArgument(Info, Args[0], ThisVal))
76397636
return false;
76407637
This = &ThisVal;
7638+
7639+
// If this is syntactically a simple assignment using a trivial
7640+
// assignment operator, start the lifetimes of union members as needed,
7641+
// per C++20 [class.union]5.
7642+
if (Info.getLangOpts().CPlusPlus20 && OCE &&
7643+
OCE->getOperator() == OO_Equal && MD->isTrivial() &&
7644+
!HandleUnionActiveMemberChange(Info, Args[0], ThisVal))
7645+
return false;
7646+
76417647
Args = Args.slice(1);
76427648
} else if (MD && MD->isLambdaStaticInvoker()) {
76437649
// Map the static invoker for the lambda back to the call operator.

clang/test/SemaCXX/constant-expression-cxx2a.cpp

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1447,3 +1447,29 @@ namespace PR48582 {
14471447
constexpr bool b = [a = S(), b = S()] { return a.p == b.p; }();
14481448
static_assert(!b);
14491449
}
1450+
1451+
namespace PR45879 {
1452+
struct A { int n; };
1453+
struct B { A a; };
1454+
constexpr A a = (A() = B().a);
1455+
1456+
union C {
1457+
int n;
1458+
A a;
1459+
};
1460+
1461+
constexpr bool f() {
1462+
C c = {.n = 1};
1463+
c.a = B{2}.a;
1464+
return c.a.n == 2;
1465+
}
1466+
static_assert(f());
1467+
1468+
// Only syntactic assignments change the active union member.
1469+
constexpr bool g() { // expected-error {{never produces a constant expression}}
1470+
C c = {.n = 1};
1471+
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}}
1472+
return c.a.n == 2;
1473+
}
1474+
static_assert(g()); // expected-error {{constant expression}} expected-note {{in call}}
1475+
}

0 commit comments

Comments
 (0)