Add Map<K, V> collection type (#62, PR 1/3)#332
Conversation
Implement Map<K, V> as an opaque host-import type with Eq<K> + Hash<K> ability constraints. Keys must be hashable primitives; values may be any type. All operations are pure (functional copy-on-write semantics). Built-in operations: map_new, map_insert, map_get, map_contains, map_remove, map_size, map_keys, map_values. Host runtime maintains Python dicts (wasmtime) / JS Maps (browser) behind i32 handles. Type inference fix: skip ADT args whose TypeVars match the callee own forall vars during unification (prevents map_new returning Map<K, V> from poisoning inference). Bidirectional coercion for FnCall with unresolved TypeVars when expected type is available. Compiler changes: checker/resolution.py (built-in type + inference fix), environment.py (function signatures), codegen pipeline (compilability, core, assembly, functions, modules), wasm/ (calls, context, inference), smt.py (uninterpreted map_size), browser/runtime.mjs (JS host bindings). Tests: 57 conformance programs (was 56), 2754 total tests (was 2749). New ch09_map.vera exercises all 8 operations across 10 test functions. Spec 9.4.3 updated (no longer Future), SKILL.md documents Map operations, README.md features table updated. Closes #62 (Map portion; Set and Decimal deferred to follow-up PRs). Co-Authored-By: Claude <noreply@anthropic.invalid>
|
Note Reviews pausedIt 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 Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughAdds first-class, pure functional Map<K, V> support across spec, docs, checker, environment, codegen, WASM translation, SMT, runtime, tests, scripts, and a new conformance test; introduces builtins Changes
Sequence DiagramsequenceDiagram
autonumber
participant Dev as Developer
participant Checker as Type Checker
participant Codegen as Code Generator
participant WASM as WASM Emitter
participant Runtime as Browser Runtime
Dev->>Checker: Write Vera using map_* builtins
Checker->>Checker: Resolve Map<K,V>, enforce Eq/Hash constraints
Checker-->>Codegen: Type-checked AST (map ops present)
Codegen->>Codegen: Record _map_ops_used and _map_imports
Codegen->>WASM: Request monomorphized import names
WASM->>WASM: Register per-type imports, emit calls returning i32/ptrs
WASM-->>Runtime: Module imports like "vera.map_insert$k<int>_v<string>"
Runtime->>Runtime: buildImportObject matches regex, implements mapStore[handle]
Runtime-->>Dev: Returns i32 handle / Option pointer / arrays
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Suggested labels
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #332 +/- ##
==========================================
- Coverage 95.88% 95.40% -0.48%
==========================================
Files 45 45
Lines 15150 15626 +476
==========================================
+ Hits 14526 14908 +382
- Misses 624 718 +94 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
Eight spec code blocks in chapter 9 were no longer allowlisted after the Map section insertion shifted their line numbers. Add the correct entries. Co-Authored-By: Claude <noreply@anthropic.invalid>
There was a problem hiding this comment.
Actionable comments posted: 6
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
vera/checker/resolution.py (1)
163-173: 🧹 Nitpick | 🔵 TrivialCorrect logic for forall_vars propagation.
The conversion to
set(forall_vars)enables O(1) membership checks in the unification loop.Static analysis flags
zip()withoutstrict=(Ruff B905). Ifparam_typesandarg_typesare guaranteed equal length by the caller, this is fine. Otherwise, considerstrict=Trueor length assertions for defensive checking in compiler code.🔧 Optional: Add strict=True for defensive checking
- for param_ty, arg_ty in zip(param_types, arg_types): + for param_ty, arg_ty in zip(param_types, arg_types, strict=True):🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@vera/checker/resolution.py` around lines 163 - 173, The conversion of forall_vars to a set (forall_set = set(forall_vars)) is fine for O(1) membership checks, but the iteration uses zip(param_types, arg_types) without strict checking; update _infer_type_args to guard against mismatched lengths by either using zip(..., strict=True) or asserting len(param_types) == len(arg_types) before the loop, and keep the existing behavior of skipping None/UnknownType arg_types and calling _unify_for_inference(param_ty, arg_ty, mapping, forall_set).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@scripts/check_skill_examples.py`:
- Around line 114-120: The ALLOWLIST dict has duplicate key 1326 so the second
entry ("Wrong: if/else without braces (bare expression)") silently overwrites
the first ("Wrong: non-exhaustive match (missing arm)"); locate the ALLOWLIST in
scripts/check_skill_examples.py and correct one of the numeric keys (either
change the second 1326 to the actual fence line number in SKILL.md for the
if/else fragment or change the first 1326 to the correct fence line for the
non-exhaustive match) so each entry (e.g., the entries labeled "Wrong:
non-exhaustive match (missing arm)" and "Wrong: if/else without braces (bare
expression)") has a unique key matching its fence line in SKILL.md.
In `@spec/09-standard-library.md`:
- Around line 256-259: The doc overstates Map support: update the Map section so
it matches the current ABI instead of claiming "Values may be any type" —
specify that keys require Eq and Hash and are limited to the presently
marshalled primitives (i64/Int/Nat mapping to i64, f64/Float64, String) and that
map entries in the ABI are marshalled as single i32 handles for opaque/value
shapes; change the sentence about values to state that values must be types
supported by the host marshalling (primitive f64/i64/String or single-i32
handle) and keep the note that Map is opaque and that map_insert and map_remove
are pure functional operations (retain those symbols Map<K,V>, Eq, Hash,
map_insert, map_remove to locate the text).
- Around line 260-275: Add a small, valid vera example demonstrating Map usage
so CI can parse it: include a vera block that defines a function (e.g.,
map_demo) that calls map_new(), uses map_insert(...) to add a key ("answer", 42)
and then uses map_get(...) to retrieve it, returning the Some branch value (or 0
on None); reference the builtins map_new, map_insert, and map_get in that
example and ensure the function signature and match arms use the correct
`@Map`<String, Int>, `@Int` and Some/None patterns so the snippet is syntactically
valid for the parser.
In `@vera/codegen/api.py`:
- Around line 1064-1069: The helper _define_map_values is registering imports
with the wrong name suffix ('map_values$k{vt}') while the dispatched ops and
browser runtime expect 'map_values$v{vt}'; update the registration name in
_define_map_values to use 'v' (i.e., f"map_values$v{vt}") so Wasmtime defines
the symbol the module imports, and apply the same fix to the other identical
helper block (the second occurrence around the other define helper) to ensure
both registrations use the 'v' spelling.
In `@vera/wasm/calls.py`:
- Around line 7263-7288: _map_wasm_tag currently only treats "String" as a
two-word ABI and falls through Arrays to "b", causing mismatched import
signatures for map_insert/map_get/map_values when translate_expr pushes
(ptr,len). Modify _map_wasm_tag to detect Array types (e.g. string forms like
"Array<...>" or whatever Array syntax your type strings use) and return the "s"
tag for them (so _map_wasm_types will emit ["i32","i32"]) ensuring the WASM
import signature matches translate_expr's (ptr,len) ABI used by
map_insert/map_get/map_values.
---
Outside diff comments:
In `@vera/checker/resolution.py`:
- Around line 163-173: The conversion of forall_vars to a set (forall_set =
set(forall_vars)) is fine for O(1) membership checks, but the iteration uses
zip(param_types, arg_types) without strict checking; update _infer_type_args to
guard against mismatched lengths by either using zip(..., strict=True) or
asserting len(param_types) == len(arg_types) before the loop, and keep the
existing behavior of skipping None/UnknownType arg_types and calling
_unify_for_inference(param_ty, arg_ty, mapping, forall_set).
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro
Run ID: 6923cfc4-26c1-466a-b12e-5d2a7f7337eb
⛔ Files ignored due to path filters (3)
docs/llms-full.txtis excluded by!docs/**docs/sitemap.xmlis excluded by!docs/**tests/conformance/ch09_map.verais excluded by!**/*.vera
📒 Files selected for processing (23)
CLAUDE.mdCONTRIBUTING.mdREADME.mdSKILL.mdTESTING.mdscripts/check_skill_examples.pyscripts/check_spec_examples.pyspec/09-standard-library.mdtests/conformance/manifest.jsonvera/browser/runtime.mjsvera/checker/expressions.pyvera/checker/resolution.pyvera/codegen/api.pyvera/codegen/assembly.pyvera/codegen/compilability.pyvera/codegen/core.pyvera/codegen/functions.pyvera/codegen/modules.pyvera/environment.pyvera/smt.pyvera/wasm/calls.pyvera/wasm/context.pyvera/wasm/inference.py
👮 Files not reviewed due to content moderation or server errors (8)
- CLAUDE.md
- TESTING.md
- vera/wasm/inference.py
- SKILL.md
- vera/smt.py
- vera/codegen/assembly.py
- vera/codegen/core.py
- scripts/check_spec_examples.py
|
@coderabbitai Addressed all 6 review comments in the next push:
|
- 22 new unit tests (9 checker, 13 codegen) exercise all Map operations
- Fix map_values naming: use $v{vt} convention (was $k{vt})
- Fix _map_wasm_tag: handle Array<T> as pair-ABI type
- Fix _infer_map_*_from_map_arg: use SlotRef.type_args (typed AST)
- Fix _slot_name_to_wasm_type: handle Map/Set slot names
- Fix assembly.py: set _needs_memory when map ops used
- Narrow spec 9.4.3: values must have WASM representation
- Add parsable vera example to spec Map section
- Fix duplicate allowlist keys in check_spec_examples.py
- Fix duplicate allowlist key 1326 in check_skill_examples.py
- Split Decimal to separate issue #333
- Doc counts: 2776 tests (was 2754)
Co-Authored-By: Claude <noreply@anthropic.invalid>
|
Tip For best results, initiate chat on the files or code changes. 🧩 Analysis chain🏁 Script executed: Length of output: 1005 🏁 Script executed: Length of output: 10167 🏁 Script executed: Length of output: 8619 🏁 Script executed: Length of output: 10719
✅ Verified clean
❌ Still outstandingFix 1 — Duplicate key The duplicate is still there. Lines 116 and 119 both carry # line 116 — will be LOST at runtime
1326: ("FRAGMENT", "Wrong: non-exhaustive match (missing arm)"),
# ...
# line 119 — wins, silently shadowing line 116
1326: ("FRAGMENT", "Wrong: if/else without braces (bare expression)"),The entry at line 116 needs a different key (e.g. Fix 4 —
However, m = name.match(/^map_values\$v(.)$/);…which expects The fix is either to update Fix 5 — The updated function in def _map_wasm_tag(vera_type: str | None) -> str:
if vera_type in ("Int", "Nat"):
return "i"
if vera_type == "Float64":
return "f"
if vera_type in ("String",):
return "s" # i32_pair
# Bool, Byte, ADTs, Map handles → i32
return "b" # ← Array falls here — wrong!
if vera_type in ("String",) or (
vera_type is not None and vera_type.startswith("Array")
):
return "s" # i32_pairWithout this, Fix 3 — Lines 254–279 of the spec contain only the prose and operations table for |
|
@coderabbitai All four items were fixed in commit 09971e8 (the third commit on this PR). Please re-review — the fixes are in the latest push:
|
|
Tip For best results, initiate chat on the files or code changes. 🧩 Analysis chain🏁 Script executed: Length of output: 6022
All six original review comments and all four follow-up issues are now resolved. The implementation looks correct and consistent across the codegen pipeline, host runtimes, and specification. |
Remove 5 duplicate dict keys that caused silent shadowing. When fix_allowlists shifted entries to new line numbers, some landed on the same line as entries in a different section of the allowlist. Co-Authored-By: Claude <noreply@anthropic.invalid>
There was a problem hiding this comment.
Actionable comments posted: 7
♻️ Duplicate comments (1)
vera/wasm/calls.py (1)
7397-7402:⚠️ Potential issue | 🔴 CriticalDo not fall back to
bwhenMap<K, V>recovery fails.Listing
map_newin Lines 7441-7446 does not actually recover its type — zero-argmap_new()still returnsNonehere, and other non-SlotRefmap expressions do the same.map_get/map_keys/map_valuesthen mangle that as...b..., which gives well-typed code the wrong host ABI instead of failing translation. RecoverK/Vfrom the typed expression more generally, or returnNonefrom these translators when recovery fails.Also applies to: 7420-7447, 7513-7537, 7547-7568
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@scripts/check_spec_examples.py`:
- Around line 180-186: The spec index contains duplicate entries and incorrect
line numbers for several string signatures; remove redundant duplicates so each
signature appears only once and correct their line numbers to match the actual
spec: update the entries for string_upper and string_lower to distinct, correct
line numbers (they were mistakenly duplicated), ensure string_replace,
string_split, string_join, string_from_char_code, and string_repeat each have a
single unique, correct line number (remove the duplicate set from the "main
list" or "String transformation" section), and also delete the duplicate
infinity entry mentioned (keep only the correct one); locate and edit the
mapping that contains keys like "string_upper", "string_lower",
"string_replace", "string_split", "string_join", "string_from_char_code",
"string_repeat" to perform these removals/line-number corrections.
In `@tests/test_checker.py`:
- Around line 1526-1601: The test class TestMapCollection is missing negative
and inference tests: add one test (e.g., test_map_wrong_types) that calls
map_insert with a key/value type mismatch and asserts a type error via
_check_err (look for messages like "mismatched" or "expects"), and add another
test (e.g., test_map_infer_map_new) that uses a bare map_new() in a context
expecting `@Map`<K,V> (use let `@Map`<String, Nat> = map_new() or an expression like
map_size(map_new()) with an expected Map type) and assert it type-checks via
_check_ok; place these new tests inside the TestMapCollection class alongside
the existing test_map_wrong_arity, referencing the same helper functions
(_check_ok/_check_err) so the checker exercises wrong-type path and zero-arg
map_new() inference.
In `@tests/test_codegen.py`:
- Around line 7616-7747: Add three tests to TestMapCollection to cover empty-map
edge cases and typed-context inference: (1) add test_map_keys_empty that asserts
array_length(map_keys(map_new())) == 0 to verify map_keys on an empty map
returns an empty array; (2) add test_map_values_empty that asserts
array_length(map_values(map_new())) == 0 to verify map_values on an empty map
returns an empty array; and (3) add test_map_new_inference that forces
type-inference for a bare map_new (e.g. by binding let `@Map`<String, Nat> =
map_new() or using map_get/map_insert on map_new()) and asserts expected
behavior (size 0 or map_keys length 0) to cover the typed-context path; place
these new tests alongside the existing TestMapCollection methods (see map_keys,
map_values, map_new references) so they run with the other map built-in tests.
In `@vera/codegen/api.py`:
- Around line 869-874: The mapping and codegen treat the "s" suffix as a generic
two-i32 pair but the vt == "s" branches always assume String by calling
_read_wasm_string, _alloc_option_some_string, and _alloc_array_of_strings; to
fix, either propagate the concrete pair-ABI type through the import/type name
(e.g., extend _VAL_WASM_TYPES and the import/type encoding to include the inner
concrete type for "s" so codegen uses the appropriate pair-ABI readers/writers),
or explicitly reject non-String usages of "s" during codegen (raise an error
when encountering vt == "s" for types other than String in functions that
generate insert/get/values code such as the code paths touched around
_VAL_WASM_TYPES, the vt=="s" branches, and callers of
_read_wasm_string/_alloc_option_some_string/_alloc_array_of_strings); update the
affected sections (including the similar blocks around lines referenced) to use
one consistent approach.
- Around line 754-761: The _map_store/_map_next_handle allocation never reclaims
handles causing unbounded growth; add a _map_free function and a reusable
free-handle pool so _map_alloc reuses freed handles instead of always
incrementing _map_next_handle, and ensure map_remove (and map_new/map_insert
where items are removed or maps become empty) calls _map_free when a map is no
longer reachable (e.g., when its dict is emptied or when host releases a handle
during execute()). Update all places that drop a handle to call _map_free and/or
push the handle onto the free list so entries in _map_store are deleted (or set
None) and memory is reclaimed; reference symbols: _map_store, _map_next_handle,
_map_alloc, _map_free (new), map_new, map_insert, map_remove, and execute().
- Around line 826-836: The map currently stores ADT pointers as raw i32 payloads
(e.g., via _alloc_array_of_i32 and callers that call _map_store), which lets the
mark-sweep collector free those objects and produce dangling pointers on
subsequent map_get/map_values; fix by either (A) ensuring any heap-backed ADT
pointer placed into an i32 bucket is owned/pinned by making a deep copy into
WASM-managed memory and storing that owned pointer (allocate with _call_alloc
and write the ADT bytes before storing), or (B) restrict the 'b' (i32) cases to
only non-pointer scalar types and refuse/store differently for ADTs; update
_alloc_array_of_i32, the call sites that populate i32 arrays, and
_map_store/_map_get/_map_values to implement the chosen strategy so the GC
cannot collect referenced ADTs prematurely.
In `@vera/wasm/calls.py`:
- Around line 7274-7277: The conditional that treats any type name beginning
with "Array" as the actual Array type is too broad; restrict the pair-ABI check
to only the real Array type by changing the predicate that uses
vera_type.startswith("Array") to an equality check vera_type == "Array" (keep
the existing checks for "String" and the surrounding conditional), so the import
mangling logic that produces 's' only runs for the true Array type and not for
user types like ArrayList.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro
Run ID: c75fad6a-5d52-4237-9540-8555d1c814c8
📒 Files selected for processing (11)
README.mdTESTING.mdscripts/check_skill_examples.pyscripts/check_spec_examples.pyspec/09-standard-library.mdtests/test_checker.pytests/test_codegen.pyvera/codegen/api.pyvera/codegen/assembly.pyvera/wasm/calls.pyvera/wasm/inference.py
Two allowlist entries pointed at prose lines instead of code fences. Remove int_to_float entry (no language tag, heuristic handles it) and fix float_is_nan to correct fence line 876. Co-Authored-By: Claude <noreply@anthropic.invalid>
…ix fix - Add 4 new tests: bare map_new() inference, empty keys/values, get-after-remove - Fix _map_wasm_tag: use exact "Array" match (not startsWith) to avoid matching user types like ArrayList - Revert Array<T> as Map values — host runtime only round-trips String for the "s" pair-ABI tag. Documented as future enhancement in spec. - Doc counts: 2780 tests Co-Authored-By: Claude <noreply@anthropic.invalid>
Co-Authored-By: Claude <noreply@anthropic.invalid>
There was a problem hiding this comment.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
TESTING.md (2)
80-80:⚠️ Potential issue | 🟡 MinorUpdate stale conformance count in narrative text.
This line still says 56 programs, but the file now documents 57 elsewhere. Please align this sentence with the updated suite size.As per coding guidelines, Markdown files must be reviewed for factual accuracy against the codebase.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@TESTING.md` at line 80, Update the stale count in the conformance suite narrative: change the numeric literal "56" to "57" in the sentence that reads "The conformance suite is a collection of 56 small, focused programs in `tests/conformance/`..." so it matches the documented suite size elsewhere in TESTING.md; ensure the sentence now reads "57" and run a quick grep for the phrase "conformance suite" or "tests/conformance/" to confirm no other mismatched counts remain.
148-148:⚠️ Potential issue | 🟡 MinorParametrised test count is now outdated.
test_conformance.pyis listed as 285 tests (Line 73), so this inline note should no longer say 280.As per coding guidelines, Markdown files must be reviewed for factual accuracy against the codebase.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@TESTING.md` at line 148, The comment notes the inline test-count in TESTING.md ("# Via pytest (parametrized — 280 tests)") is stale; update that header to match the current count reported for test_conformance.py (285) or make the note generic. Edit the header text in TESTING.md to replace "280 tests" with "285 tests" (or change to a non-numeric phrase like "parametrized tests") so the markdown accurately reflects test_conformance.py's test count.
♻️ Duplicate comments (4)
vera/wasm/calls.py (1)
7263-7277:⚠️ Potential issue | 🔴 CriticalRecover
Map<K, V>from the inferred expression type, not from hard-coded call shapes.
_infer_map_value_from_map_arg()still only recognises three built-ins, and_infer_map_key_from_map_arg()peels the first argument off any otherast.FnCall. That still misses the typedmap_new()flow from this PR and user functions returning maps. When either helper returnsNone,_map_wasm_tag()silently falls back to"b", somap_get/map_keys/map_valuescan register the wrong host import instead of failing cleanly.Also applies to: 7436-7444, 7560-7565
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@vera/wasm/calls.py` around lines 7263 - 7277, The current mapping falls back to "b" when helpers can't find map types by call-shape; update _infer_map_value_from_map_arg and _infer_map_key_from_map_arg to recover Map<K,V> from the expression's inferred type information (e.g. inspect the AST node's resolved/inferred type or type annotation, and specifically handle map_new and user functions that return maps) rather than only pattern-matching built-in call shapes, and change _map_wasm_tag to not silently default to "b" when the key/value type is unknown — instead propagate a clear error or None so callers (map_get, map_keys, map_values) can fail fast and register the correct host import or raise a meaningful error; ensure the same fix is applied to the other occurrences that call these helpers.tests/test_codegen.py (1)
7616-7777:⚠️ Potential issue | 🟡 MinorAdd a direct codegen/runtime test for bare
map_new()type inference.The suite still does not directly exercise the zero-arg generic inference path (
let@Map<..., ...> = map_new();) that this PR introduces. That leaves a regression gap intests/test_codegen.py.Suggested test addition
class TestMapCollection: @@ + def test_map_new_infers_from_context(self) -> None: + """Bare map_new() infers K/V from expected map type.""" + source = """ +public fn main(-> `@Int`) + requires(true) ensures(true) effects(pure) +{ + let `@Map`<String, Nat> = map_new(); + map_size(`@Map`<String, Nat>.0) +} +""" + assert _run(source) == 0As per coding guidelines, "Add codegen/runtime tests in
tests/test_codegen.pyfor built-in functions — cover 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 7616 - 7777, Add a new test method in the TestMapCollection class that directly exercises the zero-arg generic inference path for map_new(): construct a bare map via "let `@Map`<SomeKey, SomeVal> = map_new()" (e.g. `@Map`<String, Nat>), then call a built-in like map_size or map_keys/map_values or map_get to verify correct runtime/codegen behavior (assert expected value, e.g. size 0 or lookup returns None). Reference the existing test names and helpers (TestMapCollection, map_new, map_size, map_keys, map_values, map_get) so the new test mirrors the style of the other map tests and covers the bare map_new() inference scenario.tests/test_checker.py (1)
1526-1613:⚠️ Potential issue | 🟠 MajorMissing wrong-type coverage for Map built-ins in checker tests.
Line 1595 adds an arity failure case, but this class still lacks a wrong-argument-type case (e.g., wrong
Vformap_insert). Please add at least one_check_errtest for type mismatch so the new Map built-ins exercise both success and wrong-type paths.🧪 Suggested test addition
class TestMapCollection: @@ def test_map_wrong_arity(self) -> None: """map_insert with wrong number of args produces error.""" _check_err(""" private fn foo(`@Unit` -> `@Int`) requires(true) ensures(true) effects(pure) { map_size(map_insert(map_new(), "k")) } """, "expects") + + def test_map_insert_wrong_value_type(self) -> None: + """map_insert rejects value type mismatch against V.""" + _check_err(""" +private fn foo(`@Unit` -> `@Int`) + requires(true) ensures(true) effects(pure) +{ + let `@Map`<String, Nat> = map_new(); + map_size(map_insert(`@Map`<String, Nat>.0, "k", "oops")) +} +""", "expected Nat")As per coding guidelines, "Add type checker tests in
tests/test_checker.pyfor built-in functions — at minimum, one test with correct types and one with wrong argument types".🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@tests/test_checker.py` around lines 1526 - 1613, Add a negative type-check test in the TestMapCollection class that mirrors test_map_insert_and_size but supplies a wrong value type to map_insert so the checker rejects it; create a new test (e.g., test_map_insert_wrong_value_type) that calls _check_err with an expression using map_insert(map_new(), "k", "not_a_nat") (or otherwise wrong V type) and assert an appropriate type-mismatch error (use the same error substring style as test_map_wrong_arity), referencing map_insert and map_new so both success and wrong-type paths are covered.scripts/check_spec_examples.py (1)
64-67:⚠️ Potential issue | 🔴 CriticalDuplicate
ALLOWLISTkey still causes silent shadowing and Ruff F601 failure.Line 166 repeats the exact key from Line 65:
("09-standard-library.md", 876). In Python dict literals, the later entry overrides the earlier one, so one mapping is silently discarded.🛠️ Suggested fix
- ("09-standard-library.md", 876): "FRAGMENT", # float_is_nan signature (no body) + # 876 handled in main section above (float_is_nan) ("09-standard-library.md", 893): "FRAGMENT", # float_is_infinite signature (no body) ("09-standard-library.md", 912): "FRAGMENT", # nan signature (no body)Run this to confirm duplicate tuple keys in
ALLOWLIST:#!/bin/bash python - <<'PY' import ast from pathlib import Path path = Path("scripts/check_spec_examples.py") tree = ast.parse(path.read_text(encoding="utf-8")) for node in tree.body: if isinstance(node, ast.AnnAssign) and isinstance(node.target, ast.Name) and node.target.id == "ALLOWLIST": d = node.value if not isinstance(d, ast.Dict): continue seen = {} for k in d.keys: if isinstance(k, ast.Tuple) and len(k.elts) == 2 and all(isinstance(e, ast.Constant) for e in k.elts): key = (k.elts[0].value, k.elts[1].value) line = k.lineno if key in seen: print(f"DUPLICATE {key}: first at line {seen[key]}, duplicated at line {line}") else: seen[key] = line PYAs per coding guidelines, "Flag actual duplicate keys within a single dict".
Also applies to: 166-167
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@scripts/check_spec_examples.py` around lines 64 - 67, The ALLOWLIST dict in scripts/check_spec_examples.py contains duplicate tuple keys (e.g. ("09-standard-library.md", 876) appears twice) which causes silent shadowing and hides entries; open the ALLOWLIST definition and remove or consolidate the duplicated tuple key so each (filename, line) tuple appears only once, ensuring any associated value strings (e.g. "FRAGMENT") are preserved or merged as appropriate; validate by scanning the ALLOWLIST literal in the file (symbol: ALLOWLIST) and run the provided duplicate-detection snippet or add a small runtime/assertion to fail fast if duplicate tuple keys are present.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@vera/wasm/calls.py`:
- Around line 7430-7435: The Map<...> fallback in the block that reads name =
expr.type_name and then uses split(", ", 1) is not nesting-aware and will break
on inner commas (e.g., Map<Result<Int, Bool>, String>); fix by replacing that
raw split with a depth-aware top-level-comma finder that walks the string and
splits at the comma where nesting depth is zero, or better yet use the parsed
TypeExpr data (e.g., expr.type_args or equivalent parsed fields) instead of
parsing expr.type_name raw string; update the same pattern used later (the
repeated code around the other Map fallback) to the same depth-aware or
parsed-data approach.
---
Outside diff comments:
In `@TESTING.md`:
- Line 80: Update the stale count in the conformance suite narrative: change the
numeric literal "56" to "57" in the sentence that reads "The conformance suite
is a collection of 56 small, focused programs in `tests/conformance/`..." so it
matches the documented suite size elsewhere in TESTING.md; ensure the sentence
now reads "57" and run a quick grep for the phrase "conformance suite" or
"tests/conformance/" to confirm no other mismatched counts remain.
- Line 148: The comment notes the inline test-count in TESTING.md ("# Via pytest
(parametrized — 280 tests)") is stale; update that header to match the current
count reported for test_conformance.py (285) or make the note generic. Edit the
header text in TESTING.md to replace "280 tests" with "285 tests" (or change to
a non-numeric phrase like "parametrized tests") so the markdown accurately
reflects test_conformance.py's test count.
---
Duplicate comments:
In `@scripts/check_spec_examples.py`:
- Around line 64-67: The ALLOWLIST dict in scripts/check_spec_examples.py
contains duplicate tuple keys (e.g. ("09-standard-library.md", 876) appears
twice) which causes silent shadowing and hides entries; open the ALLOWLIST
definition and remove or consolidate the duplicated tuple key so each (filename,
line) tuple appears only once, ensuring any associated value strings (e.g.
"FRAGMENT") are preserved or merged as appropriate; validate by scanning the
ALLOWLIST literal in the file (symbol: ALLOWLIST) and run the provided
duplicate-detection snippet or add a small runtime/assertion to fail fast if
duplicate tuple keys are present.
In `@tests/test_checker.py`:
- Around line 1526-1613: Add a negative type-check test in the TestMapCollection
class that mirrors test_map_insert_and_size but supplies a wrong value type to
map_insert so the checker rejects it; create a new test (e.g.,
test_map_insert_wrong_value_type) that calls _check_err with an expression using
map_insert(map_new(), "k", "not_a_nat") (or otherwise wrong V type) and assert
an appropriate type-mismatch error (use the same error substring style as
test_map_wrong_arity), referencing map_insert and map_new so both success and
wrong-type paths are covered.
In `@tests/test_codegen.py`:
- Around line 7616-7777: Add a new test method in the TestMapCollection class
that directly exercises the zero-arg generic inference path for map_new():
construct a bare map via "let `@Map`<SomeKey, SomeVal> = map_new()" (e.g.
`@Map`<String, Nat>), then call a built-in like map_size or map_keys/map_values or
map_get to verify correct runtime/codegen behavior (assert expected value, e.g.
size 0 or lookup returns None). Reference the existing test names and helpers
(TestMapCollection, map_new, map_size, map_keys, map_values, map_get) so the new
test mirrors the style of the other map tests and covers the bare map_new()
inference scenario.
In `@vera/wasm/calls.py`:
- Around line 7263-7277: The current mapping falls back to "b" when helpers
can't find map types by call-shape; update _infer_map_value_from_map_arg and
_infer_map_key_from_map_arg to recover Map<K,V> from the expression's inferred
type information (e.g. inspect the AST node's resolved/inferred type or type
annotation, and specifically handle map_new and user functions that return maps)
rather than only pattern-matching built-in call shapes, and change _map_wasm_tag
to not silently default to "b" when the key/value type is unknown — instead
propagate a clear error or None so callers (map_get, map_keys, map_values) can
fail fast and register the correct host import or raise a meaningful error;
ensure the same fix is applied to the other occurrences that call these helpers.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro
Run ID: d8a1e877-2b3f-4ced-8b3f-9e3d0a1ad30a
📒 Files selected for processing (7)
README.mdTESTING.mdscripts/check_spec_examples.pyspec/09-standard-library.mdtests/test_checker.pytests/test_codegen.pyvera/wasm/calls.py
Replace naive split(", ", 1) with nesting-aware parser that tracks
angle-bracket depth. Handles Map<Result<Int, Bool>, String> correctly.
Primary path still uses ast.SlotRef.type_args; this fixes the string
fallback for monomorphized composite slot names.
Co-Authored-By: Claude <noreply@anthropic.invalid>
|
@coderabbitai Fixed the nesting-aware split — replaced naive |
|
Tip For best results, initiate chat on the files or code changes. 🧩 Analysis chain🏁 Script executed: Length of output: 2265 🏁 Script executed: Length of output: 3992
The primary ✏️ Learnings added
🧠 Learnings used |
- 9 new codegen tests covering String values, Bool keys, Int keys, String+String maps, and map_keys/map_values type variants - Fix stale "56" in TESTING.md conformance narrative (now 57) - Add Decimal (#333) to ROADMAP Tier 2 - Use depth-aware comma split for Map type string fallback - Restrict Array pair-ABI: documented as future enhancement in spec - Doc counts: 2789 tests Co-Authored-By: Claude <noreply@anthropic.invalid>
Summary
Implement
Map<K, V>as an opaque host-import type withEq<K> + Hash<K>ability constraints. This is PR 1 of 3 for #62 (Map portion; Set and Decimal deferred).map_new,map_insert,map_get,map_contains,map_remove,map_size,map_keys,map_valuesdictobjects behindi32handles; JS runtime (browser) usesMapobjects. Type-specific imports (map_insert$ks_vi) handle serialization across the WASM boundarymap_insertandmap_removereturn new maps (copy-on-write)map_new()) when expected type is available; skip ADT args whose TypeVars match callee forall vars during unificationCompiler changes
checker/resolution.py,checker/expressions.py,environment.pycodegen/core.py,codegen/assembly.py,codegen/compilability.py,codegen/functions.py,codegen/modules.pywasm/calls.py,wasm/context.py,wasm/inference.pycodegen/api.py(Python/wasmtime),browser/runtime.mjs(JS)smt.py(uninterpretedmap_size)Documentation
Test plan
ch09_map.verawith 10 test functions)🤖 Generated with Claude Code
Summary by CodeRabbit
New Features
Documentation
Tests