되돌리기 비싼 결정 중 마지막 하나 — incremental 아키텍처 — 를 코드와
테스트로 닫는다.
- iface.ml: exported surface 추출과 해시. 별칭 한정(qualify)은 소비 시점에만
일어나므로 가져오는 쪽의 별칭이 정의 모듈의 hash에 새지 않는다.
- session.ml: 모듈 로딩과 고정점 전파. hash 비교가 dependents 재검사보다
앞선다 — 이 순서가 "본문만 수정 시 downstream 0건"의 전부다.
- 한정 이름(Alias.Type, Alias.Ctor, Alias.fn)을 타입 검사, 패턴, 소진성,
move 검사가 모두 하나의 키("Alias.name")로 본다.
- 패키지 경로(cool.dev/std/list)는 v0에서 해소하지 않고 불투명하게 둔다.
없다고 말하지 않는다.
- 회귀 테스트: 본문만 고치면 자기 자신만 재검사(1건), variant를 추가하면
downstream까지 전파되고 실제로 소진성이 깨진다(2건).
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019ZVDeU6KLuUVL3gs18Hm3E
철학 1이 나열한 다섯 항목 중 비어 있던 자리를 채운다. Maranget의 usefulness
알고리즘으로 반례를 만들어 "빠진 경우"를 이름으로 말한다 — 중첩된 자리의
반례도 찾는다(Some(Rect(_, _))).
이 검사가 왜 지금 필요한가: interface hash가 enum 정의 본문을 입력으로 삼는
이유가 바로 이것이다. upstream에 variant가 하나 늘면 downstream의 match가
깨져야 하는데, 검사가 없으면 깨질 것이 없다. 다음 마일스톤(모듈 경계를 넘는
재검사)의 핵심 시나리오가 여기에 걸려 있다. 테스트로 그 시나리오를 직접
고정했다 — 같은 코드가 variant 둘일 때는 통과하고 셋이 되면 깨진다.
구현 중 한 번 틀렸다. 리터럴 패턴을 와일드카드로 줄였더니 Int 리터럴 두 개로
match가 완전해져 버렸다. 리터럴은 인자 없는 생성자이고, 타입의 생성자 집합이
무한하므로 리터럴만으로는 결코 완전해지지 않는다.
생성자 집합을 알 수 없는 타입(외부 타입, 미지수)은 검사하지 않는다.
모르는 것을 위반이라고 말하지 않는다.
definite init은 문법이 이미 보장한다는 것을 문서에 적었다 — let이 항상
초기화식을 요구하므로 별도 검사가 필요 없다.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019ZVDeU6KLuUVL3gs18Hm3E