Skip to content

Commit 42a8f8f

Browse files
committed
the reason-token table and the engine no longer agree by being read
docs/50's reason-token table is a machine interface: mcpp-index's compatibility measurement reads a token out of a refusal to tell "this graph does not supply what the member asked for" from "the member did not build", and that distinction decides a published figure. A token the engine emits and the table omits is a promise nobody can rely on. FOUR MORE WERE MISSING, AND THIS WAVE HAD ALREADY "ADDED THE MISSING ONES". `apple-sdk-absent`, `lld-required-absent`, `host-tool-toolchain` and `std-module-precompile` predate this branch and were absent the whole time; the earlier commit in this same branch added four others by reading the table against the code. A set compared by reading is a set compared by sampling. check_reason_tokens.sh compares both directions mechanically --- every token `refusal.cppm` can emit is a row, every row is a token some branch emits --- and requires the 简体中文 mirror to carry the same set, which a structural check counting headings cannot see. Removing a row from either page, or adding one no branch emits, fails it; all three were exercised. THE CHECK FIRST REPORTED `reason` AS A TOKEN. The table's COLUMN HEADER is `| `reason` | |`: a backticked name in the first cell, the same shape as every row under it. Selecting rows by "a backticked name at the start of a line" selects the header too. What distinguishes a row is a non-empty second cell, so the pattern requires one --- the object set is chosen by a property rather than by a syntax that the header also satisfies. Also corrects the assembler's degradation example in docs/20 and its mirror. It was written as `degraded:` with ten-space continuations; `diag::degraded` renders through `ui::warning`, so the real output is `warning:` with `impact:` and `hint:` labels, as every other example in these documents shows.
1 parent 4b6252b commit 42a8f8f

7 files changed

Lines changed: 139 additions & 6 deletions

File tree

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
#!/usr/bin/env bash
2+
# Every reason token the engine can emit is in docs/50's table, and every row
3+
# of that table is a token the engine can emit.
4+
#
5+
# WHY THIS EXISTS. The table is a MACHINE INTERFACE: mcpp-index's compatibility
6+
# measurement reads `[interface-not-provided]` out of a refusal to tell "this
7+
# graph does not supply what the member asked for" from "the member did not
8+
# build", and that distinction decides a published figure. A token the engine
9+
# emits and the table omits is a promise nobody can rely on; a row naming a
10+
# token no branch emits is one a consumer will wait for forever.
11+
#
12+
# IT EXISTS BECAUSE ADDING THE MISSING ONES BY HAND MISSED SOME. Four tokens
13+
# were added to this table on 2026-09-18 as "the ones it was missing"; a
14+
# later enumeration found four more that had been absent the whole time. A set
15+
# compared by reading is a set compared by sampling.
16+
set -u
17+
cd "$(dirname "$0")/../.."
18+
19+
src=src/build/refusal.cppm
20+
doc=docs/50-machine-output.md
21+
[ -f "$src" ] && [ -f "$doc" ] || { echo "::error::missing $src or $doc"; exit 1; }
22+
23+
# `none` is the sentinel for "no refusal was recorded" and names no branch.
24+
# `other` stays in BOTH sets: it is emitted and it is documented.
25+
emitted=$(grep -oE 'return "[a-z][a-z0-9-]*";' "$src" \
26+
| sed 's/return "//; s/";//' | grep -vx none | sort -u)
27+
28+
# The table is the contiguous run of `| \`token\` |` rows containing `other`,
29+
# which is its documented catch-all. Anchoring on a row rather than on a
30+
# heading keeps this working when the prose around it is rewritten.
31+
n=$(grep -n '^| `other` |' "$doc" | head -1 | cut -d: -f1)
32+
[ -n "$n" ] || { echo "::error::$doc has no \`other\` row; the table moved"; exit 1; }
33+
start=$n
34+
while [ "$start" -gt 1 ] && sed -n "$((start-1))p" "$doc" | grep -q '^|'; do
35+
start=$((start-1))
36+
done
37+
# A ROW IS SELECTED BY HAVING A DESCRIPTION, NOT BY LOOKING LIKE A ROW. This
38+
# table's COLUMN HEADER is `| \`reason\` | |` --- a backticked name in the first
39+
# cell, exactly the shape of the rows beneath it --- so a pattern that matches
40+
# "backticked name at the start of a line" reports `reason` as a documented
41+
# token. The second cell is what tells a header from a row, so the pattern
42+
# requires a non-empty one.
43+
documented=$(sed -n "${start},${n}p" "$doc" \
44+
| grep -oE '^\| `[a-z][a-z0-9-]*` \| [^|]+ \|' \
45+
| sed 's/^| `//; s/`.*//' | sort -u)
46+
47+
missing=$(comm -23 <(printf '%s\n' "$emitted") <(printf '%s\n' "$documented"))
48+
extra=$(comm -13 <(printf '%s\n' "$emitted") <(printf '%s\n' "$documented"))
49+
50+
rc=0
51+
if [ -n "$missing" ]; then
52+
echo "::error::these reason tokens are emitted by $src and absent from $doc:"
53+
printf ' %s\n' $missing
54+
rc=1
55+
fi
56+
if [ -n "$extra" ]; then
57+
echo "::error::$doc names these tokens and no branch in $src emits them:"
58+
printf ' %s\n' $extra
59+
rc=1
60+
fi
61+
# AND THE 简体中文 MIRROR CARRIES THE SAME SET. A translated page falls behind
62+
# by losing rows, and a structural check that only counts headings cannot see
63+
# it: the token names are identical in both languages, so they compare
64+
# directly even though nothing else on the page does.
65+
zh=docs/zh/50-machine-output.md
66+
if [ -f "$zh" ]; then
67+
zn=$(grep -n '^| `other` |' "$zh" | head -1 | cut -d: -f1)
68+
if [ -z "$zn" ]; then
69+
echo "::error::$zh has no \`other\` row; the mirror's table moved"
70+
rc=1
71+
else
72+
zstart=$zn
73+
while [ "$zstart" -gt 1 ] && sed -n "$((zstart-1))p" "$zh" | grep -q '^|'; do
74+
zstart=$((zstart-1))
75+
done
76+
zdoc=$(sed -n "${zstart},${zn}p" "$zh" \
77+
| grep -oE '^\| `[a-z][a-z0-9-]*` \| [^|]+ \|' \
78+
| sed 's/^| `//; s/`.*//' | sort -u)
79+
zmiss=$(comm -23 <(printf '%s\n' "$documented") <(printf '%s\n' "$zdoc"))
80+
zextra=$(comm -13 <(printf '%s\n' "$documented") <(printf '%s\n' "$zdoc"))
81+
if [ -n "$zmiss" ]; then
82+
echo "::error::$zh is missing reason tokens that $doc documents:"
83+
printf ' %s\n' $zmiss
84+
rc=1
85+
fi
86+
if [ -n "$zextra" ]; then
87+
echo "::error::$zh documents reason tokens $doc does not:"
88+
printf ' %s\n' $zextra
89+
rc=1
90+
fi
91+
fi
92+
fi
93+
94+
[ "$rc" -eq 0 ] && echo "OK: $(printf '%s\n' "$emitted" | grep -c .) reason tokens; engine, table and 简体中文 mirror agree"
95+
exit $rc

