Files
AILang/design/contracts/0016-method-dispatch.md
T
Brummel 832375f2ac convention: counter-prefix file naming across docs/specs/, docs/plans/, design/contracts/, design/models/
All 176 files in the four accumulating directories now use a
zero-padded 4-digit counter prefix that reflects creation order
(`NNNN-slug.md`). The counter is assigned per directory in strict
git-log creation order; ties broken alphabetically by original name.
The old `YYYY-MM-DD-` prefix on docs/specs/ and docs/plans/ files is
dropped — the date is recoverable from git log and the counter
carries the ordering.

A file's counter is stable for the life of the file: never reassigned,
never reused, never compacted. Deleted files retire their counter;
subsequent files do not fill the gap. This is the property that lets
cross-references stay literal — refs use the full filename including
the counter (`design/contracts/0007-honesty-rule.md`) so they grep
cleanly and resolve directly without a glob step.

313 cross-references updated across .md/.rs/.toml/.c/.json files
(test pins, include_str! paths, design-INDEX entries, baseline notes,
runtime C comments, inter-contract markdown links incl. bare basename
and `../models/foo.md` forms).

CLAUDE.md gets a new "File-naming convention" section spelling out
the rule and rationale. skills/brainstorm/SKILL.md and
skills/planner/SKILL.md updated so new spec/plan creation produces
counter-prefixed names from the start.

The full test suite (cargo test --workspace) passes.
2026-05-28 13:31:31 +02:00

2.5 KiB

Method dispatch

Method dispatch

The class-method resolution rule that pairs with the typeclasses contract; the Show/Eq/Ord instances the rule dispatches against live in prelude classes.

Dispatch is two-mode:

Polymorphic call sites (inside a fn body with forall + constraint set): the constraint names the class via the qualified Constraint.class field (canonical form: bare for same-module, <module>.<Class> for cross-module; see memory model for the canonical-form rule). Synth's residual carries the class name directly; constraint-discharge at fn-body-end matches against the workspace registry by (class, type_hash) key.

Monomorphic call sites: synth consults the workspace-flat Env.method_to_candidate_classes: BTreeMap<MethodName, BTreeSet<QualifiedClassName>> index, then runs the 5-step dispatch rule:

  1. Parse the Term::Var.name for an optional class qualifier (last-dot-segment is the method name; everything before is the qualified class).
  2. If method_to_candidate_classes has no entry for the method name, fall through to the existing Var-arm branches (free fn lookup, dot-qualified cross-module).
  3. Qualifier present: filter candidates to the named class. Empty result fires UnknownClass. Singleton survivor proceeds.
  4. Qualifier empty (bare-method form): singleton candidate proceeds directly; multiple candidates yield a multi-candidate residual for discharge-time refinement.
  5. At discharge, refinement runs: concrete type_ filters candidates via the workspace registry; rigid-var type_ filters via the active fn's declared constraints (env.active_declared_constraints). Single survivor discharges; multiple survivors fire AmbiguousMethodResolution (concrete) or MissingConstraint (rigid-var); zero survivors fire NoInstance (concrete) or MissingConstraint (rigid-var).

The method_to_candidate_classes index is the load-bearing data structure for this routing — its construction in build_check_env inverts the per-module class_methods maps (themselves tuple-keyed by (qualified-class, method)) to a workspace-flat method-name-to-class-set map.

Class-fn collisions resolve at the call site, not at workspace load time: the fn lookup precedence (locals → caller-module-fn → imported-fn) runs ahead of the class-method branch. When both sides have a match, the fn wins and a class-method-shadowed-by-fn warning surfaces the shadow.

Ratified by: crates/ail/tests/show_no_instance_e2e.rs.