Skip to content

Quiet gcc's ASan -Wmaybe-uninitialized false positives in expected<void, E> - #104

Merged
steve-downey merged 2 commits into
bemanproject:mainfrom
steve-downey:asan-void-union-member
Oct 19, 2026
Merged

steve-downey merged 2 commits into
bemanproject:mainfrom
steve-downey:asan-void-union-member

Conversation

@steve-downey

Copy link
Copy Markdown
Member

gcc's -fsanitize=address build reports -Wmaybe-uninitialized on reads of unex_ in value-state expected<void, E> objects. Every report is a false positive, since has_val_ guards each read exactly. They were 43 of the 44 warnings in the default Asan build with gcc 15.

  • Fix at the source. Give the union an empty struct {} void_; alternative and initialize it in every constructor that can end in the value state, as libstdc++'s expected<void, E> does. The member is unmarked, so specgen leaves it out of the synopsis. Its rationale sits after the class body, because a comment inside the union would be carried into the synopsis.
  • Move the remaining suppression. The gcc-14/15/16 toolchain files each added -Wno-maybe-uninitialized to the Asan flags, but the default toolchain and gcc-12/13 did not. A plain make warned 44 times where make TOOLCHAIN=gcc-16 was clean. The suppression isn't version-gated, so it now lives in gcc-flags.cmake. It's still needed for expected<T&, E>, whose value constructor stores a runtime address, so the same folding can't apply.

gcc-16: 1249/1249 tests pass.

The generated wording is unchanged. I checked that by regenerating it with specgen 0.4.0 on top of this branch. Earlier specgen releases could leak void_; into the expected<void, E> synopsis once this member sat beside the \omitted copy assignment from #100 (steve-downey/specgen#147), so regenerate with 0.4.0 or later.

… under ASan

Under -fsanitize=address, the use-after-scope markers stop gcc forwarding a
just-stored has_val_ to the load that guards the read of unex_, so a
value-state expected<void, E> whose union was never written gets that read
reported as -Wmaybe-uninitialized. Every instance is a false positive; the
guard is exact. It accounted for 43 of the 44 warnings in the default Asan
build with gcc 15.

Add an empty `struct {} void_;` alternative to the union and initialize it in
every constructor that can end in the value state, as libstdc++'s
expected<void, E> does. The front end folds the constructor to an aggregate,
and because an empty member does not fill the union the gimplifier zero-clears
the whole object before storing the fields, so nothing is left unwritten.

The member is unmarked, so specgen drops it from the synopsis; the generated
wording is unchanged. The explanation lives after the class body because a
comment inside the union is carried into the synopsis.
The gcc-14, gcc-15 and gcc-16 toolchain files each appended
-Wno-maybe-uninitialized to the Asan flags; the default toolchain.cmake and
gcc-12/13 did not, so a plain `make` warned 44 times where
`make TOOLCHAIN=gcc-16` was clean.

The suppression is not version-gated, so it belongs in gcc-flags.cmake. It is
still needed after expected<void, E> was fixed at the source: expected<T&, E>'s
value constructor stores a runtime address, so the same folding cannot apply,
and its copy constructor with a non-trivial E still trips the false positive.
@steve-downey
steve-downey merged commit 4dc0a86 into bemanproject:main Oct 19, 2026
78 checks passed
@steve-downey
steve-downey deleted the asan-void-union-member branch October 19, 2026 18:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant