Skip to content

lowering: matching Some/None pulls in the Option classes - #29

Merged
simontreanor merged 1 commit into
mainfrom
fix/option-import-on-match
Jul 30, 2026
Merged

lowering: matching Some/None pulls in the Option classes#29
simontreanor merged 1 commit into
mainfrom
fix/option-import-on-match

Conversation

@simontreanor

Copy link
Copy Markdown
Owner

Fixes #25.

needs_option decides whether the runtime import (project build) or the inlined Option prelude (single file) is emitted, and only construction sites set it. lower_pattern''s Pattern::Ctor arm sets needs_result for Ok/Error but had no matching line for Some/None, so a module that merely consumed an Option emitted case None_(): with nothing defining or importing that name — NameError at runtime, after check and compile both passed.

Cross-module is where this is easy to hit, because the producer lives in the other file:

NameError: name ''None_'' is not defined. Did you mean: ''None''?

But it was never really about modules. A single file that only matches — a function whose Option arrives as a parameter — emitted the same broken output on main:

def probe(o):
    match o:
        case None_():        # nothing defines None_
            return "none"
        case Some(k):
            return k

Fix

Set needs_option for Some/None in the constructor-pattern arm, mirroring the Ok/Error line directly above it.

Tests

  • tests/compile.rs::matching_an_option_pulls_in_the_option_prelude — the single-file case.
  • tests/project.rs::a_module_that_only_matches_an_imported_option_imports_it — the cross-module case: asserts the from _pyfun_rt import Some, None_ header and runs the program end to end.

Both verified to fail on main. cargo test green, cargo clippy --all-targets clean, cargo fmt applied.

`needs_option` decides whether the runtime import (project build) or the
inlined Option prelude (single file) is emitted, and only *construction* sites
set it. `lower_pattern`'s Pattern::Ctor arm set `needs_result` for Ok/Error
but had no matching line for Some/None, so a module that merely consumed an
Option emitted `case None_():` with nothing defining or importing that name:
NameError at runtime, after `check` and `compile` both passed.

Cross-module made it easy to hit (the producer is in the other file), but a
single file that only matches -- a function whose Option comes in as a
parameter -- emitted the same broken output.

Fixes #25
@simontreanor
simontreanor force-pushed the fix/option-import-on-match branch from 05fa6ce to a4357aa Compare July 30, 2026 15:30
@simontreanor
simontreanor merged commit bd3aa16 into main Jul 30, 2026
11 checks passed
@simontreanor
simontreanor deleted the fix/option-import-on-match branch July 30, 2026 15:33
@simontreanor simontreanor mentioned this pull request Jul 31, 2026
simontreanor added a commit that referenced this pull request Jul 31, 2026
Two user-visible additions since 0.2.0 -- the `input` prelude builtin
(`string ->{io} string`, #19) and caller-supplied `extern` keyword slots
(`kw = ...`, #21) -- plus five fixes, four of them from the first program
written in Pyfun in anger:

* imported schemes reused the exporting module's type-variable ids, silently
  blocking generalization in the importing module and cascading to its
  consumers (#26 / #27)
* a recursive function of arity >= 2 could not perform any effect -- the shape
  of every loop in a language with no `while` (#24 / #28)
* a module that only matched an imported `Option` emitted `None_` without
  importing it (#25 / #29)
* user identifiers collided with Python keywords, builtins the emitter calls,
  and modules an `extern` imports (#23 / #30)
* the local-binder module-alias shadowing residual (#18)
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.

A module that only matches an imported Option omits the runtime import (NameError)

1 participant