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:
+83
-31
@@ -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 ->
|
||||
|
||||
Reference in New Issue
Block a user