.github/workflows/ci-linux.yml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,18 @@ jobs:
120120
- name: Documented target tiers agree with the table
121121
run: python3 .github/tools/check_target_tiers.py
122122

123+
# The reason-token table in docs/50 is a MACHINE INTERFACE: mcpp-index's
124+
# compatibility measurement reads a token out of a refusal to tell "this
125+
# graph does not supply what the member asked for" from "the member did
126+
# not build", and that distinction decides a published figure. A token
127+
# the engine emits and the table omits is a promise nobody can rely on.
128+
#
129+
# Four tokens were added to that table by hand as "the ones it was
130+
# missing"; a later enumeration found four more that had been absent the
131+
# whole time. A set compared by reading is a set compared by sampling.
132+
- name: Reason tokens agree with the engine, and with the mirror
133+
run: bash .github/tools/check_reason_tokens.sh
134+
123135
- uses: ./.github/actions/bootstrap-mcpp
124136

125137
- name: Configure mirror + Build mcpp from source (self-host)

CHANGELOG.md

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,24 @@ requires-interfaces = ["openkal.fs", "openkal.net"]
8686
`docs/50` 的 reason 令牌表同时补上了 2026.9.18.1 起一直在发却从未列出的三个:
8787
`c-env-unrealisable`、`c-env-verification-mismatch`、`platform-dependency`。
8888

89+
### 理由令牌表与引擎不再靠人读对齐
90+
91+
`docs/50` 的 reason 令牌表是**机器接口**:mcpp-index 的兼容性测量就是从拒绝里读一个
92+
令牌,来区分「这个图没有提供该成员要的东西」与「该成员没构建成功」,而这条区分决定一个
93+
会被发布的数字。引擎能发而表里没有的令牌,是一条没有人能依赖的承诺。
94+
95+
2026.9.18.1 那轮往这张表里补过「缺的那四个」;本轮枚举发现**另外四个**一直缺着——
96+
`apple-sdk-absent`、`lld-required-absent`、`host-tool-toolchain`、`std-module-precompile`。
97+
**靠读来比较的集合,比较的是样本。**
98+
99+
四条补齐,并新增 `.github/tools/check_reason_tokens.sh`:它双向比对
100+
`refusal.cppm` 能发出的令牌与表里的行,并要求简体中文镜像携带同一个集合。
101+
两个方向各去掉一条都会红。
102+
103+
⚠️ 这张表的**列头**是 `| \`reason\` | |`——第一格里一个反引号名字,形状与下面每一行
104+
完全相同。按「行首反引号名字」匹配会把 `reason` 当成一个令牌。行与列头的区别在**第二格
105+
非空**,所以判据按性质挑对象,不按语法挑。
106+
89107
### `[c-abi-absent]`:枚举例外,不枚举规则
90108

