Skip to main content

cairo_lang_lowering/
objects.rs

1//! Intermediate representation objects after lowering from semantic.
2//!
3//! This representation is SSA (static single-assignment): each variable is defined before usage and
4//! assigned once. It is also normal form: each function argument is a variable, rather than a
5//! compound expression.
6
7use std::ops::{Deref, DerefMut, Index};
8
9use cairo_lang_debug::DebugWithDb;
10use cairo_lang_defs::diagnostic_utils::StableLocation;
11use cairo_lang_diagnostics::{DiagnosticNote, Diagnostics};
12use cairo_lang_proc_macros::HeapSize;
13use cairo_lang_semantic as semantic;
14use cairo_lang_semantic::corelib::{concrete_destruct_trait, concrete_panic_destruct_trait};
15use cairo_lang_semantic::expr::inference::InferenceError;
16use cairo_lang_semantic::expr::inference::solver::Ambiguity;
17use cairo_lang_semantic::items::constant::ConstValueId;
18use cairo_lang_semantic::items::imp::ImplLookupContextId;
19use cairo_lang_semantic::types::{TypeInfo, TypesSemantic};
20use cairo_lang_semantic::{ConcreteEnumId, ConcreteVariant};
21use cairo_lang_utils::Intern;
22use cairo_lang_utils::ordered_hash_map::OrderedHashMap;
23use id_arena::{Arena, DefaultArenaBehavior, Id};
24
25pub mod blocks;
26pub use blocks::BlockId;
27use salsa::Database;
28use semantic::MatchArmSelector;
29
30use self::blocks::Blocks;
31use crate::analysis::StatementLocation;
32use crate::diagnostic::LoweringDiagnostic;
33use crate::fmt::LoweredFormatter;
34use crate::ids::{FunctionId, LocationId, Signature};
35
36/// The Location struct represents the source location of a lowered object. It is used to store the
37/// most relevant source location for a lowering object.
38#[derive(Clone, Debug, Eq, Hash, PartialEq, HeapSize, salsa::Update)]
39pub struct Location<'db> {
40    /// The stable location of the object.
41    pub stable_location: StableLocation<'db>,
42    /// Additional notes about the origin of the object, for example if the object was
43    /// auto-generated by the compiler.
44    /// New notes are appended to the end of the vector.
45    pub notes: Vec<DiagnosticNote<'db>>,
46    /// Function call locations where this value was inlined from.
47    pub inline_locations: Vec<StableLocation<'db>>,
48}
49impl<'db> Location<'db> {
50    pub fn new(stable_location: StableLocation<'db>) -> Self {
51        Self { stable_location, notes: vec![], inline_locations: vec![] }
52    }
53
54    /// Creates a new Location with the given note as the last note.
55    pub fn with_note(mut self, note: DiagnosticNote<'db>) -> Self {
56        self.notes.push(note);
57        self
58    }
59
60    /// Creates a new Location with the given note as the last note.
61    pub fn maybe_with_note(mut self, note: Option<DiagnosticNote<'db>>) -> Self {
62        let Some(note) = note else {
63            return self;
64        };
65        self.notes.push(note);
66        self
67    }
68
69    /// Creates a new Location with a note from the given text and location.
70    pub fn add_note_with_location(
71        self,
72        db: &'db dyn Database,
73        text: &str,
74        location: LocationId<'db>,
75    ) -> Self {
76        self.with_note(DiagnosticNote::with_location(
77            text.into(),
78            location.long(db).stable_location.span_in_file(db),
79        ))
80    }
81}
82
83impl<'db> DebugWithDb<'db> for Location<'db> {
84    type Db = dyn Database;
85    fn fmt(&self, f: &mut std::fmt::Formatter<'_>, db: &'db dyn Database) -> std::fmt::Result {
86        self.stable_location.span_in_file(db).fmt(f, db)?;
87
88        for note in &self.notes {
89            f.write_str("\nnote: ")?;
90            note.fmt(f, db)?;
91        }
92        Ok(())
93    }
94}
95
96impl<'db> LocationId<'db> {
97    /// Returns the location with the added inlining location of it.
98    pub fn inlined(self, db: &'db dyn Database, inlining_location: StableLocation<'db>) -> Self {
99        let mut location = self.long(db).clone();
100        location.inline_locations.push(inlining_location);
101        location.intern(db)
102    }
103    /// Returns all relevant stable pointers of the location.
104    pub fn all_locations(self, db: &'db dyn Database) -> Vec<StableLocation<'db>> {
105        let location = self.long(db);
106        let mut all_locations = vec![location.stable_location];
107        all_locations.extend(location.inline_locations.iter().cloned());
108        all_locations
109    }
110}
111
112#[derive(Clone, Debug, PartialEq, Eq)]
113pub struct VariableMarker;
114pub type VariableId = Id<VariableMarker>;
115pub type VariableArena<'db> = Arena<Variable<'db>, DefaultArenaBehavior<VariableMarker>>;
116
117/// Represents a usage of a variable.
118///
119/// For example if we have:
120///
121/// fn foo(a: u32) {
122///     1 + a
123/// }
124///
125/// Then the right hand side of the tail expression `1 + a` is a VarUsage object with
126/// the variable id of the variable `a` and the location:
127///     1 + a
128///         ^
129/// Note that the location associated with the variable that was assigned to 'a' is
130/// fn foo(a: u32)
131///        ^
132/// and it is different from the location in the VarUsage.
133///
134/// The tail expression `1 + a`  is also going to be assigned a variable and a VarUsage.
135/// in that case, the location of both the variable and the usage will be the same.
136#[derive(Copy, Clone, Debug, Eq, PartialEq)]
137pub struct VarUsage<'db> {
138    pub var_id: VariableId,
139    pub location: LocationId<'db>,
140}
141
142/// A lowered function code using flat blocks.
143#[derive(Clone, Debug, PartialEq, Eq)]
144pub struct Lowered<'db> {
145    /// Diagnostics produced while lowering.
146    pub diagnostics: Diagnostics<'db, LoweringDiagnostic<'db>>,
147    /// Function signature.
148    pub signature: Signature<'db>,
149    /// Arena of allocated lowered variables.
150    pub variables: VariableArena<'db>,
151    /// Arena of allocated lowered blocks.
152    pub blocks: Blocks<'db>,
153    /// function parameters, including implicits.
154    pub parameters: Vec<VariableId>,
155}
156
157impl<'db> Index<StatementLocation> for Lowered<'db> {
158    type Output = Statement<'db>;
159
160    fn index(&self, location: StatementLocation) -> &Self::Output {
161        &self.blocks[location]
162    }
163}
164
165unsafe impl<'db> salsa::Update for Lowered<'db> {
166    unsafe fn maybe_update(old_pointer: *mut Self, new_value: Self) -> bool {
167        let old_value = unsafe { &mut *old_pointer };
168        let res = unsafe {
169            Diagnostics::maybe_update(&mut old_value.diagnostics, new_value.diagnostics)
170                | Signature::maybe_update(&mut old_value.signature, new_value.signature)
171        } | (old_value.blocks != new_value.blocks);
172        if res {
173            old_value.variables = new_value.variables;
174            old_value.parameters = new_value.parameters;
175            old_value.blocks = new_value.blocks;
176        }
177        res
178    }
179}
180
181/// Remapping of lowered variable ids. Useful for convergence of branches.
182#[derive(Clone, Debug, Default, PartialEq, Eq)]
183pub struct VarRemapping<'db> {
184    /// Map from new_var to old_var (since new_var cannot appear twice, but old_var can).
185    pub remapping: OrderedHashMap<VariableId, VarUsage<'db>>,
186}
187impl<'db> Deref for VarRemapping<'db> {
188    type Target = OrderedHashMap<VariableId, VarUsage<'db>>;
189
190    fn deref(&self) -> &Self::Target {
191        &self.remapping
192    }
193}
194impl<'db> DerefMut for VarRemapping<'db> {
195    fn deref_mut(&mut self) -> &mut Self::Target {
196        &mut self.remapping
197    }
198}
199
200/// A block of statements.
201#[derive(Clone, Debug, PartialEq, Eq)]
202pub struct Block<'db> {
203    /// Statements sequence running one after the other in the block, in a linear flow.
204    pub statements: Vec<Statement<'db>>,
205    /// Describes how this block ends.
206    pub end: BlockEnd<'db>,
207}
208impl<'db> Default for Block<'db> {
209    fn default() -> Self {
210        Self { statements: Default::default(), end: BlockEnd::NotSet }
211    }
212}
213impl<'db> Block<'db> {
214    pub fn is_set(&self) -> bool {
215        !matches!(self.end, BlockEnd::NotSet)
216    }
217}
218
219/// Describes what happens to the program flow at the end of a [`Block`].
220#[derive(Clone, Debug, PartialEq, Eq)]
221pub enum BlockEnd<'db> {
222    /// The block was created but still needs to be populated. Block must not be in this state in
223    /// the end of the lowering phase.
224    NotSet,
225    /// This block ends with a `return` statement, exiting the function.
226    Return(Vec<VarUsage<'db>>, LocationId<'db>),
227    /// This block ends with a panic.
228    Panic(VarUsage<'db>),
229    /// This block ends with a jump to a different block.
230    Goto(BlockId, VarRemapping<'db>),
231    Match {
232        info: MatchInfo<'db>,
233    },
234}
235
236impl<'db> BlockEnd<'db> {
237    /// Returns the location of this block end, if available.
238    pub fn location(&self) -> Option<LocationId<'db>> {
239        match self {
240            BlockEnd::Return(_, location) => Some(*location),
241            BlockEnd::Panic(var) => Some(var.location),
242            BlockEnd::Match { info } => Some(*info.location()),
243            BlockEnd::Goto(_, _) | BlockEnd::NotSet => None,
244        }
245    }
246}
247
248/// Lowered variable representation.
249#[derive(Clone, Debug, PartialEq, Eq)]
250pub struct Variable<'db> {
251    /// Semantic type of the variable.
252    pub ty: semantic::TypeId<'db>,
253    /// Location of the variable.
254    pub location: LocationId<'db>,
255    /// The semantic type info of the variable.
256    pub info: TypeInfo<'db>,
257}
258
259impl<'db> DebugWithDb<'db> for Variable<'db> {
260    type Db = LoweredFormatter<'db>;
261
262    fn fmt(&self, f: &mut std::fmt::Formatter<'_>, _ctx: &Self::Db) -> std::fmt::Result {
263        write!(f, "Variable({:?})", self.ty)
264    }
265}
266
267impl<'db> Variable<'db> {
268    pub fn new(
269        db: &'db dyn Database,
270        ctx: ImplLookupContextId<'db>,
271        ty: semantic::TypeId<'db>,
272        location: LocationId<'db>,
273    ) -> Self {
274        Self { ty, location, info: db.type_info(ctx, ty) }
275    }
276
277    /// Returns a new variable with the type, with info calculated with the default context.
278    pub fn with_default_context(
279        db: &'db dyn Database,
280        ty: semantic::TypeId<'db>,
281        location: LocationId<'db>,
282    ) -> Self {
283        Self {
284            ty,
285            location,
286            info: TypeInfo {
287                copyable: db.copyable(ty),
288                droppable: db.droppable(ty),
289                destruct_impl: Err(InferenceError::Ambiguity(Ambiguity::WillNotInfer(
290                    concrete_destruct_trait(db, ty),
291                ))),
292                panic_destruct_impl: Err(InferenceError::Ambiguity(Ambiguity::WillNotInfer(
293                    concrete_panic_destruct_trait(db, ty),
294                ))),
295            },
296        }
297    }
298}
299
300/// Lowered statement.
301#[derive(Clone, Debug, PartialEq, Eq)]
302pub enum Statement<'db> {
303    // Values.
304    Const(StatementConst<'db>),
305
306    // Flow control.
307    Call(StatementCall<'db>),
308
309    // Structs (including tuples).
310    StructConstruct(StatementStructConstruct<'db>),
311    StructDestructure(StatementStructDestructure<'db>),
312
313    // Enums.
314    EnumConstruct(StatementEnumConstruct<'db>),
315
316    Snapshot(StatementSnapshot<'db>),
317    Desnap(StatementDesnap<'db>),
318
319    // Boxing.
320    IntoBox(StatementIntoBox<'db>),
321    Unbox(StatementUnbox<'db>),
322}
323impl<'db> Statement<'db> {
324    pub fn inputs(&self) -> &[VarUsage<'db>] {
325        match &self {
326            Statement::Const(_stmt) => &[],
327            Statement::Call(stmt) => stmt.inputs.as_slice(),
328            Statement::StructConstruct(stmt) => stmt.inputs.as_slice(),
329            Statement::StructDestructure(stmt) => std::slice::from_ref(&stmt.input),
330            Statement::EnumConstruct(stmt) => std::slice::from_ref(&stmt.input),
331            Statement::Snapshot(stmt) => std::slice::from_ref(&stmt.input),
332            Statement::Desnap(stmt) => std::slice::from_ref(&stmt.input),
333            Statement::IntoBox(stmt) => std::slice::from_ref(&stmt.input),
334            Statement::Unbox(stmt) => std::slice::from_ref(&stmt.input),
335        }
336    }
337
338    pub fn inputs_mut(&mut self) -> &mut [VarUsage<'db>] {
339        match self {
340            Statement::Const(_stmt) => &mut [],
341            Statement::Call(stmt) => stmt.inputs.as_mut_slice(),
342            Statement::StructConstruct(stmt) => stmt.inputs.as_mut_slice(),
343            Statement::StructDestructure(stmt) => std::slice::from_mut(&mut stmt.input),
344            Statement::EnumConstruct(stmt) => std::slice::from_mut(&mut stmt.input),
345            Statement::Snapshot(stmt) => std::slice::from_mut(&mut stmt.input),
346            Statement::Desnap(stmt) => std::slice::from_mut(&mut stmt.input),
347            Statement::IntoBox(stmt) => std::slice::from_mut(&mut stmt.input),
348            Statement::Unbox(stmt) => std::slice::from_mut(&mut stmt.input),
349        }
350    }
351
352    pub fn outputs(&self) -> &[VariableId] {
353        match self {
354            Statement::Const(stmt) => std::slice::from_ref(&stmt.output),
355            Statement::Call(stmt) => stmt.outputs.as_slice(),
356            Statement::StructConstruct(stmt) => std::slice::from_ref(&stmt.output),
357            Statement::StructDestructure(stmt) => stmt.outputs.as_slice(),
358            Statement::EnumConstruct(stmt) => std::slice::from_ref(&stmt.output),
359            Statement::Snapshot(stmt) => stmt.outputs.as_slice(),
360            Statement::Desnap(stmt) => std::slice::from_ref(&stmt.output),
361            Statement::IntoBox(stmt) => std::slice::from_ref(&stmt.output),
362            Statement::Unbox(stmt) => std::slice::from_ref(&stmt.output),
363        }
364    }
365
366    pub fn outputs_mut(&mut self) -> &mut [VariableId] {
367        match self {
368            Statement::Const(stmt) => std::slice::from_mut(&mut stmt.output),
369            Statement::Call(stmt) => stmt.outputs.as_mut_slice(),
370            Statement::StructConstruct(stmt) => std::slice::from_mut(&mut stmt.output),
371            Statement::StructDestructure(stmt) => stmt.outputs.as_mut_slice(),
372            Statement::EnumConstruct(stmt) => std::slice::from_mut(&mut stmt.output),
373            Statement::Snapshot(stmt) => stmt.outputs.as_mut_slice(),
374            Statement::Desnap(stmt) => std::slice::from_mut(&mut stmt.output),
375            Statement::IntoBox(stmt) => std::slice::from_mut(&mut stmt.output),
376            Statement::Unbox(stmt) => std::slice::from_mut(&mut stmt.output),
377        }
378    }
379    pub fn location(&self) -> Option<LocationId<'db>> {
380        // TODO(Gil): Add location to all statements.
381        match &self {
382            Statement::Const(_) => None,
383            Statement::Call(stmt) => Some(stmt.location),
384            Statement::StructConstruct(_) => None,
385            Statement::StructDestructure(stmt) => Some(stmt.input.location),
386            Statement::EnumConstruct(stmt) => Some(stmt.input.location),
387            Statement::Snapshot(stmt) => Some(stmt.input.location),
388            Statement::Desnap(stmt) => Some(stmt.input.location),
389            Statement::IntoBox(stmt) => Some(stmt.input.location),
390            Statement::Unbox(stmt) => Some(stmt.input.location),
391        }
392    }
393    pub fn location_mut(&mut self) -> Option<&mut LocationId<'db>> {
394        match self {
395            Statement::Const(_) => None,
396            Statement::Call(stmt) => Some(&mut stmt.location),
397            Statement::StructConstruct(_) => None,
398            Statement::StructDestructure(stmt) => Some(&mut stmt.input.location),
399            Statement::EnumConstruct(stmt) => Some(&mut stmt.input.location),
400            Statement::Snapshot(stmt) => Some(&mut stmt.input.location),
401            Statement::Desnap(stmt) => Some(&mut stmt.input.location),
402            Statement::IntoBox(stmt) => Some(&mut stmt.input.location),
403            Statement::Unbox(stmt) => Some(&mut stmt.input.location),
404        }
405    }
406}
407
408/// A statement that binds a const value to a variable.
409#[derive(Clone, Debug, PartialEq, Eq)]
410pub struct StatementConst<'db> {
411    /// The value of the const.
412    pub value: ConstValueId<'db>,
413    /// The variable to bind the value to.
414    pub output: VariableId,
415    /// Is the const wrapped in a box.
416    pub boxed: bool,
417}
418impl<'db> StatementConst<'db> {
419    /// Creates a new const statement, with the option to wrap the value in a box.
420    pub fn new(value: ConstValueId<'db>, output: VariableId, boxed: bool) -> Self {
421        Self { value, output, boxed }
422    }
423    /// Creates a new const statement, not boxed.
424    pub fn new_flat(value: ConstValueId<'db>, output: VariableId) -> Self {
425        Self::new(value, output, false)
426    }
427    /// Creates a new const statement with the value wrapped in a box.
428    pub fn new_boxed(value: ConstValueId<'db>, output: VariableId) -> Self {
429        Self::new(value, output, true)
430    }
431}
432
433/// A statement that calls a user function.
434#[derive(Clone, Debug, PartialEq, Eq)]
435pub struct StatementCall<'db> {
436    /// A function to "call".
437    pub function: FunctionId<'db>,
438    /// Living variables in current scope to move to the function, as arguments.
439    pub inputs: Vec<VarUsage<'db>>,
440    /// Is the last input a coupon for the function call. See
441    /// [semantic::ExprFunctionCall::coupon_arg] for more information.
442    pub with_coupon: bool,
443    /// New variables to be introduced into the current scope from the function outputs.
444    pub outputs: Vec<VariableId>,
445    /// Is the call to be inlined as part of the specialization wrapper function.
446    pub is_specialization_base_call: bool,
447    /// Location for the call.
448    pub location: LocationId<'db>,
449}
450
451/// A statement that constructs a variant of an enum with a single argument, and binds it to a
452/// variable.
453#[derive(Clone, Debug, PartialEq, Eq)]
454pub struct StatementEnumConstruct<'db> {
455    pub variant: ConcreteVariant<'db>,
456    /// A living variable in current scope to wrap with the variant.
457    pub input: VarUsage<'db>,
458    /// The variable to bind the value to.
459    pub output: VariableId,
460}
461
462/// A statement that constructs a struct (tuple included) into a new variable.
463#[derive(Clone, Debug, PartialEq, Eq)]
464pub struct StatementStructConstruct<'db> {
465    pub inputs: Vec<VarUsage<'db>>,
466    /// The variable to bind the value to.
467    pub output: VariableId,
468}
469
470/// A statement that destructures a struct (tuple included), introducing its elements as new
471/// variables.
472#[derive(Clone, Debug, PartialEq, Eq)]
473pub struct StatementStructDestructure<'db> {
474    /// A living variable in current scope to destructure.
475    pub input: VarUsage<'db>,
476    /// The variables to bind values to.
477    pub outputs: Vec<VariableId>,
478}
479
480/// A statement that takes a snapshot of a variable.
481#[derive(Clone, Debug, PartialEq, Eq)]
482pub struct StatementSnapshot<'db> {
483    pub input: VarUsage<'db>,
484    pub outputs: [VariableId; 2],
485}
486impl<'db> StatementSnapshot<'db> {
487    pub fn new(
488        input: VarUsage<'db>,
489        output_original: VariableId,
490        output_snapshot: VariableId,
491    ) -> Self {
492        Self { input, outputs: [output_original, output_snapshot] }
493    }
494    pub fn original(&self) -> VariableId {
495        self.outputs[0]
496    }
497    pub fn snapshot(&self) -> VariableId {
498        self.outputs[1]
499    }
500}
501
502/// A statement that desnaps a variable.
503#[derive(Clone, Debug, PartialEq, Eq)]
504pub struct StatementDesnap<'db> {
505    pub input: VarUsage<'db>,
506    /// The variable to bind the value to.
507    pub output: VariableId,
508}
509
510/// A statement that constructs a box from a value.
511#[derive(Clone, Debug, PartialEq, Eq)]
512pub struct StatementIntoBox<'db> {
513    /// The value to box.
514    pub input: VarUsage<'db>,
515    /// The variable to bind the boxed value to.
516    pub output: VariableId,
517}
518
519/// A statement that unboxes a value.
520#[derive(Clone, Debug, PartialEq, Eq)]
521pub struct StatementUnbox<'db> {
522    /// The boxed value to unbox.
523    pub input: VarUsage<'db>,
524    /// The variable to bind the unboxed value to.
525    pub output: VariableId,
526}
527
528/// An arm of a match statement.
529#[derive(Clone, Debug, PartialEq, Eq)]
530pub struct MatchArm<'db> {
531    /// The selector of the arm.
532    pub arm_selector: MatchArmSelector<'db>,
533
534    /// The block_id where the relevant arm is implemented.
535    pub block_id: BlockId,
536
537    /// The list of variable ids introduced in this arm.
538    pub var_ids: Vec<VariableId>,
539}
540
541/// A statement that calls an extern function with branches, and "calls" a possibly different block
542/// for each branch.
543#[derive(Clone, Debug, PartialEq, Eq)]
544pub struct MatchExternInfo<'db> {
545    // TODO(spapini): ConcreteExternFunctionId once it exists.
546    /// A concrete external function to call.
547    pub function: FunctionId<'db>,
548    /// Living variables in current scope to move to the function, as arguments.
549    pub inputs: Vec<VarUsage<'db>>,
550    /// Match arms. All blocks should have the same rets.
551    /// Order must be identical to the order in the definition of the enum.
552    pub arms: Vec<MatchArm<'db>>,
553    /// Location for the call.
554    pub location: LocationId<'db>,
555}
556
557/// A statement that matches an enum, and "calls" a possibly different block for each branch.
558#[derive(Clone, Debug, PartialEq, Eq)]
559pub struct MatchEnumInfo<'db> {
560    pub concrete_enum_id: ConcreteEnumId<'db>,
561    /// A living variable in current scope to match on.
562    pub input: VarUsage<'db>,
563    /// Match arms. All blocks should have the same rets.
564    /// Order must be identical to the order in the definition of the enum.
565    pub arms: Vec<MatchArm<'db>>,
566    /// Location for the match.
567    pub location: LocationId<'db>,
568}
569/// A statement that matches an index enum for matching on felt252, and "calls" a possibly different
570/// block for each branch.
571#[derive(Clone, Debug, PartialEq, Eq)]
572pub struct MatchEnumValue<'db> {
573    pub num_of_arms: usize,
574
575    /// A living variable in current scope to match on.
576    pub input: VarUsage<'db>,
577    /// Match arms. All blocks should have the same rets.
578    pub arms: Vec<MatchArm<'db>>,
579    /// Location for the match.
580    pub location: LocationId<'db>,
581}
582
583#[derive(Clone, Debug, PartialEq, Eq)]
584pub enum MatchInfo<'db> {
585    Enum(MatchEnumInfo<'db>),
586    Extern(MatchExternInfo<'db>),
587    Value(MatchEnumValue<'db>),
588}
589impl<'db> MatchInfo<'db> {
590    pub fn inputs(&self) -> &[VarUsage<'db>] {
591        match self {
592            MatchInfo::Enum(s) => std::slice::from_ref(&s.input),
593            MatchInfo::Extern(s) => s.inputs.as_slice(),
594            MatchInfo::Value(s) => std::slice::from_ref(&s.input),
595        }
596    }
597
598    pub fn inputs_mut(&mut self) -> &mut [VarUsage<'db>] {
599        match self {
600            MatchInfo::Enum(s) => std::slice::from_mut(&mut s.input),
601            MatchInfo::Extern(s) => s.inputs.as_mut_slice(),
602            MatchInfo::Value(s) => std::slice::from_mut(&mut s.input),
603        }
604    }
605    pub fn arms(&self) -> &[MatchArm<'db>] {
606        match self {
607            MatchInfo::Enum(s) => &s.arms,
608            MatchInfo::Extern(s) => &s.arms,
609            MatchInfo::Value(s) => &s.arms,
610        }
611    }
612    pub fn arms_mut(&mut self) -> &mut [MatchArm<'db>] {
613        match self {
614            MatchInfo::Enum(s) => &mut s.arms,
615            MatchInfo::Extern(s) => &mut s.arms,
616            MatchInfo::Value(s) => &mut s.arms,
617        }
618    }
619    pub fn location(&self) -> &LocationId<'db> {
620        match self {
621            MatchInfo::Enum(s) => &s.location,
622            MatchInfo::Extern(s) => &s.location,
623            MatchInfo::Value(s) => &s.location,
624        }
625    }
626    pub fn location_mut(&mut self) -> &mut LocationId<'db> {
627        match self {
628            MatchInfo::Enum(s) => &mut s.location,
629            MatchInfo::Extern(s) => &mut s.location,
630            MatchInfo::Value(s) => &mut s.location,
631        }
632    }
633}
634
635/// Used in graph algorithms, and describes how to construct the edges in function dependency graph.
636#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
637pub enum DependencyType {
638    /// A function depends on another function if it may call it.
639    Call,
640    /// A function depends on another function if its cost depends on the other function's cost.
641    Cost,
642}
643
644/// The requested lowering stage.
645#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
646pub enum LoweringStage {
647    /// Direct translation from the semantic stage and concretization.
648    Monomorphized,
649    /// After lowering stages that may change the signature of functions, such as `lower_panics`.
650    /// Specifically:
651    /// * Adds `withdraw_gas` calls.
652    /// * Adds panics.
653    /// * Adds destructor calls.
654    /// * scrub units.
655    PreOptimizations,
656    /// Lowering with baseline optimizations - specifically, adds the stages at
657    /// `baseline_optimization_strategy`.
658    PostBaseline,
659    /// Lowering with all of the optimizations - specifically, adds the stages at
660    /// `final_optimization_strategy`
661    Final,
662}