typecheck: 타입 검사기와 scope 부모 문법 확정
세 가지를 자율 결정으로 닫고 타입 검사까지 세웠다.
1. scope 부모 문법: scope 자식 = 부모 { ... }로 확정. 부모를 적지 않으면
자식의 부모가 "가장 가까운 스코프"가 되는데 그것이 정확히 ambient
authority다. 권한 사슬이 main의 루트 TaskScope부터 끊기지 않으려면 모든
자식이 부모를 이름으로 지목해야 한다. 이름 해소가 이 구멍을 잡아준 건이라,
주석 처리했던 중첩 예제를 되살렸다.
2. prelude: 타입 이름은 그 타입에 딸린 함수의 이름공간이다(String.len,
File.close). 정적 메서드를 위한 별도 문법을 두지 않는다.
3. 타입 검사: 이 모듈 안에서 아는 것만 검사한다. 외부 이름은 TUnknown이
되어 무엇과도 맞는다 — 모르는 것을 틀렸다고 말하지 않기 위해서다.
제네릭 해소는 호출 지점의 지역 unification이고 함수 하나를 넘지 않는다.
클로저 파라미터 타입은 기대 타입에서 읽어온다(양방향 검사, 로컬).
단계 소유권을 하나 정정했다. affinity는 타입 동등성의 일부가 아니다. 값이
affine인지는 무엇을 capture했는지로 정해지는 substructural 성질이고
move/affinity 검사가 소유한다. 타입 검사가 이걸 판정하려다 정당한 코드를
거부하는 것을 06에서 확인하고 unify에서 분리했다.
unify 버그 하나: 같은 미지수끼리 unify할 때 occurs check가 자기 자신을
발견해 실패하고 있었다. 02의 fold 호출에서 잡혔다.
09_type_errors.cool 추가 — 외부 타입이 하나도 없어 검사기가 TUnknown으로
빠져나갈 구석이 없는 파일이다. 18개 진단이 전부 잡히고, 첫 오류에서 멈추지
않고 모두 보고한다.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019ZVDeU6KLuUVL3gs18Hm3E
This commit is contained in:
+4
-1
@@ -197,7 +197,10 @@ if_expr = "if" , expr_ns , block , [ "else" , ( block | if_expr ) ] ;
|
||||
match_expr = "match" , expr_ns , "{" , { arm } , "}" ;
|
||||
arm = pattern , "=>" , ( expr | block ) , { NEWLINE } ,
|
||||
[ "," , { NEWLINE } ] ;
|
||||
scope_expr = "scope" , ident , block ;
|
||||
scope_expr = "scope" , ident , "=" , ident , block ;
|
||||
(* scope 자식 = 부모 { ... }
|
||||
* 부모를 구문에 적는다. 적지 않으면 자식의 부모가 "가장 가까운 스코프"가 되어
|
||||
* 정확히 ambient authority가 된다 — 이 언어가 배제하는 것 *)
|
||||
|
||||
(* expr_ns = struct_lit로 시작하지 않는 expr.
|
||||
* if/match/scope의 머리 자리에서 "{"가 블록의 시작인지 struct 리터럴인지
|
||||
|
||||
+13
-6
@@ -119,13 +119,15 @@ Affinity 전이 (보안 주장의 필수 전제):
|
||||
※ v0는 affine 미소비(누수)를 허용하므로 handle 기반 보장은 애초에 불가능하다.
|
||||
실행 의미로 옮겨야 그 허점이 사라진다
|
||||
- 루트 TaskScope는 main에 주입 → ambient authority 없이 권한 사슬이 main부터 닫힌다
|
||||
- scope는 이름을 갖는다: scope sc { sc.spawn(...) }. 암묵 바인딩은 채택하지 않는다
|
||||
※ 중첩 시 결정적이다. scope outer { scope inner { ... } }에서 안쪽 블록이
|
||||
바깥 스코프에 spawn하는 것(수명이 다른 태스크를 의도적으로 부모에 붙이는
|
||||
정당한 패턴)이 암묵 바인딩으로는 표현 불가이거나 섀도잉 규칙이라는
|
||||
새 복잡성을 부른다. 이름이 있으면 outer.spawn / inner.spawn으로 그냥 갈린다
|
||||
- scope는 자식을 만들고 부모를 구문에 적는다: scope inner = outer { ... }
|
||||
※ 부모를 적지 않으면 자식의 부모가 "가장 가까운 스코프"가 되는데, 그것이
|
||||
정확히 ambient authority다. 권한 사슬이 main의 루트 TaskScope부터
|
||||
끊기지 않고 이어지려면 모든 자식이 부모를 이름으로 지목해야 한다
|
||||
※ 중첩이 결정적이다. 안쪽 블록에서 바깥 스코프에 붙이는 것(수명이 다른
|
||||
태스크를 의도적으로 부모에 붙이는 정당한 패턴)이 outer.spawn /
|
||||
inner.spawn으로 그냥 갈린다
|
||||
※ "어느 스코프에 붙는 태스크인가"가 리뷰어 눈에 보이는 것 자체가 이 언어가
|
||||
파는 물건이다. 축약형은 체감 불만이 실제로 쌓이면 v1에서 검토
|
||||
파는 물건이다. 타이핑을 줄이려고 여기서 깎으면 팔 물건이 없어진다
|
||||
|
||||
Effect 다형성 (철학 2,5에서 파생):
|
||||
- effect 변수는 타입 파라미터와 정확히 같은 규율을 따른다:
|
||||
@@ -243,6 +245,8 @@ Generics (철학 2에서 파생):
|
||||
※ 가드가 붙는 순간 그 분기가 패턴 전체를 덮는다고 말할 수 없어 exhaustiveness가
|
||||
흐려지고, SMT 없이는 보수적으로 _ 분기를 강요하게 된다. 철학 1의 대표 항목을
|
||||
문법 편의와 바꾸지 않는다. 필요하면 분기 본문에서 if를 쓴다
|
||||
- 타입 이름은 그 타입에 딸린 함수의 이름공간이다: String.len(s), File.close(f).
|
||||
별도의 정적 메서드 문법을 두지 않는다
|
||||
- ?는 Result 전용으로 하드코딩한다 (trait solver가 없으므로 일반화 경로가 없다)
|
||||
※ 조기 탈출도 블록 종료이므로 scope의 join/cancel은 ? 경로에서도 실행된다
|
||||
※ v1에서 linear를 넣으면 ?의 조기 탈출 경로마다 해제가 필요해진다.
|
||||
@@ -261,6 +265,9 @@ Generics (철학 2에서 파생):
|
||||
|
||||
포함 (되돌리기 비싼 것 전부):
|
||||
- parse → name resolution → type check → effect/capability check
|
||||
※ 각 단계는 그 단계가 소유한 성질만 판정한다. 예: affinity는 타입 동등성이
|
||||
아니라 substructural 성질이므로 타입 검사가 아니라 move 검사가 소유한다.
|
||||
단계가 서로의 결론을 앞지르면 진단이 엉뚱한 곳에서 난다
|
||||
- move/affinity 검사, capability use 규칙, affinity 전이
|
||||
- effect 변수 (effect 다형성)
|
||||
- interface artifact + hash 기반 incremental invalidation
|
||||
|
||||
Reference in New Issue
Block a user