Skip to main content

cairo_lang_lowering/
fmt.rs

1use cairo_lang_debug::DebugWithDb;
2use cairo_lang_debug::debug::DebugWithDbOverride;
3use cairo_lang_defs::ids::NamedLanguageElementId;
4use itertools::Itertools;
5use salsa::Database;
6
7use crate::objects::{
8    MatchExternInfo, Statement, StatementCall, StatementConst, StatementIntoBox,
9    StatementStructDestructure, StatementUnbox, VariableId,
10};
11use crate::{
12    Block, BlockEnd, Lowered, MatchArm, MatchEnumInfo, MatchEnumValue, MatchInfo, StatementDesnap,
13    StatementEnumConstruct, StatementSnapshot, StatementStructConstruct, VarRemapping, VarUsage,
14    VariableArena,
15};
16
17/// Holds all the information needed for formatting lowered representations.
18/// Acts like a "db" for DebugWithDb.
19pub struct LoweredFormatter<'db> {
20    pub db: &'db dyn Database,
21    pub variables: &'db VariableArena<'db>,
22    pub include_usage_location: bool,
23}
24impl<'db> LoweredFormatter<'db> {
25    pub fn new(db: &'db dyn Database, variables: &'db VariableArena<'db>) -> Self {
26        Self { db, variables, include_usage_location: false }
27    }
28}
29
30impl<'db> DebugWithDb<'db> for VarRemapping<'db> {
31    type Db = LoweredFormatter<'db>;
32
33    fn fmt(&self, f: &mut std::fmt::Formatter<'_>, _ctx: &Self::Db) -> std::fmt::Result {
34        let mut remapping = self.iter().peekable();
35        write!(f, "{{")?;
36        while let Some((dst, src)) = remapping.next() {
37            write!(f, "v{:?} -> v{:?}", src.var_id.index(), dst.index())?;
38            if remapping.peek().is_some() {
39                write!(f, ", ")?;
40            }
41        }
42        write!(f, "}}")?;
43        Ok(())
44    }
45}
46impl<'db> DebugWithDb<'db> for Lowered<'db> {
47    type Db = LoweredFormatter<'db>;
48
49    fn fmt(&self, f: &mut std::fmt::Formatter<'_>, ctx: &Self::Db) -> std::fmt::Result {
50        write!(f, "Parameters:")?;
51        let mut inputs = self.parameters.iter().peekable();
52        while let Some(var) = inputs.next() {
53            write!(f, " ")?;
54            format_var_with_ty(*var, f, ctx)?;
55            if inputs.peek().is_some() {
56                write!(f, ",")?;
57            }
58        }
59        writeln!(f)?;
60        let mut blocks = self.blocks.iter();
61        if let Some((root_block_id, root_block)) = blocks.next() {
62            write!(f, "{root_block_id:?}")?;
63            writeln!(f, " (root):")?;
64            root_block.fmt(f, ctx)?;
65            writeln!(f)?;
66        }
67        for (block_id, block) in blocks {
68            write!(f, "{block_id:?}")?;
69            writeln!(f, ":")?;
70            block.fmt(f, ctx)?;
71            writeln!(f)?;
72        }
73        Ok(())
74    }
75}
76
77impl<'db> DebugWithDb<'db> for Block<'db> {
78    type Db = LoweredFormatter<'db>;
79
80    fn fmt(&self, f: &mut std::fmt::Formatter<'_>, ctx: &Self::Db) -> std::fmt::Result {
81        writeln!(f, "Statements:")?;
82        for stmt in &self.statements {
83            write!(f, "  ")?;
84            stmt.fmt(f, ctx)?;
85            writeln!(f)?;
86        }
87
88        writeln!(f, "End:")?;
89        self.end.fmt(f, ctx)?;
90        writeln!(f)
91    }
92}
93
94impl<'db> DebugWithDb<'db> for BlockEnd<'db> {
95    type Db = LoweredFormatter<'db>;
96
97    fn fmt(&self, f: &mut std::fmt::Formatter<'_>, ctx: &Self::Db) -> std::fmt::Result {
98        match &self {
99            BlockEnd::Return(returns, _location) => {
100                write!(f, "  Return(")?;
101                let mut it = returns.iter().peekable();
102                while let Some(var_usage) = it.next() {
103                    write!(f, "v{:?}", var_usage.var_id.index())?;
104                    if it.peek().is_some() {
105                        write!(f, ", ")?;
106                    }
107                }
108                write!(f, ")")
109            }
110            BlockEnd::Panic(data) => {
111                write!(f, "  Panic(v{:?})", data.var_id.index())
112            }
113            BlockEnd::Goto(block_id, remapping) => {
114                write!(f, "  Goto({block_id:?}, {:?})", remapping.debug(ctx))
115            }
116            BlockEnd::NotSet => write!(f, "  Not set"),
117            BlockEnd::Match { info } => write!(f, "  Match({:?})", info.debug(ctx)),
118        }
119    }
120}
121
122fn format_var_with_ty(
123    var_id: VariableId,
124    f: &mut std::fmt::Formatter<'_>,
125    ctx: &LoweredFormatter<'_>,
126) -> std::fmt::Result {
127    write!(f, "v{:?}", var_id.index())?;
128    let var = &ctx.variables[var_id];
129    write!(f, ": {}", var.ty.format(ctx.db))
130}
131
132impl<'db> DebugWithDb<'db> for VarUsage<'db> {
133    type Db = LoweredFormatter<'db>;
134
135    fn fmt(&self, f: &mut std::fmt::Formatter<'_>, ctx: &Self::Db) -> std::fmt::Result {
136        write!(f, "v{:?}", self.var_id.index(),)?;
137        if ctx.include_usage_location {
138            write!(
139                f,
140                "{{`{}`}}",
141                self.location
142                    .long(ctx.db)
143                    .stable_location
144                    .syntax_node(ctx.db)
145                    .get_text_without_trivia(ctx.db)
146                    .long(ctx.db)
147                    .lines()
148                    .map(|s| s.trim())
149                    .join(" ")
150            )?;
151        }
152        Ok(())
153    }
154}
155
156impl<'db> DebugWithDbOverride<'db, LoweredFormatter<'db>> for VariableId {
157    fn fmt_override(
158        &self,
159        f: &mut std::fmt::Formatter<'_>,
160        _db: &'db LoweredFormatter<'db>,
161    ) -> std::fmt::Result {
162        write!(f, "v{:?}", self.index())
163    }
164}
165
166impl<'db> DebugWithDb<'db> for Statement<'db> {
167    type Db = LoweredFormatter<'db>;
168
169    fn fmt(&self, f: &mut std::fmt::Formatter<'_>, ctx: &Self::Db) -> std::fmt::Result {
170        write!(f, "(")?;
171        let mut outputs = self.outputs().iter().peekable();
172        while let Some(var) = outputs.next() {
173            format_var_with_ty(*var, f, ctx)?;
174            if outputs.peek().is_some() {
175                write!(f, ", ")?;
176            }
177        }
178        write!(f, ") <- ")?;
179        match self {
180            Statement::Const(stmt) => stmt.fmt(f, ctx),
181            Statement::Call(stmt) => stmt.fmt(f, ctx),
182            Statement::StructConstruct(stmt) => stmt.fmt(f, ctx),
183            Statement::StructDestructure(stmt) => stmt.fmt(f, ctx),
184            Statement::EnumConstruct(stmt) => stmt.fmt(f, ctx),
185            Statement::Snapshot(stmt) => stmt.fmt(f, ctx),
186            Statement::Desnap(stmt) => stmt.fmt(f, ctx),
187            Statement::IntoBox(stmt) => stmt.fmt(f, ctx),
188            Statement::Unbox(stmt) => stmt.fmt(f, ctx),
189        }
190    }
191}
192
193impl<'db> DebugWithDb<'db> for MatchInfo<'db> {
194    type Db = LoweredFormatter<'db>;
195
196    fn fmt(&self, f: &mut std::fmt::Formatter<'_>, ctx: &Self::Db) -> std::fmt::Result {
197        match self {
198            MatchInfo::Extern(s) => s.fmt(f, ctx),
199            MatchInfo::Enum(s) => s.fmt(f, ctx),
200            MatchInfo::Value(s) => s.fmt(f, ctx),
201        }
202    }
203}
204
205impl<'db> DebugWithDb<'db> for StatementConst<'db> {
206    type Db = LoweredFormatter<'db>;
207
208    fn fmt(&self, f: &mut std::fmt::Formatter<'_>, ctx: &Self::Db) -> std::fmt::Result {
209        self.value.fmt(f, ctx.db)?;
210        if self.boxed {
211            write!(f, ".into_box()")?
212        }
213        Ok(())
214    }
215}
216
217impl<'db> DebugWithDb<'db> for StatementCall<'db> {
218    type Db = LoweredFormatter<'db>;
219
220    fn fmt(&self, f: &mut std::fmt::Formatter<'_>, ctx: &Self::Db) -> std::fmt::Result {
221        write!(f, "{:?}(", self.function.long(ctx.db).debug(ctx.db))?;
222        for (i, var) in self.inputs.iter().enumerate() {
223            let is_last = i == self.inputs.len() - 1;
224            if is_last && self.with_coupon {
225                write!(f, "__coupon__: ")?;
226            }
227            var.fmt(f, ctx)?;
228            if !is_last {
229                write!(f, ", ")?;
230            }
231        }
232        write!(f, ")")
233    }
234}
235
236impl<'db> DebugWithDb<'db> for MatchExternInfo<'db> {
237    type Db = LoweredFormatter<'db>;
238
239    fn fmt(&self, f: &mut std::fmt::Formatter<'_>, ctx: &Self::Db) -> std::fmt::Result {
240        write!(f, "match {:?}(", self.function.long(ctx.db).debug(ctx.db))?;
241        let mut inputs = self.inputs.iter().peekable();
242        while let Some(var) = inputs.next() {
243            var.fmt(f, ctx)?;
244            if inputs.peek().is_some() {
245                write!(f, ", ")?;
246            }
247        }
248        writeln!(f, ") {{")?;
249        for arm in &self.arms {
250            arm.fmt(f, ctx)?;
251            writeln!(f)?;
252        }
253        write!(f, "  }}")
254    }
255}
256
257impl<'db> DebugWithDb<'db> for MatchArm<'db> {
258    type Db = LoweredFormatter<'db>;
259
260    fn fmt(&self, f: &mut std::fmt::Formatter<'_>, ctx: &Self::Db) -> std::fmt::Result {
261        write!(f, "    {:?}", self.arm_selector.debug(ctx.db))?;
262
263        if !self.var_ids.is_empty() {
264            write!(f, "(")?;
265            let mut var_ids = self.var_ids.iter().peekable();
266            while let Some(var_id) = var_ids.next() {
267                write!(f, "v{:?}", var_id.index())?;
268                if var_ids.peek().is_some() {
269                    write!(f, ", ")?;
270                }
271            }
272            write!(f, ")")?;
273        }
274
275        write!(f, " => {:?},", self.block_id)
276    }
277}
278
279impl<'db> DebugWithDb<'db> for MatchEnumInfo<'db> {
280    type Db = LoweredFormatter<'db>;
281
282    fn fmt(&self, f: &mut std::fmt::Formatter<'_>, ctx: &Self::Db) -> std::fmt::Result {
283        write!(f, "match_enum(")?;
284        self.input.fmt(f, ctx)?;
285        writeln!(f, ") {{")?;
286        for arm in &self.arms {
287            arm.fmt(f, ctx)?;
288            writeln!(f)?;
289        }
290        write!(f, "  }}")
291    }
292}
293
294impl<'db> DebugWithDb<'db> for MatchEnumValue<'db> {
295    type Db = LoweredFormatter<'db>;
296
297    fn fmt(&self, f: &mut std::fmt::Formatter<'_>, ctx: &Self::Db) -> std::fmt::Result {
298        write!(f, "match_enum.(")?;
299        self.input.fmt(f, ctx)?;
300        writeln!(f, ") {{")?;
301        for arm in &self.arms {
302            arm.fmt(f, ctx)?;
303            writeln!(f)?;
304        }
305        write!(f, "  }}")
306    }
307}
308
309impl<'db> DebugWithDb<'db> for StatementEnumConstruct<'db> {
310    type Db = LoweredFormatter<'db>;
311
312    fn fmt(&self, f: &mut std::fmt::Formatter<'_>, ctx: &Self::Db) -> std::fmt::Result {
313        let enum_name = self.variant.concrete_enum_id.enum_id(ctx.db).name(ctx.db).long(ctx.db);
314        let variant_name = self.variant.id.name(ctx.db).long(ctx.db);
315        write!(f, "{enum_name}::{variant_name}(",)?;
316        self.input.fmt(f, ctx)?;
317        write!(f, ")")
318    }
319}
320
321impl<'db> DebugWithDb<'db> for StatementStructConstruct<'db> {
322    type Db = LoweredFormatter<'db>;
323
324    fn fmt(&self, f: &mut std::fmt::Formatter<'_>, ctx: &Self::Db) -> std::fmt::Result {
325        write!(f, "struct_construct(")?;
326        let mut inputs = self.inputs.iter().peekable();
327        while let Some(var) = inputs.next() {
328            var.fmt(f, ctx)?;
329            if inputs.peek().is_some() {
330                write!(f, ", ")?;
331            }
332        }
333        write!(f, ")")
334    }
335}
336
337impl<'db> DebugWithDb<'db> for StatementStructDestructure<'db> {
338    type Db = LoweredFormatter<'db>;
339
340    fn fmt(&self, f: &mut std::fmt::Formatter<'_>, ctx: &Self::Db) -> std::fmt::Result {
341        write!(f, "struct_destructure(")?;
342        self.input.fmt(f, ctx)?;
343        write!(f, ")")
344    }
345}
346
347impl<'db> DebugWithDb<'db> for StatementSnapshot<'db> {
348    type Db = LoweredFormatter<'db>;
349
350    fn fmt(&self, f: &mut std::fmt::Formatter<'_>, ctx: &Self::Db) -> std::fmt::Result {
351        write!(f, "snapshot(")?;
352        self.input.fmt(f, ctx)?;
353        write!(f, ")")
354    }
355}
356
357impl<'db> DebugWithDb<'db> for StatementDesnap<'db> {
358    type Db = LoweredFormatter<'db>;
359
360    fn fmt(&self, f: &mut std::fmt::Formatter<'_>, ctx: &Self::Db) -> std::fmt::Result {
361        write!(f, "desnap(")?;
362        self.input.fmt(f, ctx)?;
363        write!(f, ")")
364    }
365}
366
367impl<'db> DebugWithDb<'db> for StatementIntoBox<'db> {
368    type Db = LoweredFormatter<'db>;
369
370    fn fmt(&self, f: &mut std::fmt::Formatter<'_>, ctx: &Self::Db) -> std::fmt::Result {
371        write!(f, "into_box(")?;
372        self.input.fmt(f, ctx)?;
373        write!(f, ")")
374    }
375}
376
377impl<'db> DebugWithDb<'db> for StatementUnbox<'db> {
378    type Db = LoweredFormatter<'db>;
379
380    fn fmt(&self, f: &mut std::fmt::Formatter<'_>, ctx: &Self::Db) -> std::fmt::Result {
381        write!(f, "unbox(")?;
382        self.input.fmt(f, ctx)?;
383        write!(f, ")")
384    }
385}