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};
8
9use cairo_lang_debug::DebugWithDb;
10use cairo_lang_defs::diagnostic_utils::StableLocation;
11use cairo_lang_diagnostics::{DiagnosticNote, Diagnostics};
12use cairo_lang_semantic as semantic;
13use cairo_lang_semantic::items::imp::ImplLookupContext;
14use cairo_lang_semantic::types::TypeInfo;
15use cairo_lang_semantic::{ConcreteEnumId, ConcreteVariant};
16use cairo_lang_utils::ordered_hash_map::OrderedHashMap;
17use cairo_lang_utils::{Intern, LookupIntern};
18use id_arena::{Arena, Id};
19
20pub mod blocks;
21pub use blocks::BlockId;
22use semantic::MatchArmSelector;
23use semantic::expr::inference::InferenceError;
24use semantic::items::constant::ConstValue;
25use semantic::items::imp::ImplId;
26
27use self::blocks::FlatBlocks;
28use crate::db::LoweringGroup;
29use crate::diagnostic::LoweringDiagnostic;
30use crate::ids::{FunctionId, LocationId, Signature};
31
32/// The Location struct represents the source location of a lowered object. It is used to store the
33/// most relevant source location for a lowering object.
34#[derive(Clone, Debug, Eq, Hash, PartialEq)]
35pub struct Location {
36    /// The stable location of the object.
37    pub stable_location: StableLocation,
38    /// Additional notes about the origin of the object, for example if the object was
39    /// auto-generated by the compiler.
40    /// New notes are appended to the end of the vector.
41    pub notes: Vec<DiagnosticNote>,
42    /// Function call locations where this value was inlined from.
43    pub inline_locations: Vec<StableLocation>,
44}
45impl Location {
46    pub fn new(stable_location: StableLocation) -> Self {
47        Self { stable_location, notes: vec![], inline_locations: vec![] }
48    }
49
50    /// Creates a new Location with the given note as the last note.
51    pub fn with_note(mut self, note: DiagnosticNote) -> Self {
52        self.notes.push(note);
53        self
54    }
55
56    /// Creates a new Location with the given note as the last note.
57    pub fn maybe_with_note(mut self, note: Option<DiagnosticNote>) -> Self {
58        let Some(note) = note else {
59            return self;
60        };
61        self.notes.push(note);
62        self
63    }
64
65    /// Creates a new Location with a note from the given text and location.
66    pub fn add_note_with_location(
67        self,
68        db: &dyn LoweringGroup,
69        text: &str,
70        location: LocationId,
71    ) -> Self {
72        self.with_note(DiagnosticNote::with_location(
73            text.into(),
74            location.lookup_intern(db).stable_location.diagnostic_location(db),
75        ))
76    }
77}
78
79impl DebugWithDb<dyn LoweringGroup> for Location {
80    fn fmt(&self, f: &mut std::fmt::Formatter<'_>, db: &dyn LoweringGroup) -> std::fmt::Result {
81        let files_db = db.upcast();
82        self.stable_location.diagnostic_location(db).fmt(f, files_db)?;
83
84        for note in &self.notes {
85            f.write_str("\nnote: ")?;
86            note.fmt(f, files_db)?;
87        }
88        Ok(())
89    }
90}
91
92impl LocationId {
93    /// Returns the location with the added inlining location of it.
94    pub fn inlined(self, db: &dyn LoweringGroup, inlining_location: StableLocation) -> Self {
95        let mut location = self.lookup_intern(db);
96        location.inline_locations.push(inlining_location);
97        location.intern(db)
98    }
99    /// Returns all relevant stable pointers of the location.
100    pub fn all_locations(self, db: &dyn LoweringGroup) -> Vec<StableLocation> {
101        let location = self.lookup_intern(db);
102        let mut all_locations = vec![location.stable_location];
103        all_locations.extend(location.inline_locations.iter().cloned());
104        all_locations
105    }
106}
107
108pub type VariableId = Id<Variable>;
109
110/// Represents a usage of a variable.
111///
112/// For example if we have:
113///
114/// fn foo(a: u32) {
115///     1 + a
116/// }
117///
118/// Then the right hand side of the tail expression `1 + a` is a VarUsage object with
119/// the variable id of the variable `a` and the location:
120///     1 + a
121///         ^
122/// Note that the location associated with the variable that was assigned to 'a' is
123/// fn foo(a: u32)
124///        ^
125/// and it is different from the location in the VarUsage.
126///
127/// The tail expression `1 + a`  is also going to be assigned a variable and a VarUsage.
128/// in that case, the location of both the variable and the usage will be the same.
129#[derive(Copy, Clone, Debug, Eq, PartialEq)]
130pub struct VarUsage {
131    pub var_id: VariableId,
132    pub location: LocationId,
133}
134
135/// A lowered function code using flat blocks.
136#[derive(Clone, Debug, PartialEq, Eq)]
137pub struct FlatLowered {
138    /// Diagnostics produced while lowering.
139    pub diagnostics: Diagnostics<LoweringDiagnostic>,
140    /// Function signature.
141    pub signature: Signature,
142    /// Arena of allocated lowered variables.
143    pub variables: Arena<Variable>,
144    /// Arena of allocated lowered blocks.
145    pub blocks: FlatBlocks,
146    /// function parameters, including implicits.
147    pub parameters: Vec<VariableId>,
148}
149
150/// Remapping of lowered variable ids. Useful for convergence of branches.
151#[derive(Clone, Debug, Default, PartialEq, Eq)]
152pub struct VarRemapping {
153    /// Map from new_var to old_var (since new_var cannot appear twice, but old_var can).
154    pub remapping: OrderedHashMap<VariableId, VarUsage>,
155}
156impl Deref for VarRemapping {
157    type Target = OrderedHashMap<VariableId, VarUsage>;
158
159    fn deref(&self) -> &Self::Target {
160        &self.remapping
161    }
162}
163impl DerefMut for VarRemapping {
164    fn deref_mut(&mut self) -> &mut Self::Target {
165        &mut self.remapping
166    }
167}
168
169/// A block of statements. Unlike [`FlatBlock`], this has no reference information,
170/// and no panic ending.
171#[derive(Clone, Debug, PartialEq, Eq)]
172pub struct FlatBlock {
173    /// Statements sequence running one after the other in the block, in a linear flow.
174    /// Note: Inner blocks might end with a `return`, which will exit the function in the middle.
175    /// Note: Match is a possible statement, which means it has control flow logic inside, but
176    /// after its execution is completed, the flow returns to the following statement of the block.
177    pub statements: Vec<Statement>,
178    /// Describes how this block ends: returns to the caller or exits the function.
179    pub end: FlatBlockEnd,
180}
181impl Default for FlatBlock {
182    fn default() -> Self {
183        Self { statements: Default::default(), end: FlatBlockEnd::NotSet }
184    }
185}
186impl FlatBlock {
187    pub fn is_set(&self) -> bool {
188        !matches!(self.end, FlatBlockEnd::NotSet)
189    }
190}
191
192/// Describes what happens to the program flow at the end of a [`FlatBlock`].
193#[derive(Clone, Debug, PartialEq, Eq)]
194pub enum FlatBlockEnd {
195    /// The block was created but still needs to be populated. Block must not be in this state in
196    /// the end of the lowering phase.
197    NotSet,
198    /// This block ends with a `return` statement, exiting the function.
199    Return(Vec<VarUsage>, LocationId),
200    /// This block ends with a panic.
201    Panic(VarUsage),
202    /// This block ends with a jump to a different block.
203    Goto(BlockId, VarRemapping),
204    Match {
205        info: MatchInfo,
206    },
207}
208
209/// Lowered variable representation.
210#[derive(Clone, Debug, PartialEq, Eq)]
211pub struct Variable {
212    /// Can the type be (trivially) dropped.
213    pub droppable: Result<ImplId, InferenceError>,
214    /// Can the type be (trivially) copied.
215    pub copyable: Result<ImplId, InferenceError>,
216    /// A Destruct impl for the type, if found.
217    pub destruct_impl: Result<ImplId, InferenceError>,
218    /// A PanicDestruct impl for the type, if found.
219    pub panic_destruct_impl: Result<ImplId, InferenceError>,
220    /// Semantic type of the variable.
221    pub ty: semantic::TypeId,
222    /// Location of the variable.
223    pub location: LocationId,
224}
225impl Variable {
226    pub fn new(
227        db: &dyn LoweringGroup,
228        ctx: ImplLookupContext,
229        ty: semantic::TypeId,
230        location: LocationId,
231    ) -> Self {
232        let TypeInfo { droppable, copyable, destruct_impl, panic_destruct_impl } =
233            match db.type_info(ctx, ty) {
234                Ok(info) => info,
235                Err(diag_added) => TypeInfo {
236                    droppable: Err(InferenceError::Reported(diag_added)),
237                    copyable: Err(InferenceError::Reported(diag_added)),
238                    destruct_impl: Err(InferenceError::Reported(diag_added)),
239                    panic_destruct_impl: Err(InferenceError::Reported(diag_added)),
240                },
241            };
242        Self { copyable, droppable, destruct_impl, panic_destruct_impl, ty, location }
243    }
244}
245
246/// Lowered statement.
247#[derive(Clone, Debug, PartialEq, Eq)]
248pub enum Statement {
249    // Values.
250    Const(StatementConst),
251
252    // Flow control.
253    Call(StatementCall),
254
255    // Structs (including tuples).
256    StructConstruct(StatementStructConstruct),
257    StructDestructure(StatementStructDestructure),
258
259    // Enums.
260    EnumConstruct(StatementEnumConstruct),
261
262    Snapshot(StatementSnapshot),
263    Desnap(StatementDesnap),
264}
265impl Statement {
266    pub fn inputs(&self) -> &[VarUsage] {
267        match &self {
268            Statement::Const(_stmt) => &[],
269            Statement::Call(stmt) => stmt.inputs.as_slice(),
270            Statement::StructConstruct(stmt) => stmt.inputs.as_slice(),
271            Statement::StructDestructure(stmt) => std::slice::from_ref(&stmt.input),
272            Statement::EnumConstruct(stmt) => std::slice::from_ref(&stmt.input),
273            Statement::Snapshot(stmt) => std::slice::from_ref(&stmt.input),
274            Statement::Desnap(stmt) => std::slice::from_ref(&stmt.input),
275        }
276    }
277
278    pub fn inputs_mut(&mut self) -> &mut [VarUsage] {
279        match self {
280            Statement::Const(_stmt) => &mut [],
281            Statement::Call(stmt) => stmt.inputs.as_mut_slice(),
282            Statement::StructConstruct(stmt) => stmt.inputs.as_mut_slice(),
283            Statement::StructDestructure(stmt) => std::slice::from_mut(&mut stmt.input),
284            Statement::EnumConstruct(stmt) => std::slice::from_mut(&mut stmt.input),
285            Statement::Snapshot(stmt) => std::slice::from_mut(&mut stmt.input),
286            Statement::Desnap(stmt) => std::slice::from_mut(&mut stmt.input),
287        }
288    }
289
290    pub fn outputs(&self) -> &[VariableId] {
291        match self {
292            Statement::Const(stmt) => std::slice::from_ref(&stmt.output),
293            Statement::Call(stmt) => stmt.outputs.as_slice(),
294            Statement::StructConstruct(stmt) => std::slice::from_ref(&stmt.output),
295            Statement::StructDestructure(stmt) => stmt.outputs.as_slice(),
296            Statement::EnumConstruct(stmt) => std::slice::from_ref(&stmt.output),
297            Statement::Snapshot(stmt) => stmt.outputs.as_slice(),
298            Statement::Desnap(stmt) => std::slice::from_ref(&stmt.output),
299        }
300    }
301
302    pub fn outputs_mut(&mut self) -> &mut [VariableId] {
303        match self {
304            Statement::Const(stmt) => std::slice::from_mut(&mut stmt.output),
305            Statement::Call(stmt) => stmt.outputs.as_mut_slice(),
306            Statement::StructConstruct(stmt) => std::slice::from_mut(&mut stmt.output),
307            Statement::StructDestructure(stmt) => stmt.outputs.as_mut_slice(),
308            Statement::EnumConstruct(stmt) => std::slice::from_mut(&mut stmt.output),
309            Statement::Snapshot(stmt) => stmt.outputs.as_mut_slice(),
310            Statement::Desnap(stmt) => std::slice::from_mut(&mut stmt.output),
311        }
312    }
313    pub fn location(&self) -> Option<LocationId> {
314        // TODO(Gil): Add location to all statements.
315        match &self {
316            Statement::Const(_) => None,
317            Statement::Call(stmt) => Some(stmt.location),
318            Statement::StructConstruct(_) => None,
319            Statement::StructDestructure(stmt) => Some(stmt.input.location),
320            Statement::EnumConstruct(stmt) => Some(stmt.input.location),
321            Statement::Snapshot(stmt) => Some(stmt.input.location),
322            Statement::Desnap(stmt) => Some(stmt.input.location),
323        }
324    }
325    pub fn location_mut(&mut self) -> Option<&mut LocationId> {
326        match self {
327            Statement::Const(_) => None,
328            Statement::Call(stmt) => Some(&mut stmt.location),
329            Statement::StructConstruct(_) => None,
330            Statement::StructDestructure(stmt) => Some(&mut stmt.input.location),
331            Statement::EnumConstruct(stmt) => Some(&mut stmt.input.location),
332            Statement::Snapshot(stmt) => Some(&mut stmt.input.location),
333            Statement::Desnap(stmt) => Some(&mut stmt.input.location),
334        }
335    }
336}
337
338/// A statement that binds a const value to a variable.
339#[derive(Clone, Debug, PartialEq, Eq)]
340pub struct StatementConst {
341    /// The value of the const.
342    pub value: ConstValue,
343    /// The variable to bind the value to.
344    pub output: VariableId,
345}
346
347/// A statement that calls a user function.
348#[derive(Clone, Debug, PartialEq, Eq)]
349pub struct StatementCall {
350    /// A function to "call".
351    pub function: FunctionId,
352    /// Living variables in current scope to move to the function, as arguments.
353    pub inputs: Vec<VarUsage>,
354    /// Is the last input a coupon for the function call. See
355    /// [semantic::ExprFunctionCall::coupon_arg] for more information.
356    pub with_coupon: bool,
357    /// New variables to be introduced into the current scope from the function outputs.
358    pub outputs: Vec<VariableId>,
359    /// Location for the call.
360    pub location: LocationId,
361}
362
363/// A statement that construct a variant of an enum with a single argument, and binds it to a
364/// variable.
365#[derive(Clone, Debug, PartialEq, Eq)]
366pub struct StatementEnumConstruct {
367    pub variant: ConcreteVariant,
368    /// A living variable in current scope to wrap with the variant.
369    pub input: VarUsage,
370    /// The variable to bind the value to.
371    pub output: VariableId,
372}
373
374/// A statement that constructs a struct (tuple included) into a new variable.
375#[derive(Clone, Debug, PartialEq, Eq)]
376pub struct StatementStructConstruct {
377    pub inputs: Vec<VarUsage>,
378    /// The variable to bind the value to.
379    pub output: VariableId,
380}
381
382/// A statement that destructures a struct (tuple included), introducing its elements as new
383/// variables.
384#[derive(Clone, Debug, PartialEq, Eq)]
385pub struct StatementStructDestructure {
386    /// A living variable in current scope to destructure.
387    pub input: VarUsage,
388    /// The variables to bind values to.
389    pub outputs: Vec<VariableId>,
390}
391
392/// A statement that takes a snapshot of a variable.
393#[derive(Clone, Debug, PartialEq, Eq)]
394pub struct StatementSnapshot {
395    pub input: VarUsage,
396    pub outputs: [VariableId; 2],
397}
398impl StatementSnapshot {
399    pub fn new(input: VarUsage, output_original: VariableId, output_snapshot: VariableId) -> Self {
400        Self { input, outputs: [output_original, output_snapshot] }
401    }
402    pub fn original(&self) -> VariableId {
403        self.outputs[0]
404    }
405    pub fn snapshot(&self) -> VariableId {
406        self.outputs[1]
407    }
408}
409
410/// A statement that desnaps a variable.
411#[derive(Clone, Debug, PartialEq, Eq)]
412pub struct StatementDesnap {
413    pub input: VarUsage,
414    /// The variable to bind the value to.
415    pub output: VariableId,
416}
417
418/// An arm of a match statement.
419#[derive(Clone, Debug, PartialEq, Eq)]
420pub struct MatchArm {
421    /// The selector of the arm.
422    pub arm_selector: MatchArmSelector,
423
424    /// The block_id where the relevant arm is implemented.
425    pub block_id: BlockId,
426
427    /// The list of variable ids introduced in this arm.
428    pub var_ids: Vec<VariableId>,
429}
430
431/// A statement that calls an extern function with branches, and "calls" a possibly different block
432/// for each branch.
433#[derive(Clone, Debug, PartialEq, Eq)]
434pub struct MatchExternInfo {
435    // TODO(spapini): ConcreteExternFunctionId once it exists.
436    /// A concrete external function to call.
437    pub function: FunctionId,
438    /// Living variables in current scope to move to the function, as arguments.
439    pub inputs: Vec<VarUsage>,
440    /// Match arms. All blocks should have the same rets.
441    /// Order must be identical to the order in the definition of the enum.
442    pub arms: Vec<MatchArm>,
443    /// Location for the call.
444    pub location: LocationId,
445}
446
447/// A statement that matches an enum, and "calls" a possibly different block for each branch.
448#[derive(Clone, Debug, PartialEq, Eq)]
449pub struct MatchEnumInfo {
450    pub concrete_enum_id: ConcreteEnumId,
451    /// A living variable in current scope to match on.
452    pub input: VarUsage,
453    /// Match arms. All blocks should have the same rets.
454    /// Order must be identical to the order in the definition of the enum.
455    pub arms: Vec<MatchArm>,
456    /// Location for the match.
457    pub location: LocationId,
458}
459/// A statement that matches an index enum for matching on felt252, and "calls" a possibly different
460/// block for each branch.
461#[derive(Clone, Debug, PartialEq, Eq)]
462pub struct MatchEnumValue {
463    pub num_of_arms: usize,
464
465    /// A living variable in current scope to match on.
466    pub input: VarUsage,
467    /// Match arms. All blocks should have the same rets.
468    pub arms: Vec<MatchArm>,
469    /// Location for the match.
470    pub location: LocationId,
471}
472
473#[derive(Clone, Debug, PartialEq, Eq)]
474pub enum MatchInfo {
475    Enum(MatchEnumInfo),
476    Extern(MatchExternInfo),
477    Value(MatchEnumValue),
478}
479impl MatchInfo {
480    pub fn inputs(&self) -> &[VarUsage] {
481        match self {
482            MatchInfo::Enum(s) => std::slice::from_ref(&s.input),
483            MatchInfo::Extern(s) => s.inputs.as_slice(),
484            MatchInfo::Value(s) => std::slice::from_ref(&s.input),
485        }
486    }
487
488    pub fn inputs_mut(&mut self) -> &mut [VarUsage] {
489        match self {
490            MatchInfo::Enum(s) => std::slice::from_mut(&mut s.input),
491            MatchInfo::Extern(s) => s.inputs.as_mut_slice(),
492            MatchInfo::Value(s) => std::slice::from_mut(&mut s.input),
493        }
494    }
495    pub fn arms(&self) -> &[MatchArm] {
496        match self {
497            MatchInfo::Enum(s) => &s.arms,
498            MatchInfo::Extern(s) => &s.arms,
499            MatchInfo::Value(s) => &s.arms,
500        }
501    }
502    pub fn arms_mut(&mut self) -> &mut [MatchArm] {
503        match self {
504            MatchInfo::Enum(s) => &mut s.arms,
505            MatchInfo::Extern(s) => &mut s.arms,
506            MatchInfo::Value(s) => &mut s.arms,
507        }
508    }
509    pub fn location(&self) -> &LocationId {
510        match self {
511            MatchInfo::Enum(s) => &s.location,
512            MatchInfo::Extern(s) => &s.location,
513            MatchInfo::Value(s) => &s.location,
514        }
515    }
516    pub fn location_mut(&mut self) -> &mut LocationId {
517        match self {
518            MatchInfo::Enum(s) => &mut s.location,
519            MatchInfo::Extern(s) => &mut s.location,
520            MatchInfo::Value(s) => &mut s.location,
521        }
522    }
523}
524
525/// Used in graph algorithms, and describes how to construct the edges in function dependency graph.
526#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
527pub enum DependencyType {
528    /// A function depends on another function if it may call it.
529    Call,
530    /// A function depends on another function if its cost depends on the other function's cost.
531    Cost,
532}