Skip to main content

MirExpr

Enum MirExpr 

Source
pub enum MirExpr {
Show 23 variants Literal(Spanned<Literal>), Local(Spanned<MirLocal>), Let(Spanned<MirLet>), Call(Spanned<MirCall>), TailCall(Spanned<MirTailCall>), BinOp(Spanned<MirBinOp>), Neg(Box<Spanned<MirExpr>>), Match(Spanned<MirMatch>), Construct(Spanned<MirConstruct>), RecordCreate(Spanned<MirRecordCreate>), RecordUpdate(Spanned<MirRecordUpdate>), Project(Spanned<MirProject>), IfThenElse(Spanned<MirIfThenElse>), Try(Box<Spanned<MirExpr>>), List(Vec<Spanned<MirExpr>>), Tuple(Vec<Spanned<MirExpr>>), MapLiteral(Vec<(Spanned<MirExpr>, Spanned<MirExpr>)>), InterpolatedStr(Vec<MirStrPart>), IndependentProduct(Spanned<MirIndependentProduct>), Return(Box<Spanned<MirExpr>>), FnValue(String), Box(Box<Spanned<MirExpr>>), Unbox(Box<Spanned<MirExpr>>),
}
Expand description

One MIR expression. Every variant is a value — there’s no separate statement form at this phase. Sequencing happens via Let.

Variants§

§

Literal(Spanned<Literal>)

A literal value (Int, Float, Bool, String, Unit). Same vocabulary the existing typed AST already uses.

§

Local(Spanned<MirLocal>)

Read a previously-bound local. The LocalId was introduced either as a function parameter (MirParam::local) or via a Let in this body’s lexical scope. Phase 6 wave 4: carries a last_use flag the lowerer propagates from HIR’s ResolvedExpr::Resolved { last_use, .. } so backends can emit MOVE_LOCAL (skipping ref-count bumps) on the final read of a slot.

§

Let(Spanned<MirLet>)

let binding = value; body — sequence two expressions and surface the second’s value. Phase 2a’s only sequencing primitive; everything else inside a function body composes from this + the value-form variants below.

§

Call(Spanned<MirCall>)

Apply a callee (user fn / builtin) to arguments. The callee kind discriminates so backends know whether to look up via FnId (typed identity) or via the named-builtin registry.

§

TailCall(Spanned<MirTailCall>)

Tail call to a user fn — same SCC as the surrounding fn. Backends decide the final shape (wasm-gc tail-call insn, VM tail dispatch, Rust loop rewrite).

§

BinOp(Spanned<MirBinOp>)

Binary operator over numeric / boolean operands. Same set as ast::BinOp — MIR doesn’t normalize arithmetic here; that’s a Phase 6 optimizer concern.

§

Neg(Box<Spanned<MirExpr>>)

Unary numeric negation. Distinct from BinOp(Sub, 0, x) so IEEE-754 -0.0 semantics are preserved on Float.

§

Match(Spanned<MirMatch>)

match <subject> { arm₁ ; arm₂ ; … } — structured. Phase 4 VM walks arms in order, picks the first matching pattern.

§

Construct(Spanned<MirConstruct>)

Construct a sum-type variant by CtorId. The variant’s declared fields are filled in argument order.

§

RecordCreate(Spanned<MirRecordCreate>)

Build a fresh record of a named product type. Fields are (field_name, value) pairs to keep the dump readable; the declared field order is determined by TypeId and validated at lowering time.

§

RecordUpdate(Spanned<MirRecordUpdate>)

T.update(base, field = v, …) — produce a new record that matches base except for the named field overrides.

§

Project(Spanned<MirProject>)

Field access (base.field) on a record value.

§

IfThenElse(Spanned<MirIfThenElse>)

if cond { then } else { else } — direct conditional shape introduced by Phase 6 wave 9’s bool_match_to_if pass. Lowering never produces this node directly; the optimizer pass rewrites qualifying two-arm Bool match expressions into it so every backend gets a uniform if/else node instead of re-implementing the recognition.

