Skip to content

types: bare Ok/Err/Some constructors as sugar for Result::Ok/Err and Option::Some#55

Merged
rfunix merged 2 commits intomainfrom
feat/result-custom-errors
Mar 31, 2026
Merged

types: bare Ok/Err/Some constructors as sugar for Result::Ok/Err and Option::Some#55
rfunix merged 2 commits intomainfrom
feat/result-custom-errors

Conversation

@rfunix
Copy link
Copy Markdown
Owner

@rfunix rfunix commented Mar 31, 2026

Summary

Closes #52.

Implements bare Ok(v), Err(e), and Some(v) constructors as idiomatic sugar for the qualified forms Result::Ok(v), Result::Err(e), and Option::Some(v).

Before this PR, the following code failed with undefined type \Ok``:

fn find_user(id: Int) -> Result<String, AppError> {
    if id == 1 { return Ok("alice") }
    return Err(AppError::NotFound)
}

After this PR, both forms are accepted:

// qualified (already worked)
return Result::Ok("alice")
return Result::Err(AppError::NotFound)

// bare (new — matches the issue #52 target)
return Ok("alice")
return Err(AppError::NotFound)

Changes

  • crates/kodo_types/src/expr.rs: In check_call, intercept bare Ok, Err, and Some identifiers before the generic-function lookup and delegate to check_enum_variant_expr("Result"/"Option", ...). This reuses all existing type-inference, monomorphisation, and ownership-tracking logic.

  • crates/kodo_mir/src/lowering/expr.rs: In lower_call, mirror the type-checker interception: redirect Ok/Err/Some to lower_enum_variant so codegen emits the correct discriminant-based EnumVariant instruction.

  • tests/ui/types/result_bare_constructors.ko + .stdout: New run-pass UI test covering all three match patterns (Ok(name), Err(AppError::Variant), Err(_)).

  • examples/result_bare_constructors.ko: Runnable example demonstrating the feature end-to-end.

Test plan

  • cargo fmt --all -- --check — passes
  • cargo clippy --workspace -- -D warnings — zero warnings
  • cargo test --workspace — all 1,063 tests pass
  • make ui-test — 84 UI tests pass (1 new)
  • Example builds and produces correct output (alice, unauthorized, not found)

🤖 Generated with Claude Code

…and Option::Some

Allow `Ok(v)`, `Err(e)`, and `Some(v)` without the `Result::`/`Option::`
namespace prefix, matching the idiomatic usage described in issue #52.

The type checker intercepts these bare names in `check_call` and delegates
to `check_enum_variant_expr("Result", "Ok", ...)` etc., so type inference,
monomorphisation and ownership tracking are identical to the qualified form.

The MIR lowering mirrors this: `lower_call` detects `Ok`/`Err`/`Some` and
redirects to `lower_enum_variant` so codegen emits the correct discriminant-
based EnumVariant instruction.

Custom error enums in `Result<T, E>` with nested pattern matching
(`Err(AppError::NotFound)`) continue to work unchanged.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@rfunix rfunix added the agent-generated Changes generated by the Kōdo Architect autonomous agent label Mar 31, 2026
Copy link
Copy Markdown
Owner Author

@rfunix rfunix left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review — REVIEWER agent

Grade: B (Bom, com um bug menor a corrigir antes do merge)

✅ Pontos positivos

  • Correção: A abordagem de interceptar Ok/Err/Some no type checker e delegar para check_enum_variant_expr é cirúrgica e reutiliza toda a lógica existente de inferência, monomorphização e ownership tracking.
  • CI: Todos os 12 checks passando (clippy, fmt, tests, fuzz, docs, coverage, MSRV).
  • Testes: UI test run-pass com output verificado + exemplo completo cobrindo todos os casos de AppError.
  • Comentários: Doc comments explicativos no diff, com referência a [TAPL] Ch.22 — adequado.
  • Zero unwrap/expect introduzidos no diff do PR.
  • Zero unsafe no diff.

🐛 Bug: None implementado no type checker mas ausente no MIR lowering

Arquivo: crates/kodo_mir/src/lowering/expr.rs

O type checker em kodo_types/src/expr.rs intercepta None (linha adicionada):

if name == "None" && args.is_empty() {
    return self.check_enum_variant_expr("Option", "None", args, span);
}

Mas o MIR lowering em crates/kodo_mir/src/lowering/expr.rs não trata None:

match callee_name.as_str() {
    "Ok"   => return self.lower_enum_variant("Result", "Ok", args),
    "Err"  => return self.lower_enum_variant("Result", "Err", args),
    "Some" => return self.lower_enum_variant("Option", "Some", args),
    // None está faltando aqui!
    _ => {}
}

Consequência: código com return None ou let x: Option<Int> = None passa type checking mas falha no MIR lowering / codegen — bug silencioso em runtime.

Fix necessário — adicionar ao match em lowering/expr.rs:

"None" => return self.lower_enum_variant("Option", "None", &[]),

Também recomendado: adicionar um UI test para None bare constructor:

fn maybe_value(flag: Bool) -> Option<Int> {
    if flag { return Some(42) }
    return None
}

Minor

  • O UI test cobre Ok/Err/Some mas não None — adicionar após o fix acima.
  • Os comentários no diff mencionam Option::None em dois lugares distintos — ligeiramente redundante mas não problemático.

Ação: corrigir o None no MIR lowering + adicionar teste antes do merge.

`return None` was parsed as `Expr::Ident("None", span)` rather than a
call expression, so the existing `check_call` interception in the type
checker never fired — producing E0201 "undefined type `None`".  The MIR
lowering had the same gap in `lower_ident`.

Fix by intercepting `"None"` in two places:
- `kodo_types/src/expr.rs` (`Expr::Ident` branch of `check_expr`):
  redirect to `check_enum_variant_expr("Option", "None", &[], span)`
  before the environment lookup.
- `kodo_mir/src/lowering/expr.rs` (`lower_ident`): redirect to
  `lower_enum_variant("Option", "None", &[])` so codegen always emits
  the correct discriminant-based EnumVariant instruction.

Adds UI test `tests/ui/types/option_none_bare.ko` (run-pass) covering
bare `None` in a branch and as a return value, with baseline stdout.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@rfunix rfunix merged commit 9c8e403 into main Mar 31, 2026
12 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

agent-generated Changes generated by the Kōdo Architect autonomous agent

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat: Result<T, E> with custom error enums end-to-end (parity with Mojo typed errors)

1 participant