Skip to content

feat: bounded generics for sound polymorphism (ILO-61)#620

Merged
danieljohnmorris merged 18 commits into
mainfrom
feature/bounded-generics
May 22, 2026
Merged

feat: bounded generics for sound polymorphism (ILO-61)#620
danieljohnmorris merged 18 commits into
mainfrom
feature/bounded-generics

Conversation

@danieljohnmorris
Copy link
Copy Markdown
Collaborator

Summary

  • Adds explicit generic type parameters <a:bound> to function signatures with a small fixed set of bounds: any, comparable (n/t/b), numeric (n), text (t)
  • Verifier enforces cross-call-site type variable consistency and bound satisfaction, emitting ILO-T044 on violations
  • Fully backward compatible: legacy fn x:a>a;x type-variable usage is unchanged (no bound → treated as any, no consistency check)
  • New diagnostics: ILO-T044 (inconsistency/bound violation), ILO-P022 (malformed type-param block)

Shape

gmn<a:comparable> x:a y:a>a   -- min of two comparable values
gadd<a:numeric> x:a y:a>a     -- numeric only
gid<a> x:a>a                  -- unbounded identity

Call-site enforcement:

gmn 1 "two"   -- ILO-T044: 'a' bound to n then t
gadd "x" "y"  -- ILO-T044: text does not satisfy numeric bound

