Files
coolguyandClaude Opus 5 78ef07d2ee panic: panic/Never와 내장 테스트 — 문법을 먼저 고치고 대조 장치가 파서를 지적했다
순서가 요점이다. 문법에 test_decl과 panic_expr을 넣고 파서는 안 고친 채로
대조 장치를 돌렸더니 즉시 잡혔다:

  문장 300개 중 파서가 거부한 것 140개
  [1] 선언 (fn, struct, enum, capability, const)이(가) 필요합니다 — test 발견

파서를 따라가게 하니 다시 0건. 문법과 구현이 어긋나는 상태가 관측 가능한
것이 되었다는 뜻이다.

panic:
- 키워드다. prelude가 없어 함수로 두면 쓸 때마다 import해야 한다
- effect가 아니다. 경계 검사 하나에 {Panic}이 호출자 전부로 전염되면
  effect 절은 신호가 아니라 잡음이 된다
- Never는 어떤 타입 자리에도 놓인다. 없으면 panic을 match 팔에서 못 쓴다
- 언어 수준 recover 없음. 되감기 없음. 자원 해제 여부는 열어둔다
- 0으로 나누기, assert 실패가 이 하나로 모인다

test:
- 파라미터가 없어 capability를 받을 수 없고, 만들 문법도 없다. 그래서
  effect-free임이 증명된다 — 관례가 아니라 검사다. 시험해 보니 실제로
  "테스트는 effect를 수행할 수 없습니다"로 거부한다
- 일반 코드와 같은 타입/effect/move 검사를 받는다
- interface hash에서 제외 — 테스트를 고쳤다고 downstream이 재검사되면 안 된다
- 격리는 런타임의 일이다. 하나가 죽어도 나머지는 돈다

assert는 std/test.cool에 coollang으로 쓰였다 — panic 위의 설탕임이 코드로
보이고, std에서 본문이 있는 첫 함수가 됐다. 그 바람에 std/런타임 양방향
테스트가 걸렸고(본문 있는 함수에 런타임 구현을 요구했다), 그 구분을 넣었다.

samples/app/config.cool에 첫 테스트 넷.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019ZVDeU6KLuUVL3gs18Hm3E
2026-08-30 17:51:56 +09:00

198 lines
6.9 KiB
OCaml

