Skip to content

Fix monomorphization of opaque handle types (#341)#344

Merged
aallan merged 10 commits into
mainfrom
fix/monomorphization-opaque-types
Mar 25, 2026
Merged

Fix monomorphization of opaque handle types (#341)#344
aallan merged 10 commits into
mainfrom
fix/monomorphization-opaque-types

Conversation

@aallan

@aallan aallan commented Mar 25, 2026

Copy link
Copy Markdown
Owner

Summary

  • Core fix: Add _BUILTIN_VERA_RETURN_TYPES lookup table to monomorphize.py so the monomorphizer correctly infers Vera return types for Decimal/Map/Set builtins instead of mapping all i32 returns to "Bool"
  • WASM fix: Add Decimal/Map/Set to _infer_expr_wasm_type and _infer_block_result_type in inference.py so if-expressions inside monomorphized functions get correct result type annotations
  • Workaround removal: Replace awkward floor(decimal_to_float(...)) workarounds in existing tests with proper option_unwrap_or and match usage
  • New conformance test: ch09_decimal_generics (60 programs, was 59) — exercises option_unwrap_or, match, and decimal_compare with Decimal
  • Limitation removed: Remove monomorphization gap from Known Bugs (README), spec §9.7.2, SKILL.md, and vera/README.md
  • Release: Bump to v0.0.98

Partial fix for #341 (Decimal fixed; Map/Set still have gap — see xfail tests).

What was wrong

_infer_fncall_vera_type_simple() in monomorphize.py mapped WASM return types back to Vera types: i64→Int, i32→Bool, f64→Float64. But Decimal, Map, and Set are all i32 opaque handles — NOT Bool. So option_unwrap_or<T> with T=Decimal was monomorphized as option_unwrap_or$Bool, which is wrong.

Test plan

  • All 21 pre-commit hooks pass
  • 2,903 tests pass
  • 60 conformance programs pass
  • mypy clean
  • Manual: vera run with option_unwrap_or(Some(decimal_from_int(42)), default) → 1
  • CI passes

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Bug Fixes

    • Fixed type inference so generic prelude operations (option handling and pattern matching) work with opaque handle types (Decimal, Map, Set).
  • New Features

    • Added a conformance case covering Decimal generics.
  • Tests

    • Added unit and end-to-end tests for Decimal monomorphisation paths; conformance suite increased to 60 programs.
  • Documentation

    • Removed the documented limitation on opaque-handle monomorphisation and updated Decimal usage guidance.
  • Chores

    • Project version bumped to 0.0.98 and changelog updated.

Add _BUILTIN_VERA_RETURN_TYPES lookup table to the monomorphizer so
generic prelude functions (option_unwrap_or, match) work with
Option<Decimal>, Ordering from decimal_compare, and Option<Decimal>
from decimal_div/decimal_from_string. Previously all opaque i32
handle types were incorrectly inferred as "Bool".

Also fix _infer_expr_wasm_type and _infer_block_result_type in
wasm/inference.py to recognize Decimal/Map/Set as i32 handle types
for correct if-expr result annotations.

Remove workarounds from existing tests, add 10 new monomorphization
tests, new ch09_decimal_generics conformance test (60 programs).
Remove limitation notes from spec, SKILL.md, README Known Bugs.
Bump version to v0.0.98. Closes #341.

Co-Authored-By: Claude <noreply@anthropic.invalid>
@coderabbitai

coderabbitai Bot commented Mar 25, 2026

Copy link
Copy Markdown

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds builtin return-type inference for opaque host-managed handles (Decimal, Map, Set) so generics (e.g. option_unwrap_or, match) can be specialised for Option<Decimal>/Ordering; bumps project version to 0.0.98, updates docs/tests, and adds a conformance test (suite size 60).

Changes

Cohort / File(s) Summary
Version & Metadata
CHANGELOG.md, pyproject.toml, vera/__init__.py
Add 0.0.98 changelog entry and compare links; bump project version and exported __version__.
Monomorphizer
vera/codegen/monomorphize.py
Introduce _BUILTIN_VERA_RETURN_TYPES and _BUILTIN_PARAMETERIZED_RETURNS; _infer_fncall_vera_type_simple() and arg-type handling return builtin opaque/parameterized types to enable type-variable unification for opaque handles.
WASM / Type inference
vera/wasm/inference.py
Treat Decimal, Map, Set SlotRefs as opaque i32 handles; add explicit WASM return mappings for Map/Set/Decimal builtins; update block/expr inference to return "i32"/"i32_pair"/"i64" where appropriate.
Tests / Codegen tests
tests/test_codegen.py, tests/test_readme.py
Refactor and add tests exercising Option<Decimal>, decimal_div, decimal_from_string, decimal_compare, and monomorphization paths; adjust README allowlist line.
Conformance manifest
tests/conformance/manifest.json
Add ch09_decimal_generics manifest entry (chapter 9, run level) covering decimals, generics, option usage and related builtins.
Docs & Specs
README.md, vera/README.md, ROADMAP.md, TESTING.md, SKILL.md, CLAUDE.md, spec/09-standard-library.md, CHANGELOG.md
Remove documented monomorphization limitation; update Decimal guidance; advance roadmap/version and increment conformance counts (59→60).
Test infra & Allowlists
scripts/check_readme_examples.py, scripts/check_spec_examples.py
Adjust allowlist line numbers for README and 09-standard-library.md FUTURE entries to match shifted content.

Sequence Diagram(s)

sequenceDiagram
    participant Src as Vera Source
    participant Mono as Monomorphizer
    participant Infer as WASM Type Inference
    participant CG as Codegen/Backend

    Src->>Mono: parse call-sites and builtin calls
    Mono->>Mono: consult _BUILTIN_VERA_RETURN_TYPES / _BUILTIN_PARAMETERIZED_RETURNS
    Mono->>Infer: supply inferred Vera types (e.g. Option<Decimal>, Ordering)
    Infer->>CG: map opaque handles to wasm types (i32, i32_pair, i64)
    CG->>Mono: emit specialised functions (e.g. option_unwrap_or$Decimal)
    CG->>Src: execute tests / produce artifacts
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested labels

compiler, tests, spec, ci, docs

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title 'Fix monomorphization of opaque handle types (#341)' accurately describes the primary change: resolving the monomorphization gap that prevented generic functions from working with opaque handle types like Decimal, Map, and Set.
Linked Issues check ✅ Passed The PR comprehensively addresses all coding requirements from issue #341: monomorphizer now recognises opaque handles via _BUILTIN_VERA_RETURN_TYPES lookup, WASM inference treats Decimal/Map/Set as i32, generic prelude functions (option_unwrap_or, match) now work with Option and Ordering, tests and conformance suite properly exercise the fixed behaviour.
Out of Scope Changes check ✅ Passed All changes are directly scoped to issue #341: core monomorphizer and WASM inference fixes, test/conformance updates validating the fix, documentation reflecting resolution, and version bump v0.0.97→v0.0.98. No unrelated refactoring or ancillary feature work detected.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/monomorphization-opaque-types

Comment @coderabbitai help to get the list of available commands and usage tips.

@codecov

codecov Bot commented Mar 25, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 90.32258% with 3 lines in your changes missing coverage. Please review.
✅ Project coverage is 92.01%. Comparing base (4daac70) to head (cca1113).
⚠️ Report is 11 commits behind head on main.

Files with missing lines Patch % Lines
vera/wasm/inference.py 86.36% 3 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #344      +/-   ##
==========================================
+ Coverage   91.96%   92.01%   +0.05%     
==========================================
  Files          47       47              
  Lines       17849    17879      +30     
  Branches      211      211              
==========================================
+ Hits        16415    16452      +37     
+ Misses       1430     1423       -7     
  Partials        4        4              
Flag Coverage Δ
javascript 59.71% <ø> (ø)
python 95.43% <90.32%> (+0.05%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@aallan

aallan commented Mar 25, 2026

Copy link
Copy Markdown
Owner Author

The fix required changes at two layers: (1) the monomorphizer needed to know that decimal_from_int() returns "Decimal" not "Bool", and (2) the WASM inference layer needed to recognize @decimal slot references as i32 so the if block inside option_unwrap_or$Decimal gets the correct (result i32) annotation. Without both fixes, the generated WASM was invalid — values on the stack with no type annotation on the if block.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@TESTING.md`:
- Line 76: The README has inconsistent test counts for test_conformance.py (one
entry shows 300, another 295); open test_conformance.py to determine the actual
number of parametrized tests and then update all occurrences in TESTING.md (the
table row referencing `test_conformance.py` and the later “parametrized — 295
tests” sentence) so they match that verified count and ensure the surrounding
description still reads correctly.

In `@tests/conformance/manifest.json`:
- Around line 815-831: The manifest entry "ch09_decimal_generics" only tests
Decimal generics; add companion conformance programs that exercise
Option<Map<...>> and Option<Set<...>> so the opaque-handle monomorphization for
Map and Set is covered. Create minimal Vera programs (following
tests/conformance style) that use Option<Map<K,V>> and Option<Set<T>> wrappers
and perform simple operations, nesting map_new() and set_new() inside the
map/set operations (e.g., Option::Some(map_new(...)) /
Option::Some(set_new(...))) so the compiler can infer generic types; ensure the
new program IDs are added to manifest features similar to
"ch09_decimal_generics" and reference decimal/generic usage patterns already
present.

In `@tests/test_codegen.py`:
- Around line 8369-8373: The test is shadowing the first Ordering value with a
second `let `@Ordering`` so the `match `@Ordering.0` { ... Equal -> 0 ... }` never
exercises the Equal branch; change the test to preserve both comparison results
(e.g. store the result of `decimal_compare(decimal_from_int(5),
decimal_from_int(5))` in a distinct binding and use that binding in the `match`,
or compute the two comparisons separately so one `@Ordering` binding covers the
Equal case), update references to the original `@Ordering` binding (or rename
the second binding) so the `Equal` arm is actually reached and asserted.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 8c2a5516-c091-42e4-abea-9e95d805028a

📥 Commits

Reviewing files that changed from the base of the PR and between 4daac70 and 47e46ce.

⛔ Files ignored due to path filters (6)
  • docs/index.html is excluded by !docs/**
  • docs/index.md is excluded by !docs/**
  • docs/llms-full.txt is excluded by !docs/**
  • docs/llms.txt is excluded by !docs/**
  • docs/sitemap.xml is excluded by !docs/**
  • tests/conformance/ch09_decimal_generics.vera is excluded by !**/*.vera
📒 Files selected for processing (17)
  • CHANGELOG.md
  • CLAUDE.md
  • README.md
  • ROADMAP.md
  • SKILL.md
  • TESTING.md
  • pyproject.toml
  • scripts/check_readme_examples.py
  • scripts/check_spec_examples.py
  • spec/09-standard-library.md
  • tests/conformance/manifest.json
  • tests/test_codegen.py
  • tests/test_readme.py
  • vera/README.md
  • vera/__init__.py
  • vera/codegen/monomorphize.py
  • vera/wasm/inference.py
💤 Files with no reviewable changes (2)
  • README.md
  • vera/README.md

Comment thread TESTING.md
Comment thread tests/conformance/manifest.json
Comment thread tests/test_codegen.py Outdated
- Fix stale parametrized test count (295→300) in TESTING.md
- Fix test_decimal_compare_equal_and_greater to assert both Equal
  and Greater arms independently instead of shadowing
- Add Map/Set builtins to _infer_fncall_wasm_type for correct
  if-expression result type annotations
- Add 3 if-expr result type tests (Decimal, Map, Set) exercising
  the _infer_block_result_type paths

Co-Authored-By: Claude <noreply@anthropic.invalid>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@tests/test_codegen.py`:
- Around line 8452-8493: The tests test_decimal_if_expr_result_type,
test_map_if_expr_result_type, and test_set_if_expr_result_type only exercise the
then-branch; add corresponding assertions that exercise the else-branch at
runtime by creating a second variant of each source (or toggling the
if-condition) so the condition evaluates false and assert the expected else
result (e.g., decimal_from_int(0) for Decimal, empty map size 0 for Map, empty
set size 0 for Set); update the _run(...) assertions to verify both control-flow
paths for the opaque-handle inference paths in those tests.

In `@vera/wasm/inference.py`:
- Around line 277-294: The comment for map_get/map_contains is misleading: both
return "i32" but map_get is an Option heap pointer while map_contains is a
primitive Bool (i32). Update the handling in _infer_fncall_wasm_type (the
expr.name map case) to either split the branch or adjust the comment so it
states: map_get -> "i32" (Option/heap pointer) and map_contains -> "i32" (Bool
primitive), ensuring the comment correctly describes each symbol (map_get,
map_contains).

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 3dc908cd-1df3-4b2e-a21f-466f9a3cc7ac

📥 Commits

Reviewing files that changed from the base of the PR and between 47e46ce and 741f19e.

📒 Files selected for processing (3)
  • TESTING.md
  • tests/test_codegen.py
  • vera/wasm/inference.py

Comment thread tests/test_codegen.py
Comment thread vera/wasm/inference.py
aallan and others added 2 commits March 25, 2026 10:11
- Split map_get/map_contains in _infer_fncall_wasm_type with
  accurate comments (Option heap pointer vs Bool)
- Add else-branch tests for Decimal/Map/Set if-expressions
- Add map_get, map_size, set_contains inference tests
- Fix doc counts (2912 tests)

Co-Authored-By: Claude <noreply@anthropic.invalid>
Exercise remaining _infer_fncall_wasm_type branches for Map/Set
builtins in if-expression contexts. Fix doc counts (2916 tests).

Co-Authored-By: Claude <noreply@anthropic.invalid>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@tests/test_codegen.py`:
- Around line 8576-8721: The tests only exercise Option<Decimal> and
Ordering—add explicit monomorphization coverage for Map and Set by extending
TestDecimalMonomorphization (or creating a new TestMapSetMonomorphization) with
tests that mirror the Decimal cases: e.g., an option_unwrap_or<Map<...>> and
option_unwrap_or<Set<...>> using Some/None, match on Option<Map<...>> and
Option<Set<...>>, and simple Map/Set operations (insert/get, contains,
empty-case) to exercise codegen/runtime paths for map_insert/map_get and
set_insert/set_contains; ensure you reference the same helper builtins
(option_unwrap_or, match, map/get/insert, set/insert/contains) so the
monomorphizer generates Map/Set instantiations.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: f00e13a8-15e2-4aa5-8b7f-b70ac8f005ae

📥 Commits

Reviewing files that changed from the base of the PR and between 741f19e and 0913f97.

📒 Files selected for processing (3)
  • TESTING.md
  • tests/test_codegen.py
  • vera/wasm/inference.py

Comment thread tests/test_codegen.py
Tests exercise option_unwrap_or with decimal_div() and
decimal_from_string() passed directly (no let binding). Fix doc
counts (2918 tests).

Co-Authored-By: Claude <noreply@anthropic.invalid>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

♻️ Duplicate comments (1)
tests/test_codegen.py (1)

8630-8775: ⚠️ Potential issue | 🟡 Minor

Add direct generic coverage for Map and Set as opaque handles.

This class still only proves monomorphisation for Decimal/Ordering. The production change also extends opaque-handle specialisation to Map and Set, so a regression in option_unwrap_or<Map<...>> or option_unwrap_or<Set<...>> would still slip through here.

🔧 Minimal coverage to force `Map`/`Set` instantiations
 class TestDecimalMonomorphization:
@@
     def test_decimal_from_string_invalid_match(self) -> None:
         """decimal_from_string with invalid input returns None."""
         source = """
 public fn main(-> `@Int`)
   requires(true) ensures(true) effects(pure)
 {
   let `@Option`<Decimal> = decimal_from_string("not_a_number");
   match `@Option`<Decimal>.0 {
     None -> 1,
     Some(`@Decimal`) -> 0
   }
 }
 """
         assert _run(source) == 1
+
+    def test_option_unwrap_or_map(self) -> None:
+        """option_unwrap_or<Map<...>> preserves the Some value."""
+        source = """
+public fn main(-> `@Int`)
+  requires(true) ensures(true) effects(pure)
+{
+  let `@Map`<String, Int> = map_insert(map_new(), "k", 42);
+  let `@Option`<Map<String, Int>> = Some(`@Map`<String, Int>.0);
+  let `@Map`<String, Int> = option_unwrap_or(
+    `@Option`<Map<String, Int>>.0,
+    map_insert(map_new(), "fallback", 0));
+  option_unwrap_or(map_get(`@Map`<String, Int>.0, "k"), 0)
+}
+"""
+        assert _run(source) == 42
+
+    def test_option_unwrap_or_set(self) -> None:
+        """option_unwrap_or<Set<...>> preserves the Some value."""
+        source = """
+public fn main(-> `@Int`)
+  requires(true) ensures(true) effects(pure)
+{
+  let `@Set`<Int> = set_add(set_new(), 7);
+  let `@Option`<Set<Int>> = Some(`@Set`<Int>.0);
+  let `@Set`<Int> = option_unwrap_or(
+    `@Option`<Set<Int>>.0,
+    set_add(set_new(), 0));
+  if set_contains(`@Set`<Int>.0, 7) then { 1 } else { 0 }
+}
+"""
+        assert _run(source) == 1

As per coding guidelines, "Add codegen/runtime tests in tests/test_codegen.py for built-in functions covering normal cases, edge cases (empty inputs, zero values), and composition with other built-ins."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/test_codegen.py` around lines 8630 - 8775, Add tests to instantiate Map
and Set generics to force monomorphisation: inside the
TestDecimalMonomorphization class add new test methods that create let
`@Option`<Map<@Int,`@Int`>> = Some(map_from_entries([(decimal_from_int(1),
decimal_from_int(2))])) and let `@Option`<Map<@Int,`@Int`>> = None (and analogous
for `@Option`<Set<@Int>> using set_from_list([decimal_from_int(1)])/None), then
call option_unwrap_or(`@Option`<Map<...>>.0, map_empty() / set_empty()) and assert
the returned map/set contents (and default behavior) via _run; ensure tests
reference the symbols option_unwrap_or, Map, Set, map_from_entries,
set_from_list, map_empty, set_empty and add assertions for both Some and None
cases.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@tests/test_codegen.py`:
- Around line 8630-8775: Add tests to instantiate Map and Set generics to force
monomorphisation: inside the TestDecimalMonomorphization class add new test
methods that create let `@Option`<Map<@Int,`@Int`>> =
Some(map_from_entries([(decimal_from_int(1), decimal_from_int(2))])) and let
`@Option`<Map<@Int,`@Int`>> = None (and analogous for `@Option`<Set<@Int>> using
set_from_list([decimal_from_int(1)])/None), then call
option_unwrap_or(`@Option`<Map<...>>.0, map_empty() / set_empty()) and assert the
returned map/set contents (and default behavior) via _run; ensure tests
reference the symbols option_unwrap_or, Map, Set, map_from_entries,
set_from_list, map_empty, set_empty and add assertions for both Some and None
cases.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 7a2ea940-5857-45aa-860f-dea3b594726e

📥 Commits

Reviewing files that changed from the base of the PR and between 0913f97 and 5c4881a.

📒 Files selected for processing (2)
  • TESTING.md
  • tests/test_codegen.py

@aallan

aallan commented Mar 25, 2026

Copy link
Copy Markdown
Owner Author

Codecov patch is ~80% — the remaining uncovered lines are structural and cannot be meaningfully exercised:

  1. map_contains/set_contains returns in _infer_fncall_wasm_type (2 lines) — contains operations return Bool which is always used as a condition in if expressions, never as a block result. The inference function is only called when a FnCall is the direct block result expression.

  2. _BUILTIN_PARAMETERIZED_RETURNS lookup in _get_arg_type_info (3 lines) — this path resolves T=Decimal from Option<T> parameters when the Option comes from a direct FnCall like decimal_div(). In practice, T is always already resolved from the second parameter (the default value), so this lookup is defensive but unreachable.

  3. _infer_block_result_type opaque handle check (2 lines) — reached via the SlotRef path instead of the explicit check, since let-bound variables are SlotRefs.

Project coverage is 95.36% — only -0.01% from baseline. These entries are correct and defensive, but the compiler's dispatch structure means they're bypassed by the normal code paths.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

♻️ Duplicate comments (1)
tests/test_codegen.py (1)

8630-8805: ⚠️ Potential issue | 🟠 Major

Coverage gap: generic monomorphization is still Decimal-only.

Line 8630 onward adds solid Option<Decimal>/Ordering checks, but there is still no direct option_unwrap_or<Map<...>> or option_unwrap_or<Set<...>> runtime coverage. That leaves the Map/Set opaque-handle specialisation path unguarded.

Suggested minimal additions
 class TestDecimalMonomorphization:
@@
     def test_decimal_from_string_inline_unwrap(self) -> None:
@@
         assert _run(source) == 1
+
+    def test_option_unwrap_or_map(self) -> None:
+        """option_unwrap_or<Map<...>> keeps the Some value."""
+        source = """
+public fn main(-> `@Int`)
+  requires(true) ensures(true) effects(pure)
+{
+  let `@Map`<String, Int> = map_insert(map_new(), "k", 42);
+  let `@Option`<Map<String, Int>> = Some(`@Map`<String, Int>.0);
+  let `@Map`<String, Int> = option_unwrap_or(
+    `@Option`<Map<String, Int>>.0,
+    map_insert(map_new(), "fallback", 0));
+  option_unwrap_or(map_get(`@Map`<String, Int>.0, "k"), 0)
+}
+"""
+        assert _run(source) == 42
+
+    def test_option_unwrap_or_set(self) -> None:
+        """option_unwrap_or<Set<...>> keeps the Some value."""
+        source = """
+public fn main(-> `@Int`)
+  requires(true) ensures(true) effects(pure)
+{
+  let `@Set`<Int> = set_add(set_new(), 7);
+  let `@Option`<Set<Int>> = Some(`@Set`<Int>.0);
+  let `@Set`<Int> = option_unwrap_or(
+    `@Option`<Set<Int>>.0,
+    set_add(set_new(), 0));
+  if set_contains(`@Set`<Int>.0, 7) then { 1 } else { 0 }
+}
+"""
+        assert _run(source) == 1

As per coding guidelines, "Add codegen/runtime tests in tests/test_codegen.py for built-in functions covering normal cases, edge cases (empty inputs, zero values), and composition with other built-ins."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/test_codegen.py` around lines 8630 - 8805, Add tests that exercise
monomorphization for opaque-handle types by creating Option<Map<...>> and
Option<Set<...>> cases and calling option_unwrap_or on them (both Some and None
paths) so the Map/Set specialization path is covered; specifically add minimal
test methods in tests/test_codegen.py (near class TestDecimalMonomorphization)
that construct a Some(Map) and Some(Set), unwrap them with option_unwrap_or,
assert behavior via a map operation (e.g., map_get or equivalent) and a set
operation (e.g., set_contains or equivalent), and add corresponding None/default
tests to hit the default-return branch.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@TESTING.md`:
- Line 9: Update the Tests row in TESTING.md where it currently reads "**Tests**
| 2,918 across 26 files (~33,100 lines of test code)" to reconcile with the
verified run: either change the count to reflect the verified run ("2,903 tests
passed") or explicitly mark the row as an inventory/total test count (e.g.,
"2,918 tests (total in repo) — 2,903 passed in verified run") so the metric is
not stale or ambiguous; ensure the edited text includes both the numeric
reference and clear context so readers understand whether the number represents
inventory or run results.

---

Duplicate comments:
In `@tests/test_codegen.py`:
- Around line 8630-8805: Add tests that exercise monomorphization for
opaque-handle types by creating Option<Map<...>> and Option<Set<...>> cases and
calling option_unwrap_or on them (both Some and None paths) so the Map/Set
specialization path is covered; specifically add minimal test methods in
tests/test_codegen.py (near class TestDecimalMonomorphization) that construct a
Some(Map) and Some(Set), unwrap them with option_unwrap_or, assert behavior via
a map operation (e.g., map_get or equivalent) and a set operation (e.g.,
set_contains or equivalent), and add corresponding None/default tests to hit the
default-return branch.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 69d5086b-699b-4cfa-b3b4-3673a55ae60a

📥 Commits

Reviewing files that changed from the base of the PR and between 5c4881a and 866b15f.

📒 Files selected for processing (2)
  • TESTING.md
  • tests/test_codegen.py

Comment thread TESTING.md Outdated
aallan and others added 5 commits March 25, 2026 11:02
- test_option_unwrap_or_map: xfail — confirms #341 gap for Map
- test_match_option_set: passes — match is inline, no monomorphization
  needed for Set handles
- Update TESTING.md counts (2920 tests)

Co-Authored-By: Claude <noreply@anthropic.invalid>
Map/Set option_unwrap_or monomorphization gap is a known bug.
Decimal case is fixed; Map/Set still fail (xfail test confirms).

Co-Authored-By: Claude <noreply@anthropic.invalid>
Partial fix only — Decimal works, Map/Set still have the gap.
PR no longer closes #341.

Co-Authored-By: Claude <noreply@anthropic.invalid>
Co-Authored-By: Claude <noreply@anthropic.invalid>
#293 (combinator bare-constructor inference) is a bug — the type
checker should infer T from the second argument. Added bug label
to #293 and #341 on GitHub.

Updated check_limitations_sync.py to also check the Bugs table,
so issues tracked in either table count as documented.

Co-Authored-By: Claude <noreply@anthropic.invalid>
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.

1 participant