mirror-llvm-project/clang
Adam Smith ec2bf56676
[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.
2026-07-31 15:49:40 -05:00
..
bindings [libclang/python] Remove CompletionString.AvailabilityKindCompat (#210682) 2026-07-25 12:38:13 +09:00
cmake [CMake][Fuchsia] Compile runtimes with -ffuchsia-api-level (#211753) 2026-07-29 10:59:32 -07:00
docs [Clang][Sema] Synthesize a memcpy body for defaulted union assignment (#206579) 2026-07-31 15:49:40 -05:00
examples [PassManager] Make passes inherit from RequiredPassInfoMixin/OptionalPassInfoMixin (#192120) 2026-05-06 10:00:07 -07:00
include [Clang][Sema] Synthesize a memcpy body for defaulted union assignment (#206579) 2026-07-31 15:49:40 -05:00
lib [Clang][Sema] Synthesize a memcpy body for defaulted union assignment (#206579) 2026-07-31 15:49:40 -05:00
runtime
test [Clang][Sema] Synthesize a memcpy body for defaulted union assignment (#206579) 2026-07-31 15:49:40 -05:00
tools [clang][DependencyScanning] Implement the Streaming Style By-name Scanning API (#211406) 2026-07-31 08:54:13 -07:00
unittests [clang][Analysis] Add argument accessors to clang::AnyCall (#212934) 2026-07-31 13:06:52 +01:00
utils [CIR] Implement fenv lowering to constrained intrinsic calls (#213105) 2026-07-31 11:21:35 -07:00
www [Clang] Implement P3658R1 (#212131) 2026-07-28 07:46:23 +02:00
.clang-format
.clang-tidy
.gitignore
AreaTeamMembers.txt
CMakeLists.txt Add build option CLANG_INCLUDE_EXAMPLES (#187317) 2026-07-11 04:27:34 +02:00
INSTALL.txt
LICENSE.TXT
Maintainers.md [clang][doc] Use my private email for now (#213058) 2026-07-31 06:24:07 +00:00
NOTES.txt
README.md

C language Family Front-end

Welcome to Clang.

This is a compiler front-end for the C family of languages (C, C++ and Objective-C) which is built as part of the LLVM compiler infrastructure project.

Unlike many other compiler frontends, Clang is useful for a number of things beyond just compiling code: we intend for Clang to be host to a number of different source-level tools. One example of this is the Clang Static Analyzer.

If you're interested in more (including how to build Clang) it is best to read the relevant websites. Here are some pointers: