[clang] Top-level __asm__ in the global module fragment is silently dropped — unless -Wunsafe-buffer-usage is enabled
- Dominant language
- LLVM
- Stars
- 40.5k
- Forks
- 18.7k
- PR merge metrics
- PR metrics pending
Description
while testing a Clang build of [mbun](https://github.com/Sunrisepeak/mbun), I found a behavior difference between Clang and GCC: Clang drops symbols in the
global module fragment (GMF), while GCC does not.
Note: This text was AI-assisted.
Reproduction:
## Summary
In a C++20 module interface unit, a top-level `__asm__` declaration in the
global module fragment (GMF) is silently discarded by clang: no warning, no
error, and the symbols defined by the asm are missing from the object file.
The failure surfaces only at link time as `undefined reference`, which is very
hard to diagnose. Two things make this reportable:
1. **No diagnostic.** GCC accepts the same code as an extension and warns
(`-Wglobal-module`: "global module fragment contents must be from
preprocessor inclusion"). Clang does neither: it just drops the asm.
2. **Codegen depends on an unrelated warning flag.** Enabling
`-Wunsafe-buffer-usage` makes clang emit the exact same asm. Same source,
same compiler version — whether the symbols exist in the `.o` is controlled
by a warning flag that has nothing to do with code generation.
## Reproducer
`gmf_asm.cppm`:
```cpp
module;
__asm__(
".text\n"
".globl repro_symbol\n"
".type repro_symbol, @function\n"
"repro_symbol:\n"
" ret\n");
export module gmf_asm;
export int f() { return 42; }
```
`main.cc`:
```cpp
extern "C" void repro_symbol();
int main() { repro_symbol(); }
```
Commands (clang 20.1.8):
```console
$ clang++ -std=c++26 -c gmf_asm.cppm -o gmf_asm.o
# compiles cleanly, no diagnostics
$ nm gmf_asm.o | grep repro_symbol
# (nothing — the symbol is gone)
$ clang++ -std=c++26 gmf_asm.o main.cc
/usr/bin/ld: main.cc:(.text+0x10): undefined reference to `repro_symbol'
clang: error: linker command failed with exit code 1 (use -v to see invocation)
# Same source with -Wunsafe-buffer-usage: the asm IS emitted
$ clang++ -std=c++26 -Wunsafe-buffer-usage -c gmf_asm.cppm -o gmf_asm.o
$ nm gmf_asm.o | grep repro_symbol
0000000000000000 T repro_symbol
```
The same drop happens when the asm reaches the GMF via `#include` (the
common real-world pattern, e.g. FFI trampolines included from a header in
the GMF).
## GCC comparison
```console
$ g++ -std=c++26 -fmodules -c gmf_asm.cppm
warning: global module fragment contents must be from preprocessor inclusion [-Wglobal-module]
$ nm gmf_asm.o | grep repro_symbol
0000000000000000 T repro_symbol
```
GCC diagnoses the non-conforming GMF content but emits the asm anyway, so the
program links. Clang neither diagnoses nor emits.
## Standard position
[module.global.frag]/3 restricts the GMF to preprocessing directives only, so
the source is ill-formed — clang is not required to emit the symbol. But a
"shall" violation is generally expected to produce a diagnostic, and silently
dropping a symbol that the rest of the TU (and other TUs) reference leads to a
mystifying link failure many steps away from the cause. The observed
flag-dependent codegen (`-Wunsafe-buffer-usage` flips the behavior) also
suggests this is an accidental gap in the default GMF handling path rather
than a deliberate policy.
## Expected behavior
Either, in the default configuration:
- emit the asm (matching GCC's lenient behavior), or
- at minimum, diagnose the non-preprocessing GMF content (analogous to GCC's
`-Wglobal-module`), so users never hit a silent undefined reference.
## Environment
- Ubuntu clang 20.1.8 (`++20250804090239+87f0227cb601-1~exp1`)
- Linux x86_64, `-std=c++26` (also reproduces with `-std=c++20`)
Contributor guide
Research direction
Start by compiling the provided gmf_asm.cppm reproducer with and without -Wunsafe-buffer-usage, then inspect Clang's handling of top-level asm in the global module fragment. Compare the object-file output and diagnostics with the commands shown. Done means the invalid GMF content no longer disappears silently: Clang should either emit a diagnostic or consistently emit the asm.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- cpp
- Domain
- compilers
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 45/100