modules: 모듈 경계, interface hash, 그리고 고정점 invalidation

되돌리기 비싼 결정 중 마지막 하나 — 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
This commit is contained in:
2026-08-30 14:11:30 +09:00
co-authored by Claude Opus 5
parent 5218bc59a7
commit 801a7b330b
12 changed files with 646 additions and 54 deletions
+83 -31
View File
@@ -26,6 +26,9 @@ type env = {
fns : (string, scheme) Hashtbl.t;
consts : (string, T.t) Hashtbl.t;
ctors : (string, string) Hashtbl.t; (* variant -> enum *)
(* 가져온 모듈의 별칭. `Alias.x`는 "Alias.x"라는 하나의 키로 찾는다 —
별칭은 식별자에 쓸 수 없는 점(.)을 포함하므로 지역 이름과 충돌하지 않는다. *)
aliases : string list;
mutable locals : (string * T.t) list list;
mutable ret : T.t; (* 현재 함수의 선언된 반환 타입 *)
(* 현재 본문이 수행한 effect. 위치를 같이 들고 다녀야 "어디서 수행했는지"를
@@ -71,15 +74,16 @@ let conv_eff_result (atoms : Ast.eff_result) : T.eff =
let rec conv env (gen : string list) (t : Ast.ty) : T.t =
match t with
| T_named { name; args; _ } -> (
| T_named { modl; name; args; _ } -> (
let args =
List.filter_map
(function TA_ty t -> Some (conv env gen t) | TA_eff _ -> None)
args
in
if List.mem name gen then T.TVar name
let name = match modl with Some a -> a ^ "." ^ name | None -> name in
if modl = None && List.mem name gen then T.TVar name
else
match name with
match if modl = None then name else "" with
| "Int" -> T.TInt
| "Bool" -> T.TBool
| "String" -> T.TString
@@ -491,33 +495,59 @@ and infer_field env obj name pos =
(Printf.sprintf
"capability %s의 메서드는 값을 통해서만 부를 수 있습니다 (%s를 파라미터로 받아야 합니다)" n n)
| _ -> ());
let t = infer env obj in
match T.resolve t with
| T.TUnknown -> T.TUnknown
| T.TCon (cname, args) -> (
match Hashtbl.find_opt env.structs cname with
| Some (gen, fields) -> (
let sub = List.map2 (fun v a -> (v, a)) gen (adjust gen args) in
match List.assoc_opt name fields with
| Some ft -> T.subst sub [] ft
| None ->
err env pos (Printf.sprintf "%s에 %s 필드가 없습니다" cname name);
T.TUnknown)
match obj with
| E_ident (a, _) when lookup env a = None && List.mem a env.aliases -> (
(* 모듈 별칭을 통한 접근. 가져온 표면은 "Alias.name" 키로 들어와 있다. *)
let key = a ^ "." ^ name in
let unknown () =
err env pos (Printf.sprintf "%s에 %s이(가) 없습니다" a name);
T.TUnknown
in
match Hashtbl.find_opt env.consts key with
| Some t -> t
| None -> (
match Hashtbl.find_opt env.caps cname with
| Some methods -> (
match List.assoc_opt name methods with
| Some s ->
let params, eff, ret = instantiate s in
T.TFn { affine = false; params; eff; ret }
match Hashtbl.find_opt env.fns key with
| Some s ->
let params, eff, ret = instantiate s in
T.TFn { affine = false; params; eff; ret }
| None -> (
match Hashtbl.find_opt env.ctors key with
| Some enum -> (
match Hashtbl.find_opt env.enums enum with
| Some (_, variants)
when List.assoc_opt key variants = Some [] ->
nullary_ctor env enum key
| Some _ -> ctor_fn env enum key
| None -> T.TUnknown)
| None -> unknown ())))
| _ -> (
let t = infer env obj in
match T.resolve t with
| T.TUnknown -> T.TUnknown
| T.TCon (cname, args) -> (
match Hashtbl.find_opt env.structs cname with
| Some (gen, fields) -> (
let sub = List.map2 (fun v a -> (v, a)) gen (adjust gen args) in
match List.assoc_opt name fields with
| Some ft -> T.subst sub [] ft
| None ->
err env pos
(Printf.sprintf "capability %s에 %s 메서드가 없습니다" cname name);
err env pos (Printf.sprintf "%s에 %s 필드가 없습니다" cname name);
T.TUnknown)
| None -> T.TUnknown))
| other ->
err env pos (Printf.sprintf "%s에는 필드가 없습니다" (T.show other));
T.TUnknown
| None -> (
match Hashtbl.find_opt env.caps cname with
| Some methods -> (
match List.assoc_opt name methods with
| Some s ->
let params, eff, ret = instantiate s in
T.TFn { affine = false; params; eff; ret }
| None ->
err env pos
(Printf.sprintf "capability %s에 %s 메서드가 없습니다" cname name);
T.TUnknown)
| None -> T.TUnknown))
| other ->
err env pos (Printf.sprintf "%s에는 필드가 없습니다" (T.show other));
T.TUnknown)
and adjust gen args =
let n = List.length gen in
@@ -567,7 +597,8 @@ and check_pattern env (scrutinee : T.t) (p : pattern) =
| Some enum ->
check_ctor env scrutinee enum n [] Token.{ line = 0; col = 0 }
| None -> bind env n scrutinee)
| P_ctor { name; args; pos } -> (
| P_ctor { modl; name; args; pos } -> (
let name = match modl with Some a -> a ^ "." ^ name | None -> name in
match Hashtbl.find_opt env.ctors name with
| Some enum -> check_ctor env scrutinee enum name args pos
| None -> List.iter (check_pattern env T.TUnknown) args)
@@ -671,7 +702,7 @@ let check_fn env (d : fn_decl) =
env.performed <- [];
pop env
let check (m : modul) : error list =
let check ?(imports : item list = []) (m : modul) : error list =
let env =
{
structs = Hashtbl.create 16;
@@ -680,6 +711,27 @@ let check (m : modul) : error list =
fns = Hashtbl.create 16;
consts = Hashtbl.create 16;
ctors = Hashtbl.create 16;
(* 표면을 실제로 받아온 별칭만 안다. 해소되지 않은 모듈(예: 아직 가져오지
못한 패키지)의 별칭은 모르는 것이므로 그 아래 이름을 틀렸다고 말하지
않는다. *)
aliases =
List.sort_uniq compare
(List.filter_map
(fun it ->
let n =
match it with
| I_fn { decl; _ } -> decl.fn_name
| I_struct { name; _ }
| I_enum { name; _ }
| I_capability { name; _ }
| I_const { name; _ } ->
name
| _ -> ""
in
match String.index_opt n '.' with
| Some i -> Some (String.sub n 0 i)
| None -> None)
imports);
locals = [];
ret = T.TUnit;
performed = [];
@@ -698,7 +750,7 @@ let check (m : modul) : error list =
List.iter (fun v -> Hashtbl.replace env.ctors v.v_name name) variants
| I_capability { name; _ } -> Hashtbl.replace env.caps name []
| _ -> ())
m.items;
(imports @ m.items);
(* 2차: 본문을 채운다 *)
List.iter
(fun it ->
@@ -722,7 +774,7 @@ let check (m : modul) : error list =
| I_const { name; ty; _ } ->
Hashtbl.replace env.consts name (conv env [] ty)
| _ -> ())
m.items;
(imports @ m.items);
(* 3차: 본문 검사 *)
List.iter
(fun it ->