Skip to content

check: support web bundle idioms so full type-check passes#81

Merged
ecto merged 1 commit into
mainfrom
claude/blissful-austin-2d0eae
Jul 7, 2026
Merged

check: support web bundle idioms so full type-check passes#81
ecto merged 1 commit into
mainfrom
claude/blissful-austin-2d0eae

Conversation

@ecto

@ecto ecto commented Jul 7, 2026

Copy link
Copy Markdown
Owner

What

Running loon check on the concatenated web bundle (built by web/build.ts into web/dist/bundle.loon) failed with 1000+ errors — starting with [E0205] no Eq implementation for type Vec. This made npm run build:full (the Rust-enabled build with type-checking, step 6 of web/build.ts) exit non-zero, blocking pre-rendering-from-source. The Vercel deploy only worked because it uses the --no-rust path that skips type-checking entirely.

The interpreter runs all of this code fine — every error was a type-checker gap, not a bug in web/src. So this PR fixes the checker (no web/src changes).

Changes (all in crates/loon-lang/src/check/mod.rs)

  • Vec/Map/Set Eq (the original E0205): registered builtin Eq impls for containers, which compare structurally at runtime.
  • Variadic & rest params (~1,040 arity errors): split params at &, bind the rest param as Vec elem, and at call sites to known variadic fns collect extra args into the rest Vec (rest elements stay unconstrained — HTML children are heterogeneous).
  • Undeclared type names in ADT fields act as implicit type parameters — Props in [El String Props [Vec VNode]] was becoming a rigid nominal type nothing could unify with.
  • get: 3-arg [get coll key default] no longer forces field presence; dynamic (non-literal) keys on records return an unconstrained type.
  • Union-friendly if/vector literals: branches/elements with distinct concrete types back off to an unconstrained type instead of erroring, and if no longer ties two still-unknown branch types together (which wrongly equated unrelated params in [if [map? x] x y] dispatch code). [if true 1 2] still infers Int.
  • Empty {} stays polymorphic instead of forcing Map t t (used as empty props throughout).
  • Overloaded builtins: loosened len, contains?, and nth to match runtime polymorphism (strings, tuples, maps).
  • Forward references: check_program pre-declares top-level fn names with a ∀a. a placeholder so mutual recursion across definitions (coerce-childcoerce-children) and cross-file use-before-def resolve; the real scheme replaces the placeholder at the definition site. Genuine typos still error.

Trade-off for reviewers

Several of these are deliberate gradual-typing back-offs: heterogeneous unions and forward-referenced calls become unchecked rather than errors. This matches the "invisible types" philosophy and was the only way to type the ui.loon runtime-dispatch style ([if [map? first] ...]) without occurrence typing.

Verification

  • npx tsx web/build.ts (full, with type-check) → exit 0, warnings only.
  • cargo test --workspace → all pass (700+, zero failures).

🤖 Generated with Claude Code

Running `loon check` on the concatenated web bundle failed with 1000+
errors (starting with E0205 no `Eq` for `Vec`), so `npm run build:full`
exited non-zero and pre-rendering-from-source was blocked. The
interpreter handled all of this fine — these were checker gaps, not
web/src bugs. Fixes, all in the checker:

- Register builtin `Eq` (and it already had) impls for Vec/Map/Set,
  which compare structurally at runtime.
- Variadic `& rest` params: split params at `&`, bind rest as `Vec elem`,
  and collect extra call-site args into the rest Vec.
- Undeclared type names in ADT field positions act as implicit type
  params (e.g. `Props` in `[El String Props ...]`) instead of becoming
  rigid nominal types.
- `get`: 3-arg `[get coll key default]` no longer forces field presence;
  dynamic (non-literal) keys on records return unconstrained.
- Union-friendly `if`/vector literals: heterogeneous branches/elements
  back off to an unconstrained type; `if` no longer ties two unknown
  branch types together. `[if true 1 2]` still infers Int.
- Empty `{}` stays polymorphic (used as empty props everywhere).
- Loosen `len`/`contains?`/`nth` to match runtime polymorphism
  (strings, tuples, maps).
- Pre-declare top-level `fn` names so forward/mutual references resolve;
  genuine typos still error.

`npx tsx web/build.ts` (full, with type-check) now completes; all
workspace tests pass.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@cursor

cursor Bot commented Jul 7, 2026

Copy link
Copy Markdown

Bugbot is not enabled for your account, so this pull request was not reviewed.

Enable Bugbot in the Cursor dashboard to get automatic reviews on future PRs.

@vercel

vercel Bot commented Jul 7, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
loon Ready Ready Preview, Comment Jul 7, 2026 6:04am

Request Review

@ecto ecto merged commit 5094442 into main Jul 7, 2026
6 checks passed
@chojiai

chojiai Bot commented Jul 7, 2026

Copy link
Copy Markdown

What shipped

This is a purely internal change with no user-visible effect. The type-checker used by the build toolchain has been updated to handle patterns common in the web codebase, so the full build process now completes successfully without errors.


Plain-English summary generated by Choji from this pull request.

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