1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
//! Block-level IR statement types: `IrBlockStatement` and `IrMatchArm`.
use super::{expr::IrExpr, BindingId, ResolvedType, VariantIdx};
/// A statement within a block expression.
#[expect(
clippy::exhaustive_enums,
reason = "IR types are matched exhaustively by code generators"
)]
#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
pub enum IrBlockStatement {
/// Let binding: `let x = expr` or `let mut x = expr`
Let {
/// Per-function-unique identifier assigned by
/// `ResolveReferencesPass`. Lowering emits `BindingId(0)` and the
/// pass overwrites it.
binding_id: BindingId,
/// Binding name (preserved for diagnostics).
name: String,
/// Whether the binding is mutable
mutable: bool,
/// Optional type annotation
ty: Option<ResolvedType>,
/// Value expression
value: IrExpr,
/// Source span for DWARF / source-map emission.
#[serde(default, skip_serializing_if = "super::IrSpan::is_default")]
span: super::IrSpan,
},
/// Assignment: `x = expr`
Assign {
/// Target expression (variable or field path)
target: IrExpr,
/// Value expression
value: IrExpr,
/// Source span for DWARF / source-map emission.
#[serde(default, skip_serializing_if = "super::IrSpan::is_default")]
span: super::IrSpan,
},
/// Expression statement (evaluated for side effects).
/// The wrapped `IrExpr` carries its own span; no statement-level
/// span field is needed for this variant.
Expr(IrExpr),
}
impl IrBlockStatement {
/// Transform every expression in this statement with `f`. Used by passes
/// like constant folding and DCE.
#[must_use]
pub fn map_exprs<F>(self, mut f: F) -> Self
where
F: FnMut(IrExpr) -> IrExpr,
{
match self {
Self::Let {
binding_id,
name,
mutable,
ty,
value,
span,
} => Self::Let {
binding_id,
name,
mutable,
ty,
value: f(value),
span,
},
Self::Assign {
target,
value,
span,
} => Self::Assign {
target: f(target),
value: f(value),
span,
},
Self::Expr(expr) => Self::Expr(f(expr)),
}
}
}
/// A match arm: `Variant(bindings) => body` or `_ => body`
#[expect(
clippy::exhaustive_structs,
reason = "IR types are constructed directly by consumer code"
)]
#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
pub struct IrMatchArm {
/// Variant name being matched (empty string for wildcard); preserved
/// alongside [`Self::variant_idx`] for diagnostics.
pub variant: String,
/// Position of the matched variant in the scrutinee enum's `variants`
/// vector. Lowering emits `VariantIdx(0)` and `ResolveReferencesPass`
/// overwrites it.
pub variant_idx: VariantIdx,
/// Whether this is a wildcard pattern (`_`)
pub is_wildcard: bool,
/// Bindings for associated data: `(name, binding_id, type)`. The
/// `binding_id` is a fresh per-function identifier introduced by
/// the match arm; lowering emits `BindingId(0)` and
/// `ResolveReferencesPass` overwrites it. Backends key on the
/// `BindingId` to reach the slot the arm writes the payload into.
pub bindings: Vec<(String, BindingId, ResolvedType)>,
/// Body expression
pub body: IrExpr,
}