마커가 없으면 러너는 '거부되기만 하면 통과'로 판정한다. 엉뚱한 이유로
거부돼도 초록이었다. fixture 별로 무엇을 검사하는지 읽고 지금 나오는 진단이
그 규칙을 짚는지 확인한 뒤 줄과 문구를 고정했다. 근거는 fixture-report.md에
있다.
pin 58 -> 94. 통과 수는 150/188 그대로다.
밑줄만 제거하고 충돌한 이름에 숫자를 붙인 결과라 이름이 무엇을 검사하는지
오히려 덜 드러낸다. own/badfld 와 types/badfld 가 서로 다른 것을 검사하는데
둘 다 badfmem 이 된 것이 그 증거다. 파일을 열어 판단하는 작업이므로
마커 판정과 함께 다시 한다.
Units start here, with the part that needs no import graph: what a unit is
called and where it must live.
The parser only ever read a single identifier after `unit` and `import`, so
`unit game.main;` and `import std.io;` were syntax errors -- which is why the
dotted fixtures failed at the semicolon. It now reads a dotted path and stores
it canonically, dots included, since that spelling is the unit's identity
everywhere else. `import a.b as c;` parses too, with the alias on the node.
resolve.c is the new pass between parsing and checking, for the questions that
span files. It carries SPEC 8.1 so far: each path segment is ASCII lowercase,
starts with a letter, continues with letters, digits or underscore, and is at
most eight characters; and the dotted path must match the source path it was
read from, so game.world.map has to come from game/world/map.fe. The source
side is folded to lowercase before comparing, because a case-insensitive host
must not let two spellings become two units.
That rule then applied to the fixtures, which were not obeying it: 57 declared
a unit name unrelated to their file, left over from the milestone directories,
and eight had names too long to be legal. Both are now aligned -- the rule is
worth having only if the tree follows it.
units: badupper, badlong and unitbad pass. 138 -> 146 of 188. The rest of
units/ needs the import graph, which is the next piece: resolution, cycles,
bindings and visibility.
Checking the markers for the first time found five disagreements in areas that
are implemented. Four were the marker's fault:
- own/badarg pinned "self", but the rule being broken is that a returned
reference must derive from a parameter -- `self` has nothing to do with it.
- own/badbrmov pinned line 9, which is the closing brace; the second destroy is
on line 8.
- own/badloop pinned line 8, the destroy after the loop. The diagnostic is on
line 6, inside it, and line 6 is right: the second iteration moves the same
value again, so the loop body is where it is caught. Whoever wrote the marker
expected the error after the loop.
- optional/badcatch pinned line 14, the body of the catch block. The catch
expression on line 13 is what cannot fall through.
The fifth was the compiler's. own/badweak assigns a `&mut i32` to a `&i32` and
got "initializer type mismatch", which says nothing about why. Weakening an
exclusive borrow to a shared one is a specific rule and now says so, for
references and slices alike.
own/ is fully green: 50/50. Overall 133 -> 138 of 188. The six remaining marker
disagreements are all under units/ and generic/, where nothing is implemented
yet, so there is no diagnostic to compare against and no way to tell whether
the marker is right.
The milestone structure had stopped describing the compiler and started
shaping it: m7.c, check_m7.c, tests/m2..m9, and a checker and emitter that had
each grown past 2,500 lines because there was nowhere else to put anything.
Restart from the pipeline instead.
What is left is the front end -- lexer, parser, types, ownership, semantic
analysis -- and the fixtures that describe it. The C backend, the DOSBox-X
runner, the milestone registry and the batch build are removed. The driver now
stops after semantic analysis; a code generator attaches where emit_c did.
Fixtures move from milestone directories to what they check:
parse/ grammar own/ ownership and borrowing
types/ type rules optional/ optionals and error unions
format/ formatting, try units/ units and visibility
generic/ generics pending-backend/
pending-backend/ holds the three fixtures that can only be checked by running
a program -- that the bounds check traps, that --no-checks removes it, and that
drops and defers actually fire, verified through a fake allocator. Those are
not front-end tests and are not pretending to be; they come back first when
there is a code generator.
tests/run.py replaces the DOSBox-X harness. It builds the front end with the
pinned Watcom's Windows-hosted driver and runs every fixture in about two
seconds, and it does something the old runner structurally could not: it reads
the `// ERROR:line:text` marker each fixture carries and checks the diagnostic
against it. Those markers have been in the tree all along, unverified, because
DOS could not redirect the compiler's stderr and only the exit code was ever
compared.
133/188 pass. The 55 failures are not regressions -- they are what was already
true and invisible:
- units (27) and generic (23): `import`, `comptime` and generic declarations
parse and are then dropped on the floor. No pass looks at them. The old
registry did not list these fixtures at all, so nothing said so.
- five in own/, optional/ and generic/: a marker disagrees with the diagnostic
about the line or the wording. Each is either a wrong marker or a wrong
diagnostic and has to be read individually.
Everything removed is in git history.