Skip to content

Commit 37c6796

Browse files
The engine gaps left open after the SDK batch (2026.9.12.2) (#619)
* docs(design): the engine gaps left open after the SDK batch Issues #564 to #618 checked against main at c688fca: six were fixed and are closed, two are not engine work, and four remain. Their designs, together with the two gaps the 2026-09-11 record left open, of which one turns out to be misdiagnosed: the per-target tool declaration exists under [target.<selector>.xlings.workspace], measured on linux-x86_64. For review before implementation. * docs(design): review decisions, three measured corrections, and the task list The four review questions are answered: windows_subsystem takes "windows", windows_entry ships now, a resolve-time refusal is enough for #613, and the graph-wide switch is an abi table. Measuring the plan corrected it three times. The #613 refusal already exists as requires = ["mcpp:c++-abi=libstdc++"]. The whole-graph channel G2 asked for is dialect_cxxflags: -pthread there links the openkal-emscripten threads feature, while the same flag in cxxflags reproduces the recorded error. And a new defect, T1: mcpp build --toolchain llvm@22.1.8 after a gcc build replays the gcc build in 0.00s. * feat: the engine gaps left open after the SDK batch (2026.9.12.2) Implements .agents/docs/2026-09-12-engine-gaps-after-the-sdk-batch.md (M1-M9). - #618: `[targets.<name>] windows_subsystem` and `windows_entry`, rendered per ABI onto the declaring executable's own link (`/SUBSYSTEM:` with the CRT `/ENTRY:` symbol on MSVC, `-mwindows`/`-municode` on GNU), inert off PE, and refused on library targets. Build-program protocol 10 adds the `windows-subsystem` and `windows-entry` directives for a named target. - T1: the fast path compares the toolchain request (`--toolchain`, `MCPP_TOOLCHAIN`, and `[toolchain] default`); a build asking for another toolchain no longer replays the previous one. - #615: `runtime.deploy = [{ from, to }]` places runtime files in a directory relative to the executable, in mcpp.toml and in descriptors, and `mcpp pack` carries both `deploy_files` and `deploy` at the same relative paths. - `[target.<selector>.abi] threads` as a typed graph-wide switch, with `requires_abi` on a package or feature refused before compilation when the root does not state it. - #613: a dependency's install hook receives MCPP_COMPILER, MCPP_CXX_STDLIB and the target names, computed by the function the build-program environment uses; docs state the `mcpp:c++-abi` recipe for source-built static packages. - #614: one function decides an xlings invocation's environment, the Windows project scope is restored after each invocation, and xlings' own error lines follow a failed install. - #609 and G1: a documented toolchain hazard, and the refusal that names `[target.<selector>.xlings.workspace]`, with examples/13 using it. * fix: remove module-boundary shapes that crash clang on the MSVC ABI; correct the hook environment Windows CI (clang 20.1.7, x86_64-pc-windows-msvc) crashed compiling directives.cppm. The change introduced constructs of the kind src/pack/pack.cppm records crashing that toolchain: pointers to members of an imported type and a std::span over an exported constexpr array, plus optional-bearing types in an exported interface. They are replaced by plain code: `windows_choice_problem` states the accepted values once for both parsers, the directive check and apply are per field, `invocation_env` returns a plain struct, ScopedInvocationEnv keeps plain members, and the hook scope uses six ScopedEnv locals. tests/e2e/648 measured that a dependency's install hook runs before the toolchain is resolved, because prepare resolves it after the dependency graph. The hook therefore receives the build's target and empty toolchain names; the code comment, docs/22, docs/32, the CHANGELOG and the design record now state this, and 648 asserts it, including that inherited values do not leak through. Test fixes: 645 resolves its helper directory before changing directory and drives `mcpp run` through MCPP_TOOLCHAIN; 647 writes each manifest to its own package directory. * test(645): the machine-default leg takes the newest other installed version The first other version found was gcc 13.3.0, which rejects -fmodules, so the leg measured the compiler rather than the fast path. Measured after the change: build A-B-A, run A-B-A, and gcc@16.1.0 -> gcc@15.1.0 with the artefact's version string, all passing. * test(645): a host whose fast path declines for another reason is reported, not failed macOS and Windows CI found an unchanged second build resolving the toolchain: try_fast_build requires an ELF runtime-validation verdict for every artefact, which Mach-O and PE never record (#400). The control now compares the toolchain= lines the two builds record; identical lines report the host as not measured and print the entry, differing lines fail. The design record notes the ELF-only fast path as outside this change. --------- Co-authored-by: speak-agent <248744407+speak-agent@users.noreply.github.com>
1 parent c688fca commit 37c6796

51 files changed

Lines changed: 3548 additions & 132 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.agents/docs/2026-09-12-a-verified-web-run-that-asked-the-host-for-node.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ status: landed
55

66
# A verified Web run that asked the host for node
77

8-
**Status:** implemented in mcpp 2026.9.12.1 and openxlings/xim-pkgindex#823.
8+
**Status:** implemented in mcpp 2026.9.12.2 (2026.9.12.1 was not published) and openxlings/xim-pkgindex#823.
99

1010
## What was measured
1111

.agents/docs/2026-09-12-engine-gaps-after-the-sdk-batch.md

Lines changed: 605 additions & 0 deletions
Large diffs are not rendered by default.

.agents/docs/README.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ superseded_by: 2026-09-07-....md # when status is superseded
1818
---
1919
```
2020

21-
279 records.
21+
280 records.
2222

2323
## By subject
2424

@@ -52,12 +52,14 @@ Records that declare one. Everything else is listed by date below.
5252

5353
### triage
5454

55+
- [The engine gaps left open after the SDK batch](2026-09-12-engine-gaps-after-the-sdk-batch.md) — landed
5556
- [Six open issues: what each one actually is, and what would answer it](2026-09-11-six-open-issues-analysis.md) — active
5657

5758
## By date
5859

5960
### 2026-09
6061

62+
- [The engine gaps left open after the SDK batch](2026-09-12-engine-gaps-after-the-sdk-batch.md) — landed
6163
- [A verified Web run that asked the host for node](2026-09-12-a-verified-web-run-that-asked-the-host-for-node.md) — landed
6264
- [Six open issues: what each one actually is, and what would answer it](2026-09-11-six-open-issues-analysis.md) — active
6365
- [SDK toolchains, the payload/engine seam, and openkal across iOS, Android and Web](2026-09-11-sdk-toolchains-and-ios-local-verification.md) — landed

.github/workflows/ci-macos-ios.yml

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -203,11 +203,11 @@ jobs:
203203
[target.aarch64-ios-sim]
204204
runner = ["simctl-run"]
205205
206-
# Declared at the top level here and not in examples/13, because a
207-
# tool declaration is not conditional on a target and this package
208-
# exists for macOS alone. This fixture is macOS-only, so it can say
209-
# it; a portable manifest cannot.
210-
[xlings.workspace]
206+
# Declared on the row that runs it, as examples/13 does. A target
207+
# section's `xlings.workspace` is installed only when that target is
208+
# built, so the step below that runs the simulator artefact through
209+
# `simctl-run` is also the measurement that this declaration works.
210+
[target.aarch64-ios-sim.xlings.workspace]
211211
"xim:apple-simulator-tools" = ""
212212
TOML
213213
cat > /tmp/iostest/src/main.cpp << 'CPP'

CHANGELOG.md

Lines changed: 74 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,9 @@
55
66
## [Unreleased]
77

8-
## [2026.9.12.1] - 2026-09-12
8+
## [2026.9.12.2] - 2026-09-12
9+
10+
2026.9.12.1 未单独发布,其条目并入本版本。
911

1012
### Web 产物的运行不再依赖宿主的 `node`
1113

@@ -34,6 +36,77 @@ Emscripten 链接产出的是首行为 `#!/usr/bin/env node` 的 JavaScript 启
3436
忽略它,所以配方可以先发;在此之前安装的 emsdk 载荷没有描述文件,行为与之前相同,重新
3537
安装后获得。
3638

39+
### Windows GUI 可执行文件:`windows_subsystem``windows_entry`(#618)
40+
41+
- `[targets.<name>]` 增加 `windows_subsystem = "console" | "windows"`
42+
`windows_entry = "main" | "wmain" | "WinMain" | "wWinMain"`。它们是字段而不是链接标志,因为正确的
43+
标志取决于 ABI:MSVC ABI 渲染为 `/SUBSYSTEM:` 与 CRT 启动符号 `/ENTRY:<entry>CRTStartup`(经 GNU
44+
风格驱动时带 `-Wl,`),只要任一键偏离默认值两条都写出;GNU ABI 渲染为 `-mwindows``-municode`
45+
在 ELF、Mach-O 与 WebAssembly 上不产生任何标志,产物逐字节不变。
46+
- 只到达声明它们的可执行目标的链接;同包的其他可执行文件、测试二进制与消费者保持控制台子系统。库目标
47+
声明任一键被拒绝,拒绝信息指出目标与键名;取值不在集合内被拒绝并列出可接受的取值。
48+
- 构建程序协议升至 10:`mcpp::windows_subsystem(target, value)``mcpp::windows_entry(target, value)`
49+
为本包的可执行目标设置同一字段。指向未声明的目标、非可执行目标,或与 mcpp.toml 矛盾的取值,在应用任何
50+
指令之前被拒绝;缓存命中的路径施加同一检查。
51+
- `[targets.<name>]` 不支持键的警告所列出的键表改为由解析器接受的键表生成;此前手写的副本漏掉了 `exports`
52+
53+
### 快路径比较工具链请求
54+
55+
- 快路径此前比较目标三元组、profile、缓存模式与 feature,但不比较 `--toolchain`(即 `MCPP_TOOLCHAIN`)
56+
与本机默认工具链(config.toml 的 `[toolchain] default`)。实测:以 gcc 构建后执行
57+
`mcpp build --toolchain llvm@22.1.8`,输出 `Finished dev in 0.00s` 并保留 gcc 产物,解析阶段的检查
58+
全部被跳过。
59+
- 构建缓存记录增加 `toolchain=` 行,`mcpp build``mcpp run` 的快路径都比较它。早于该行的记录被拒绝
60+
一次,随后的构建重新写入。`--offline``--locked``--jobs` 不改变解析的选择,不参与比较。
61+
62+
### `runtime.deploy`:把运行期文件放进相对可执行文件的目录(#615)
63+
64+
- `[runtime] deploy = [{ from = "...", to = "..." }]` 与描述文件的 `runtime.deploy`:`from` 相对声明它的
65+
包,`to` 相对可执行文件所在目录,`"."` 表示该目录本身。`deploy_files` 把每一项放在可执行文件旁,无法
66+
满足从固定子目录读取的加载器,例如 macOS 上的 Vulkan loader 读取 `<可执行文件目录>/vulkan/icd.d`
67+
- mcpp.toml 与描述文件使用同一条路径规则:以 `/` 分隔,不得为绝对路径、不得指定盘符、不得含空分量、`.`
68+
`..` 分量;违反的项按序号被拒绝。同一目标位置的两个来源被拒绝,同名文件放进不同目录不构成冲突。
69+
测试二进制看到同样的布局。
70+
- `mcpp pack``deploy_files``deploy` 的文件放到打包后可执行文件旁的同一相对位置;此前打包过程不读取
71+
这两个列表中的任何一个。
72+
- 它是独立的键,而不是 `deploy_files` 的表形式:早于它的描述文件读取器在 `deploy_files` 中遇到 `{`
73+
不会终止,而对不认识的 `runtime` 键会跳过。
74+
75+
### 产物的 ABI 开关:`[target.<selector>.abi] threads``requires_abi`
76+
77+
- 线程支持是整个产物共享的性质:标准库模块预构建、依赖扫描、每个包的每个翻译单元与链接必须一致。根
78+
manifest 以 `[target.<selector>.abi] threads = true` 声明;在既非 PE 也非 freestanding 的目标上渲染为
79+
`-pthread`,并经方言 flag 进入依赖缓存键。未知成员与非布尔的 `threads` 被拒绝。
80+
- 依赖以 `[package] requires_abi = { threads = true }`
81+
`[features] <name> = { requires_abi = { threads = true } }` 声明需求;根包未满足时在编译之前被拒绝,
82+
拒绝信息指出包与 feature,并给出满足它的表。依赖自己写的 `[target.<selector>.abi]` 被报告
83+
(`abi/dependency-table`)且不生效。
84+
85+
### 安装钩子的环境与 `c++-abi` 的做法(#613)
86+
87+
- 依赖包的安装钩子收到 `MCPP_TARGET``MCPP_TARGET_OS``MCPP_TARGET_ARCH``MCPP_TARGET_ENV`,名称
88+
与规则同构建程序一致,由同一个函数计算。`MCPP_COMPILER``MCPP_CXX_STDLIB` 同样总是写出,但在依赖
89+
安装时为空:工具链在依赖图之后才解析,此时没有可陈述的编译器与标准库。每个变量都显式写出,钩子不会读到
90+
从父进程继承的值;钩子不得把某种变体构建进名称未体现该变体的存储目录。
91+
- 从源码构建静态库的包以 `requires = ["mcpp:c++-abi=<stdlib>"]` 声明它所针对的标准库;工具链解析出另一
92+
实现的工程被拒绝,拒绝信息指出两个实现。docs/06、docs/22 与 docs/32 记录这一做法。
93+
94+
### xlings 调用的环境与错误输出(#614)
95+
96+
- 一次 xlings 调用的环境由一个函数决定,全局模式是不存在的 `XLINGS_PROJECT_DIR`。POSIX 把它渲染进
97+
命令前缀;Windows 在调用期间以作用域守卫施加,调用结束后恢复原值,项目目录不再留在 mcpp 之后启动的
98+
进程的环境中。`install_packages` 的回退路径此前在 POSIX 上无视项目目录、一律按全局模式拼写,现与直接
99+
路径一致。
100+
- 安装失败时,xlings 自己的错误级别输出(含 `error``E_` 或以 `[xim]` 开头的行,最多最后 20 行)以
101+
`xlings:` 前缀附在 mcpp 的诊断之后;此前 stderr 被丢弃。
102+
103+
### 其他
104+
105+
- `[target.<selector>.xlings]` 下写包名时,拒绝信息指出正确的位置 `[target.<selector>.xlings.workspace]`;
106+
示例 13 与 iOS 模拟器 CI 夹具改用该写法声明 `xim:apple-simulator-tools`,示例 README 不再要求手动安装。
107+
- docs/20 记录 clang 与 MSVC STL 14.51 组合下 `std::find` 作用于宽平凡可比较类型时的已知工具链缺陷
108+
(microsoft/STL#6294,#609)。
109+
37110
## [2026.9.11.4] - 2026-09-11
38111

39112
### iOS 三行:生态编译器与定位到的 SDK

docs/04-mcpp-toml.md

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,55 @@ A `soname` is meaningful on `kind = "lib"` too — see
202202
[`dependency_linkage`](#dependency_linkage--static-or-shared-is-the-consumers-decision)
203203
below, where the form a library takes becomes the consumer's decision.
204204

205+
#### `windows_subsystem` and `windows_entry` — a Windows GUI executable (mcpp 2026.9.12.2+)
206+
207+
```toml
208+
[targets.myapp]
209+
kind = "bin"
210+
main = "src/main.cpp"
211+
windows_subsystem = "windows" # "console" (default) | "windows"
212+
windows_entry = "main" # "main" (default) | "wmain" | "WinMain" | "wWinMain"
213+
```
214+
215+
A PE executable records a subsystem. `"console"` attaches a console, and
216+
`"windows"` produces a GUI program that starts without one. `windows_entry`
217+
names the function the program defines, not the startup symbol that calls it,
218+
and it is independent of the subsystem: a console program may define `wmain`,
219+
and a GUI program may keep a portable `int main()`.
220+
221+
The keys are fields rather than link flags because the correct flags depend on
222+
the ABI, and a flag cannot state which ABI it addresses:
223+
224+
| `windows_subsystem` / `windows_entry` | MSVC ABI (cl, clang-cl, clang targeting `*-windows-msvc`) | GNU ABI (MinGW gcc, clang targeting `*-windows-gnu`) |
225+
|---|---|---|
226+
| `"console"` / `"main"` | nothing | nothing |
227+
| `"windows"` / `"main"` | `/SUBSYSTEM:WINDOWS /ENTRY:mainCRTStartup` | `-mwindows` |
228+
| `"windows"` / `"WinMain"` | `/SUBSYSTEM:WINDOWS /ENTRY:WinMainCRTStartup` | `-mwindows` |
229+
| `"windows"` / `"wWinMain"` | `/SUBSYSTEM:WINDOWS /ENTRY:wWinMainCRTStartup` | `-mwindows -municode` |
230+
| `"console"` / `"wmain"` | `/SUBSYSTEM:CONSOLE /ENTRY:wmainCRTStartup` | `-municode` |
231+
232+
On the MSVC ABI both flags are written whenever either key differs from its
233+
default, because the linker infers each from the other when one is absent: the
234+
GUI subsystem alone selects `WinMainCRTStartup`, which a portable `int main()`
235+
does not satisfy, and `/ENTRY:main` skips CRT initialisation, static
236+
constructors included. A GNU-style driver receives the MSVC-ABI flags as
237+
`-Wl,/SUBSYSTEM:...`.
238+
239+
The keys reach the link of the declaring target only. A second executable, the
240+
`mcpp test` binaries and the consumers of the package keep the console
241+
subsystem, which is why `[build] ldflags` is not the place for these flags: that
242+
channel reaches every link in the graph. On ELF, Mach-O and WebAssembly the keys
243+
render nothing and the artefact is byte-identical to one built without them, so
244+
a cross-platform manifest needs no `cfg` block. A library target that declares
245+
either key is refused, and the refusal names the target and the key.
246+
247+
A build program sets the same fields for an executable of its own package with
248+
`mcpp::windows_subsystem("<target>", "windows")` and
249+
`mcpp::windows_entry("<target>", "wmain")` ([build.mcpp](30-build-mcpp.md)).
250+
251+
Application bundles, application manifests and DPI awareness are not part of
252+
these keys; they belong to packaging formats and to `[resources]`.
253+
205254
#### Per-target keys
206255

207256
```toml
@@ -223,6 +272,8 @@ required_features = ["gui"] # only built when feature `gui` is
223272
| `defines` | Preprocessor macros (`name` or `name=value`); desugar to `-D<x>` on both the C and C++ entry compile. |
224273
| `cxxflags` / `cflags` | Extra compile flags for this target. Do **not** put `-std=...` here — use `[package].standard`. |
225274
| `required_features` | The target is emitted only when **every** listed feature is active in the build; otherwise it is silently skipped. A gate only — it does not activate features (use `--features` / `[features].default`). **One exception, and it is not a second rule:** when this target is requested as a host tool (`tools = [...]`, §2.14), the target is what was *asked for*, so its `required_features` become the sub-build's *inputs*. Same field, one meaning — the resolution just runs in the opposite direction. |
275+
| `windows_subsystem` *(2026.9.12.2+)* | The PE subsystem of an executable: `"console"` (the default) or `"windows"`, a GUI program that starts without a console. Reaches this target's link and no other, and renders nothing on a target that is not PE. See the section above. |
276+
| `windows_entry` *(2026.9.12.2+)* | The entry function the program defines: `"main"` (the default), `"wmain"`, `"WinMain"` or `"wWinMain"`. See the section above. |
226277

227278
> **Scope (important):** `defines` / `cxxflags` / `cflags` on a target apply **only to that
228279
> target's exclusive entry source** (its `main`) — never to shared module/impl objects, which
@@ -1055,6 +1106,7 @@ transitive_needed_dirs = ["runtime/closure"]
10551106
runtime_search_dirs = ["runtime"]
10561107
frameworks = ["WindowKit"]
10571108
deploy_files = ["bin/widget.dll"]
1109+
deploy = [ { from = "share/vulkan/icd.d/widget_icd.json", to = "vulkan/icd.d" } ]
10581110

10591111
# Use an exact canonical identity when multiple providers exist.
10601112
[runtime."display.present"]
@@ -1109,6 +1161,23 @@ Link intent keeps discovery stages separate:
11091161
| `runtime_search_dirs` | RUNPATH/rpath only, never `-L` | rpath only | no flag |
11101162
| `frameworks` | no flag | `-framework` | no flag |
11111163
| `deploy_files` | copy edge | copy edge | copy beside the output; never a linker flag |
1164+
| `deploy` *(2026.9.12.2+)* | copy edge into `bin/<to>/` | copy edge into `bin/<to>/` | copy edge into `bin/<to>/`; never a linker flag |
1165+
1166+
`deploy` places a file in a directory relative to the executable, which
1167+
`deploy_files` cannot express because it places every entry beside the
1168+
executable. A loader that reads a fixed subdirectory needs it: the Vulkan loader
1169+
on macOS reads driver manifests from `<executable dir>/vulkan/icd.d`. Each entry
1170+
is a table of exactly two strings. `from` is relative to the declaring package's
1171+
root, and `to` is relative to the executable's directory, where `"."` means that
1172+
directory itself. Both are separated by `/` on every host, and neither may be
1173+
absolute, name a drive, or contain an empty, `.` or `..` component; an entry
1174+
that does is refused, and the refusal names its index. Two sources for one
1175+
destination are refused naming the destination, while one file name in two
1176+
directories is not a collision. `deploy` is a key of its own rather than a table
1177+
form of `deploy_files`, because a descriptor reader that predates it meets `{`
1178+
inside `deploy_files` and does not terminate, whereas it skips a `runtime` key it
1179+
does not know. `mcpp pack` stages the files of both keys at the same relative
1180+
path beside the packed executable.
11121181

11131182
For one compatibility train, `library_dirs` maps only to runtime search,
11141183
`dlopen_libs` maps to required run-phase soname requirements, and

docs/06-features-and-capabilities.md

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,24 @@ provides = ["mcpp:compiler-runtime=compiler-rt", "mcpp:c++-abi=libc++"]
192192
requires = ["mcpp:compiler=llvm"]
193193
```
194194

195+
A requirement on the artefact's ABI switch is stated with `requires_abi`, on the
196+
package or on one feature, rather than as a layer:
197+
198+
```toml
199+
[package]
200+
requires_abi = { threads = true }
201+
202+
[features]
203+
mt = { requires_abi = { threads = true } }
204+
```
205+
206+
Only the root manifest sets the switch (`[target.<selector>.abi]`, [22 — The
207+
Target Side](22-target-side.md)); a requirement it does not satisfy is refused
208+
before compilation, naming the package and the feature. A package whose install
209+
hook compiles a static library against one C++ standard library states that
210+
implementation as a layer requirement, `requires = ["mcpp:c++-abi=libstdc++"]`,
211+
for the reason given in the same chapter.
212+
195213
A package that is a standard library states its `std` module source under
196214
`[build]`, where the flags it needs become conditional like any other build
197215
input.

docs/20-toolchains.md

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -873,6 +873,37 @@ Tracked as [mcpp#256](https://github.com/mcpp-community/mcpp/issues/256).
873873
bundled LLVM toolchains, so a future Clang bump that fixes — or re-breaks —
874874
this becomes visible instead of silently changing what packages can express.
875875

876+
## Known Toolchain Hazard: `std::find` Over a Wide Trivially Comparable Type (clang + MSVC STL 14.51)
877+
878+
A translation unit that calls `std::find` over a trivially copyable type wider
879+
than eight bytes fails to compile inside the standard library when the compiler
880+
is clang and the standard library is the MSVC STL at 14.51 (Visual Studio 18):
881+
882+
```text
883+
xutility:320:23: error: static assertion failed: unexpected size
884+
xutility:6542:49: note: in instantiation of function template specialization
885+
'std::_Find_vectorized<const T, T>' requested here
886+
```
887+
888+
This is the shape of mcpp's default Windows toolchain, clang targeting
889+
`x86_64-pc-windows-msvc`, so the failure surfaces through `mcpp build` although
890+
nothing in mcpp or in the program is wrong. The MSVC STL admits the type to its
891+
vectorized path through a clang-only trait that has no upper size bound, and
892+
the function it dispatches to implements 1-, 2-, 4- and 8-byte elements only.
893+
MSVC's own front end never takes that path. The same source compiles against the
894+
MSVC STL 14.3x that `windows-2022` ships.
895+
896+
The defect is upstream, tracked as
897+
[microsoft/STL#6294](https://github.com/microsoft/STL/issues/6294). Two
898+
workarounds were measured downstream:
899+
900+
- Give the element type a user-written `operator==` instead of a defaulted one.
901+
The type is then not trivially equality-comparable, and the STL keeps its
902+
scalar path.
903+
- Build on an image whose MSVC STL predates 14.51, such as `windows-2022`.
904+
905+
Tracked as [mcpp#609](https://github.com/mcpp-community/mcpp/issues/609).
906+
876907
## The C++ runtime contract (`cxx_runtime`)
877908

878909
`cxx_runtime` states what the produced artifact promises about the machine that

0 commit comments

Comments
 (0)