(* interface artifact와 그 해시.
hash 입력 = 모듈 exported surface 전체의 의미적 정규형이다 (문서 P7).
함수 시그니처(effect 포함), 타입 정의 본문(struct 필드, enum variant),
타입의 affinity, 상수의 타입과 값, capability 선언, 그리고 reexport된
선언을 완전히 해소한 정의 본문.
원칙: downstream 검사 결과에 영향을 줄 수 있는 모든 것을 포함한다.
의심스러우면 넣는다 — 과잉 포함의 비용은 재검사지만 누락의 비용은
잘못된 캐시라는 비대칭이 있다.
함수 본문은 들어가지 않는다. 본문 한 줄 수정이 해시를 흔들면 incremental
전제가 무너진다. *)
open Ast
type t = { items : item list; (* 본문을 벗긴 exported surface *) hash : string }
let strip_body (d : fn_decl) = { d with fn_body = None }
let is_exported = function
| I_fn { pub; _ } -> pub
| I_struct { pub; _ } -> pub
| I_enum { pub; _ } -> pub
| I_capability { pub; _ } -> pub
| I_const { pub; _ } -> pub
| I_reexport _ -> true
(* 테스트는 표면이 아니다. 테스트를 고쳤다고 downstream이 재검사되면
안 된다 — 함수 본문과 같은 이유다 *)
| I_test _ -> false
| I_import _ -> false
let strip = function
| I_fn { pub; decl } -> I_fn { pub; decl = strip_body decl }
| it -> it
let item_name = function
| I_fn { decl; _ } -> decl.fn_name
| I_struct { name; _ } -> name
| I_enum { name; _ } -> name
| I_capability { name; _ } -> name
| I_const { name; _ } -> name
| I_reexport { name; _ } -> name
| I_test { name; _ } -> name
| I_import { alias; _ } -> alias
(* reexport는 이름이 아니라 해소된 정의 본문이 hash에 들어간다.
A의 enum에 variant가 추가되면 B의 소스가 그대로여도 B의 hash가 변하고,
C의 exhaustive match가 재검사된다. *)
let surface (m : modul) : item list =
let is_definition = function
| I_reexport _ | I_import _ -> false
| _ -> true
in
let find name =
List.find_opt (fun it -> is_definition it && item_name it = name) m.items
in
List.concat_map
(fun it ->
match it with
| I_reexport { name; _ } -> (
match find name with Some d -> [ strip d ] | None -> [])
| it when is_exported it && is_definition it -> [ strip it ]
| _ -> [])
m.items
(* 정규형: 항목을 이름순으로 정렬해 선언 순서가 해시에 새지 않게 한다.
소스에서 함수 둘의 위치를 바꾸는 것은 downstream에 아무 영향이 없다. *)
let render (items : item list) : string =
items |> List.map show_item |> List.sort compare |> String.concat "\n"
let of_module (m : modul) : t =
let items = surface m in
{ items; hash = Digest.to_hex (Digest.string (render items)) }
(* ------------------------------------------------------------------ *)
(* 소비 측 한정 *)
(* ------------------------------------------------------------------ *)
(* 가져온 모듈의 exported surface를 소비 측 이름 공간으로 옮긴다.
`import "shapes" as Shapes`라면 Shape는 "Shapes.Shape"가 된다.
왜 소비 시점인가 — 별칭은 가져오는 쪽의 선택이므로 정의한 모듈의
interface hash에 새어서는 안 된다. of_module은 한정하지 않은 표면을
해시하고, 한정은 여기서만 한다.
v0의 한계 두 가지, 의도적으로 남긴다:
- 가져온 모듈이 다시 다른 모듈의 타입을 참조하면(전이 참조) 불투명해진다.
- effect atom의 capability 이름은 한정하지 않는다. 즉 effect 이름은 v0에서
전역이다. 모듈별 identity는 v1 과제다. *)
let builtin_ty_names =
[ "Int"; "Bool"; "String"; "Unit"; "List"; "Option"; "Result"; "TaskScope" ]
let opaque pos = T_named { modl = None; name = "«외부»"; args = []; pos }
let rec q_ty alias defined gen (t : ty) : ty =
match t with
| T_named { modl = Some _; pos; _ } -> opaque pos
| T_named { modl = None; name; args; pos } ->
let args = List.map (q_targ alias defined gen) args in
if List.mem name gen || List.mem name builtin_ty_names then
T_named { modl = None; name; args; pos }
else if List.mem name defined then
T_named { modl = None; name = alias ^ "." ^ name; args; pos }
else opaque pos
| T_fn { affine; params; eff; ret; pos } ->
T_fn
{
affine;
params = List.map (q_ty alias defined gen) params;
eff;
ret = Option.map (q_ty alias defined gen) ret;
pos;
}
and q_targ alias defined gen = function
| TA_ty t -> TA_ty (q_ty alias defined gen t)
| TA_eff e -> TA_eff e
let q_fn alias defined (d : fn_decl) : fn_decl =
let gen = List.map (fun g -> g.gp_name) d.fn_gen in
{
d with
fn_params =
List.map
(fun p -> { p with p_ty = q_ty alias defined gen p.p_ty })
d.fn_params;
fn_ret = Option.map (q_ty alias defined gen) d.fn_ret;
fn_body = None;
}
let defined_names (items : item list) =
List.filter_map
(function
| I_struct { name; _ } | I_enum { name; _ } | I_capability { name; _ } ->
Some name
| _ -> None)
items
let qualify (alias : string) (items : item list) : item list =
let defined = defined_names items in
let p n = alias ^ "." ^ n in
List.map
(fun it ->
match it with
| I_fn { pub; decl } ->
I_fn
{
pub;
decl = { (q_fn alias defined decl) with fn_name = p decl.fn_name };
}
| I_struct { pub; copyable; name; gen; fields; pos } ->
let g = List.map (fun x -> x.gp_name) gen in
I_struct
{
pub;
copyable;
name = p name;
gen;
fields =
List.map
(fun f -> { f with f_ty = q_ty alias defined g f.f_ty })
fields;
pos;
}
| I_enum { pub; name; gen; variants; pos } ->
let g = List.map (fun x -> x.gp_name) gen in
I_enum
{
pub;
name = p name;
gen;
variants =
List.map
(fun v ->
{
v with
v_name = p v.v_name;
v_args = List.map (q_ty alias defined g) v.v_args;
})
variants;
pos;
}
| I_capability { pub; name; methods; pos } ->
I_capability
{
pub;
name = p name;
methods = List.map (q_fn alias defined) methods;
pos;
}
| I_const { pub; name; ty; value; pos } ->
I_const
{ pub; name = p name; ty = q_ty alias defined [] ty; value; pos }
| it -> it)
items