[all-commits] [llvm/llvm-project] ec2bf5: [Clang][Sema] Synthesize a memcpy body for default...

Adam Smith via All-commits all-commits at lists.llvm.org
Fri Jul 31 13:50:03 PDT 2026


  Branch: refs/heads/main
  Home:   https://github.com/llvm/llvm-project
  Commit: ec2bf56676ae4a46f47e3fbc62d5208b729792a1
      https://github.com/llvm/llvm-project/commit/ec2bf56676ae4a46f47e3fbc62d5208b729792a1
  Author: Adam Smith <adams at nvidia.com>
  Date:   2026-07-31 (Fri, 31 Jul 2026)

  Changed paths:
    M clang/docs/ReleaseNotes.md
    M clang/include/clang/Basic/Diagnostic.h
    M clang/lib/CIR/CodeGen/CIRGenClass.cpp
    M clang/lib/Sema/SemaDeclCXX.cpp
    M clang/lib/Sema/SemaExpr.cpp
    A clang/test/AST/ast-dump-union-assign-address-space.clcpp
    A clang/test/AST/ast-dump-union-assign-explicit-object.cpp
    A clang/test/AST/ast-dump-union-copy-move-assign.cpp
    A clang/test/AST/ast-print-union-assign.cpp
    R clang/test/CIR/CodeGen/trivial-union-assign-nyi.cpp
    A clang/test/CIR/CodeGen/union-copy-move-assignment.cpp
    A clang/test/CodeGenCXX/union-copy-move-assignment.cpp
    A clang/test/SemaCXX/union-assign-constexpr.cpp
    A clang/test/SemaCXX/union-assign-memcpy-nontrivial.cpp

  Log Message:
  -----------
  [Clang][Sema] Synthesize a memcpy body for defaulted union assignment (#206579)

A defaulted copy or move assignment operator for a union is synthesized
with an empty body. `DefineImplicitCopyAssignment` /
`DefineImplicitMoveAssignment` skip union members in the memberwise
loop, and the implied copy of the object representation has no AST
representation, a FIXME that has sat at that skip for a long time. The
operator ends up copying nothing.

Classic CodeGen hides this at ordinary call sites by lowering a trivial
assignment to a memcpy at the call site, so `u1 = u2` works even though
the operator body is a no-op. But when the operator is genuinely called,
through a pointer-to-member for instance, it silently copies nothing.
ClangIR calls the assignment operator at the call site rather than
eliding it, so it hits the empty body directly and drops every union
assignment. The no-op is then deleted at `-O3`. That is the MultiSource
`kc` miscompile, where a `YYSTYPE` union assignment (`*++yyvsp =
yylval`) becomes a no-op.

This implements the FIXME by mirroring the defaulted union copy
constructor (`CGClass.cpp`: "union copy constructor, we must emit a
memcpy") in the AST: when the class is a union,
`DefineImplicit{Copy,Move}Assignment` emits a single whole-object copy
through `buildMemcpyForAssignmentOp`, the helper already used for
trivially-copyable array members, instead of the skipped per-member
assignments. The operator stays trivial (triviality is fixed at
declaration time, before the body is synthesized), so constant
evaluation, which copies trivial unions through its own semantic path
and never executes the body, is unchanged.

Copying the object representation is correct even when a union member is
not trivially copyable, but the synthesized memcpy would then trip
`-Wnontrivial-memcall`. A `void*` cast would drop pointee qualifiers
such as the address space and inject an explicit cast node the source
never wrote. Instead the call keeps its typed union-pointer arguments,
and the union branch in `DefineImplicit{Copy,Move}Assignment` wraps the
build in `IgnoreAllWarningDiagRAII`, a save-and-restore around the
engine's existing ignore-all-warnings state. `CheckMemaccessArguments`
defers the warning and its note through `DiagRuntimeBehavior`, and that
queue flushes after the RAII has restored the previous ignore state, so
`DiagIfReachable` samples that state as it enqueues and drops what is
not error-class. Under `-w` the same queue is discarded before it
flushes, so nothing outside the scoped region changes.

Classic CodeGen now emits the copy when the operator is odr-used, and
ClangIR's union assignment lowers to a real memcpy. On the Sema side,
tests cover the synthesized AST for the implicit-`this` and C++23
explicit-object spellings, the `-Wnontrivial-memcall` suppression, the
preserved address space, the clean `-ast-print` output, and constexpr
active-member copy. The classic and ClangIR lowering is checked across
named, anonymous, and tail-padded unions.

#198918 added a defaulted-union `errorNYI` in ClangIR and deferred this
AST fix to a separate change. This PR makes that fix and drops the
guard, so the now-non-empty body lowers to a `cir.call @memcpy` instead
of erroring. Its own `union-copy-move-assignment.cpp` CIR test, which
expects that call, is the regression check.



To unsubscribe from these emails, change your notification settings at https://github.com/llvm/llvm-project/settings/notifications


More information about the All-commits mailing list