91109
一个 C 库供给的名字集合在清单里不可枚举(POSIX 约一千二百个),枚举它正是 §3.3 记录下

docs/20-toolchains.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -704,9 +704,9 @@ order is now the other way, and when the host copy is the one that served, the
704704
build says so:
705705

706706
```
707-
degraded: the assembler for this build is the host's ('/usr/bin/nasm'), not the one this engine pins
708-
two machines can assemble the same source with different assemblers, and the build records only this line
709-
run `xlings install nasm` so the pinned copy is used
707+
warning: the assembler for this build is the host's ('/usr/bin/nasm'), not the one this engine pins
708+
impact: two machines can assemble the same source with different assemblers, and the build records only this line
709+
hint: run `xlings install nasm` so the pinned copy is used
710710
```
711711

712712
**A host tool that reaches a build is not by itself the defect. A host tool

docs/50-machine-output.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -416,6 +416,10 @@ a program classifying the outcome reads `reason`:
416416
| `c-env-verification-mismatch` | the probe compiled with the realised `[c-abi]` configuration disagrees with what was declared *(2026.9.18.1+)* |
417417
| `platform-dependency` | `[build] platform-dependencies = "refuse"` and a package in the graph brings a platform SDK *(2026.9.18.1+)* |
418418
| `interface-not-provided` | a package's `[kernel-abi] requires-interfaces` names an interface the resolved implementation does not provide *(2026.9.20.1+)* |
419+
| `apple-sdk-absent` | the target needs an Apple SDK this machine does not provide; it is located rather than installed, because it is not redistributable |
420+
| `lld-required-absent` | the target links through lld directly and the resolved toolchain payload ships none |
421+
| `host-tool-toolchain` | `build.mcpp` under a cross `--target` needs a resolvable HOST toolchain and none is set |
422+
| `std-module-precompile` | the standard library's module could not be precompiled for this configuration |
419423
| `other` | a refusal whose branch has not been given a token yet |
420424

421425
**One token is also printed by `mcpp build` itself.**

docs/zh/20-toolchains.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -628,9 +628,9 @@ C 库与 C++ 运行时(包)、`ninja` 与 `patchelf`(xlings),以及 `ar` / `stri
628628
一行说它用的是哪一个。现在顺序反过来了;当服务的是宿主那一份时,构建会说出来:
629629

630630
```
631-
degraded: the assembler for this build is the host's ('/usr/bin/nasm'), not the one this engine pins
632-
two machines can assemble the same source with different assemblers, and the build records only this line
633-
run `xlings install nasm` so the pinned copy is used
631+
warning: the assembler for this build is the host's ('/usr/bin/nasm'), not the one this engine pins
632+
impact: two machines can assemble the same source with different assemblers, and the build records only this line
633+
hint: run `xlings install nasm` so the pinned copy is used
634634
```
635635

636636
**一个宿主工具到达构建,本身不是缺陷;一个宿主工具**静默地**到达构建才是。**

docs/zh/50-machine-output.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -365,6 +365,10 @@ mcpp why toolchain [--target <triple>] [--toolchain <spec>] --format json
365365
| `c-env-verification-mismatch` | 用实现出来的 `[c-abi]` 配置编译的探针,与声明不符 *(2026.9.18.1+)* |
366366
| `platform-dependency` | `[build] platform-dependencies = "refuse"`,而图里有包带进了平台 SDK *(2026.9.18.1+)* |
367367
| `interface-not-provided` | 某个包的 `[kernel-abi] requires-interfaces` 点名了解析出的实现不提供的接口 *(2026.9.20.1+)* |
368+
| `apple-sdk-absent` | 目标需要本机没有的 Apple SDK;它不可再分发,所以 mcpp 定位它而不安装它 |
369+
| `lld-required-absent` | 目标直接经 lld 链接,而解析出的工具链载荷不带 lld |
370+
| `host-tool-toolchain` | 交叉 `--target` 下的 `build.mcpp` 需要一个可解析的**宿主**工具链,而一个都没设 |
371+
| `std-module-precompile` | 标准库的模块在这个配置下无法预编译 |
368372
| `other` | 一处还没有被命名的拒绝分支 |
369373

370374
**其中一个令牌也由 `mcpp build` 自己打印。** `interface-not-provided` 出现在拒绝消息里,

0 commit comments

Comments
 (0)