Skip to content

feat: Eliminate any type from Casa#159

Merged
frendsick merged 15 commits intomainfrom
feat/eliminate-any
Apr 28, 2026
Merged

feat: Eliminate any type from Casa#159
frendsick merged 15 commits intomainfrom
feat/eliminate-any

Conversation

@frendsick
Copy link
Copy Markdown
Owner

@frendsick frendsick commented Apr 27, 2026

Summary

Replaces the any wildcard type with generics + trait bounds (Eq, Ord, Display, Word) end-to-end. After this branch, the any keyword is rejected at parse time, the ANY_TYPE constant is gone, and the method-dispatch fallback for unknown receivers is replaced by a real "method not found" error.

Eleven slices, each behind a green CI pin (casafmt, self-host fixed point, full compiler+example test suites):

Part of #147.

Follow-up: error message polish

After the trait migration exposed terser diagnostics, two follow-up commits clean up the underflow path:

  • Stack underflow now reports as an error outside lambdas (was: silently inferred parameters)
  • Underflow messages name the slot (value with trait \Display`, argument 1 of `rot``) instead of the generic "expected a value"
  • Cascade placeholder renamed <error><missing> so user-facing text reads "earlier error tainted this slot"
  • New STACK_UNDERFLOW and Cascade Errors sections in docs/errors.md

Test plan

  • ./casafmt clean on all changed .casa files
  • Self-host fixed point: ./casac → stage1 → stage2 → stage3, cmp stage2.s stage3.s identical
  • tests/test_compiler.sh < /dev/null — 27 suites pass (incl. new test_underflow_messages)
  • tests/test_examples.sh — 40 examples pass
  • rg '\bany\b' compiler/ lib/ returns only the rejection check in syntax.casa:211
  • LSP signature strings free of any

Introduce TYPE_UNKNOWN constant in compiler/common.casa, both bound to
the string "any". All compiler-internal sentinel uses (stack
underflow placeholder, types_match, unify_type, bind_type_var,
is_type_variable, bind_fn_param, check_push_var, type_satisfies_trait,
check_method_call, signature_matches, and related helpers) now
reference TYPE_UNKNOWN. ANY_TYPE remains as a string-equal alias for
the duration of the migration so user-visible "any" keyword behaviour
is unchanged.

Part of #147
Declare Eq (eq required, ne default), Ord (lt required, le/gt/ge
defaults derived from lt), and Word (empty marker) in lib/std.casa.
Display already existed and is left in place.

Eq impls: int (already had), bool, char, str (already had), cstr, ptr.
Ord impls: int, char. Lexicographic str comparison is intentionally
out of scope.

Word is satisfied structurally by every type today; bound enforcement
for single-slot-only types lands with US-008.

Part of #147
- Add CasaTrait.supertraits: List[TraitBound] and parser support for
  trait Sub: Super (+ Super2) syntax
- collect_trait_methods walks trait + transitive supertraits
- build_hidden_trait_methods, inject_trait_fn_ptrs, type_satisfies_trait,
  simulate_trait_exec, check_method_call, check_abstract_methods_present
  all use the expanded list so K::eq dispatches via Eq supertrait
- lib/std.casa: trait Hashable: Eq removes duplicated eq method
- Fix double-decrement bug in inject_trait_fn_ptrs verify loop that
  silently skipped the method after a default in the iteration order

Closes #150
Surface signature switches from `any -> str` to the explicit generic
form `[T] T -> str`. Behaviour is unchanged: typeof pops a value of
any type and prints its static type name. The check_typeof body
already reads the popped slot's concrete type for the bytecode
annotation, so no functional changes were needed there.

Updated:
- LSP hover string for OpValue::Typeof
- docs/intrinsics.md `typeof` signature
- tests/compiler/test_typeof.casa: added typecheck and bytecode
  coverage for typeof on array literals.

Part of #147
Migration: drop the cast (the binding annotation already provides the
type) or write the concrete type instead.
Replace `any`-based hover strings for drop/dup/swap/over/rot with
explicit `[T]` / `[T1 T2]` / `[T1 T2 T3]` generic surface forms in
LSP, intrinsics docs, and functions-and-lambdas docs. The typechecker
side was already polymorphic — `check_stack_ops` pops/pushes the
actual stack types — so the compiler change is hover-only. Adds a
polymorphic stack-shaped `keep_top[T1 T2] T1 T2 -> T1 { swap drop }`
example with two type instantiations, plus LSP hover tests for over
and rot.

