P0~P4 가 끝났다. GOAL.md 는 계획이 아니라 무엇이 나왔는지를 적는다 -- 특히 각 항목이 어떤 fixture 로 고정됐는지. TODO 의 셀프호스팅 목록을 다시 매겼다. 다음은 리졸버를 Ferro 로이고, 도구는 다 있다: intern, map, Node.bind, Map.clear. 정해진 것에 셋을 더했다: 정수 리터럴의 타입, store 폭은 자리가 정한다는 것, 그리고 std 밖 unsafe 예산이 0 이며 run.py 가 그것을 검사한다는 것. 245/245, 38/38.
4.3 KiB
4.3 KiB
GOAL — 부트스트랩까지 남은 작업
외부 스펙 감사(v0.1.8)와, 그 항목들을 실제로 빌드해서 확인한 결과를 합친 실행 계획이었다. P0~P4 는 전부 끝났다. 감사는 문서를 읽었고 여기 적힌 것은 컴파일러에 물어본 답이다.
uv run python tests/run.py 245/245 + std 밖 unsafe/*T 예산 검사
uv run python tests/exec.py 38/38
끝난 것
P0 — 조용히 틀린 것
| # | 무엇이었나 | |
|---|---|---|
| 0-1 | 니치 옵셔널(?^T, ?&T)이 포인터가 아니라 포인터가 든 자리를 넘겼다. if let 이 컴파일되고 쓰레기를 반환했다 |
exec/optref.fe |
| 0-2 | Handle(Node){ raw: 7 } 이 파싱되지 않았다. 제네릭 인스턴스는 Self{...} 나 생성자로만 지을 수 있었다 |
exec/geninst/ |
P1 — 스펙의 빈칸
| # | 결정 | |
|---|---|---|
| 1-1 | 정수 리터럴은 문맥이 요구하는 타입, 없으면 i32. 범위를 벗어나면 거부 |
types/badlit.fe, types/oklit.fe |
| — | 그 자리에서 나온 것: store 폭이 목적지가 아니라 값에서 왔다. let b: u8 = 200; 이 4바이트를 1바이트 자리에 써서 옆 지역을 지웠다 |
exec/narrow.fe |
| 1-2 | 미사용 타입 파라미터는 정상. typed handle 이 그 모양이다 | generic/okphant.fe, badphant.fe |
| 1-3 | --no-checks 에서 오버플로는 랩어라운드로 정의된다 |
SPEC §7.4 |
| 1-4 | 작은 규칙 일곱 (SPEC §7.9). 다섯은 이미 옳았고, defer 안의 return 과 순회 중 원본 변경 둘은 그냥 통과하고 있었다 |
own/bdefret.fe, own/badforwr.fe, own/okforrd.fe |
P2 — SPEC 에서 죽은 C 백엔드 제거
.fei, --emit-c, fe_errors.h, "호스트 C 방출", "C 방출 시 static inline"
열 군데. 감사가 그 문서를 충실히 읽고 존재하지 않는 문제(C 의 부호 있는
오버플로 UB)를 보고했다 — 명세가 거짓말을 하면 그것을 읽는 사람이 틀린 답을
낸다. 오류 코드 절은 실제대로 다시 썼고, --target=/--model= 은 드라이버에서
없앴다.
P3 — stdlib
| # | ||
|---|---|---|
| 3-1 | arena.Handle(T) — 8바이트 고정. 세대·epoch·arena id 를 담아 놓아준 뒤, reset 뒤, 다른 아레나에 물으면 null |
exec/arenat.fe |
| 3-2 | arena.Arena(T) — 짧은 대여만. get 은 사본을 준다. get_mut 은 두지 않았다 |
|
| 3-3 | list.List(T) 에 pop/take/swap/slice/slice_mut/clear |
exec/listmor.fe |
| 3-4 | intern.Interner — str 을 꺼내는 API 없음. eq/len_of/hash_of/find/copy_into |
exec/interns.fe |
| 3-5 | std.map 이 StrId 를 바이트 키로 받는다. 별도 IntMap 불필요 |
길에서 고친 것 둘:
- 메서드 호출이 시그니처를 호출자 유닛에서 풀었다.
- 제네릭 인스턴스를 필드로 담은 구조체가 크기 0 으로 굳었다 — 짓는 중인
인스턴스가 배치되어 버려서.
Holder가 36 바이트 대신 12 였다.
P4 — 측정
--report-unsafe, --report-instances. 그리고 그 숫자를 run.py 가 검사한다:
std.mem/std.sys 밖의 unsafe 와 *T 는 0 이어야 하고, 늘어나면 빌드가
실패한다.
unit unsafe *T unchecked
std.io 6 1 0
std.sys 10 12 0
total 16 13 0
outside std 0 0 0
채택하지 않은 것
| 감사 항목 | 판정 | 근거 |
|---|---|---|
참조 튜플 -> (&mut T, &mut T) |
보류 | struct 는 필드 단위 대여로 풀렸다. 컨테이너의 두 원소만 남는데 렉서·파서에서 필요했던 자리가 0 번 |
Arena.get_mut -> ?&mut T |
거부 | 아레나 하나를 통째로 잠그는 참조를 오래 들고 있게 하는 API |
fmt.fmt_strid |
거부 | @print 가 interner 인스턴스에 닿을 수 없다 (R10) |
별도 IntMap(V) |
불필요 | std.map 이 이미 포괄 |
| C 오버플로 방출 규칙 | 대체 | C 백엔드가 없다. 결론만 1-3 으로 흡수 |
| R4 완화 | 영구 제외 |
다음
부트스트랩까지 남은 것은 TODO.md 에 있다. 도구는 다 갖췄다 — 아레나, 핸들,
맵, interner, 그리고 리졸버가 쓸 Node.bind 와 Map.clear.