Generate [expected] wording from the annotated headers via specgen - #94
Draft
steve-downey wants to merge 8 commits into
Draft
steve-downey wants to merge 8 commits into
steve-downey wants to merge 8 commits into
Conversation
catch_discover_tests defaults to DISCOVERY_MODE POST_BUILD, which runs the freshly-linked (ASan-instrumented) test binaries during the ninja build itself to enumerate their test cases. Under the CodeQL Advanced workflow, CodeQL's build tracer injects its own LD_PRELOAD ahead of the ASan runtime, and ASan aborts immediately with "ASan runtime does not come first in initial library list", failing the build before analysis can run. Switching to DISCOVERY_MODE PRE_TEST moves the enumeration step to ctest invocation time instead of build time, so no instrumented binary runs while CodeQL is tracing the build. Verified locally: gcc-debug preset builds cleanly and `ctest` still discovers and passes all 1178 tests.
fix: defer Catch2 test discovery to ctest time to unblock CodeQL build
Add //! docblocks (Effects, Constraints, Mandates, Returns, Throws, Remarks) to every declaration in unexpected.hpp, bad_expected_access.hpp, and expected.hpp, sourced from the real standard text for the already-standardized members and from papers/expected-new.tex for the expected-over-references additions (unexpected<E&>, expected<T&,E>). specgen generate --validate passes cleanly on all three headers, so the headers are now a source of truth specgen can turn into wording directly.
Add papers/wording/generate.sh (wired up as `make wording`), which runs
specgen against the three headers and assembles the result two ways:
- papers/wording/fragments/*.tex: one file per top-level clause, for
\input into a standalone paper (specgen numbers a fragment's \rSec
markers one level deeper than written, so a paper's own
\rSec1[expected]{Expected objects} supplies the level these assume).
- papers/wording/expected.tex: the same content concatenated in real
standard clause order, at the draft's own absolute numbering, with no
\rSec2[expected] wrapper and no \input directives -- the basis for a
patch to source/utilities.tex in the actual C++ working draft
(github.com/cplusplus/draft), where [expected.general] and
[expected.syn] are untouched and only the subclauses from
[expected.unexpected] on are replaced/extended.
[expected.general] and [expected.syn] are prose, not generated from any
one declaration; they stay hand-authored in papers/expected-new.tex.
| \returns | ||
| \tcode{!x.has_value() && static_cast<bool>(x.error() == e.error())}. | ||
| \end{itemdescr} | ||
|
|
There was a problem hiding this comment.
[pre-commit] reported by reviewdog 🐶
Suggested change
Contributor
|
You are seeing this message because GitHub Code Scanning has recently been set up for this repository, or this pull request contains the workflow file for the Code Scanning tool. What Enabling Code Scanning Means:
For more information about GitHub Code Scanning, check out the documentation. |
…s document A newer specgen enforces that a name used in one generate invocation's wording must be documented in that same run, which broke generating expected.hpp on its own: it uses unexpected, unexpect, unexpect_t, reference_constructs_from_temporary_v, and bad_expected_access, all declared in the other two headers. Wrap expected.hpp's existing #includes of unexpected.hpp and bad_expected_access.hpp in a gathered \rSec2[expected.syn] region so specgen treats all three headers as one document, and add a throwaway \rSec2[expected.detail] marker so the exposition-only helper templates above [expected.expected] don't bleed into [expected.bad]'s fragment once nothing else is there to close the section. generate.sh now runs specgen once on expected.hpp instead of three times, mapping the same six clause fragments as before plus the two new non-clause fragments, excluded exactly like the previous per-header root fragments were. unexpected.tex/bad.tex/bad-void.tex regenerate byte-identical to the prior per-header runs; object.tex/void.tex/ref.tex pick up only this specgen version's own docblock-element reordering, confirmed by equal itemdecl counts before and after.
…ected.tex The per-fragment loop echoed a separator after every fragment, including the last, leaving a second trailing newline that pre-commit's end-of-file-fixer then had to strip on every regeneration. Insert the separator between fragments instead.
steve-downey
marked this pull request as draft
September 10, 2026 11:54
generate.sh relied on specgen's hard-coded latex base depth of 3, and the banner and README rationalized the result as "one level deeper than written, matching the real standard's absolute numbering". It does not match: in source/utilities.tex [expected] is \rSec1 and [expected.unexpected] is \rSec2, so emitting [expected.unexpected] as \rSec3 and its subclauses as \rSec4 numbered them one level too deep for both consumers -- a patch against the draft, and an \input into a paper whose own \rSec1[expected] makes its sibling clauses \rSec2. specgen now reaches that depth from the command line (specgen#97), so pass --base-section-depth 2 and drop the compensation from the prose. Every \rSec marker a clause writes in a header now means what it says. Regenerating changes nothing but the heading levels: the stable names and their nesting are unchanged, and every clause the draft already has now sits at exactly the draft's own level. The other two specgen workarounds stay, both still load-bearing: - the gathered \rSec2[expected.syn] region over expected.hpp's #includes. specgen#109 (validate across the union of a paper's documents) removes the unexpected/bad_expected_access findings, but a split run still reports unexpect and unexpect_t (\omit'd here, since they belong to the hand-authored [expected.syn]) and reference_constructs_from_temporary_v (a using-declaration, which contributes no documented name) as foreign. - the throwaway \rSec2[expected.detail] marker. Without it the five exposition-only helpers above [expected.expected] still bleed into [expected.bad]'s fragment. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This was referenced Sep 15, 2026
2b737a4 left two workarounds in place and spelled out why each was still load-bearing. specgen has since closed both underlying issues, so both come out. specgen#113 (PR #116) adds a sibling document's \elsewhere and exposition-only using-declared names to the paper-wide documented set, so a split run no longer reports unexpect, unexpect_t, or reference_constructs_from_temporary_v as foreign. The gathered \rSec2[expected.syn] region over expected.hpp's #includes is gone: generate.sh now emits one IR per header and renders the three together with --from-ir, so validation still sees the union of documented names. specgen#114 (PR #115) stops a declaration between two clauses landing in the preceding one, so the throwaway \rSec2[expected.detail] marker is gone too. The exposition-only helpers land in the root fragments, which generate.sh already discards: expected.root.tex holds converts-from-any-cvref, is-expected-specialization and unexpect-dangles-v, expected.unexpected.root.tex holds is-unexpected-specialization. No helper leaks into a kept fragment. Two marker corrections fall out of #116 actually acting on these declarations: - unexpect_t and unexpect move from \omit to \elsewhere. They belong to the hand-authored [expected.syn], and \elsewhere is what promises that to a sibling document rather than hiding them outright. - reference_constructs_from_temporary_v moves from \expos to \elsewhere on both arms of its #ifdef. \expos on a using-declaration used to be a no-op; now that it contributes a name, every use started rendering as an exposition-only *reference-constructs-from-temporary-v*. It is the real std:: trait from [meta.rel], not a library invention, so the wording has to keep the plain spelling. With \elsewhere, fragments/unexpected.tex regenerates byte-identical to before. Also reattach [expected.ref.assign]'s trivial copy assignment docblock. A blank line had crept in between it and the declaration, silently dropping the whole itemdescr -- five paragraphs, no diagnostic. Moving the "// Copy assignment (trivial path)" comment above the docblock, as the non-trivial path a few lines down already does, restores them, and also stops that comment being swallowed into the end of the Remarks prose, which is how it read before. Regenerating leaves the section set, the itemdecl count (167) and the itemdescr count (171) unchanged. A sorted-line diff against the previous wording is exactly: the two [expected.ref.assign] \remarks merged into one, the swallowed-comment paragraph gone, and "// Copy assignment (trivial path)" added to the synopsis. The rest of the diff is declaration reordering from the #114 fix. make papers still builds D4280R0.pdf, 116 pages. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
unexpected.hpp,bad_expected_access.hpp, andexpected.hppwith//!specgen docblocks (Effects, Constraints, Mandates, Returns, Throws, Remarks) for every declaration, sourced from the real standard text for already-standardized members and frompapers/expected-new.texfor the expected-over-references additions (unexpected<E&>,expected<T&,E>).specgen generate --validatepasses cleanly on all three headers.papers/wording/generate.sh(wired up asmake wording) to turn those docblocks into wording, landed two ways:papers/wording/fragments/*.tex— one file per top-level clause, for\inputinto a standalone paper.papers/wording/expected.tex— the same content concatenated in real standard clause order at the draft's own absolute numbering, with no\rSec2[expected]wrapper and no\inputdirectives — the basis for a patch tosource/utilities.texin the actual C++ working draft (cplusplus/draft), where[expected.general]and[expected.syn]stay untouched (hand-authored prose, not generated from any one declaration) and only the subclauses from[expected.unexpected]on are replaced/extended.The headers are now a source of truth specgen can turn into wording directly, instead of the wording and the implementation being able to drift apart.
Test plan
specgen generate --validatepasses on all three headerscmake --build --preset gcc-debugctest --preset gcc-debug— 1178/1178 tests passpapers/wording/expected.texread correctly against the current[expected]clause andpapers/expected-new.tex?