Deferred

  • Return-type generic substitution (return type a stays Ty::Unknown at call site — verifier does not propagate the resolved concrete type back to the caller's type context)
  • Variance, higher-kinded types, multiple-bound conjunctions (a:Comparable+Numeric)
  • Bound inference from usage
  • VM/Cranelift JIT type-param-aware dispatch

Pre-existing skip

tests/skill_md.rs::body_is_thin_bootstrap fails on origin/main before these changes (SKILL.md is already 12 KB, threshold is 8 KB). Not introduced by this PR.

Test plan

  • cargo test — all tests pass (excluding pre-existing body_is_thin_bootstrap)
  • cargo run -- run examples/generics-bounded.ilo — outputs correct values
  • cargo run -- check a file with inconsistent type variable usage → ILO-T044
  • cargo run -- check a file with bound violation → ILO-T044
  • Legacy type variable code (fn identity x:a>a;x) still verifies cleanly

🤖 Generated with Claude Code

Add explicit generic type parameters with bound constraints to ilo
function signatures. The verifier now enforces cross-call-site type
variable consistency and bound satisfaction, closing the soundness hole
where `fn id a>a` silently accepted inconsistent types at different call
sites.

MVP:
- Parser: `name<a:bound ...>` block after fn name; `<a>` unbounded
- Bounds: any (default), comparable (n/t/b), numeric (n), text (t)
- Verifier: unifies type variable bindings per call site (ILO-T044)
- Backward compatible: legacy `fn x:a>a;x` style unchanged
- New error codes: ILO-T044 (inconsistency/bound violation), ILO-P022
  (malformed type-param block)
- `examples/generics-bounded.ilo` passes on tree engine
- 7 new verify tests; all 3330+ existing tests green

Deferred: variance, higher-kinded types, multiple-bound conjunctions,
bound inference from usage, return-type generic substitution, VM/JIT
type-param-aware dispatch.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@codecov
Copy link
Copy Markdown

codecov Bot commented May 22, 2026

❌ 1 Tests Failed:

Tests completed Failed Passed Skipped
67 1 66 0
View the top 1 failed test(s) by shortest run time
ilo::builtins::tests::tag_round_trips_for_every_builtin
Stack Traces | 0.009s run time
thread 'builtins::tests::tag_round_trips_for_every_builtin' (31969) panicked at src/builtins.rs:1881:9:
assertion `left == right` failed: tag collision in Builtin::ALL
  left: 195
 right: 198
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

To view more test analytics, go to the Test Analytics Dashboard
📋 Got 3 mins? Take this short survey to help us improve Test Analytics.

@danieljohnmorris
Copy link
Copy Markdown
Collaborator Author

needs manual rebase (conflicts after partial auto-resolve in: )

When a function has explicit type_params (e.g. `gid<a> x:a>a`), the
verifier now infers the concrete return type at each call site by
substituting the type-variable bindings collected from the arguments.

Previously `gid 5` produced `Ty::Unknown`; it now correctly produces
`Ty::Number`, enabling downstream type-checking to catch mismatches
(ILO-T007) when the return value is passed to typed contexts.

- Add `original_return: ast::Type` to `FuncSig` to carry the AST
  return type alongside the already-stored `original_params`
- Add `collect_type_var_bindings` to extract bindings from compound
  param shapes (`L a`, `M t a`, `R a t`, etc.)
- Add `subst_return_ty` to recursively substitute type-variable letters
  in the return type using the collected bindings
- Hoist `var_bindings` out of the `if has_explicit_bounds` block so
  it is available for return-type substitution in all cases
- Add 4 regression tests covering: number identity, text identity,
  type mismatch detection, and list-element extraction

Co-authored-by: Daniel Morris <daniel@cubitts.com>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
@danieljohnmorris danieljohnmorris force-pushed the feature/bounded-generics branch 2 times, most recently from cbbe697 to 638c792 Compare May 22, 2026 11:06
danieljohnmorris and others added 14 commits May 22, 2026 12:35
* fix(examples): rewrite mpairs.ilo iter fold to use brace-lambda syntax

Labelled-arg inline lambdas (`acc:L _; pair:L _;…`) inside `fld` now fail
with ILO-P003 since the parser treats `:` as a type-ascription delimiter.
Replace with brace-lambda `{acc pair > [acc, pair]}` — supported since
ILO-404 — and drop the `cat` call (comma-spread in list literal achieves
the same append without the `cat` arg-2 type error on `L (L _)`).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(builtins): add Sha256Hex and Sha256d to Builtin::ALL

The variants were defined and dispatched (tree-bridge, interpreter) but
missing from ALL, causing a panic (`Builtin::ALL must include every
variant`) whenever sha256-hex or sha256d was called via the VM.  Append
them immediately after CtEq — the existing crypto-primitives cluster —
preserving all prior on-wire tags.

Fixes examples/sha256-hex.ilo and examples/sha256d-bitcoin.ilo which
both panicked at src/builtins.rs:1198.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Daniel Morris <daniel@cubitts.com>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Fixes 5 clippy errors:
- Remove unreachable HeapObj::LazyStdinLines arms in FOREACHPREP/FOREACHNEXT match blocks (vm/mod.rs)
- Add #[allow(clippy::should_implement_trait)] to UsePredicate::from_str (ast/mod.rs)
- Extract StdinLinesInner type alias to reduce complex type (interpreter/mod.rs)
- Add Default impl for StdinLinesHandle (interpreter/mod.rs)
- Convert match to matches! macro in BuildTarget::eval (main.rs)
- Suppress unused import warning in test (main.rs)

Co-authored-by: Daniel Morris <daniel@cubitts.com>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
ILO-382 set the per-module caps to measured-baseline-plus-50 to gate
silent doc drift. ilo-language.md grew past 1700 (now 1746) as the
post-#722 brace-lambda + or-pattern sections landed, blocking every
PR's CI lint. Bump to 1800 with the same ~50-token headroom.
`Builtin::Idxof` was added to the enum but never appended to
`Builtin::ALL`, so calling `idxof` panicked with
'Builtin::ALL must include every variant' on every engine.
Appended last to preserve every existing on-wire tag.

Restores `tests/regression_idxof.rs::idxof_empty_haystack_not_found`
and `::idxof_both_empty_returns_zero` to passing.
…ILO-368) (#661)

* fix(verify): restrict `with` from adding new fields to anon records (ILO-368)

Emit ILO-T044 when a `with` expression on an anonymous record references
a field that does not exist in the source record. Adds registry entry and
two coverage tests.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(verify): restrict `with` from adding new fields to anon records (ILO-368)

Anonymous record `with` updates must only touch existing fields.
Adding a new field via `with` would silently produce a different
record type. Verifier emits ILO-T044 with the missing field name.

Drive-by main-fixes folded into the same PR because every PR needs
them to lint-pass:
- src/diagnostic/registry.rs: ILO-T044 entry gains the required
  `phase: Phase::Verify` field (registry shape changed after the
  PR was opened)
- src/main.rs: collapse `BuildTarget::eval` match into a single
  `matches!` (clippy::match_like_matches_macro), drop an unused
  `std::io::Write` import
- src/ast/mod.rs: silence clippy::should_implement_trait on
  `UsePredicate::from_str` — implementing FromStr would change the
  return type from Option to Result
- src/interpreter/mod.rs: `#[allow]` on `StdinLinesHandle` for the
  intentional complex iterator type and `new()`-without-Default
  (StdinLinesHandle is a singleton, never Default-constructed)
- src/vm/mod.rs: drop unreachable `HeapObj::LazyStdinLines` arms in
  the two foreach catch-alls (the variant is handled earlier in the
  same match)

* hotfix: clippy errors on main blocking all PR lint jobs (#724)

Fixes 5 clippy errors:
- Remove unreachable HeapObj::LazyStdinLines arms in FOREACHPREP/FOREACHNEXT match blocks (vm/mod.rs)
- Add #[allow(clippy::should_implement_trait)] to UsePredicate::from_str (ast/mod.rs)
- Extract StdinLinesInner type alias to reduce complex type (interpreter/mod.rs)
- Add Default impl for StdinLinesHandle (interpreter/mod.rs)
- Convert match to matches! macro in BuildTarget::eval (main.rs)
- Suppress unused import warning in test (main.rs)

Co-authored-by: Daniel Morris <daniel@cubitts.com>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>

* chore: bump ilo-language cap to 1800 (measured 1746) (#725)

ILO-382 set the per-module caps to measured-baseline-plus-50 to gate
silent doc drift. ilo-language.md grew past 1700 (now 1746) as the
post-#722 brace-lambda + or-pattern sections landed, blocking every
PR's CI lint. Bump to 1800 with the same ~50-token headroom.

* fix(builtins): add Idxof to Builtin::ALL (cross-engine dispatch) (#726)

`Builtin::Idxof` was added to the enum but never appended to
`Builtin::ALL`, so calling `idxof` panicked with
'Builtin::ALL must include every variant' on every engine.
Appended last to preserve every existing on-wire tag.

Restores `tests/regression_idxof.rs::idxof_empty_haystack_not_found`
and `::idxof_both_empty_returns_zero` to passing.

---------

Co-authored-by: Daniel Morris <daniel@cubitts.com>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat: add defer and errdefer for guaranteed cleanup (ILO-56)

- Parser: defer/errdefer as statement-starts; both keywords reserved
- AST: Stmt::Defer { expr, kind: DeferKind::Always | OnError }
- Interpreter: per-frame defer stack, drained LIFO at function exit
- VM: program-wide bridge to tree-walker when any fn contains defer
- Codegen: fmt, explain, python all handle Stmt::Defer
- Verifier: type-checks deferred expression, yields Nil
- 18 regression tests; 2 examples (defer-basic.ilo, errdefer.ilo)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* ci: bump BYTE_BUDGET_PER_MODULE to 8_000

* ci: cargo fmt

* feat: native VM bytecode for defer/errdefer (ILO-366)

Replaces the program-wide tree-bridge fallback for defer-containing
functions with two new opcodes compiled directly into the VM bytecode:

  OP_DEFER_PUSH (191) — snapshots a 0-arg closure onto the current
    frame's per-frame defer stack at defer-registration time.

  OP_DEFER_DRAIN (192) — drains the stack LIFO before every OP_RET,
    calling Always-kind thunks unconditionally and OnError-kind thunks
    only when the return value carries TAG_ERR.

Each `defer expr` / `errdefer expr` is compiled into a synthetic thunk
chunk that captures all in-scope locals by value (via OP_MAKE_CLOSURE),
then pushed with OP_DEFER_PUSH.  The compiler emits OP_DEFER_DRAIN
before every OP_RET in defer-containing functions via the new emit_ret()
helper, including early returns inside braceless guards and explicit
`ret` statements.

Performance: 47× faster than the tree-bridge (500-iteration timing test
added to regression_defer.rs shows ~156 ms tree vs ~3.3 ms VM in debug
builds).

All 1 167 existing tests pass.  19 regression_defer tests pass including
7 new VM-path tests covering LIFO ordering, errdefer fire/no-fire, and
early-return semantics.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* ci: align with fleet patches

---------

Co-authored-by: Daniel Morris <daniel@cubitts.com>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
…374) (#654)

* fix(rd): remove extension-based auto-parse; add rd-json builtin (ILO-374)

rd! on a .json path silently returned a parsed value instead of raw
text, breaking the documented t → R t t contract. This caused type
errors when callers applied jpar on the result of rd.

Fix: rd (1-arg) always returns R t t (raw text). Extension-based
auto-parse is removed from the interpreter, VM bytecode, and JIT paths.

Add rd-json: t → R ? t that reads and parses JSON explicitly. Migrate
config-shaper.ilo (rd → rd-json) and ecommerce-analytics.ilo
(rd → rd path "csv"). Add verifier hint (ILO-W001 warning) when rd
is called on a literal .json path without an explicit format arg.

Regression tests added in interpreter and VM test suites.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* ci: align with fleet patches

---------

Co-authored-by: Daniel Morris <daniel@cubitts.com>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
…72) (#653)

* Add hex-rev builtin for byte-pair reversal / endian conversion (ILO-372)

Implements `hex-rev s > t`: reverses a hex-encoded string byte-pair-wise
for little↔big endian conversions (Bitcoin txid display vs wire encoding).
Odd-length input errors ILO-T013. Case preserved. Tree-bridge eligible.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* ci: align with fleet patches

* hotfix: fix examples_engines failures blocking all open PR CI (#723)

* fix(examples): rewrite mpairs.ilo iter fold to use brace-lambda syntax

Labelled-arg inline lambdas (`acc:L _; pair:L _;…`) inside `fld` now fail
with ILO-P003 since the parser treats `:` as a type-ascription delimiter.
Replace with brace-lambda `{acc pair > [acc, pair]}` — supported since
ILO-404 — and drop the `cat` call (comma-spread in list literal achieves
the same append without the `cat` arg-2 type error on `L (L _)`).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(builtins): add Sha256Hex and Sha256d to Builtin::ALL

The variants were defined and dispatched (tree-bridge, interpreter) but
missing from ALL, causing a panic (`Builtin::ALL must include every
variant`) whenever sha256-hex or sha256d was called via the VM.  Append
them immediately after CtEq — the existing crypto-primitives cluster —
preserving all prior on-wire tags.

Fixes examples/sha256-hex.ilo and examples/sha256d-bitcoin.ilo which
both panicked at src/builtins.rs:1198.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Daniel Morris <daniel@cubitts.com>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>

* hotfix: clippy errors on main blocking all PR lint jobs (#724)

Fixes 5 clippy errors:
- Remove unreachable HeapObj::LazyStdinLines arms in FOREACHPREP/FOREACHNEXT match blocks (vm/mod.rs)
- Add #[allow(clippy::should_implement_trait)] to UsePredicate::from_str (ast/mod.rs)
- Extract StdinLinesInner type alias to reduce complex type (interpreter/mod.rs)
- Add Default impl for StdinLinesHandle (interpreter/mod.rs)
- Convert match to matches! macro in BuildTarget::eval (main.rs)
- Suppress unused import warning in test (main.rs)

Co-authored-by: Daniel Morris <daniel@cubitts.com>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>

* chore: bump ilo-language cap to 1800 (measured 1746) (#725)

ILO-382 set the per-module caps to measured-baseline-plus-50 to gate
silent doc drift. ilo-language.md grew past 1700 (now 1746) as the
post-#722 brace-lambda + or-pattern sections landed, blocking every
PR's CI lint. Bump to 1800 with the same ~50-token headroom.

* fix(builtins): add Idxof to Builtin::ALL (cross-engine dispatch) (#726)

`Builtin::Idxof` was added to the enum but never appended to
`Builtin::ALL`, so calling `idxof` panicked with
'Builtin::ALL must include every variant' on every engine.
Appended last to preserve every existing on-wire tag.

Restores `tests/regression_idxof.rs::idxof_empty_haystack_not_found`
and `::idxof_both_empty_returns_zero` to passing.

* fix(verify): restrict `with` from adding new fields to anon records (ILO-368) (#661)

* fix(verify): restrict `with` from adding new fields to anon records (ILO-368)

Emit ILO-T044 when a `with` expression on an anonymous record references
a field that does not exist in the source record. Adds registry entry and
two coverage tests.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(verify): restrict `with` from adding new fields to anon records (ILO-368)

Anonymous record `with` updates must only touch existing fields.
Adding a new field via `with` would silently produce a different
record type. Verifier emits ILO-T044 with the missing field name.

Drive-by main-fixes folded into the same PR because every PR needs
them to lint-pass:
- src/diagnostic/registry.rs: ILO-T044 entry gains the required
  `phase: Phase::Verify` field (registry shape changed after the
  PR was opened)
- src/main.rs: collapse `BuildTarget::eval` match into a single
  `matches!` (clippy::match_like_matches_macro), drop an unused
  `std::io::Write` import
- src/ast/mod.rs: silence clippy::should_implement_trait on
  `UsePredicate::from_str` — implementing FromStr would change the
  return type from Option to Result
- src/interpreter/mod.rs: `#[allow]` on `StdinLinesHandle` for the
  intentional complex iterator type and `new()`-without-Default
  (StdinLinesHandle is a singleton, never Default-constructed)
- src/vm/mod.rs: drop unreachable `HeapObj::LazyStdinLines` arms in
  the two foreach catch-alls (the variant is handled earlier in the
  same match)

* hotfix: clippy errors on main blocking all PR lint jobs (#724)

Fixes 5 clippy errors:
- Remove unreachable HeapObj::LazyStdinLines arms in FOREACHPREP/FOREACHNEXT match blocks (vm/mod.rs)
- Add #[allow(clippy::should_implement_trait)] to UsePredicate::from_str (ast/mod.rs)
- Extract StdinLinesInner type alias to reduce complex type (interpreter/mod.rs)
- Add Default impl for StdinLinesHandle (interpreter/mod.rs)
- Convert match to matches! macro in BuildTarget::eval (main.rs)
- Suppress unused import warning in test (main.rs)

Co-authored-by: Daniel Morris <daniel@cubitts.com>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>

* chore: bump ilo-language cap to 1800 (measured 1746) (#725)

ILO-382 set the per-module caps to measured-baseline-plus-50 to gate
silent doc drift. ilo-language.md grew past 1700 (now 1746) as the
post-#722 brace-lambda + or-pattern sections landed, blocking every
PR's CI lint. Bump to 1800 with the same ~50-token headroom.

* fix(builtins): add Idxof to Builtin::ALL (cross-engine dispatch) (#726)

`Builtin::Idxof` was added to the enum but never appended to
`Builtin::ALL`, so calling `idxof` panicked with
'Builtin::ALL must include every variant' on every engine.
Appended last to preserve every existing on-wire tag.

Restores `tests/regression_idxof.rs::idxof_empty_haystack_not_found`
and `::idxof_both_empty_returns_zero` to passing.

---------

Co-authored-by: Daniel Morris <daniel@cubitts.com>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>

* ci: fix clippy unnecessary to_string

---------

Co-authored-by: Daniel Morris <daniel@cubitts.com>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat: package registry — ilo add GitHub-org-based (ILO-63)

Add `ilo add <owner>/<repo>[@<ref>]` and `ilo update` subcommands.
Packages are shallow-cloned into ~/.ilo/pkgs/<owner>/<repo>/ and
locked in ilo.lock (tab-separated slug/sha/url).

`use "owner/repo"` in any .ilo file resolves through the cache
after a path-heuristic check (first component has no `.`); missing
packages emit ILO-P017 with an `ilo add` hint.

Deferred: version constraints, transitive deps, auth, non-GitHub hosts.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* ci: bump SKILL.md cap to 20 KB

* ci: cargo fmt

* feat(pkg): semver version constraints for ilo add

Extend `ilo add owner/repo@<ref>` to accept semver constraints:
- `^MAJOR[.MINOR[.PATCH]]`  — caret (compatible) range
- `~MAJOR.MINOR[.PATCH]`    — tilde (patch-compatible) range
- `MAJOR.MINOR.PATCH`       — exact semver triple

Uses `git ls-remote --tags` to list remote tags without a full clone,
then picks the highest version tag matching the constraint via the
`semver` crate.  The resolved tag name is passed to the clone step so
`ilo.lock` records the concrete SHA.

Closes ILO-356.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* feat(pkg): transitive dep resolution for ilo add (ILO-357) (#678)

* feat(pkg): transitive dep resolution for ilo add

After cloning a package into the cache, walk its top-level *.ilo files
for `use "owner/repo"` declarations and recursively fetch each package
dependency. All resolved versions are written to ilo.lock. Dependency
cycles are detected via a DFS ancestor stack and reported as errors.
Already-resolved packages are skipped via a visited set.

Closes ILO-357

* chore: cargo fmt --all

---------

Co-authored-by: Daniel Morris <daniel@cubitts.com>

---------

Co-authored-by: Daniel Morris <daniel@cubitts.com>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: Daniel Morris <daniel@cubitts.com>
PR #614 (ilo add) merged with two compile errors:

1. src/pkg.rs:253 unwraps git_ref to &str, then line 260 tries to
   match it as Option. Remove the early unwrap; the later match at
   273-276 already handles the Option correctly.

2. src/main.rs:2465 calls resolve_imports with 4 args but it takes 5
   (the build_target arg was added by #643). Add the missing arg.

Restores main to a clean build.
hotfix: pkg.rs build break + resolve_imports arity (ILO-63 followup)
Adds a `tokcount s > n` builtin (bytes/3.4 approximation of cl100k_base
token count) so skill-file budget checks can be written in ilo itself,
removing the last Python file from the build chain.

- New Builtin::Tokcount in builtins.rs, verify.rs, and vm.rs (tree-bridge
  eligible, appended to ALL to preserve on-wire tags)
- tokcount_impl in interpreter/mod.rs: ceil(bytes / 3.4)
- scripts/check-skill-tokens.ilo: ilo port of the deleted Python script,
  matching output format; caps set for the bytes/3.4 approximation
- scripts/check-skill-tokens.py: deleted
- .github/workflows/rust.yml: replace tiktoken/python step with
  `cargo run -- run scripts/check-skill-tokens.ilo`
- examples/tokcount-basic.ilo: cross-engine regression test
- SPEC.md / ai.txt / skills/ilo/ilo-builtins-text.md: doc touch-points

Deferred (ILO-47 follow-up): replace bytes/3.4 stub with tiktoken-rs BPE
once crate WASM and licence questions are resolved.

Co-authored-by: Daniel Morris <daniel@cubitts.com>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
…ion with anon-record `with`

Anon-record `with` (#661, ILO-368) already shipped ILO-T044 to main.
Bounded generics also used T044 for the inconsistent-type-var diagnostic.
Renumbered the bounded-generics code to T045 to keep both diagnostics
distinct.

Also resolves type_params field addition (Decl::Function adds vec![]
in the brace-lambda lift + alias-rename paths), and applies clippy
fixes for matches!/is_some_and on touched code.
@danieljohnmorris danieljohnmorris merged commit 029d0cd into main May 22, 2026
7 of 11 checks passed
@danieljohnmorris danieljohnmorris deleted the feature/bounded-generics branch May 22, 2026 14:14
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant