Skip to content

Commit bcde510

Browse files
committed
fix(toolchain): per-file C++ force for cl, so object inputs stay objects
cl.exe rejected the std and mcpp module objects with "C2018: character 'U+10' is not permitted here" — it was compiling them as C++ source. `/TP` is not the counterpart of `-x c++`: GNU's is positional and lasts until `-x none`, cl's applies to EVERY input on the line, so the objects that follow are fed to the C++ frontend. The dialect now carries the per-FILE form (`/Tp<file>`) alongside the positional one, and the build.mcpp compile uses whichever the driver has. That is the same structural difference the row already records for libFlag (prefix vs suffix): the two drivers are not spelling the same concept differently, they have different concepts.
1 parent 186bfd2 commit bcde510

3 files changed

Lines changed: 29 additions & 2 deletions

File tree

.agents/docs/2026-08-02-host-compile-implementation-plan.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -219,6 +219,16 @@ diff /tmp/stdcmd-base.txt /tmp/stdcmd-after.txt # 必
219219

220220
---
221221

222+
## 实施记录(只有 CI 能发现的)
223+
224+
| 发现 | 教训 |
225+
|---|---|
226+
| **扩大 `build_program.cppm` 的匿名 ns 同样触发 clang 误编译** |`build_mcpp_module` 在原地改写加大 → macOS 全部 build.mcpp e2e 段错误,和 PR#332 一模一样。约束不是「别加新函数」,是**「别再往那个 ns 加代码」**。修法=整块搬到 `src/build/hostprogram.cppm` |
227+
| **「扫缓存比对字节等价」是假验证** | std 缓存共享且累积,两次快照都含**陈旧条目**,diff 恒为空 —— 我因此放过了一次真实的字符串改动(`-stdlib=libc++` 位置)。**把主张写成测试**:用合成的 `ClangDriverModel`/`ToolchainLinkModel` 直接断言渲染出的字面串 |
228+
| **`-stdlib=libc++` 的位置是兼容面** | 它进 `std_build_commands` → 进 metadata → **决定 std 缓存目录名**。挪一个 flag = 让每个用户的 std BMI 全量失效 |
229+
| **「信任 cfg」不等于「什么都不发」** | 生产者在 trust-cfg 分支提前 `return`,把 deployment target 也跳过了;而 macOS 上 build.mcpp 走的正是这条分支 → std BMI 配置不匹配。旧的手写实现把它放在**最前、无条件**,注释还专门写了 "FIRST and unconditionally" —— 重构时要读懂那句话为什么在 |
230+
| **删掉能力门 = 死代码变活代码** | `-x none` 常年无条件发出,只因 MSVC 到不了那里才无害。门一删,cl 立刻 `D9002: ignoring unknown option '-x'`**删门时要把门后所有「反正到不了」的分支重新审一遍** |
231+
222232
## Self-Review
223233

224234
**设计覆盖**:§4.1 token 生产者 → Task 2/3;§4.2 三种渲染 → Task 4;

src/build/build_program.cppm

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -663,8 +663,15 @@ std::expected<void, std::string> run_build_program(
663663
for (auto& sf : stdFlags) compileArgv.push_back(sf);
664664
// The `.mcpp` extension is unknown to every driver, so without this the
665665
// file is handed to the linker as a linker script.
666-
for (auto f : dial.forceCxxLangArgv) compileArgv.emplace_back(f);
667-
compileArgv.push_back(src.string());
666+
// Per-file where the driver has that form (cl's /Tp), positional
667+
// otherwise. Object files follow on this same command line, and cl's
668+
// global /TP would compile them as C++ source.
669+
if (!dial.perFileCxxPrefix.empty()) {
670+
compileArgv.push_back(std::string(dial.perFileCxxPrefix) + src.string());
671+
} else {
672+
for (auto f : dial.forceCxxLangArgv) compileArgv.emplace_back(f);
673+
compileArgv.push_back(src.string());
674+
}
668675
if (usesModule || !stdObjects.empty()) {
669676
// Link the module objects. GNU drivers need the input language reset
670677
// first, or the .o that follows `-x c++` is handed to the frontend as

src/toolchain/dialect.cppm

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,14 @@ struct CommandDialect {
5454
// better off without (see the note on `alwaysFlagsArgv`).
5555
std::string_view forceCxxLang; // "-x c++" | "/TP"
5656
std::span<const std::string_view> forceCxxLangArgv;
57+
// Per-FILE language force, for a command line that also carries object
58+
// files. The two drivers differ structurally, not just in spelling: GNU's
59+
// `-x c++` is positional and stays in effect until `-x none`, while
60+
// cl.exe's `/TP` applies to EVERY input — so an object listed after it is
61+
// fed to the C++ frontend and dies with C2018. cl's per-file form is
62+
// `/Tp<file>`; GNU has none, and uses the positional pair plus a reset.
63+
// Empty means "no per-file form — use forceCxxLangArgv and reset after".
64+
std::string_view perFileCxxPrefix; // "" | "/Tp"
5765
// Static CRT / runtime. On MSVC this is a compile-time CRT model, not a
5866
// link mode — there is no /MT equivalent of `-static` for the whole image.
5967
std::string_view staticRuntime; // "-static"| "/MT"
@@ -127,6 +135,7 @@ constexpr CommandDialect kGnuDialect{
127135
.libSearchPrefix = "-L",
128136
.forceCxxLang = "-x c++",
129137
.forceCxxLangArgv = kGnuForceCxxArgv,
138+
.perFileCxxPrefix = "",
130139
.staticRuntime = "-static",
131140
.outputExePrefix = "-o ",
132141
.objExt = ".o",
@@ -154,6 +163,7 @@ constexpr CommandDialect kMsvcDialect{
154163
.libSearchPrefix = "/LIBPATH:",
155164
.forceCxxLang = "/TP",
156165
.forceCxxLangArgv = kMsvcForceCxxArgv,
166+
.perFileCxxPrefix = "/Tp",
157167
.staticRuntime = "/MT",
158168
.outputExePrefix = "/Fe:",
159169
.objExt = ".obj",

0 commit comments

Comments
 (0)