Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Resolution

Calling convention (rule plane). Trait::member(args) qualified is always legal. Bare member(args) is legal when exactly one provider of that name and arity is in lexical scope; ambiguity is an error naming the candidates. There is no postfix p.member() sugar in rule bodies — p.x is field-access space (Rule-atom grammar).

The coverage gate. A bare member atom requires the argument’s static type to be fully covered — you cannot call what isn’t implemented, statically. Coverage is computed over the workspace-closed catalog via the abstract modifier (metatype, RFD 0027 D6): a static type T is fully covered iff every instantiable (non-abstract) declared S ⊑ T (including T itself when non-abstract) is some impl target. Abstract types admit no direct instances (OE0233 refuses construction) — every individual is classified through some non-abstract subtype — so they need no covering themselves; their non-abstract descendants do. The instantiability oracle is a pure wire-flag read (Argon.TypeSystem.Conformance. instantiableOfAbstract); no axis vocabulary is consulted — a sortality::non_sortal binding is the package’s own ontology and exempts nothing by itself. The default is instantiable: an unmodified declaration (the neutral std::core::type baseline, any pub metatype m = { };) obligates coverage — the gate can only over-refuse, never silently admit a vacuous member call, and the exemption is the explicit abstract opt-in. Partial coverage is legal only under an explicit conformance guard:

pub derive CanVote(p: Person) :-
    implements(meta(p), Adulthood),     -- the visible branch
    Adult(p), p.registered

Uncovered or unguarded-partial atoms are OE1327, whose help names the uncovered types and offers both fixes (add the impl, or write the guard). Dispatch that can fail is always visible in the source. Under multiple classification meta(p) is multi-valued (Reflection); the guard is existential by constructionmeta is a relation and the conjunction is a join, so the guard holds iff some <:-minimal classifier of p is covered, and the clause that fires is the one whose target that classifier satisfies.

Invocation-plane dispatch. Invocation-plane members are end to end through the execution surfaces Argon has — the dispatch surfaces, where the only type information is the receiver individual’s committed classification:

  • A member is invoked by its callable name Trait::member (qualified path, or any unambiguous ::-suffix — the same resolution rules as mutations and rule heads; ambiguity is an error naming the candidates), or pinned to one impl by its per-impl identity Trait::member@Target. The pinned form is an explicit escape hatch: it bypasses receiver dispatch and does not re-verify that the receiver is covered by that impl’s target (parity with ordinary mutations, which do not re-verify update x: T against x’s classification).
  • The self argument carries the receiver individual; dispatch selects the unique impl whose target covers the receiver’s actual committed type (<:-aware). No covering impl, and a receiver under two <:-incomparable covered targets (the Conformance residual), are loud refusals naming the types / the impls — never an arbitrary pick.
  • mutate members run through every mutation surface (the CLI demo harness, serve’s /v1/dispatch/mutation, the generated SDK’s member endpoint); fn members run through the compute surface (/v1/dispatch/compute, SDK) — exactly the surfaces where top-level fn declarations execute.

Source-level call sites are not an execution surface — for any fn. Top-level fn declarations cannot be called from rule bodies or from fn/mutate bodies (term-position user-function application does not resolve); method-call syntax x.foo() parses but has no semantics — the call expression builds, resolves to nothing, and derives nothing (the same silent-vacuity bucket as term-position application; tracked for a loud gate). fn members inherit exactly that envelope — they are declared, conformance-checked, lowered, and dispatchable wherever top-level fns are, and nowhere else. Source-level calls bind to the rules this section reserves: build-time resolution (first x’s inherent impls, then trait impls; one concrete body, no vtables), Trait1::foo(x) (UFCS) on collision, bare calls legal where exactly one trait in scope provides the member, and the receiver’s static type subject to the same coverage gate.

Supertraits are requires-constraints. pub trait Renewable: Resource makes impl Renewable for Lease well-formed only if impl Resource for Lease exists (OE0674). Nothing is inherited — no member bodies, no identity, no lattice position.

Orphan rule. An impl Trait for Type must live in the package declaring Trait or the one declaring Type (OE0672), so every workspace’s impl set is unambiguous.