§

Try(Box<Spanned<MirExpr>>)

value? — the canonical ? propagation. Phase 1’s most-important pin: this stays a node. Lowering to nested Match is a per-backend choice, not a pipeline-wide transform. Rust will eventually emit ? native, VM emits tag-check + early return.

The bound form let x = step()?; body is expressed as MirExpr::Let { binding: x, value: MirExpr::Try(step()), body } — no dedicated TryBind variant. The original design had one, but it duplicated the semantics of Let { value: Try(_), ... } exactly. Consumers that need to recognize the ?-bind pattern do so by walking Let and inspecting value.node for MirExpr::Try.

§

List(Vec<Spanned<MirExpr>>)

[a, b, c] — list literal. Elements lower to MIR expressions; the resulting value is List<T> with T inferred at type-check time.

§

Tuple(Vec<Spanned<MirExpr>>)

(a, b, c) — tuple literal.

§

MapLiteral(Vec<(Spanned<MirExpr>, Spanned<MirExpr>)>)

{"k" => v, …} — map literal. Keys + values lower as MIR expressions; the resulting value is Map<K, V>.

§

InterpolatedStr(Vec<MirStrPart>)

"…{expr}…" — interpolated string. Each part is either a literal text segment or an embedded MIR expression whose value gets stringified at runtime.

§

IndependentProduct(Spanned<MirIndependentProduct>)

Independent product: (a, b, c)! or (a, b, c)?!. The unwrap_results flag captures the ? form (every element must be Result<…>; Err short-circuits with the first error). Schedule (complete / cancel / sequential) is an aver.toml runtime policy and is NOT carried in MIR.

§

Return(Box<Spanned<MirExpr>>)

Early return value; — used in lowered bodies that have a natural early-return shape (the ? propagation lowering inside a backend is the canonical example). Functions that don’t return early end their body with the final expression itself; Return is only for the explicit early-exit case.

§

FnValue(String)

A fn referenced as a value (not called): callWith(dbl) passes dbl. Carries the canonical fn / builtin name; the backend resolves it to a symbol reference (the VM pushes a symbol_ref constant, mirroring the HIR walker’s StaticRef leaf-op). The walker falls back to HIR if the name doesn’t resolve (a genuinely unresolved ident — typecheck-rejected input).

§

Box(Box<Spanned<MirExpr>>)

Int-representation boundary (ETAP-2): raw i64Int. The inner expression evaluates to a raw machine i64 (a value the Int “unboxing” analysis proved bare); Box wraps it back into the arbitrary-precision Int (aver_rt::AverInt) at every escape (a return-as-Int, a boxed-callee-param arg, an aggregate/record/map store, a stringify, a boxed Let crossing, a boxed-arithmetic operand).

Inserted ONLY by the bare_i64::rewrite_for_rust MIR→MIR pass, which runs LATE (after proof export + shape recognition) on a per-target clone — so the VM / wasm-gc / proof MIR never contains this node. Backends that may see it (the Rust codegen) lower it to aver_rt::AverInt::from_i64(<inner raw>); every other walker treats it as a transparent pass-through to inner (it never appears on their path, the arm exists only for exhaustiveness).

§

Unbox(Box<Spanned<MirExpr>>)

Int-representation boundary (ETAP-2): Int → raw i64. The dual of MirExpr::Box: the inner expression evaluates to an Int (aver_rt::AverInt) and Unbox narrows it to a raw machine i64 via the checked to_i64() (the analysis only marks a slot bare when it provably fits i64, so the narrowing never loses information). Inserted by the same rewrite; the Rust codegen lowers it to <inner boxed>.to_i64().expect(...).

Trait Implementations§

Source§

impl Clone for MirExpr

Source§

fn clone(&self) -> MirExpr

Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for MirExpr

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<V, T> VZip<V> for T
where V: MultiLane<T>,

Source§

fn vzip(self) -> V