Skip to content

Commit 533786b

Browse files
hyperpolymathclaude
andcommitted
fix(stdlib): prelude unwrap/unwrap_result diverge via panic (#134)
`prelude.affine`'s `unwrap` and `unwrap_result` only `println`'d on the None/Err arm and then fell through, returning Unit from a `-> T` signature (interpreter-era unsoundness; surfaced during #128 triage as `Unify.TypeMismatch (T, Unit)`). Replace the println+fall-through arms with `panic(...)` — the existing `panic(String) -> Never` builtin, exactly as `option.affine` and `result.affine` already do — so the failure arm has type Never and the typed return is sound. Adds two interpreter tests asserting the panic path (`unwrap(None)` and `unwrap_result(Err _)` both raise the documented RuntimeError). Full suite green (216 tests). Closes #134 Refs #128 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent dab2f39 commit 533786b

2 files changed

Lines changed: 45 additions & 8 deletions

File tree

stdlib/prelude.affine

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,7 @@ fn is_none<T>(opt: Option<T>) -> Bool {
2525
fn unwrap<T>(opt: Option<T>) -> T {
2626
match opt {
2727
Some(value) => value,
28-
None => {
29-
println("Called unwrap on None");
30-
// TODO: panic!() once implemented
31-
}
28+
None => panic("Called unwrap on None")
3229
}
3330
}
3431

@@ -62,10 +59,7 @@ fn is_err<T, E>(res: Result<T, E>) -> Bool {
6259
fn unwrap_result<T, E>(res: Result<T, E>) -> T {
6360
match res {
6461
Ok(value) => value,
65-
Err(_) => {
66-
println("Called unwrap on Err");
67-
// TODO: panic!() once implemented
68-
}
62+
Err(_) => panic("Called unwrap on Err")
6963
}
7064
}
7165

test/test_e2e.ml

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -774,12 +774,55 @@ let test_interp_full_pipeline () =
774774
| Error msg -> Alcotest.fail msg
775775
| Ok _env -> ()
776776

777+
(* Issue #134: prelude `unwrap`/`unwrap_result` previously only println'd on
778+
None/Err and fell through returning Unit (unsound for a `-> T` signature).
779+
They must now diverge via `panic`. These assert the panic path. *)
780+
let test_unwrap_none_panics () =
781+
let src = {|
782+
type Option<T> = Some(T) | None
783+
fn unwrap<T>(opt: Option<T>) -> T {
784+
match opt {
785+
Some(value) => value,
786+
None => panic("Called unwrap on None")
787+
}
788+
}
789+
const result: Int = unwrap(None);
790+
|} in
791+
let prog = Parse_driver.parse_string ~file:"<test>" src in
792+
match Interp.eval_program prog with
793+
| Ok _ -> Alcotest.fail "expected unwrap(None) to panic; evaluation succeeded"
794+
| Error (Value.RuntimeError msg) ->
795+
Alcotest.(check string) "panic message" "Called unwrap on None" msg
796+
| Error e -> Alcotest.failf "expected RuntimeError, got: %s"
797+
(Value.show_eval_error e)
798+
799+
let test_unwrap_result_err_panics () =
800+
let src = {|
801+
type Result<T, E> = Ok(T) | Err(E)
802+
fn unwrap_result<T, E>(res: Result<T, E>) -> T {
803+
match res {
804+
Ok(value) => value,
805+
Err(_) => panic("Called unwrap on Err")
806+
}
807+
}
808+
const result: Int = unwrap_result(Err("boom"));
809+
|} in
810+
let prog = Parse_driver.parse_string ~file:"<test>" src in
811+
match Interp.eval_program prog with
812+
| Ok _ -> Alcotest.fail "expected unwrap_result(Err) to panic; evaluation succeeded"
813+
| Error (Value.RuntimeError msg) ->
814+
Alcotest.(check string) "panic message" "Called unwrap on Err" msg
815+
| Error e -> Alcotest.failf "expected RuntimeError, got: %s"
816+
(Value.show_eval_error e)
817+
777818
let interp_tests = [
778819
Alcotest.test_case "simple evaluation" `Quick test_interp_simple;
779820
Alcotest.test_case "bitwise" `Quick test_interp_bitwise;
780821
Alcotest.test_case "arithmetic" `Quick test_interp_arithmetic;
781822
Alcotest.test_case "lambda" `Quick test_interp_lambda;
782823
Alcotest.test_case "full pipeline" `Quick test_interp_full_pipeline;
824+
Alcotest.test_case "#134 unwrap(None) panics" `Quick test_unwrap_none_panics;
825+
Alcotest.test_case "#134 unwrap_result(Err) panics" `Quick test_unwrap_result_err_panics;
783826
]
784827

785828
(* ============================================================================

0 commit comments

Comments
 (0)