From 2307bafda29c1927205cfe418adc4a2dbd4b42ca Mon Sep 17 00:00:00 2001 From: coolguy Date: Sun, 30 Aug 2026 18:21:05 +0900 Subject: [PATCH] =?UTF-8?q?naming:=20panic=EC=9D=84=20crash=EB=A1=9C=20?= =?UTF-8?q?=E2=80=94=20=EA=B7=B8=EB=A6=AC=EA=B3=A0=20=EC=9D=B4=EB=A6=84?= =?UTF-8?q?=EC=9D=84=20=EA=B3=A0=EB=A5=B4=EB=8A=94=20=EC=9B=90=EC=B9=99?= =?UTF-8?q?=EC=9D=84=20=EC=B2=A0=ED=95=99=EC=97=90=20=EB=84=A3=EB=8A=94?= =?UTF-8?q?=EB=8B=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 철학 6번을 추가했다: 이름은 관례가 아니라 뜻에서 고른다 — 낯섦은 한 번 치르고 끝나지만 부정확함은 읽는 사람마다 매번 치른다. 판정 방법도 같이 적었다. 그 단어로 평범한 문장을 써 보고, 단어가 문장을 도우면 맞는 이름이고 싸우면 틀린 이름이다. "크래시는 복구하는 것이 아니라 조사하는 것이다" — 돕는다 "패닉은 복구할 수 없다" — 다른 언어에서는 할 수 있어 싸운다 panic의 자연어 뜻은 "갑작스러운 공포"다. 반응하는 쪽의 감정이지 결함에 대한 말이 아니다. 그리고 Go/Rust에서는 붙잡을 수 있어 이름이 거짓말을 한다. crash는 "계획 없이 갑자기 완전히 망가져 끝남"이고 복구의 함의가 없다 — 크래시는 복구하는 게 아니라 조사하는 것이다. 어휘의 출신도 이유가 됐다. panic+recover는 Go 전통이고 거기엔 감독이 없다. crash+supervision은 얼랭 전통이며, 우리가 만드는 것이 그쪽이다. 한국어 용어도 세 층으로 정리했다: 실패(Result) / 결함(crash) / 감독. 세 층이 세 가지 다른 기제로 규율된다 — 타입, 없음(발산), capability. "상황이 나쁨"은 결함이 아니라 실패다. 이 선을 안 그으면 crash가 게으름의 배출구가 된다. "오류"는 컴파일러 진단에만 쓴다. 얼랭 질문에 대한 답도 기록했다: 감독은 가져오고 비구조적 spawn은 안 가져온다. sc.spawn과 sup.spawn(sc, f)로 갈리며 문법 변경이 없다 — 실패를 삼키려면 Supervisor를 받았어야 하고 그것이 시그니처에 보인다. 개명은 문법을 먼저 고치고 대조 장치로 확인했다. 문장 500개, 파일 26개 모두 갈림 0건. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_019ZVDeU6KLuUVL3gs18Hm3E --- docs/grammar.ebnf | 12 ++++---- docs/thesis.md | 66 +++++++++++++++++++++++++++++++++++-------- lib/ast.ml | 6 ++-- lib/interp.ml | 2 +- lib/ir.ml | 4 +-- lib/move.ml | 4 +-- lib/parser.ml | 4 +-- lib/resolve.ml | 2 +- lib/token.ml | 10 +++---- lib/typecheck.ml | 6 ++-- lib/types.ml | 4 +-- std/test.cool | 4 +-- test/test_coollang.ml | 28 +++++++++--------- 13 files changed, 97 insertions(+), 55 deletions(-) diff --git a/docs/grammar.ebnf b/docs/grammar.ebnf index 1372735..2a6be44 100644 --- a/docs/grammar.ebnf +++ b/docs/grammar.ebnf @@ -55,7 +55,7 @@ * "pub" | "fn" | "struct" | "enum" | "capability" | "const" | * "import" | "as" | "reexport" | "let" | "mut" | "own" | "affine" | * "copyable" | "effects" | "return" | "if" | "else" | "match" | - * "scope" | "panic" | "test" | "true" | "false" + * "scope" | "crash" | "test" | "true" | "false" (* 생성 끝 *) * * 다중 줄 목록(파라미터, 인자, 필드, variant, 리스트 리터럴)은 후행 콤마가 @@ -244,13 +244,13 @@ primary = literal | match_expr | scope_expr | "(" , expr , ")" - | panic_expr + | crash_expr | name_or_struct ; -(* panic은 복구 불가능한 실패다. 되돌아올 수 없으므로 타입이 Never이고, +(* crash는 복구 불가능한 결함이다. 되돌아올 수 없으므로 타입이 Never이고, * Never는 어떤 타입 자리에도 놓일 수 있다 — 그래야 match 팔에서 쓸 수 있다. * - * effect가 아니다. 경계 검사 하나 넣었다고 {Panic}이 호출자 전부로 + * effect가 아니다. 경계 검사 하나 넣었다고 {Crash}가 호출자 전부로 * 전염되면 effect 절은 신호가 아니라 잡음이 된다. 발산이 effect가 아닌 * 것과 같은 이유다 — 무한 루프도 추적하지 않는다. * @@ -258,11 +258,11 @@ primary = literal * 시그니처에 안 적히므로 철학 1과 충돌한다. 런타임은 격리 경계를 가질 수 * 있다 (테스트 러너가 첫 사례). * - * 되감기를 하지 않는다. panic 시 자원 해제 여부는 자원 모델과 함께 + * 되감기를 하지 않는다. crash 시 자원 해제 여부는 자원 모델과 함께 * 결정한다 — 지금은 열어둔다. * * 키워드인 이유: prelude가 없어서 함수로 두면 쓸 때마다 import해야 한다 *) -panic_expr = "panic" , "(" , expr , ")" ; +crash_expr = "crash" , "(" , expr , ")" ; (* ident 하나로는 이름인지 struct 리터럴인지 갈리지 않는다. "{"를 보고 * 갈리므로 왼쪽으로 인수분해해 적는다 — 파서가 실제로 하는 일이다 *) diff --git a/docs/thesis.md b/docs/thesis.md index 8fdefc3..f80ce1a 100644 --- a/docs/thesis.md +++ b/docs/thesis.md @@ -28,6 +28,16 @@ AI의 코드 생성 속도 >> 신뢰 확보 속도. → "Effects: +payment.refund" 수준으로 리뷰 압축 5. 한 개념 = 한 방식: syntax variant 최소화, 공식 formatter 하나, 공식 toolchain 하나. 단순성의 기준은 "작성자"가 아니라 "리뷰어와 검증기" +6. 이름은 관례가 아니라 뜻에서 고른다 — 낯섦은 한 번 치르고 끝나지만 + 부정확함은 읽는 사람마다 매번 치른다 + → 판정: 그 단어로 그것이 무엇인지 평범한 문장을 써 보라. 단어가 문장을 + 도우면 맞는 이름, 문장과 싸우면 틀린 이름이다 + "크래시는 복구하는 것이 아니라 조사하는 것이다" — 돕는다 + "패닉은 복구할 수 없다" — 다른 언어에서는 할 수 있어 싸운다 + → 널리 쓰인다는 것은 옳다는 증거가 아니다. 운영체제의 "fault"가 그렇다 — + 잘못이 아닌 것을 잘못이라 부르고, 그 오류까지 함께 전파된다 + ※ 관례가 정확하면 관례를 따른다. 관례라서가 아니라 정확해서다 + ※ 이름에만 적용된다. 동작을 관례와 다르게 만들 이유는 아니다 ■ 파생 결정 @@ -338,29 +348,43 @@ L2 빠른 테스트 / L3 fuzzing / L4 formal proof → 요청 시, 분리 실행 invalidation 범위를 넓히는가? / unrelated code 의미를 바꾸는가? / 기존 개념의 중복 표현인가? → 강한 이유 없으면 거절 -■ 복구 불가능한 실패 — panic -panic(message) -> Never. 키워드다 (prelude가 없어 함수로 두면 매번 import). -- effect가 아니다. 경계 검사 하나에 {Panic}이 호출자 전부로 전염되면 +■ 실패의 세 층 — 실패 / 결함 / 감독 +서로 다른 세 질문에 답한다. 아래 층은 위 층 없이도 성립한다. + + 실패 (Result) 호출자가 대처할 수 있는가? 타입이 강제한다 + 결함 (crash) 이 태스크가 계속할 수 있는가? 컴파일러는 추적하지 않는다 + 감독 죽은 태스크를 어떻게 다루는가? capability가 강제한다 + +세 층이 세 가지 다른 기제로 규율된다는 것이 제대로 나뉘었다는 증거다. +"상황이 나쁨"은 결함이 아니라 실패다 — 디스크가 가득 찬 것은 Result다. +이 선을 긋지 않으면 crash가 게으름의 배출구가 된다. +※ 취소는 이 셋 중 어디에도 없다. 취소는 실패가 아니라 "더 이상 필요 없음" + 이며 별도의 결정으로 남는다. +"오류"는 컴파일러가 내는 진단에만 쓴다. + +■ 결함 — crash +crash(message) -> Never. 키워드다 (prelude가 없어 함수로 두면 매번 import). +- effect가 아니다. 경계 검사 하나에 {Crash}가 호출자 전부로 전염되면 effect 절은 신호가 아니라 잡음이 된다. 발산이 effect가 아닌 것과 같은 이유다 — 무한 루프도 추적하지 않는다. - Never는 어떤 타입 자리에도 놓인다. 그래야 match 팔에서 쓸 수 있고, - 그게 없으면 panic은 식 자리에서 못 쓴다. + 그게 없으면 crash는 식 자리에서 못 쓴다. - 언어 수준 recover가 없다. 붙잡는 것이 있으면 그것은 예외이고, 예외는 시그니처에 안 적히므로 철학 1과 충돌한다. -- 되감기를 하지 않는다. ※ panic 시 자원 해제 여부는 자원 모델과 함께 +- 되감기를 하지 않는다. ※ crash 시 자원 해제 여부는 자원 모델과 함께 결정한다 — 지금은 열어둔다. - 0으로 나누기, assert 실패, 미래의 범위·오버플로가 전부 이 하나로 모인다. -※ panic은 프로그램의 버그를 말한다. 예상되는 실패는 Result다. 호출자가 - 대처할 수 있는 것을 panic으로 처리하면 오용이다. +※ crash는 프로그램의 결함을 말한다. 예상되는 실패는 Result다. 호출자가 + 대처할 수 있는 것을 crash로 처리하면 오용이다. -■ panic은 어디까지 올라가는가 (실패의 단위) +■ crash는 어디까지 올라가는가 (실패의 단위) 콜 스택의 root가 아니라 scope 트리의 root다. 즉 실패의 단위는 프로세스가 아니라 태스크다. scope가 이미 그 모양이기 때문이다 — 렉시컬이고, 블록을 나가는 것이 join이며, 자식이 죽었다는 사실이 부모에게 도달하는 지점이 문법에 이미 있다(닫는 중괄호). - panic은 scope 트리를 타고 올라간다. 자식이 panic하면 그 scope는 join - 지점에서 panic한다. root까지 도달하면 실행이 끝난다. + crash는 scope 트리를 타고 올라간다. 자식이 crash하면 그 scope는 join + 지점에서 crash한다. root까지 도달하면 실행이 끝난다. v0는 태스크가 하나뿐이라 이 규칙이 관측되지 않는다 — "태스크가 죽는다"와 "프로그램이 죽는다"가 같은 사건이다. 그래도 지금 적는 이유는, 동시성이 @@ -369,9 +393,27 @@ v0는 태스크가 하나뿐이라 이 규칙이 관측되지 않는다 — "태 전파는 취소를 정하지 않고도 정의된다. 취소가 생기면 형제들이 언제 멈추는지가 바뀔 뿐, scope가 실패한다는 사실은 바뀌지 않는다. ※ 형제 취소는 여전히 미정이다. 대가를 알고 미룬다 — 취소가 없으면 형제 - 하나가 끝나지 않을 때 죽은 자식의 panic이 join에 도달하지 못한다. + 하나가 끝나지 않을 때 죽은 자식의 crash가 join에 도달하지 못한다. 실패가 hang에 가려진다. 이것이 취소가 필요한 이유이기도 하다. +■ 얼랭 방식은 되는가 — 감독은 되고 비구조적 spawn은 안 된다 +공유 가변 상태 없음, let it crash, 죽은 쪽의 상태를 물려받지 않음 — +얼랭과 이미 같다. 다른 경로로 같은 결론에 도착했다. +가져오지 않는 것: 얼랭의 비구조적 spawn(Pid가 부모보다 오래 산다). +부모 없는 태스크는 ambient authority와 같은 문제이고, 구조적 동시성을 고른 +이유가 그것이다. +가져오는 것: 감독. scope 트리가 이미 supervision 트리 모양이다. + sc.spawn(f) 실패가 부모로 전파된다 + sup.spawn(sc, f) 실패가 여기서 멈춘다 +spawn은 하나의 개념이고 실패가 어디까지 가느냐가 권한으로 갈린다 — 실패를 +삼키려면 Supervisor를 받았어야 하고 그것이 시그니처에 보인다. 문법 변경 없음. +재시작 가능한 자식은 copyable 클로저여야 한다(fn vs affine fn). 얼랭이 관례로 +지키는 것을 affinity가 이미 검사한다. 재시작 전략은 라이브러리다(얼랭도 OTP). +※ 전제조건은 자원 모델이다. 죽은 태스크가 잡았던 것이 정리되지 않으면 + 재시작이 곧 누수다. +※ 얼랭의 보장은 BEAM의 선점 스케줄링과 프로세스별 힙 위에 선다. 우리에겐 + 아직 둘 다 없다 — 이것은 설계 층의 답이지 v0가 보여줄 수 있는 것이 아니다. + ■ 격리 경계는 recover가 아니다 런타임은 격리 경계를 가질 수 있다. 테스트 러너가 첫 사례이고, 서버의 요청 경계가 두 번째가 될 것이다. 구분선은 이것이다: @@ -397,7 +439,7 @@ capability를 만드는 문법도 없다. 따라서 effect-free임이 증명된 interface hash에 들어가지 않는다 — 테스트를 고쳤다고 downstream이 재검사되면 안 된다. 컴파일 타임 메타프로그래밍 없음. assert는 std/test.cool에 coollang으로 -쓰인다 (panic 위의 설탕) — std에서 본문이 있는 첫 함수다. 실패 메시지에 +쓰인다 (crash 위의 설탕) — std에서 본문이 있는 첫 함수다. 실패 메시지에 값이 안 나오는 것은 의도다. 표현식 텍스트를 잡으려면 매크로가 필요하다. ※ effect 있는 코드는 테스트할 수 없다. 가짜 capability를 만드는 수단이 없기 때문이다. 실제로 불편해진 뒤에 판단한다. diff --git a/lib/ast.ml b/lib/ast.ml index a7b3821..4f83528 100644 --- a/lib/ast.ml +++ b/lib/ast.ml @@ -75,7 +75,7 @@ type expr = | E_try of { inner : expr; pos : pos } | E_unary of { op : unop; operand : expr; pos : pos } (* 복구 불가능한 실패. 타입은 Never — 돌아오지 않으므로 어떤 자리에도 놓인다 *) - | E_panic of { msg : expr; pos : pos } + | E_crash of { msg : expr; pos : pos } | E_binary of { op : binop; lhs : expr; rhs : expr; pos : pos } and closure = { @@ -249,8 +249,8 @@ let binop_name = function let rec buf_expr b = function | E_lit (l, _) -> buf_lit b l - | E_panic { msg; _ } -> - Buffer.add_string b "(panic "; + | E_crash { msg; _ } -> + Buffer.add_string b "(crash "; buf_expr b msg; Buffer.add_char b ')' | E_ident (n, _) -> Buffer.add_string b n diff --git a/lib/interp.ml b/lib/interp.ml index 6e5cc63..87b0f8c 100644 --- a/lib/interp.ml +++ b/lib/interp.ml @@ -320,7 +320,7 @@ let rec eval st (env : env) (e : Ir.t) : value = | [ f ] -> apply st pos f [] | _ -> fail pos "spawn은 함수 하나를 받습니다") | other -> fail pos (Printf.sprintf "%s에는 필드가 없습니다" (show other))) - | Ir.I_panic (msg, pos) -> ( + | Ir.I_crash (msg, pos) -> ( match eval st env msg with VStr s -> fail pos s | v -> fail pos (show v)) | Ir.I_unary (op, e, pos) -> ( match (op, eval st env e) with diff --git a/lib/ir.ml b/lib/ir.ml index 61b443c..3284694 100644 --- a/lib/ir.ml +++ b/lib/ir.ml @@ -45,7 +45,7 @@ type t = | I_seq of stmt list * t (* 블록: 문 나열 + 꼬리 값 *) | I_call of { callee : t; args : t list; pos : pos } | I_field of { obj : t; name : string; pos : pos } - | I_panic of t * pos + | I_crash of t * pos | I_unary of Ast.unop * t * pos | I_binary of Ast.binop * t * t * pos @@ -186,7 +186,7 @@ let rec lower_pat c (p : Ast.pattern) : pat = let rec lower c (e : Ast.expr) : t = match e with - | Ast.E_panic { msg; pos } -> I_panic (lower c msg, pos) + | Ast.E_crash { msg; pos } -> I_crash (lower c msg, pos) | Ast.E_lit (l, _) -> I_lit l | Ast.E_ident (n, pos) -> I_ref (classify c n, pos) | Ast.E_list (xs, _) -> I_list (List.map (lower c) xs) diff --git a/lib/move.ml b/lib/move.ml index 7a89c98..f68b1de 100644 --- a/lib/move.ml +++ b/lib/move.ml @@ -187,8 +187,8 @@ type ctx = Borrow | Move of string (* 어디로 옮겨가는지 — 진단에 let rec walk st (ctx : ctx) (e : expr) : vinfo = match e with | E_lit _ -> v_copy - (* panic은 돌아오지 않지만 메시지 식은 계산된다. 메시지는 빌려 쓴다 *) - | E_panic { msg; _ } -> + (* crash는 돌아오지 않지만 메시지 식은 계산된다. 메시지는 빌려 쓴다 *) + | E_crash { msg; _ } -> ignore (walk st Borrow msg); v_copy | E_ident (n, pos) -> ( diff --git a/lib/parser.ml b/lib/parser.ml index e52fcee..25d6245 100644 --- a/lib/parser.ml +++ b/lib/parser.ml @@ -398,12 +398,12 @@ and parse_primary st = | Token.Kw_fn -> parse_closure st | Token.Kw_if -> parse_if st | Token.Kw_match -> parse_match st - | Token.Kw_panic -> + | Token.Kw_crash -> adv st; expect st Token.LParen "("; let msg = with_struct_ok st (fun () -> parse_expr st) in expect_close st Token.RParen ")"; - E_panic { msg; pos = p } + E_crash { msg; pos = p } | Token.Kw_scope -> adv st; let name = ident st "새 scope 이름" in diff --git a/lib/resolve.ml b/lib/resolve.ml index 6d94c7d..2e2f0a4 100644 --- a/lib/resolve.ml +++ b/lib/resolve.ml @@ -154,7 +154,7 @@ let rec resolve_pattern st seen = function let rec resolve_expr st = function | E_lit _ -> () - | E_panic { msg; _ } -> resolve_expr st msg + | E_crash { msg; _ } -> resolve_expr st msg | E_ident (n, pos) -> if lookup_local st n = None then if Hashtbl.mem st.items n then () diff --git a/lib/token.ml b/lib/token.ml index 7e1489b..dc1084d 100644 --- a/lib/token.ml +++ b/lib/token.ml @@ -28,7 +28,7 @@ type kind = | Kw_else | Kw_match | Kw_scope - | Kw_panic + | Kw_crash | Kw_test | Kw_true | Kw_false @@ -89,7 +89,7 @@ let keyword = function | "else" -> Some Kw_else | "match" -> Some Kw_match | "scope" -> Some Kw_scope - | "panic" -> Some Kw_panic + | "crash" -> Some Kw_crash | "test" -> Some Kw_test | "true" -> Some Kw_true | "false" -> Some Kw_false @@ -119,7 +119,7 @@ let show_kind = function | Kw_else -> "else" | Kw_match -> "match" | Kw_scope -> "scope" - | Kw_panic -> "panic" + | Kw_crash -> "crash" | Kw_test -> "test" | Kw_true -> "true" | Kw_false -> "false" @@ -184,8 +184,8 @@ let next_kind = function | Kw_if -> Some Kw_else | Kw_else -> Some Kw_match | Kw_match -> Some Kw_scope - | Kw_scope -> Some Kw_panic - | Kw_panic -> Some Kw_test + | Kw_scope -> Some Kw_crash + | Kw_crash -> Some Kw_test | Kw_test -> Some Kw_true | Kw_true -> Some Kw_false | Kw_false -> Some LParen diff --git a/lib/typecheck.ml b/lib/typecheck.ml index af9d499..b37a7aa 100644 --- a/lib/typecheck.ml +++ b/lib/typecheck.ml @@ -164,11 +164,11 @@ let builtin_ctor = function let rec infer env (e : expr) : T.t = match e with - (* panic은 돌아오지 않는다. 타입은 Never이고 어떤 자리에도 놓인다 *) - | E_panic { msg; pos } -> + (* crash는 돌아오지 않는다. 타입은 Never이고 어떤 자리에도 놓인다 *) + | E_crash { msg; pos } -> let t = infer env msg in if not (T.unify t T.TString) then - mismatch env pos T.TString t "panic의 메시지"; + mismatch env pos T.TString t "crash의 메시지"; T.TNever | E_lit (L_int _, _) -> T.TInt | E_lit (L_str _, _) -> T.TString diff --git a/lib/types.ml b/lib/types.ml index b98752b..71273c2 100644 --- a/lib/types.ml +++ b/lib/types.ml @@ -10,7 +10,7 @@ type t = | TUnknown - (* 값을 내지 않는 타입. panic의 타입이고 어떤 자리에도 놓일 수 있다. + (* 값을 내지 않는 타입. crash의 타입이고 어떤 자리에도 놓일 수 있다. TUnknown과 다르다 — TUnknown은 "모른다"이고 TNever는 "돌아오지 않는다"이다. 둘 다 무엇과도 맞지만 생기는 이유가 다르다. *) | TNever @@ -141,7 +141,7 @@ let unify_eff a b = let rec unify a b = match (resolve a, resolve b) with | TUnknown, _ | _, TUnknown -> true - (* Never는 어떤 타입 자리에도 놓인다. panic이 match 팔에 설 수 있는 이유다 *) + (* Never는 어떤 타입 자리에도 놓인다. crash가 match 팔에 설 수 있는 이유다 *) | TNever, _ | _, TNever -> true | TMeta r, TMeta r' when r == r' -> true | TMeta r, t | t, TMeta r -> diff --git a/std/test.cool b/std/test.cool index e4f1891..2af981e 100644 --- a/std/test.cool +++ b/std/test.cool @@ -1,6 +1,6 @@ // 표준 라이브러리: 테스트. // -// assert는 특별한 것이 아니라 panic 위의 설탕이다. 그래서 본문이 있다 — +// assert는 특별한 것이 아니라 crash 위의 설탕이다. 그래서 본문이 있다 — // std에서 실제 coollang으로 쓰인 첫 함수다. // // 실패 메시지에 값이 안 나오는 것은 의도다. `assert(a == b)`에서 a와 b를 @@ -9,6 +9,6 @@ pub fn assert(c: Bool) { if !c { - panic("assert 실패") + crash("assert 실패") } } diff --git a/test/test_coollang.ml b/test/test_coollang.ml index ae5ca2f..d126a1e 100644 --- a/test/test_coollang.ml +++ b/test/test_coollang.ml @@ -1523,7 +1523,7 @@ let () = (* ------------------------------------------------------------------ *) let () = - let dir = Filename.concat (Filename.get_temp_dir_name ()) "cool_panictest" in + let dir = Filename.concat (Filename.get_temp_dir_name ()) "cool_crashtest" in ignore (Sys.command (Printf.sprintf "mkdir -p %s" (Filename.quote dir))); let f = Filename.concat dir "m.cool" in let run src = @@ -1540,7 +1540,7 @@ let () = pub fn name(e: E) -> String {\n\ \ match e {\n\ \ A => \"a\",\n\ - \ B => panic(\"B는 아직\"),\n\ + \ B => crash(\"B는 아직\"),\n\ \ }\n\ }\n\n\ test \"통과\" {\n\ @@ -1549,7 +1549,7 @@ let () = test \"assert 실패\" {\n\ \ Test.assert(1 == 2)\n\ }\n\n\ - test \"panic 실패\" {\n\ + test \"crash 실패\" {\n\ \ Test.assert(name(B) == \"b\")\n\ }" in @@ -1558,9 +1558,9 @@ let () = List.iter (fun (e : Session.error) -> Printf.printf " (%s)\n" e.message) es; - check "panic/test 예제가 검사를 통과한다" false + check "crash/test 예제가 검사를 통과한다" false | Ok rs -> - check "panic/test 예제가 검사를 통과한다" true; + check "crash/test 예제가 검사를 통과한다" true; check "테스트 셋이 잡힌다" (List.length rs = 3); let failed = List.filter (fun (r : Interp.test_result) -> r.t_failure <> None) rs @@ -1576,10 +1576,10 @@ let () = | Some (_, m) -> has_sub m "assert 실패" | None -> false) rs); - check "panic 메시지가 그대로 보고된다" + check "crash 메시지가 그대로 보고된다" (List.exists (fun (r : Interp.test_result) -> - r.t_name = "panic 실패" + r.t_name = "crash 실패" && match r.t_failure with | Some (_, m) -> has_sub m "B는 아직" @@ -1594,7 +1594,7 @@ let () = (* 테스트는 effect를 수행할 수 없다 — capability를 받지 않으므로 증명된다 *) let () = - let dir = Filename.concat (Filename.get_temp_dir_name ()) "cool_panictest" in + let dir = Filename.concat (Filename.get_temp_dir_name ()) "cool_crashtest" in let f = Filename.concat dir "e.cool" in write f "pub capability Db {\n\ @@ -1625,7 +1625,7 @@ let () = check "samples/app의 테스트가 전부 통과한다" (List.for_all (fun (r : Interp.test_result) -> r.t_failure = None) rs) -(* panic은 scope 트리를 타고 올라간다. 자식이 panic하면 그 scope는 실패하고, +(* crash는 scope 트리를 타고 올라간다. 자식이 crash하면 그 scope는 실패하고, scope 뒤의 코드는 실행되지 않는다. v0는 태스크가 하나라 이 규칙이 축약된 형태로만 관측된다. 아래에서 "자식 @@ -1640,16 +1640,16 @@ let () = \ c.print(\"scope 앞\")\n\ \ scope work = root {\n\ \ work.spawn(fn() { c.print(\"자식 하나\") })\n\ - \ work.spawn(fn() { panic(\"자식 둘이 죽었다\") })\n\ + \ work.spawn(fn() { crash(\"자식 둘이 죽었다\") })\n\ \ }\n\ \ c.print(\"scope 뒤\")\n\ }") with | out, Some e -> - check "자식의 panic이 scope 밖으로 전파된다" (has_sub e.message "자식 둘이 죽었다"); - check "panic 전의 출력은 남는다" (has_sub out "자식 하나"); + check "자식의 crash가 scope 밖으로 전파된다" (has_sub e.message "자식 둘이 죽었다"); + check "crash 전의 출력은 남는다" (has_sub out "자식 하나"); check "scope 뒤는 실행되지 않는다" (not (has_sub out "scope 뒤")) | _, None -> - check "자식의 panic이 scope 밖으로 전파된다" false; - check "panic 전의 출력은 남는다" false; + check "자식의 crash가 scope 밖으로 전파된다" false; + check "crash 전의 출력은 남는다" false; check "scope 뒤는 실행되지 않는다" false