Skip to content

Commit

Permalink
PR45879: Fix assert when constant evaluating union assignment.
Browse files Browse the repository at this point in the history
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=`.
  • Loading branch information
zygoloid committed Feb 3, 2022
1 parent 4a6c9b5 commit 30baa5d
Show file tree
Hide file tree
Showing 2 changed files with 35 additions and 3 deletions.
12 changes: 9 additions & 3 deletions clang/lib/AST/ExprConstant.cpp
Expand Up @@ -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;
Expand Down Expand Up @@ -7638,6 +7635,15 @@ class ExprEvaluatorBase
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.
Expand Down
26 changes: 26 additions & 0 deletions clang/test/SemaCXX/constant-expression-cxx2a.cpp
Expand Up @@ -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}}
}

0 comments on commit 30baa5d

Please sign in to comment.