1use 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#[derive(Clone, Debug, Eq, Hash, PartialEq, HeapSize, salsa::Update)]
39pub struct Location<'db> {
40 pub stable_location: StableLocation<'db>,
42 pub notes: Vec<DiagnosticNote<'db>>,
46 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 pub fn with_note(mut self, note: DiagnosticNote<'db>) -> Self {
56 self.notes.push(note);
57 self
58 }
59
60 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 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 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 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#[derive(Copy, Clone, Debug, Eq, PartialEq)]
137pub struct VarUsage<'db> {
138 pub var_id: VariableId,
139 pub location: LocationId<'db>,
140}
141
142#[derive(Clone, Debug, PartialEq, Eq)]
144pub struct Lowered<'db> {
145 pub diagnostics: Diagnostics<'db, LoweringDiagnostic<'db>>,
147 pub signature: Signature<'db>,
149 pub variables: VariableArena<'db>,
151 pub blocks: Blocks<'db>,
153 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#[derive(Clone, Debug, Default, PartialEq, Eq)]
183pub struct VarRemapping<'db> {
184 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#[derive(Clone, Debug, PartialEq, Eq)]
202pub struct Block<'db> {
203 pub statements: Vec<Statement<'db>>,
205 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#[derive(Clone, Debug, PartialEq, Eq)]
221pub enum BlockEnd<'db> {
222 NotSet,
225 Return(Vec<VarUsage<'db>>, LocationId<'db>),
227 Panic(VarUsage<'db>),
229 Goto(BlockId, VarRemapping<'db>),
231 Match {
232 info: MatchInfo<'db>,
233 },
234}
235
236impl<'db> BlockEnd<'db> {
237 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#[derive(Clone, Debug, PartialEq, Eq)]
250pub struct Variable<'db> {
251 pub ty: semantic::TypeId<'db>,
253 pub location: LocationId<'db>,
255 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 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#[derive(Clone, Debug, PartialEq, Eq)]
302pub enum Statement<'db> {
303 Const(StatementConst<'db>),
305
306 Call(StatementCall<'db>),
308
309 StructConstruct(StatementStructConstruct<'db>),
311 StructDestructure(StatementStructDestructure<'db>),
312
313 EnumConstruct(StatementEnumConstruct<'db>),
315
316 Snapshot(StatementSnapshot<'db>),
317 Desnap(StatementDesnap<'db>),
318
319 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 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#[derive(Clone, Debug, PartialEq, Eq)]
410pub struct StatementConst<'db> {
411 pub value: ConstValueId<'db>,
413 pub output: VariableId,
415 pub boxed: bool,
417}
418impl<'db> StatementConst<'db> {
419 pub fn new(value: ConstValueId<'db>, output: VariableId, boxed: bool) -> Self {
421 Self { value, output, boxed }
422 }
423 pub fn new_flat(value: ConstValueId<'db>, output: VariableId) -> Self {
425 Self::new(value, output, false)
426 }
427 pub fn new_boxed(value: ConstValueId<'db>, output: VariableId) -> Self {
429 Self::new(value, output, true)
430 }
431}
432
433#[derive(Clone, Debug, PartialEq, Eq)]
435pub struct StatementCall<'db> {
436 pub function: FunctionId<'db>,
438 pub inputs: Vec<VarUsage<'db>>,
440 pub with_coupon: bool,
443 pub outputs: Vec<VariableId>,
445 pub is_specialization_base_call: bool,
447 pub location: LocationId<'db>,
449}
450
451#[derive(Clone, Debug, PartialEq, Eq)]
454pub struct StatementEnumConstruct<'db> {
455 pub variant: ConcreteVariant<'db>,
456 pub input: VarUsage<'db>,
458 pub output: VariableId,
460}
461
462#[derive(Clone, Debug, PartialEq, Eq)]
464pub struct StatementStructConstruct<'db> {
465 pub inputs: Vec<VarUsage<'db>>,
466 pub output: VariableId,
468}
469
470#[derive(Clone, Debug, PartialEq, Eq)]
473pub struct StatementStructDestructure<'db> {
474 pub input: VarUsage<'db>,
476 pub outputs: Vec<VariableId>,
478}
479
480#[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#[derive(Clone, Debug, PartialEq, Eq)]
504pub struct StatementDesnap<'db> {
505 pub input: VarUsage<'db>,
506 pub output: VariableId,
508}
509
510#[derive(Clone, Debug, PartialEq, Eq)]
512pub struct StatementIntoBox<'db> {
513 pub input: VarUsage<'db>,
515 pub output: VariableId,
517}
518
519#[derive(Clone, Debug, PartialEq, Eq)]
521pub struct StatementUnbox<'db> {
522 pub input: VarUsage<'db>,
524 pub output: VariableId,
526}
527
528#[derive(Clone, Debug, PartialEq, Eq)]
530pub struct MatchArm<'db> {
531 pub arm_selector: MatchArmSelector<'db>,
533
534 pub block_id: BlockId,
536
537 pub var_ids: Vec<VariableId>,
539}
540
541#[derive(Clone, Debug, PartialEq, Eq)]
544pub struct MatchExternInfo<'db> {
545 pub function: FunctionId<'db>,
548 pub inputs: Vec<VarUsage<'db>>,
550 pub arms: Vec<MatchArm<'db>>,
553 pub location: LocationId<'db>,
555}
556
557#[derive(Clone, Debug, PartialEq, Eq)]
559pub struct MatchEnumInfo<'db> {
560 pub concrete_enum_id: ConcreteEnumId<'db>,
561 pub input: VarUsage<'db>,
563 pub arms: Vec<MatchArm<'db>>,
566 pub location: LocationId<'db>,
568}
569#[derive(Clone, Debug, PartialEq, Eq)]
572pub struct MatchEnumValue<'db> {
573 pub num_of_arms: usize,
574
575 pub input: VarUsage<'db>,
577 pub arms: Vec<MatchArm<'db>>,
579 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#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
637pub enum DependencyType {
638 Call,
640 Cost,
642}
643
644#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
646pub enum LoweringStage {
647 Monomorphized,
649 PreOptimizations,
656 PostBaseline,
659 Final,
662}