47 KiB
PBS Static Semantics Specification
Status: Draft v1 (Normative)
Scope: binding, callable formation, type checking, and deterministic function application
1. Purpose
This document defines the static semantics of PBS core language.
It is intended to:
- give deterministic meaning to declarations accepted by the syntax specification,
- define callable identity and application rules,
- make type checking stable across implementations,
- preserve beginner-facing clarity without hidden callable behavior.
2. Core Semantic Model
2.1 Authority and precedence
Normative precedence:
- Runtime authority (
docs/specs/hardware/topics/chapter-2.md,chapter-3.md,chapter-9.md,chapter-12.md,chapter-16.md) - Bytecode authority (
docs/specs/bytecode/ISA_CORE.md) 3. Core Syntax Specification.md- This document
If a rule here conflicts with higher authority, it is invalid.
2.2 Semantic namespaces
PBS distinguishes at least:
- type namespace,
- value namespace,
- callable namespace.
- host-owner namespace.
Rules:
declare struct,declare service,declare contract,declare error,declare enum,declare callback, anddeclare builtin typeintroduce names in the type namespace.declare hostintroduces names in the host-owner namespace.let, parameters,declare const, anddeclare globalintroduce names in the value namespace.declare service Namealso introduces the canonical singleton valueNamein the value namespace.- Top-level
fn, service methods, and host methods participate in the callable namespace. fndeclarations are not first-class values in v1 core and therefore do not become ordinary value expressions.bind(...)is a callback-formation construct, not a general function-value construct.declare hostdeclarations are reserved to SDK/toolchain-controlled modules and are not formable in ordinary user-authored PBS source.declare builtin typedeclarations are reserved to SDK/toolchain-controlled modules and are not formable in ordinary user-authored PBS source.- A host owner name is not an ordinary value expression in v1 core; it is used only as a qualified host-call owner.
- The builtin simple types
int,float,bool, andstrare always present in the type namespace. - Builtin simple types are resolved without import or declaration.
- User-authored type declarations must not reuse builtin simple type names.
2.2.1 Backend-provided symbolic asset surface
PBS static semantics may consume a backend-provided symbolic asset surface.
Rules:
- The frontend-facing symbolic asset surface is rooted at
assets. assetsis a compile-time namespace root, not an ordinary runtime value by default.- Intermediate
assets...nodes are namespace-only and MUST NOT be treated as terminal asset values. - Terminal asset leaves are represented statically as
Addressable. - For PBS v1,
Addressableis the canonical public/editorial type shape for symbolic asset references. - The PBS frontend may use a fake/frontend-owned
Addressabletype surface for semantics and tooling. - This frontend-owned surface MUST NOT transfer final validation or executable lowering ownership away from the backend.
- The backend-provided asset surface contract for v1 is
List<Addressable(address, asset_id)>. - The canonical symbolic identity of an asset reference is its normalized
address, notasset_name. asset_nameMUST NOT remain the operational compile-time identity for symbolic asset lookup.- A terminal asset path MUST NOT also act as a namespace prefix for descendant assets.
2.3 Attribute metadata
Attributes are compile-time metadata attached to declaration surfaces.
Rules:
- An attribute has no value-level or type-level meaning by itself.
- Attributes are not first-class values and are not reflectable in v1 core.
- Attributes do not automatically survive into runtime or bytecode artifacts.
- An attribute affects runtime artifacts only when another specification defines an explicit lowering for its semantic effect.
- In v1 core, the normative reserved attributes are
Host,Capability,AssetLowering,BuiltinType,BuiltinConst,IntrinsicCall,Init,Frame, andInitAllowed. Hostis valid only on a host method signature declared directly inside a reserved stdlib/interface-moduledeclare hostbody.Capabilityis valid only on a host method signature declared directly inside a reserved stdlib/interface-moduledeclare hostbody.AssetLoweringis valid only on a host method signature declared directly inside a reserved stdlib/interface-moduledeclare hostbody.Initis valid only on a top-level userlandfndeclaration with lifecycle-admissible signature.Frameis valid only on a top-level userlandfndeclaration with lifecycle-admissible signature.InitAllowedis valid only on a host method signature declared directly inside a reserved stdlib/interface-moduledeclare hostbody.Hostis invalid on ordinary user-authored modules, top-levelfn, struct methods, service methods, callbacks, contracts, and constants.Hostmetadata is consumed by the compiler during host-binding lowering.AssetLoweringMUST declare exactly the named argumentparam.AssetLowering.paramMUST be a zero-based integer literal that names a valid host parameter position.- The parameter targeted by
AssetLowering.paramMUST be statically typed asAddressable. AssetLoweringmetadata is consumed by backend-owned host-call lowering to rewrite the targeted symbolicAddressableoperand into runtime-facingasset_id.- The
Hostattribute syntax itself is not exported as runtime metadata; instead, its canonical identity participates in PBX host-binding emission as defined by the Host ABI Binding specification. BuiltinTypeis valid only on a reserved top-leveldeclare builtin typedeclaration.BuiltinConstis valid only on a reserved top-leveldeclare constdeclaration that omits an initializer.IntrinsicCallis valid only on a method signature declared directly inside a reserveddeclare builtin typebody.- Builtin metadata is consumed by the compiler during VM-owned builtin and intrinsic lowering rather than by host-binding lowering.
InitandFrameare consumed by lifecycle analysis and lowering rather than by ordinary runtime reflection.InitAllowedis consumed by init-time host-admission analysis and does not become ordinary userland metadata.
2.4 Tuple shapes
Static checking uses tuple shapes as semantic objects.
Definitions:
- Input tuple shape: ordered sequence of zero or more input slots.
- Output tuple shape: ordered sequence of zero or more output slots.
- Each slot has a type and a stable label.
Rules:
- Tuple shapes in PBS core are always named, except for unit
(). - Input slot labels come from parameter names and are local frame names.
- Output slot labels come from return annotations and are visible for multi-slot member projection.
- Tuple-shape identity for overload and resolution ignores labels.
- Tuple-shape compatibility requires matching arity and positional slot types.
- Labels must still be preserved as part of declaration surface and projection metadata.
- A single-slot tuple shape has a distinguished carrier value type equal to its slot type.
- In value positions, single-slot tuple values collapse to their carrier value type.
- Single-slot tuple shapes have no dedicated tuple-literal surface in v1 core.
3. Functions and apply
3.1 Canonical function model
Every callable fn in PBS is modeled statically as:
Fn : InputTuple -> OutputTuple
Rules:
fn f(a: A, b: B)has input tuple shape(a: A, b: B).fn f()has empty input tuple shape().-> void,-> (), and omitted return annotation all denote empty output tuple shape().-> Tdenotes a single-slot output tuple shape(value: T).-> (x: T)denotes a single-slot output tuple shape(x: T).-> (x: T, y: U)denotes a two-slot output tuple shape(x: T, y: U).-> Tand-> (x: T)are identical for overload identity and differ only in preserved label metadata.optional Tis a regular value type and may appear in parameters, locals, fields, and plain returns.optional Twith single-slot payload is internally normalized tooptional<(value: T)>.optionalandoptional voidare invalid type surfaces.-> result<E> Tdenotes a result return surface over payload shapeTand error typeE.-> result<E> Twith single-slot payload is internally normalized toresult<E, (value: T)>.-> result<E>is valid and denotes a result return surface with unit payload.- A function return surface may be plain or
result<E>. - A function return surface must not combine
optionalandresultin the same declaration. result<E>wraps the declared success payload; the payload itself may be scalar or a named tuple shape.
3.2 Callable categories
The following constructs are callable in v1 core:
- top-level
fn, - struct methods on concrete receivers,
- contract values,
- callback values,
- service methods,
- host methods on declared host owners,
- builtin intrinsic methods on declared builtin types.
Rules:
- All callable categories use the same input/output tuple model.
- A struct method is identified by
(struct type, method name, input tuple shape excluding the receiver slot, output tuple shape). - A contract value is identified at the type level by its contract type and at runtime by an underlying concrete struct instance or service singleton value plus its selected implementation.
- A callback declaration defines a nominal callable type with exactly one input/output tuple shape.
- A service method is identified by
(service name, method name, input tuple shape, output tuple shape). - A host method is identified by
(host owner name, method name, input tuple shape, output tuple shape)within the PBS declaration surface. - A builtin intrinsic method is identified within PBS declaration surface by
(builtin type name, method name, input tuple shape, output tuple shape)and lowers further by canonical builtin/intrinsic metadata. - A top-level
fnis identified by(name, input tuple shape, output tuple shape). - A callback value is identified at the type level by its declared callback type and signature.
- Contract signatures and barrel signatures must preserve the same callable shape.
- Canonical host primitive identity and binding are governed by the Host ABI Binding specification rather than by ordinary v1 core declaration identity alone.
3.3 Declaration validity
Rules:
- Parameter names in a single
ParamListmust be unique. - Output labels in a named tuple return annotation must be unique.
- Error case labels in a single
declare errorbody must be unique. - Enum case labels in a single
declare enummust be unique. - Enum case integer identifiers in a single
declare enummust be unique. - Two declarations with the same callable name and the same input/output tuple shape are duplicates.
- Changing only parameter names does not create a new callable.
- Changing only input labels in
ParamListdoes not create a new callable. - Changing only output labels does not create a new callable.
mod.barrelfunction entries are matched by callable identity, ignoring labels.- Barrel labels may differ from implementation labels.
- Any return surface that combines
optionalandresultis statically invalid. - Any payload-less
optionaltype surface is statically invalid. - Any
optional voidtype surface is statically invalid. - A
declare globaldeclaration must include an explicit type annotation. - A
declare globaldeclaration must include an explicit initializer. declare globalinitializer analysis is dependency-graph based rather than source-order based.- A
declare globalinitializer may depend on other visible globals only through their canonical owner identity. - Cycles between globals are statically invalid.
- A global import is valid only when the imported symbol resolves through a
globalentry inmod.barrel. - Imported visible names from
fn,service,global, andconstmust not collide with already-visible names in those same cross-category spaces; aliasing is required to disambiguate. [Init]and[Frame]are valid only on top-levelfndeclarations of shapefn name() -> void.[Frame]is required exactly once for an executable project.[Init]is optional.- A file may declare at most one
[Init]. - The
[Init]colocated with[Frame]is the project init. - Project init must be colocated with
[Frame]. InitAllowedis valid only on SDK host methods and is invalid on userlandfn,global,service,const,struct,contract, and callback surfaces.- A host method signature in a reserved stdlib/interface module MUST carry exactly one
Hostattribute. - A host method signature in a reserved stdlib/interface module MUST carry exactly one
Capabilityattribute. - A
Hostattribute MUST declare exactly the named argumentsmodule,name, andversion. Host.moduleandHost.nameMUST be non-empty string literals.Host.versionMUST be a positive integer literal.- A
Capabilityattribute MUST declare exactly the named argumentname. Capability.nameMUST be a non-empty lowercase capability identifier.- Two host methods in the same resolved stdlib environment MUST NOT lower to the same canonical
(module, name, version)unless they denote the same host method declaration after project/module resolution. - A
declare constdeclaration must include an explicit type annotation. - A
declare constdeclaration withoutBuiltinConstmetadata must include an initializer. - A
declare constdeclaration withBuiltinConstmetadata must omit the initializer. - A non-builtin
declare constinitializer must be a constant expression. declare constdependency resolution is module-wide and must not depend on source-file processing order.declare constdependencies must form an acyclic graph.- A
BuiltinTypeattribute MUST declare exactly the named argumentsnameandversion. BuiltinType.nameMUST be a non-empty string literal.BuiltinType.versionMUST be a positive integer literal.- Two builtin type declarations in the same resolved stdlib environment MUST NOT lower to the same canonical
(name, version)unless they denote the same builtin declaration after project/module resolution. - A builtin field type must be builtin-layout-admissible as defined by the builtin/intrinsics specification.
- Builtin field slot offsets are inferred by the compiler from canonical field order and flattened builtin layout width.
- Builtin layout composition must be acyclic.
- A builtin field that depends on a builtin layout with unresolved flattened width is statically invalid.
IntrinsicCallMUST declare exactly the named argumentnameand MAY additionally declareversion.IntrinsicCall.nameMUST be a non-empty string literal.IntrinsicCall.version, when present, MUST be a positive integer literal.- Two builtin methods in the same canonical builtin owner/version line MUST NOT lower to the same canonical intrinsic
(owner, name, version)unless they denote the same builtin method declaration after project/module resolution. BuiltinConstMUST declare exactly the named argumentstarget,name, andversion.BuiltinConst.targetandBuiltinConst.nameMUST be non-empty string literals.BuiltinConst.versionMUST be a positive integer literal.- Two builtin constant declarations in the same resolved stdlib environment MUST NOT lower to the same canonical
(target, name, version)unless they denote the same builtin constant declaration after project/module resolution. - Symbolic asset references admitted by the frontend MUST resolve against the backend-provided
Addressablesurface rather than by direct packer queries. - A valid symbolic asset reference MUST correspond to a terminal
Addressableentry in the backend-provided surface. - Intermediate
assets...namespace nodes MUST NOT type-check asAddressablevalues. - An expression statement MAY evaluate an expression that has a materialized value type.
- When an expression statement ignores a materialized value, PBS v1 MUST emit the ignored-value warning defined by the diagnostics specification.
- Unit-like expression statements MUST NOT emit the ignored-value warning.
3.4 Constant expressions
PBS core v1 restricts top-level constant evaluation to a deterministic compile-time subset.
Rules:
- A non-builtin
declare constinitializer is evaluated at compile time. - A non-builtin
declare constmay depend only on previously resolved visibledeclare constdeclarations and enum case values. - Cross-file constant resolution within a module is defined by dependency analysis, not source-file order.
- Constant evaluation fails if the dependency graph contains a cycle.
- The allowed constant-expression forms in v1 core are:
- scalar literals:
IntLit,FloatLit,StringLit,BoolLit, - enum case paths
EnumName.caseName, - a reference to another visible
declare const, - unary
-applied to an integer or float constant expression, - unary
notor!applied to a boolean constant expression, - binary arithmetic operators over numeric constant expressions,
- binary comparison operators over compatible constant expressions,
- binary boolean operators over boolean constant expressions.
- scalar literals:
- A non-builtin
declare constinitializer must not use:- callable application or call sugar,
new,bind(...),some(...)ornone,- tuple expressions,
- block expressions,
if,switch,handle,elseextraction, or!result propagation,- method calls or member access except enum case paths.
- For non-builtin
declare const, the initializer type must be compatible with the declaredconsttype. - A
BuiltinConstdeclaration is not part of ordinary compile-time constant evaluation and lowers through VM-owned builtin constant materialization instead. - A
declare constexported throughmod.barrelmay be imported from another module only through ordinary module import rules. - A
declare constdoes not introduce mutable runtime storage; implementations may inline, fold, or materialize it as an immutable constant as long as observable semantics remain unchanged. - These
declare construles do not definedeclare globalinitializer admissibility. declare globalinitializers are not general procedural bootstrap surfaces.- In v1, a
declare globalinitializer may use:- primitive literals and simple value operations,
- value-bearing member access,
new Struct(...),- and reads of other globals that preserve an acyclic dependency graph.
- In v1, a
declare globalinitializer must reject:- top-level
fncalls, some(...),none,if,- and
switch.
- top-level
3.5 Enum declarations and values
Rules:
declare enum Name(case1, case2, ...);introduces a nominal enum typeName.declare enum Name(case1 = n1, case2 = n2, ...);introduces explicit integer identifiers for enum cases.- If no enum case uses an explicit identifier, identifiers are assigned as
0..n-1in declaration order. - If any enum case uses an explicit identifier, then every enum case in that declaration must use an explicit identifier.
Name.caseis a valid enum value surface only whenNameresolves to a declared enum type andcaseresolves to a declared case of that enum.- The static type of
Name.caseis the enum typeName. - Error labels are not enum cases and must not be used where enum case values are required.
3.6 Structs, services, methods, and contracts
Rules:
- Struct values are heap-backed reference values in v1 core.
- Service values are canonical module-owned singleton values in v1 core.
new Struct(args...)allocates a new struct instance of the declared struct type.new Struct.ctorName(args...)invokes a named constructor declared in the enclosing struct body and produces a new struct instance of that type.- Struct construction arguments are matched positionally to the declared struct field order.
- Assigning a struct value copies the reference, not the underlying field storage.
- Assigning a service value copies the same canonical singleton identity; it does not create a new instance.
- A struct field with no access modifier is readable and writable only from methods and
ctorbodies of its enclosing struct. - A
pubstruct field is externally readable but remains externally non-assignable. - A
pub mutstruct field is externally readable and externally assignable. - An assignment target
expr.nameto a struct field is valid only when the access site is permitted to write that field. - Writes to a private struct field are valid only through
this.nameinside methods andctorbodies of the enclosing struct. - A struct method declared in a struct body has an implicit receiver binding
thisof typeSelf. - A service method declared in a service body has an implicit receiver binding
thisof typeSelf. thisis valid only inside struct method, service method, andctorbodies.Selfis valid only in struct method, service method, andctordeclarations and denotes the enclosing owner type.- The enclosing struct name is in scope inside its own field declarations, enabling self-referential field types.
- A named
ctordeclared in a struct body is not a method, but it does introduce an implicit under-constructionthis. - A
ctorhas no declared return annotation and is not afn. - A
ctorcompletes by falling off the end of its body after initializing the enclosing struct instance. - Every field of the enclosing struct must be definitely assigned on every completing
ctorpath. - Named constructors participate in overload resolution by name plus parameter signature.
return;andreturn expr;are both invalid insidectorbodies.- Methods declared in a struct body belong to the direct method namespace of that concrete struct type.
- Methods declared in a service body belong to the direct method namespace of that concrete service type.
receiver.method(...)on a concrete struct receiver resolves only against methods declared directly in that struct body.receiver.method(...)on a service receiver resolves only against methods declared directly in that service body.receiver.ctorName(...)is never valid on a concrete struct value.- Service values are ordinary value expressions and may be assigned, passed, and returned under their declared service type.
- Services do not have fields.
- Services do not declare named constructors and are never targets of
new. - Contract implementations do not inject their methods into the direct method namespace of the concrete struct or service.
implements Contract for Owner using s { ... }is unique per(Contract, Owner)pair, whereOwneris a declared struct or service.- A contract for a struct or service may be implemented only in the module where that owner itself is declared.
- Contracts may be declared in other modules and implemented for the local struct or service in its owner module.
- An
implementsblock has no independent visibility; its observability follows the visibility of the owner declaration, and the contract must also be nameable at the use site. - Methods declared directly inside a struct or service follow the visibility of their enclosing owner declaration; they do not have independent visibility.
- A struct value or service singleton value may be coerced to a contract value when exactly one visible
implements Contract for Owner ...applies. - Contract coercion may happen by explicit
expr as ContractTypeor by contextual typing in assignments, parameter passing, and returns. asin expression position is reserved for explicit contract coercion only; no other cast semantics exist in v1 core.- The contract name in
expr as ContractNamemust be a simple visible identifier resolved through ordinary scope and import rules. - A contract value carries the underlying struct reference or service singleton value together with the selected contract implementation for runtime dispatch.
- Calling
contractValue.method(...)uses dynamic dispatch through the contract value. - Calling
structValue.contractMethod(...)is not valid unless that method is also declared directly on the struct. - Calling
serviceValue.contractMethod(...)is not valid unless that method is also declared directly on the service. - Structs do not have static methods in v1 core; named constructors are their only type-qualified callable members.
- Services are accessed through their canonical singleton value, not through
newor static method declarations.
Example:
declare contract StasisProcess {
fn stasis() -> int;
}
declare struct Struct(pub a: int) {
fn compute() -> int {
return this.a;
}
ctor createWithSomethingSpecial(x: int) {
this.a = x;
}
}
implements StasisProcess for Struct using s {
fn stasis() -> int {
return s.compute();
}
}
fn demo() -> int {
let s = new Struct(1);
let s2 = new Struct.createWithSomethingSpecial(2);
let sp: StasisProcess = s;
let sp2 = s as StasisProcess;
let a = s.a;
let _ = s2.compute();
let b = sp.stasis();
let c = sp2.stasis();
return a + b + c;
}
3.7 Nominal callbacks
Rules:
declare callback Name(params...) -> R;introduces a nominal callback typeName.- A callback type is not a
fn, and afndeclaration does not implicitly define a callback type. - A value of callback type may be formed only from a compatible top-level
fnin v1 core. - A value of callback type may also be formed by
bind(context, fn_name)when the surrounding static type context expects a callback type. - Service methods are not assignable to callback values in v1 core.
- Host methods are not assignable to callback values in v1 core.
- Callback values do not capture local environment implicitly.
- Assignment of a top-level
fnto a callback type succeeds only when exactly one visible overload matches the callback signature. bind(context, fn_name)succeeds only when exactly one visible top-levelfnoverload matches after consuming the first parameter withcontext.- Valid expected callback contexts for
bind(context, fn_name)include variable initialization with an explicit callback type, assignment to an explicitly typed callback location, argument passing to a callback-typed parameter, and return from a callback-typed function. - The context expression in
bind(context, fn_name)is evaluated once at bind time and stored explicitly in the resulting callback value. - When that stored context is identity-bearing, the resulting callback preserves the same captured identity rather than copying an independent underlying object.
bind(context, fn_name)does not introduce general closures; it is explicit partial binding of the first parameter only.- Callback compatibility ignores labels and compares ordered input/output slot types plus the return-surface kind.
Example:
declare callback TickCb(dt: int) -> void;
fn on_tick(dt: int) -> void {
}
fn install_tick() -> void {
let cb: TickCb = on_tick;
let _ = cb;
}
Bound example:
declare callback UpdateCb(dt: int) -> void;
declare struct Enemy(hp: int);
fn update_enemy(self: Enemy, dt: int) -> void {
}
fn install_update(enemy: Enemy) -> void {
let cb: UpdateCb = bind(enemy, update_enemy);
let _ = cb;
}
Ambiguous overload example:
declare callback TickCb(dt: int) -> void;
fn tick(dt: int) -> void {
}
fn tick(time: int) -> void {
}
fn install_tick() -> void {
let cb: TickCb = tick; // error: ambiguous overloaded fn assignment
}
3.8 Canonical application
The canonical surface form for function application is:
callee apply arg
Rules:
applyis an infix operator that is right-associative.f1 apply f2 apply xis defined asf1 apply (f2 apply x).f1 apply f2 apply f3 apply paramsis defined asf1 apply (f2 apply (f3 apply params)).- The left operand of
applymust resolve to one of: a callable candidate set, a bound struct method target, a bound service method target, a contract-method target, or a callback value. - The right operand of
applyis an argument expression. - If the callable input arity is
0, the argument expression must be(). - If the callable input arity is
1, the argument expression is checked against the carrier type of the single input slot. - If the callable input arity is
2..6, the argument expression must have a named tuple shape with matching arity and positional types. - Input labels are not used for compatibility.
- No single-slot tuple argument literal is required or recognized in surface syntax.
- There is no partial application in v1 core.
- There is no currying in v1 core.
- Chained
applyis nested ordinary application, not callable composition as a first-class value. - Callback values, service methods, host methods, struct methods, and contract-method targets use the same
applyrules as top-levelfn.
3.9 Call sugar
The surface form:
callee(arg1, arg2, ..., argN)
desugars to the canonical apply surface according to arity.
Rules:
- The desugaring is purely syntactic.
f()is exact sugar forf apply ().f(x)is exact sugar forf apply x.f(x1, x2, ..., xn)forn >= 2is exact sugar forf apply (x1, x2, ..., xn).- No semantic distinction exists between direct call sugar and its canonical
applyform. - When
(arg1, ..., argN)is used as positional tuple sugar, the callee input tuple shape supplies the effective labels. - All overload resolution, diagnostics, and type checking are defined on the canonical
applyform.
3.10 Result type of application
Rules:
- If the selected callable output arity is
0, the result ofapplyis unit. - If the selected callable output arity is
1, the result ofapplyis the carrier type of the single output slot. - If the selected callable output arity is
2..6, the result ofapplyhas the named output tuple shape of the selected callable. - Multi-slot output tuple results expose their labels for member projection.
- If the selected callable is a callback value, the visible multi-slot output labels come from the callback type rather than from the original source
fn. - If the selected callable returns
(), the application result is unit.
Example:
fn func(a: int, b: int) -> (c: int, d: float) {
return (c: a + b, d: 2.0);
}
fn demo_apply() -> int {
let params: (a: int, b: int) = (1, 2);
let r = func apply params;
let c = r.c;
let d = r.d;
let _ = d;
return c;
}
In the example above:
funchas input tuple shape(a: int, b: int),funchas output tuple shape(c: int, d: float),rhas named output tuple type(c: int, d: float),r.chas typeint,r.dhas typefloat.
Single-slot example:
fn plus_one(x: int) -> int {
return x + 1;
}
fn demo_single() -> void {
let r = plus_one apply 1;
if r == 2 {
}
}
In the example above:
plus_onehas input tuple shape(x: int),- the argument
1is accepted directly as the carrier value for the single input slot, plus_onehas output tuple shape(value: int),- the value produced by
plus_one apply 1has carrier typeint, r.valueis not required and should not be used.
3.11 Overload resolution
Rules:
- Overload resolution starts from the visible callable candidates with the selected name.
- Candidate filtering first uses callable kind and input tuple arity.
- Remaining candidates are filtered by positional input type compatibility.
- If exactly one candidate remains, resolution succeeds.
- If multiple candidates remain, expected output types or expected multi-slot output tuple shapes may be used as a tie-breaker when statically available.
- If multiple candidates still remain, the
applyis ambiguous and compilation fails. - If no candidate remains, the
applyis unresolved and compilation fails. - Overloads that differ only by labels are duplicates and are forbidden.
- Overloads that differ only by output positional types are permitted, but uncontextualized
applysites may become ambiguous.
3.12 Member access and projection
Rules:
TypeName.caseis valid whenTypeNameresolves to a declared enum type andcaseresolves to one of its declared enum cases.TypeName.casehas static typeTypeName.expr.nameis valid when the static type ofexpris a declaredstructcontaining fieldnameand the access site is permitted to read that field.- For
structfield access, the projected member type is the declared field type. - Reads of a private struct field are valid only through
this.nameinside methods andctorbodies of the enclosing struct. expr.methodis a valid method-call target when the static type ofexpris a concrete struct type declaring methodmethod.expr.methodover a concrete struct receiver does not search visible contract implementations.expr.methodis a valid method-call target when the static type ofexpris a concrete service type declaring methodmethod.expr.methodover a concrete service receiver does not search visible contract implementations.HostName.methodis a valid method-call target whenHostNameresolves to a visible declared host owner whose body declares methodmethod.TypeName.ctorNameis not a general member access surface; named constructors are entered only throughnew TypeName.ctorName(...).expr.methodis a valid method-call target when the static type ofexpris a contract value whose contract declares methodmethod.- Bare extraction of a struct, service, host, builtin, or contract method as a value is invalid in v1 core because methods are not first-class.
expr.nameis valid when the static type ofexpris a multi-slot named output tuple containing labelname.- For named output tuples, the projected member type is the type of the corresponding output slot.
- Projection is label-based, not position-based.
expr.nameis valid when the static type ofexpris a builtin type declaring projection fieldname.- For builtin projection fields, the projected member type is the declared builtin field type.
expr.methodis a valid method-call target when the static type ofexpris a builtin type declaring intrinsic membermethod.expr.hasSome()andexpr.hasNone()are valid intrinsic method-call surfaces when the static type ofexprisoptional<P>.expr.hasSome()andexpr.hasNone()both have static typebool.expr.name()andexpr.key()are valid intrinsic method-call surfaces when the static type ofexpris an enum type.expr.name()has static typestr.expr.key()has static typeint.- Access to a missing output label is a compile-time error.
- Access to a missing struct field is a compile-time error.
- Read access to an inaccessible private struct field is a compile-time error.
- Write access to an inaccessible or non-
pub mutstruct field is a compile-time error. - Access to a missing struct method is a compile-time error.
- Access to a missing service method is a compile-time error.
- Access to a missing host method is a compile-time error.
- Access to a missing contract method is a compile-time error.
- Access to a missing builtin projection field is a compile-time error.
- Access to a missing builtin intrinsic member is a compile-time error.
- Member projection is not defined for single-slot collapsed carrier values.
3.13 Block expressions and function fallthrough
Rules:
- A block expression with a
TailExprhas the static type of that final expression. - A block expression without a
TailExprhas static type unit. - Only the final unsemicoloned expression in a block contributes to the block result.
- A function body does not implicitly return its trailing
TailExpr; explicitreturnand ordinary control-flow rules still apply. - Reaching the end of a plain non-unit function is a compile-time error unless all control-flow paths are proven to return explicitly.
- Reaching the end of a
voidor()function is valid and yields unit. - Reaching the end of an
optional Pfunction yieldsnone. - Reaching the end of a
result<E>function is a compile-time error.
3.14 Loop forms
Rules:
while cond { ... }requirescondto have static typebool.for i: T from start until end { ... }is the declarative counted loop form in v1 core.for i: T from start until end step s { ... }is the explicit-step counted loop form in v1 core.- In
for, the declared iteration variableiintroduces a loop-local binding of typeT. start,end, andstepexpressions informust be type-compatible with the declared iteration typeT.- If
stepis omitted, the loop uses the integer value1of the iteration type domain. foriteration proceeds by evaluatingstart,end, and optionalsteponce before loop entry.for ... until ...uses an exclusive upper bound.continueinsidewhiletransfers control to the next condition check.continueinsidefortransfers control to the step update and then to the next bound check.breakexits the nearest enclosing loop.
3.15 if and switch conditional expressions
Rules:
- In
if cond { a } else { b },condmust have static typebool. - Only the selected branch is evaluated at runtime.
- The branch expressions
aandbmust have the same static type, subject only to ordinary contextual typing already defined elsewhere in the language. - The static result type of the
ifexpression is that shared static type. - In
switch selector { pattern: { ... }, ... }, the selector expression is evaluated once. switchaccepts enum selectors and literal-comparable scalar selectors;errorselectors are invalid.- Each non-wildcard
switcharm pattern must be unique within the sameswitch. - Each non-wildcard
switcharm pattern must be type-compatible with the selector expression. - Enum-case switch patterns must resolve to declared cases of the selector's enum type.
- Error labels are not valid switch patterns.
defaultand_are equivalent wildcard spellings forswitch.- At most one wildcard arm may appear in a
switch. - A
switchmust not mixdefaultand_in the same arm set. - The blocks of all
switcharms must have the same static type, subject only to ordinary contextual typing already defined elsewhere in the language. - The static result type of the
switchexpression is that shared static type. - A
switchused as anExprStmtis valid when the selected arm blocks evaluate to unit. - A
switchexpression used in a value-required position must be exhaustive, either by a wildcard arm or by another statically proven exhaustive case set.
3.16 optional construction and extraction
Rules:
some(expr)forms anoptional<P>value wherePis the static type ofexpr, unless an expectedoptional<Q>context is present and constrainsexprto be compatible withQ.some(expr)is invalid when the payload normalizes tovoid.noneis valid only when an expectedoptional<P>type is available from context.opt else fallbackrequires the left operand to have static typeoptional<P>.- In
opt else fallback, thefallbackexpression must be compatible with payload shapeP. - The static result type of
opt else fallbackis the carrier value type for single-slot payloads and the named tuple payload shape for multi-slot payloads. opt else fallbackdoes not introduce implicit mutation or unwrapping side effects beyond normal expression evaluation.
3.17 result<E> return flow and handling
Rules:
return ok(expr);is valid only inside a function whose declared return surface isresult<E> P.- In
return ok(expr);,exprmust be compatible with the declared success payload shapeP. return err(E.label);is valid only inside a function whose declared return surface isresult<E> P.- In
return err(E.label);, the error path must resolve to a declared case of the same error typeE. ok(...)anderr(...)are special result-flow forms rather than ordinary value expressions.expr!requiresexprto have static typeresult<E> P.expr!is valid only when the enclosing function returnsresult<E> Qfor the same error typeE.- The static result type of
expr!is the extracted success payload shapeP, collapsed to its carrier value when single-slot. handle expr { ... }requiresexprto have static typeresult<E1> P.handle expr { ... }is valid only when the enclosing function returnsresult<E2> Qfor some payloadQ.- Each non-wildcard
handlearm must match a distinct declared case ofE1. handlearms must be exhaustive overE1, either by naming all cases exactly once or by providing_.- The static result type of
handle expr { ... }is the extracted success payload shapeP, collapsed to its carrier value when single-slot. - A short
handlearmE1.case -> E2.caseis valid only whenE2.caseresolves to a declared case of the target error typeE2. - A block
handlearm must terminate with eitherok(payload)orerr(E2.case). - In a
handlearm,ok(payload)is valid only whenpayloadis compatible with the success payload shapeP. - In a
handlearm,err(E2.case)is valid only whenE2.caseresolves to a declared case of the target error typeE2. - In a
handlearm,ok(payload)yields the value of thehandleexpression, whileerr(E2.case)performs immediate enclosing-function return.
3.18 Required static diagnostics
At minimum, deterministic static diagnostics are required for:
- duplicate parameter names in
fndeclarations, - duplicate declaration names in the same namespace/scope,
- duplicate output labels in named tuple returns,
- duplicate error case labels in
declare error, - duplicate enum case labels in
declare enum, - duplicate enum case identifiers in
declare enum, - invalid mixed implicit/explicit enum case identifiers,
- invalid
thisusage outside struct method, service method, orctorbody, - invalid
Selftype usage outside struct method, service method, orctordeclarations, - invalid struct method declaration shape,
- invalid service method declaration shape,
- invalid host declaration shape,
- invalid host method declaration shape,
- invalid attribute surface,
- invalid
Hostattribute target, - invalid
Capabilityattribute target, - invalid
BuiltinTypeattribute target, - invalid
BuiltinConstattribute target, - invalid
IntrinsicCallattribute target, - duplicate
Hostattribute on one host method, - missing required
Hostattribute on a reserved host method, - malformed
Host(module=..., name=..., version=...)argument set, - invalid empty
Host.moduleorHost.name, - invalid non-positive
Host.version, - malformed
Capability(name=...)argument set, - invalid empty or non-canonical
Capability.name, - invalid builtin declaration shape,
- invalid builtin method declaration shape,
- missing required
BuiltinTypeattribute on a reserved builtin declaration, - malformed
BuiltinType(name=..., version=...)argument set, - invalid empty
BuiltinType.name, - invalid non-positive
BuiltinType.version, - malformed
IntrinsicCall(name=..., version=...)argument set, - invalid empty
IntrinsicCall.name, - invalid non-positive
IntrinsicCall.version, - malformed
BuiltinConst(target=..., name=..., version=...)argument set, - invalid empty
BuiltinConst.targetorBuiltinConst.name, - invalid non-positive
BuiltinConst.version, - builtin field type not builtin-layout-admissible,
- cyclic builtin layout composition,
- unresolved flattened builtin layout width,
- duplicate canonical builtin type identity,
- duplicate canonical builtin intrinsic identity,
- duplicate canonical builtin constant identity,
- invalid
ctordeclaration shape, - invalid
returninsidector, - incomplete field initialization on a completing
ctorpath, - duplicate
implements Contract for Ownerpair, implementsdeclared outside the owner module of the struct or service,- incompatible or missing contract method implementation,
- invalid contract coercion from concrete struct or service value,
- unresolved barrel function signature after label-insensitive matching,
- unresolved host barrel entry,
- unresolved callback barrel entry,
- invalid enum case path,
- invalid enum intrinsic member call,
- invalid named constructor target,
- unresolved named constructor overload,
- ambiguous named constructor overload,
- incompatible top-level
fnassignment to callback type, - ambiguous overloaded top-level
fnassignment to callback type, - attempted assignment of service method to callback type,
- attempted assignment of host method to callback type,
- use of
bind(...)without an expected callback type, - incompatible
bind(...)context type for callback target, - ambiguous overloaded top-level
fntarget inbind(...), applyon a non-callable target,- arity mismatch in
apply, - argument type mismatch in
apply, applychain with incompatible intermediate carrier/tuple shape,- ambiguous overload resolution,
- unresolved callable application,
- possible fallthrough of plain non-unit function,
- non-boolean
whilecondition, - invalid
foriteration type, - incompatible
forbound or step type, - non-boolean
ifexpression condition, - incompatible
ifexpression branch types, - duplicate
switcharm pattern, - mixed
defaultand_wildcard arms inswitch, - invalid
switchselector type, - selector-pattern type mismatch in
switch, - invalid enum case switch pattern,
- incompatible
switcharm block types, - non-exhaustive
switchexpression in value position, - invalid mixed
optional/resultreturn surface, - invalid payload-less
optionaltype surface, - invalid
optional voidtype surface, - invalid reuse of a builtin simple type name for a user-authored type declaration,
- missing explicit type annotation on
declare const, - missing initializer on non-builtin
declare const, - non-constant
declare constinitializer, - incompatible
declare constinitializer type, - cyclic dependency among
declare constdeclarations, - unresolved referenced
declare constin constant evaluation, - invalid
noneusage without expected optional type, - invalid
elseextraction over non-optional operand, - fallback type mismatch in
elseextraction, - invalid
some(...)construction form, - use of
ok(...)orerr(...)outside allowed result-flow positions, - invalid
ok(...)construction form, - invalid
err(...)construction form, - invalid error label in
err(...), - invalid
!propagation on non-result expression, - mismatched error type in
!propagation, - invalid
handleon non-result expression, - non-exhaustive
handlemapping, - duplicate
handlemapping arm, - invalid mapped error label in
handle, - invalid
ok(...)payload inhandle, - invalid terminal form of a
handlearm block, - attempted use of a single-slot tuple literal where no such surface form exists,
- member access on a missing struct field,
- read access to an inaccessible private struct field,
- write access to an inaccessible or non-
pub mutstruct field, - member access on a missing struct method,
- member access on a missing service method,
- member access on a missing host method,
- member access on a missing contract method,
- member access on a missing builtin projection field,
- member access on a missing builtin intrinsic member,
- bare method extraction from a struct, service, host, builtin, or contract value,
- member projection on a single-slot carrier value,
- invalid optional intrinsic member call,
- invalid builtin intrinsic member call,
- member projection on a missing named output label.