Skip to content

fix(stdlib): prelude unwrap/unwrap_result diverge via panic (#134)#150

Merged
hyperpolymath merged 1 commit into
mainfrom
fix/134-prelude-unwrap-panic
May 17, 2026
Merged

fix(stdlib): prelude unwrap/unwrap_result diverge via panic (#134)#150
hyperpolymath merged 1 commit into
mainfrom
fix/134-prelude-unwrap-panic

Conversation

@hyperpolymath
Copy link
Copy Markdown
Owner

What

Fixes #134 (independent sub-item of the #128 stdlib-AOT epic). prelude.affine's unwrap/unwrap_result only println'd on the None/Err arm then fell through, returning Unit from a -> T signature — an interpreter-era soundness bug (the Unify.TypeMismatch (T, Unit) seen during #128 triage).

How

Replace the println + fall-through arms with panic(...) — the existing panic(String) -> Never builtin, matching what option.affine / result.affine already do. The failure arm now has type Never, so the typed return is sound.

Verification

  • Two new interpreter tests assert the panic path: unwrap(None) and unwrap_result(Err _) each raise the documented RuntimeError.
  • Full suite green: 216 tests, zero regressions.

Independent of the other #128 sub-items. Closes #134. Refs #128.

🤖 Generated with Claude Code

`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>
@hyperpolymath hyperpolymath enabled auto-merge (squash) May 17, 2026 05:49
@hyperpolymath hyperpolymath merged commit f879862 into main May 17, 2026
0 of 12 checks passed
@hyperpolymath hyperpolymath deleted the fix/134-prelude-unwrap-panic branch May 17, 2026 05:49
hyperpolymath added a commit that referenced this pull request May 17, 2026
Per ADR-011 (#132): one canonical binding per name, module-owned.

- prelude.affine: `module prelude;`. Keeps the core sum types
  `Option`/`Result` (+ constructors) as their canonical home and the
  generic list/numeric utilities. Removes the 8 duplicate/conflicting
  Option/Result *operations* it previously also defined (is_some,
  is_none, unwrap, unwrap_or, is_ok, is_err, unwrap_result,
  unwrap_or_result).
- option.affine: `module option;` + `use prelude::{Option, Some, None,
  Result, Ok, Err};`. Now the single canonical home for Option ops.
- result.affine: `module result;` + `use prelude::{Result, Ok, Err,
  Option, Some, None};`. Single canonical home for Result ops.

The previous flat-namespace conflicts (`prelude.map(arr,f)` vs
`option.map(f,opt)`; `prelude.unwrap`(Option) vs `result.unwrap`(Result))
are resolved by module ownership — each is now `module::name`, exactly
one definition per owning module. Verified: no leftover dup defs, no
prelude util calls a removed op, full suite green (214 tests).

Note: this removes the prelude `unwrap`/`unwrap_result` that #134
(PR #150) patched; the sound, panicking versions are `option::unwrap`
and `result::unwrap` (already correct). #150's regression tests remain
valid against those. Merge-order: if #150 lands first, resolve the
modify/delete in prelude.affine in favour of this deletion.

Closes #133
Refs #128, #132

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
hyperpolymath added a commit that referenced this pull request May 17, 2026
…152)

Per ADR-011 (#132): one canonical binding per name, module-owned.

- prelude.affine: `module prelude;`. Keeps the core sum types
  `Option`/`Result` (+ constructors) as their canonical home and the
  generic list/numeric utilities. Removes the 8 duplicate/conflicting
  Option/Result *operations* it previously also defined (is_some,
  is_none, unwrap, unwrap_or, is_ok, is_err, unwrap_result,
  unwrap_or_result).
- option.affine: `module option;` + `use prelude::{Option, Some, None,
  Result, Ok, Err};`. Now the single canonical home for Option ops.
- result.affine: `module result;` + `use prelude::{Result, Ok, Err,
  Option, Some, None};`. Single canonical home for Result ops.

The previous flat-namespace conflicts (`prelude.map(arr,f)` vs
`option.map(f,opt)`; `prelude.unwrap`(Option) vs `result.unwrap`(Result))
are resolved by module ownership — each is now `module::name`, exactly
one definition per owning module. Verified: no leftover dup defs, no
prelude util calls a removed op, full suite green (214 tests).

Note: this removes the prelude `unwrap`/`unwrap_result` that #134
(PR #150) patched; the sound, panicking versions are `option::unwrap`
and `result::unwrap` (already correct). #150's regression tests remain
valid against those. Merge-order: if #150 lands first, resolve the
modify/delete in prelude.affine in favour of this deletion.

Closes #133
Refs #128, #132

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

prelude: unwrap/unwrap_result fall through (return nothing) on None/Err

1 participant