fix(scanner, manifest, build): one answer per question, and no key that reaches no decision (2026.9.9.1) - #594
Merged
Conversation
…at reaches no decision (2026.9.9.1) Four defects with two shapes behind them: a question answered twice whose answers are read by different sites, and a key that is parsed and then reaches no decision. Design record: .agents/docs/2026-09-09-two-answers-and-two-silences.md `module : private;` was read as an implementation partition. The name tokeniser admits `:` so that `M:part` scans as one token, and the partition test is "the name contains a colon" -- but the colon in the private module fragment comes first and belongs to no name. A valid file was refused at scan time with a sentence that is false about it. A regression from #433, first released in v2026.8.18.1: on clang the same source built and ran under 2026.8.17.1. The scanner also no longer records an identity no source could have declared; a BOM combined with a fragment previously produced a BMI named after a colon. A UTF-8 byte-order mark is consumed where bytes become lines, and by every TOML document mcpp reads. MSVC writes one by default, so this is ordinary input. The failure it caused appeared at the consumer (`module 'x' not found`) rather than at the file that was wrong. UTF-16/32 is refused by name. `module_lang` and `module_output` become per-edge, bound from what the scan found rather than from the file's extension. A module-extension file need not provide a module, and where the classifier and the scanner disagreed the edge carried an interface's flags with no BMI: clang accepted `-fmodule-output=` with no value and wrote no BMI at all, and compiled implementation units as interfaces. GCC's interface spelling is the plain language, so the identical project built there and not on clang. `ConditionalConfig::empty()` replaces two hand-written gates that omitted `libraries` and `linkLibraryDirs`, so a predicate carrying only a `[target.<pred>.runtime]` table was parsed and discarded. It composes `BuildInputs::empty()` and the existing `XlingsConfig::empty()`, closing a fourth instance in the xpkg gate. All 228 descriptors in mcpp-index parse identically before and after. `[runtime]` and `[target.<pred>.runtime]` now report an unsupported key, the rule `[build]` and `[target.<triple>]` have followed since #418 and #249. Deliberately not done: no detector for GNU-spelled `ldflags` reaching MSVC. `ldflags` is a documented raw pass-through and `[runtime] libraries` is the supported neutral form; inspecting the former would be a second answer to a question the author already answered.
`[[runtime.artifacts]]` and `[[runtime.requirements]]` are arrays rather than tables, so they reach the unknown-key sweep instead of being skipped with the `[runtime.<capability>]` provider channel. They are what `mcpp pack` writes into every packed library, so a false positive there would warn on all of them. Measured as zero before this was added; the assertion is what keeps it zero.
… recorded The tokeniser stops at the first character it does not accept, so a declaration it cannot read at all yields an EMPTY name -- and `'' is not a module name` names nothing the author can find. Measured on a non-ASCII name, where `is_module_name_char` tests bytes with std::isalnum and every byte of a UTF-8 sequence is false: the message read `''` where the source said `模块`. GCC 16.1 refuses that declaration too (`unrecognized 'MODULE-EXPORT ...'`), so this is a clearer sentence for a refusal that already existed rather than a new restriction. The message now also states what a name is.
… gate `mcpp xpkg parse` does not print conditional configs, so it could not have shown a difference there. The statement with a denominator is that no descriptor in the index uses `target_cfg` at all (0 of 228), so the xpkg half of the change cannot alter a published package today.
…checks The message spelled its two keys as prose while the check compared two separate literals. The `[build]` sweep a few hundred lines above carries the note saying why that matters: its message was once a third hand-written copy that had drifted from both others, so the only spelling that turned the feature on was the one being reported as unsupported.
…ling is Windows CI reported `test_modgraph.cpp` failing to compile with optional:262: error: no matching constructor for initialization of '_SMF_control<_Optional_construct_base<basic_string<char,...>>, ...>' against `types.cppm`'s `Profile`, a struct this change never touched. The chain is `Manifest` -> `std::map<std::string, Profile>` -> `Profile`'s `std::optional<std::string>`, and that member's own comment already records the same breakage under clang with the MSVC standard library on an earlier occasion. The cause was making the new emptiness predicate an inline MEMBER of two structs this module exports: that changes what importers materialise from its BMI, and what it materialised here was a copy constructor that does not compile. `append`, the operation this one is the sibling of, has been a free function since it was written. `is_empty` now matches it, and BuildInputs and ConditionalConfig have exactly the member sets they had before. Also records where the four new e2e tests run. All declare `# requires: gcc`, which `run_all.sh` grants only on Linux, so the end-to-end leg is Linux-only and the cross-dialect coverage -- including the MSVC `/ifcOutput` spelling that no Linux runner can reach -- is in the unit tests, which run everywhere.
Windows CI could not compile `tests/unit/test_modgraph.cpp`: optional:262: error: no matching constructor for initialization of '_SMF_control<_Optional_construct_base<basic_string<char,...>>, ...>' reached through `Manifest` -> `std::map<std::string, Profile>` -> `Profile::dependencyLinkage`, an `std::optional<std::string>` DATA MEMBER of an exported struct. `TargetEntry::sysroot`'s comment in the same file already forbids that shape and prescribes the remedy -- two plain members -- after the same error on an earlier occasion. `Profile` was the last member in the module still shaped that way. Semantics are unchanged: not declared still means "whatever [build] said", which is what the optional carried and what `dependencyLinkageDeclared` carries now. WHICH EDIT IN THIS BRANCH TIPS IT IS NOT ESTABLISHED. Two diagnoses were refuted: that the new emptiness predicate's being an inline member was the cause (made it a free function; CI failed identically), and that the runner image had changed (re-ran main's own Windows job unchanged on today's image; it passed). `test_modgraph.cpp` copies a Manifest by value on main too, so the landmine was already armed; this removes the type that cannot be copied rather than the perturbation that reached it.
Sunrisepeak
added a commit
to Sunrisepeak/xrgui
that referenced
this pull request
Sep 9, 2026
…tires mcpp 2026.9.9.1 (mcpp-community/mcpp#594) fixes four defects this port had been working around or was about to be broken by. Taking the version lets three things go away, and each is deleted here rather than left as dead weight. A FLOOR, not a preference. src/font/font.ixx uses the private module fragment `module : private;`, and from 2026.8.18.1 through 2026.9.8.1 mcpp read that as an implementation partition named ":" -- "file already provides module mo_yanxi.font\; cannot also provide ':'". A regression from mcpp#433. This job is green today only because it was pinned below the break, so the whole range is unusable and the pin has to move past it, not merely forward. The BOM strip is gone. mcpp now consumes a byte-order mark where bytes become lines, so the submodules keep theirs and this job no longer rewrites checked-out sources before building them. MSVC writes a BOM by default; the workaround was against ordinary input. The submodule patch is gone, replaced by a `[scan_overrides]` entry in mcpp/mo_yanxi_vulkan_wrapper/mcpp.toml. That is the escape hatch mcpp documents for this exact shape (its own example is fmt guarding `import std;` behind `#ifdef FMT_IMPORT_STD`), and mcpp audits the declaration against the compiler's P1689 scan every build, so it cannot drift from the source it describes. The declaration is true on MSVC, where __cpp_lib_stacktrace is defined; this manifest builds a Windows/MSVC target and does not claim more. The Windows system libraries move from build.mcpp to `[target.windows.runtime] libraries`. They were only ever expressed as a C++ program because a conditional table carrying just a `[target.<pred>.runtime]` was parsed and then discarded -- silently, so the natural spelling looked like it worked. mcpp#594 applies it and adds unknown-key reporting to `[runtime]` too. What stays in build.mcpp is vulkan-1, whose SEARCH PATH genuinely depends on what is installed on the machine and so cannot be written down in advance. Verified before pushing: xrgui configures clean on Linux for the first time -- 1062 compile commands, zero scanner errors. Previously the scan always ended on either font.ixx or stack_trace.cpp.
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.
Four defects reported by a consumer project pinned at 2026.8.17.1, and two
shapes behind them: a question answered twice whose answers are read by
different sites, and a key that is parsed and then reaches no decision.
Design record:
.agents/docs/2026-09-09-two-answers-and-two-silences.md.What was measured
Same machine, gcc 16.1.0 and clang 22.1.8.
module : private;, clangmodule :private;, clang.cppmmodule 'x' not found, at the consumerpcm.cache/-.pcm.cppm, clangmissing 'export' specifier[target.linux.runtime]alone[runtime]mcpp.toml1:1: expected keyThe four
module : private;was read as an implementation partition. The nametokeniser admits
:soM:partscans as one token, and the partition test is"the name contains a colon" — but this production's colon comes first and
belongs to no name. A valid file was refused with a sentence that is false about
it. A regression from #433, first released in v2026.8.18.1. The scanner also no
longer records an identity no source could have declared.
A UTF-8 byte-order mark is consumed, in the scanner and in every TOML
document mcpp reads. MSVC writes one by default. The failure it caused surfaced
at the consumer rather than at the file that was wrong. UTF-16/32 is refused by
name.
module_langandmodule_outputare now per-edge, bound from what the scanfound rather than from the file's extension. A module-extension file need not
provide a module; where the classifier and the scanner disagreed, the edge
carried an interface's flags with no BMI. Clang accepted
-fmodule-output=withno value and wrote no BMI at all; GCC's interface spelling is the plain
language, so the identical project built there and not on clang.
ConditionalConfig::empty()replaces two hand-written gates that omittedlibrariesandlinkLibraryDirs. It composesBuildInputs::empty()and theexisting
XlingsConfig::empty(), which closed a fourth instance in the xpkggate (
privateIncludeDirs). This is the third time this struct has been read bya list that fell behind it; its own comments record #258 and #359.
Plus:
[runtime]and[target.<pred>.runtime]now report an unsupported key —the rule
[build]and[target.<triple>]have followed since #418 and #249.Deliberately not done
No detector for GNU-spelled
ldflagsreaching MSVC.ldflagsis a documentedraw pass-through and
[runtime] librariesis the supported neutral form;inspecting the former would be a second answer to a question the author already
answered.
compat.glfwand friends can already writewindows = { runtime = { libraries = { "gdi32" } } }today — themcpp.<platform>channel supports it and is target-axis-typed — so that changebelongs to
mcpp-indexon its own schedule.No
runtimekey added to xpkg'starget_cfg: the platform channel covers thedemonstrated need, so it would be a change with nothing behind it.
Evidence the criteria discriminate
Twelve new unit tests and four new e2e tests. Run against the implementation
reverted, ten of the twelve unit tests fail and all four e2e tests fail against
the released 2026.9.8.1 binary. The two that stay green are negative controls
(
WellFormedNamesSurviveTheIdentityGuard,CorrectlySpelledRuntimeKeysAreSilent), which exist to fail if the new refusalsare too broad.
The e2e criteria are assertions on the emitted graph, not on build outcome,
because the default Linux shards have gcc — where three of the four defects are
invisible.
# requires: llvmwould have skipped silently on both shards.Locally: unit suite green, all nine workspace members green, all 12 examples
build, index-wide check of 228
mcpp-indexdescriptors parses identicallybefore and after.