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 construction — meta 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 identityTrait::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-verifyupdate x: Tagainstx’s classification). - The
selfargument 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-levelfndeclarations 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.