Verified Subset Boundaries
Verified code runs under a stricter, sound subset of Sector9. Sector9 is a language derived from Motoko, but verification adds contracts, ghost state, footprints, and runtime-entry guard rules. This page summarizes the boundaries enforced by the verifier.
Trusted Boundary
- Verified by default: Code is verified unless marked
trusted. - Trusted functions still need specs: At least one
requiresandensures, plusreads/modifiesif they touch actor state.
See Trusted Modifier.
Effects and Framing
reads/modifiesare required for actor state access.- Missing transitive effects are rejected (no auto-fix).
- Calls with unknown effect summaries are forbidden unless
pureortrusted.
See Footprints.
Expression Purity
- Impure calls are materialized into statement-level temporaries (ANF). Direct
let x = await f()is rejected; usetry { await f() } catch (_) { fallback }when an awaited result is needed. - Specs (
requires/ensures/invariants) may call onlypurefunctions. - Public actor methods with caller-controlled logical
requiresneed matchingentry_requires. The runtime guard cannot mention ghost state,old(), orresult.
Async and Error Handling
- Every
await(and anyawait*that may suspend) must be wrapped intrywith a catch-all. await?andthroware rejected in verification.- Error values (
Error) cannot escape verified code. finallycan mutate state but cannotawait,return,break, orthrow.- Unawaited futures are rejected (no fire-and-forget).
See Await and Error Handling.
Aliasing and Mutability
- Deep mutable actor state is restricted. Keep public actor state shallow when possible; current owned/fresh local lanes cover some mutable-record and direct array cases, while nested array actor fields remain unsupported.
- If aliasing cannot be proven disjoint, verification rejects the code.
- Locals that may reference actor state across
awaitare rejected. - Opaque/token handles are copyable identities, but their hidden payloads are still owner-controlled. Non-owner modules may store and pass handles through capability-summarized module APIs and supported containers; they cannot inspect or mutate the sealed payload directly.
- Descriptor-backed collection snapshots can enumerate handle identities through
exact
BMap.entries/MBMap.entriesandBSeqarray/list/freeze bridges, but they do not snapshot owner payloads. DirectBMap.toArrayover handle values remains unsupported. - Opaque owner mutators may batch several payload field writes before the
invariant is rechecked. The owner must be repaired before return,
await, or a call boundary that receives or writes the dirty owner. - Stable persistence for token/opaque handles must be compiler-owned: source
code should keep handle types in stable fields and supported containers,
while the compiler preserves raw same-identity state and checks owner
identity. Stable upgrade is not a shared boundary and must not call
toShared/fromShared. - Handle-bearing actor-state carriers are conservative across
await: a fresh local handle may cross an await, but a handle recovered from actor-owned carriers such as records, arrays,BMap,MBMap, orBSeqmay not be kept live across the suspension.
See Deep Aliasing and Local vs Shared State.
Structural Subtyping
- Structural subtyping of records/modules is allowed only for spec-immutable types.
- Structural coercions involving mutable identity (arrays, mutable records, actors) are rejected.
See Verified Subtyping.
Higher-Order Function Values
- Function values must be
pureor carry type contracts. - Escaping functions that touch
$Selfrequire fullrequires/ensuresand exactreads/modifies. - Uncontracted callback types are treated as pure, so impure callbacks must use contract-typed parameters.
See Contract Subtyping.
Iteration
for-inloops accept iterator objects whosenext()method is() -> ?Twith a type contract.
Integer and Spec Restrictions
- Verified code runs in checked-int mode.
- Division/modulo in specs must be guarded against zero.
See Runtime Limitations.