Commit Graph
3 Commits
Author SHA1 Message Date
coolguy f7e667652e 대여는 변수가 아니라 place 단위다
p.a 와 p.b 는 서로 다른 자리인데 한쪽을 대여하면 다른 쪽까지 잠겼다. 메서드가
하는 일의 대부분이 한 필드에 쓰면서 다른 필드를 읽는 것이라, std.map 의 keep 은
그것 때문에 함수 둘로 갈라져 있었고 파서도 같은 자리에서 걸렸다.

FeOwnState 가 필드별 칸을 넷 갖는다. 값으로 복사되는 구조체라 흐름 병합과
스냅샷은 손댈 것이 없었다. 전체를 대여하면 모든 필드와 충돌하고, 필드를
대여하면 전체 및 같은 필드와 충돌한다. 칸이 모자라면 전체 대여로 되돌아가
더 많이 보고할 뿐 놓치지 않는다.

읽기는 식별자에서 일어나는데 그 자리에서는 자기가 무엇의 밑동인지 알 수 없다.
그래서 투영이 내려가는 길에 어느 필드인지 적어두고 식별자가 그것을 집는다.
인덱스는 갈라지지 않는다 -- xs[i] 의 i 는 상수가 아닐 수 있고, 필드 이름은
상수다.

길에서 나온 것: mem.replace 가 목적지 대여를 가져가고 돌려주지 않았다. 일반
호출 인자는 문장 끝에 돌려주는데 intrinsic 경로에만 그것이 없었다. 전에는
그 자리가 어차피 거부돼서 드러나지 않았다.

  var p = Pair{ a: 1, b: 2 };
  let r = &mut p.a;
  p.b = 3;      // ok -- 전에는 에러
  p.a = 3;      // 에러
  take(p);      // 에러

SPEC §5 R6 을 고쳤고, 옛 규칙을 그대로 적어둔 문단과 예제를 갈아치웠다.
own/badrfld 는 이제 허용되는 코드였으므로 같은 필드를 건드리도록 다시 겨눴고
okrfld·badrall·badrsame·exec/fieldbrw 를 더했다.

228/228, 32/32.
2026-08-17 15:29:12 +09:00
coolguy e84f892147 Ferro 파서를 Ferro 로, 그리고 그것이 드러낸 네 가지
렉서 다음은 파서다. 노드는 한 배열에 살고 자식은 그 안의 인덱스다 -- 노드는
^Node 를 들 수 없고(여럿이며 한 번씩 소유하지 않는다) &Node 도 들 수 없다(R4).
인덱스는 둘 다 아니다. 소스도 필드가 아니라 매 단계에 같이 다닌다.

  unit demo / fn answer @2 / let n = (+ 1 (* 2 3)) / return n / balanced

전위 표기로 다시 찍는 것이 시험의 요점이다. 1 + 2 * 3 이 어떻게 묶였는지는
그렇게만 보인다.

쓰면서 나온 컴파일러 버그 넷:

1. 다른 유닛의 타입을 필드로 쓰면 그 필드 타입이 영영 UNKNOWN 이었다. 필드
   해석이 유닛마다 선언 직후에 돌아서, 아직 선언되지 않은 유닛의 타입을 찾다
   실패하고 그 답을 굳혔다. 이제 모든 유닛이 선언을 마친 뒤에 한 번 푼다.

2. 그리고 그 해석은 타입을 선언한 유닛에서 해야 한다. 필드 타입은 그 유닛의
   import 로 쓰였는데 아무 유닛에서나 풀고 있었다. 타입 계층에 enter/leave
   콜백을 두고 체커가 그 자리로 데려간다.

3. cycle_state 를 재귀 검사와 크기 계산이 같이 썼다. 첫 번째가 보는 중인 구조체
   가 두 번째에게는 다 끝난 것으로 보여서, 필드가 하나뿐인 것처럼 1 바이트로
   자리를 잡았다 -- Parser 가 그래서 자기 토큰을 밟았다. layout_state 로 나눴다.

4. 다른 유닛의 상수(ast.NONE)를 lowering 이 필드 접근으로 봤다. 체커가 이미
   링크 이름을 붙여두었으니 그것이 있으면 전역이다.

그리고 R1 을 실제로 지키게 했다: 소유자를 놓으면 그것이 가진 것도 놓는다.
전에는 자기 drop 이 있거나 자기가 owned 일 때만이어서, drop 을 가진 타입을
필드로 담은 구조체는 그것을 놓을 방법이 없었다(drop 은 손으로 못 부른다).
이제 release_at 이 drop 을 부르고 필드로 내려간다. 그 덕에 List/Arena/Map 의
drop 이 전부 필요 없어져서 지웠다 -- 버퍼가 owned 이니 R1 이 알아서 한다.

221/221, 29/29.
2026-08-17 12:49:53 +09:00
coolguy 390345ef85 refactor: check.c 와 lower.c 를 사람이 머리에 담을 크기로 나눈다
check.c 3,937 줄, lower.c 1,913 줄이었다. 가장 큰 파일이 978 줄이 됐다.

           check.c     679   스코프·심볼·흐름·소유권 접착
           checkexp.c  739   포매팅 검사와 표현식
           checkstm.c  635   문장, 함수, 메서드
           checkgen.c  618   제네릭 실체화
           checkcal.c  978   유닛 경계 호출과 옵셔널/에러 유니온
           checkpro.c  184   선언 패스와 프로그램

           lower.c     567   타입·슬롯·지역·블록·mem.*
           lowerprn.c  238   포매팅 빌트인 전개
           lowerexp.c  468   표현식
           lowerstm.c  543   문장·함수·프로그램

줄 범위로 잘랐다. 주제별로 묶는 것보다 정확한데, 한 줄도 잃거나 겹치지
않기 때문이다. 파일 순서가 이미 단계를 따라가서 경계가 실제 이음매에 떨어진다.

모든 정의가 static 을 잃고 비공개 헤더에 프로토타입을 갖는다. 대안 --
static 을 유지하고 #include 로 텍스트만 나누는 것 -- 은 결합을 보여주는 대신
숨긴다.

두 스위트 그대로: 209/209, 21/21.
2026-08-17 07:04:35 +09:00