Skip to content

fix(python): emit wildcard default for union or-patterns (#4649)#4653

Merged
dbrattli merged 1 commit into
mainfrom
fix/python-union-or-pattern-4649
Jun 13, 2026
Merged

fix(python): emit wildcard default for union or-patterns (#4649)#4653
dbrattli merged 1 commit into
mainfrom
fix/python-union-or-pattern-4649

Conversation

@dbrattli

Copy link
Copy Markdown
Collaborator

Fixes #4649

Problem

Matching against multiple union cases in an or-pattern returned None (Python) for one of the cases:

type Case = BestCase | MidCase | WorstCase

let atLeastMid (c: Case) =
    match c with
    | BestCase | MidCase -> true
    | WorstCase -> false

transpiled to (tag 0 missing):

def at_least_mid(c: Case) -> bool:
    match c.tag:
        case 1:
            return True
        case 2:
            return False

so atLeastMid BestCase fell through the match and returned None instead of True.

Root cause

In transformSwitchPatternAsMatch, FCS lowered the match so BestCase (tag 0) became the default branch, sharing its target body (return True) with MidCase. The generator had a defaultAlreadyCovered check that dropped the wildcard case _: whenever the default's target index was already used by an explicit case — leaving the tag only reachable via the default (tag 0) unmatched.

Fix

Always emit the wildcard default (it's the only thing covering subject values not listed explicitly). Instead, drop the redundant explicit cases that route to the default's target, since the wildcard already covers them. The example now generates:

def at_least_mid(c: Case) -> bool:
    match c.tag:
        case 2:
            return False
        case _:
            return True

Testing

  • Added regression test test or-pattern over union cases sharing default target in tests/Python/TestPatternMatch.fs.
  • Full Python suite passes (2354 tests).
  • No new Pyright type-check errors.

🤖 Generated with Claude Code

When an or-pattern over union cases shares its target with the default
branch (e.g. `BestCase | MidCase -> true | WorstCase -> false`), the
Python match generator dropped the wildcard `case _:` because the
default's target index was already used by an explicit case. The union
tag that was only reachable via the default then matched nothing and the
function returned None.

Always emit the wildcard default and instead drop the redundant explicit
cases that route to the default's target, since the wildcard covers them.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown
Contributor

Python Type Checking Results (Pyright)

Metric Value
Total errors 34
Files with errors 4
Excluded files 4
New errors ✅ No
Excluded files with errors (4 files)

These files have known type errors and are excluded from CI. Remove from pyrightconfig.ci.json as errors are fixed.

File Errors Status
temp/tests/Python/test_hash_set.py 18 Excluded
temp/tests/Python/test_applicative.py 12 Excluded
temp/tests/Python/test_nested_and_recursive_pattern.py 2 Excluded
temp/tests/Python/fable_modules/thoth_json_python/encode.py 2 Excluded

@dbrattli dbrattli merged commit c96f0ec into main Jun 13, 2026
32 checks passed
@dbrattli dbrattli deleted the fix/python-union-or-pattern-4649 branch June 13, 2026 12:13
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.

[Python][Bug]: Matching against multiple union cases in OR pattern returns none

1 participant