Closes #151
Replace `any` value-type slots in store8/16/32/64 and syscall1..6
with a `Word` marker-trait bound. `check_memory` and `check_syscalls`
now thread `function_opt` through and validate each popped value/arg
type via `assert_word_bound`. Concrete types are accepted (every
single-slot value satisfies `Word` today); type variables without an
explicit bound are deferred to the call site; type variables whose
declared bound does not extend `Word` are rejected with a clear
migration message.

Hashable and Display now extend `Word` as supertraits so existing
stdlib generics (`Map[K V]` with `K: Hashable`, `Display` impls) keep
compiling unchanged. Adds `bound_implies_word` to walk supertraits.
LSP hover, intrinsics docs, traits docs, and the standard-library
trait table reflect the new signatures.

Closes #154
…tch fallback

- parse_type now raises a Syntax error for bare `any`; the redundant guard in get_op_type_cast is gone (parse_type covers casts too).
- Removed the ANY_TYPE constant alias and the `any` entries in is_builtin_type and the builtins set.
- Removed find_method_by_name and the TYPE_UNKNOWN-receiver fallback in check_method_call. Method dispatch on an unresolved type now produces a real "method not found" error.
- Removed the TYPE_UNKNOWN early-return in is_type_variable; the two call sites already guard against the sentinel.
- Replaced dot-method lambdas `{ .is_alpha }` etc. in lib/parser.casa and examples/parser.casa with explicit function references like `&char::is_alpha`, since those lambdas relied on the removed dispatch fallback.
- Test assertions and headers swapped from the literal "any" string to the TYPE_UNKNOWN constant.
- Docs pruned of `any` references; `fn[any -> str]` rewritten as `fn[T -> str]`, `drop: any -> None` as `drop: [T] T -> None`, etc.

Closes #158, #147.
Non-lambda functions previously synthesized an inferred parameter on
stack underflow, masking real bugs. Now report STACK_UNDERFLOW once the
declared parameter budget is consumed; lambdas still infer on demand.

Cascaded slots are tagged with a `TYPE_ERROR` placeholder that
`types_match` treats as a wildcard to prevent spurious follow-on
diagnostics within the same op.
Stack underflow diagnostics now name the slot the operation needed
instead of generic "expected a value":

- Trait dispatch (`print`, `==`, `<`): "value with trait `T`" — was
  "trait `T`", which read as if the trait itself was on the stack.
- Stack intrinsics (`drop`, `dup`, `swap`, `over`, `rot`): "value for
  `op`" or "argument N of `op`" with N counting from the top.

Rename the cascade placeholder from `<error>` (TYPE_ERROR) to
`<missing>` (TYPE_MISSING) so user-facing messages communicate "this
slot was tainted by an earlier error, fix that first" rather than the
internal sentinel.

Document `STACK_UNDERFLOW` and the new `Cascade Errors` section in
docs/errors.md. Cross-referenced from `TYPE_MISMATCH` so a user seeing
`got <missing>` knows to look upstream.
Replace the parse-time `any`-specific syntax error with a general
type-existence check at the TypeCast op in run_type_check_ops. The
validator covers builtins, registered structs/enums, declared signature
type variables, parameterized types (recurses into params), and `fn`
types. Unknown identifiers — `foo`, single-letter `T` outside a generic
function, or nested unknowns like `List[Foo]` — now raise a uniform
`type \`X\` does not exist` syntax error.
Skip the main-source intermediate compile so the workflow accepts
syntax not yet on main (supertraits in lib/std.casa). Restore the
two-stage flow after merge.
@frendsick frendsick merged commit b4bbce0 into main Apr 28, 2026
2 of 4 checks passed
@frendsick frendsick deleted the feat/eliminate-any branch April 28, 2026 20:05
@frendsick frendsick linked an issue Apr 28, 2026 that may be closed by this pull request
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment