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::SalsaValue)]
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, salsa::SalsaValue)]
144pub struct Lowered<'db> {
145 pub diagnostics: Diagnostics<'db, LoweringDiagnostic<'db>>,
147 pub signature: Signature<'db>,
149 #[salsa_value(unsafe(prove_safe_to_retain_manually))]
153 pub variables: VariableArena<'db>,
154 pub blocks: Blocks<'db>,
156 pub parameters: Vec<VariableId>,
158}
159
160impl<'db> Index<StatementLocation> for Lowered<'db> {
161 type Output = Statement<'db>;
162
163 fn index(&self, location: StatementLocation) -> &Self::Output {
164 &self.blocks[location]
165 }
166}
167
168#[derive(Clone, Debug, Default, PartialEq, Eq)]
170pub struct VarRemapping<'db> {
171 pub remapping: OrderedHashMap<VariableId, VarUsage<'db>>,
173}
174impl<'db> Deref for VarRemapping<'db> {
175 type Target = OrderedHashMap<VariableId, VarUsage<'db>>;
176
177 fn deref(&self) -> &Self::Target {
178 &self.remapping
179 }
180}
181impl<'db> DerefMut for VarRemapping<'db> {
182 fn deref_mut(&mut self) -> &mut Self::Target {
183 &mut self.remapping
184 }
185}
186
187#[derive(Clone, Debug, PartialEq, Eq)]
189pub struct Block<'db> {
190 pub statements: Vec<Statement<'db>>,
192 pub end: BlockEnd<'db>,
194}
195impl<'db> Default for Block<'db> {
196 fn default() -> Self {
197 Self { statements: Default::default(), end: BlockEnd::NotSet }
198 }
199}
200impl<'db> Block<'db> {
201 pub fn is_set(&self) -> bool {
202 !matches!(self.end, BlockEnd::NotSet)
203 }
204}
205
206#[derive(Clone, Debug, PartialEq, Eq)]
208pub enum BlockEnd<'db> {
209 NotSet,
212 Return(Vec<VarUsage<'db>>, LocationId<'db>),
214 Panic(VarUsage<'db>),
216 Goto(BlockId, VarRemapping<'db>),
218 Match {
219 info: MatchInfo<'db>,
220 },
221}
222
223impl<'db> BlockEnd<'db> {
224 pub fn location(&self) -> Option<LocationId<'db>> {
226 match self {
227 BlockEnd::Return(_, location) => Some(*location),
228 BlockEnd::Panic(var) => Some(var.location),
229 BlockEnd::Match { info } => Some(*info.location()),
230 BlockEnd::Goto(_, _) | BlockEnd::NotSet => None,
231 }
232 }
233}
234
235#[derive(Clone, Debug, PartialEq, Eq)]
237pub struct Variable<'db> {
238 pub ty: semantic::TypeId<'db>,
240 pub location: LocationId<'db>,
242 pub info: TypeInfo<'db>,
244}
245
246impl<'db> DebugWithDb<'db> for Variable<'db> {
247 type Db = LoweredFormatter<'db>;
248
249 fn fmt(&self, f: &mut std::fmt::Formatter<'_>, _ctx: &Self::Db) -> std::fmt::Result {
250 write!(f, "Variable({:?})", self.ty)
251 }
252}
253
254impl<'db> Variable<'db> {
255 pub fn new(
256 db: &'db dyn Database,
257 ctx: ImplLookupContextId<'db>,
258 ty: semantic::TypeId<'db>,
259 location: LocationId<'db>,
260 ) -> Self {
261 Self { ty, location, info: db.type_info(ctx, ty) }
262 }
263
264 pub fn with_default_context(
266 db: &'db dyn Database,
267 ty: semantic::TypeId<'db>,
268 location: LocationId<'db>,
269 ) -> Self {
270 Self {
271 ty,
272 location,
273 info: TypeInfo {
274 copyable: db.copyable(ty),
275 droppable: db.droppable(ty),
276 destruct_impl: Err(InferenceError::Ambiguity(Ambiguity::WillNotInfer(
277 concrete_destruct_trait(db, ty),
278 ))),
279 panic_destruct_impl: Err(InferenceError::Ambiguity(Ambiguity::WillNotInfer(
280 concrete_panic_destruct_trait(db, ty),
281 ))),
282 },
283 }
284 }
285}
286
287#[derive(Clone, Debug, PartialEq, Eq)]
289pub enum Statement<'db> {
290 Const(StatementConst<'db>),
292
293 Call(StatementCall<'db>),
295
296 StructConstruct(StatementStructConstruct<'db>),
298 StructDestructure(StatementStructDestructure<'db>),
299
300 EnumConstruct(StatementEnumConstruct<'db>),
302
303 Snapshot(StatementSnapshot<'db>),
304 Desnap(StatementDesnap<'db>),
305
306 IntoBox(StatementIntoBox<'db>),
308 Unbox(StatementUnbox<'db>),
309}
310impl<'db> Statement<'db> {
311 pub fn inputs(&self) -> &[VarUsage<'db>] {
312 match &self {
313 Statement::Const(_stmt) => &[],
314 Statement::Call(stmt) => stmt.inputs.as_slice(),
315 Statement::StructConstruct(stmt) => stmt.inputs.as_slice(),
316 Statement::StructDestructure(stmt) => std::slice::from_ref(&stmt.input),
317 Statement::EnumConstruct(stmt) => std::slice::from_ref(&stmt.input),
318 Statement::Snapshot(stmt) => std::slice::from_ref(&stmt.input),
319 Statement::Desnap(stmt) => std::slice::from_ref(&stmt.input),
320 Statement::IntoBox(stmt) => std::slice::from_ref(&stmt.input),
321 Statement::Unbox(stmt) => std::slice::from_ref(&stmt.input),
322 }
323 }
324
325 pub fn inputs_mut(&mut self) -> &mut [VarUsage<'db>] {
326 match self {
327 Statement::Const(_stmt) => &mut [],
328 Statement::Call(stmt) => stmt.inputs.as_mut_slice(),
329 Statement::StructConstruct(stmt) => stmt.inputs.as_mut_slice(),
330 Statement::StructDestructure(stmt) => std::slice::from_mut(&mut stmt.input),
331 Statement::EnumConstruct(stmt) => std::slice::from_mut(&mut stmt.input),
332 Statement::Snapshot(stmt) => std::slice::from_mut(&mut stmt.input),
333 Statement::Desnap(stmt) => std::slice::from_mut(&mut stmt.input),
334 Statement::IntoBox(stmt) => std::slice::from_mut(&mut stmt.input),
335 Statement::Unbox(stmt) => std::slice::from_mut(&mut stmt.input),
336 }
337 }
338
339 pub fn outputs(&self) -> &[VariableId] {
340 match self {
341 Statement::Const(stmt) => std::slice::from_ref(&stmt.output),
342 Statement::Call(stmt) => stmt.outputs.as_slice(),
343 Statement::StructConstruct(stmt) => std::slice::from_ref(&stmt.output),
344 Statement::StructDestructure(stmt) => stmt.outputs.as_slice(),
345 Statement::EnumConstruct(stmt) => std::slice::from_ref(&stmt.output),
346 Statement::Snapshot(stmt) => stmt.outputs.as_slice(),
347 Statement::Desnap(stmt) => std::slice::from_ref(&stmt.output),
348 Statement::IntoBox(stmt) => std::slice::from_ref(&stmt.output),
349 Statement::Unbox(stmt) => std::slice::from_ref(&stmt.output),
350 }
351 }
352
353 pub fn outputs_mut(&mut self) -> &mut [VariableId] {
354 match self {
355 Statement::Const(stmt) => std::slice::from_mut(&mut stmt.output),
356 Statement::Call(stmt) => stmt.outputs.as_mut_slice(),
357 Statement::StructConstruct(stmt) => std::slice::from_mut(&mut stmt.output),
358 Statement::StructDestructure(stmt) => stmt.outputs.as_mut_slice(),
359 Statement::EnumConstruct(stmt) => std::slice::from_mut(&mut stmt.output),
360 Statement::Snapshot(stmt) => stmt.outputs.as_mut_slice(),
361 Statement::Desnap(stmt) => std::slice::from_mut(&mut stmt.output),
362 Statement::IntoBox(stmt) => std::slice::from_mut(&mut stmt.output),
363 Statement::Unbox(stmt) => std::slice::from_mut(&mut stmt.output),
364 }
365 }
366 pub fn location(&self) -> Option<LocationId<'db>> {
367 match &self {
369 Statement::Const(_) => None,
370 Statement::Call(stmt) => Some(stmt.location),
371 Statement::StructConstruct(_) => None,
372 Statement::StructDestructure(stmt) => Some(stmt.input.location),
373 Statement::EnumConstruct(stmt) => Some(stmt.input.location),
374 Statement::Snapshot(stmt) => Some(stmt.input.location),
375 Statement::Desnap(stmt) => Some(stmt.input.location),
376 Statement::IntoBox(stmt) => Some(stmt.input.location),
377 Statement::Unbox(stmt) => Some(stmt.input.location),
378 }
379 }
380 pub fn location_mut(&mut self) -> Option<&mut LocationId<'db>> {
381 match self {
382 Statement::Const(_) => None,
383 Statement::Call(stmt) => Some(&mut stmt.location),
384 Statement::StructConstruct(_) => None,
385 Statement::StructDestructure(stmt) => Some(&mut stmt.input.location),
386 Statement::EnumConstruct(stmt) => Some(&mut stmt.input.location),
387 Statement::Snapshot(stmt) => Some(&mut stmt.input.location),
388 Statement::Desnap(stmt) => Some(&mut stmt.input.location),
389 Statement::IntoBox(stmt) => Some(&mut stmt.input.location),
390 Statement::Unbox(stmt) => Some(&mut stmt.input.location),
391 }
392 }
393}
394
395#[derive(Clone, Debug, PartialEq, Eq)]
397pub struct StatementConst<'db> {
398 pub value: ConstValueId<'db>,
400 pub output: VariableId,
402 pub boxed: bool,
404}
405impl<'db> StatementConst<'db> {
406 pub fn new(value: ConstValueId<'db>, output: VariableId, boxed: bool) -> Self {
408 Self { value, output, boxed }
409 }
410 pub fn new_flat(value: ConstValueId<'db>, output: VariableId) -> Self {
412 Self::new(value, output, false)
413 }
414 pub fn new_boxed(value: ConstValueId<'db>, output: VariableId) -> Self {
416 Self::new(value, output, true)
417 }
418}
419
420#[derive(Clone, Debug, PartialEq, Eq)]
422pub struct StatementCall<'db> {
423 pub function: FunctionId<'db>,
425 pub inputs: Vec<VarUsage<'db>>,
427 pub with_coupon: bool,
430 pub outputs: Vec<VariableId>,
432 pub is_specialization_base_call: bool,
434 pub location: LocationId<'db>,
436}
437
438#[derive(Clone, Debug, PartialEq, Eq)]
441pub struct StatementEnumConstruct<'db> {
442 pub variant: ConcreteVariant<'db>,
443 pub input: VarUsage<'db>,
445 pub output: VariableId,
447}
448
449#[derive(Clone, Debug, PartialEq, Eq)]
451pub struct StatementStructConstruct<'db> {
452 pub inputs: Vec<VarUsage<'db>>,
453 pub output: VariableId,
455}
456
457#[derive(Clone, Debug, PartialEq, Eq)]
460pub struct StatementStructDestructure<'db> {
461 pub input: VarUsage<'db>,
463 pub outputs: Vec<VariableId>,
465}
466
467#[derive(Clone, Debug, PartialEq, Eq)]
469pub struct StatementSnapshot<'db> {
470 pub input: VarUsage<'db>,
471 pub outputs: [VariableId; 2],
472}
473impl<'db> StatementSnapshot<'db> {
474 pub fn new(
475 input: VarUsage<'db>,
476 output_original: VariableId,
477 output_snapshot: VariableId,
478 ) -> Self {
479 Self { input, outputs: [output_original, output_snapshot] }
480 }
481 pub fn original(&self) -> VariableId {
482 self.outputs[0]
483 }
484 pub fn snapshot(&self) -> VariableId {
485 self.outputs[1]
486 }
487}
488
489#[derive(Clone, Debug, PartialEq, Eq)]
491pub struct StatementDesnap<'db> {
492 pub input: VarUsage<'db>,
493 pub output: VariableId,
495}
496
497#[derive(Clone, Debug, PartialEq, Eq)]
499pub struct StatementIntoBox<'db> {
500 pub input: VarUsage<'db>,
502 pub output: VariableId,
504}
505
506#[derive(Clone, Debug, PartialEq, Eq)]
508pub struct StatementUnbox<'db> {
509 pub input: VarUsage<'db>,
511 pub output: VariableId,
513}
514
515#[derive(Clone, Debug, PartialEq, Eq)]
517pub struct MatchArm<'db> {
518 pub arm_selector: MatchArmSelector<'db>,
520
521 pub block_id: BlockId,
523
524 pub var_ids: Vec<VariableId>,
526}
527
528#[derive(Clone, Debug, PartialEq, Eq)]
531pub struct MatchExternInfo<'db> {
532 pub function: FunctionId<'db>,
535 pub inputs: Vec<VarUsage<'db>>,
537 pub arms: Vec<MatchArm<'db>>,
540 pub location: LocationId<'db>,
542}
543
544#[derive(Clone, Debug, PartialEq, Eq)]
546pub struct MatchEnumInfo<'db> {
547 pub concrete_enum_id: ConcreteEnumId<'db>,
548 pub input: VarUsage<'db>,
550 pub arms: Vec<MatchArm<'db>>,
553 pub location: LocationId<'db>,
555}
556#[derive(Clone, Debug, PartialEq, Eq)]
559pub struct MatchEnumValue<'db> {
560 pub num_of_arms: usize,
561
562 pub input: VarUsage<'db>,
564 pub arms: Vec<MatchArm<'db>>,
566 pub location: LocationId<'db>,
568}
569
570#[derive(Clone, Debug, PartialEq, Eq)]
571pub enum MatchInfo<'db> {
572 Enum(MatchEnumInfo<'db>),
573 Extern(MatchExternInfo<'db>),
574 Value(MatchEnumValue<'db>),
575}
576impl<'db> MatchInfo<'db> {
577 pub fn inputs(&self) -> &[VarUsage<'db>] {
578 match self {
579 MatchInfo::Enum(s) => std::slice::from_ref(&s.input),
580 MatchInfo::Extern(s) => s.inputs.as_slice(),
581 MatchInfo::Value(s) => std::slice::from_ref(&s.input),
582 }
583 }
584
585 pub fn inputs_mut(&mut self) -> &mut [VarUsage<'db>] {
586 match self {
587 MatchInfo::Enum(s) => std::slice::from_mut(&mut s.input),
588 MatchInfo::Extern(s) => s.inputs.as_mut_slice(),
589 MatchInfo::Value(s) => std::slice::from_mut(&mut s.input),
590 }
591 }
592 pub fn arms(&self) -> &[MatchArm<'db>] {
593 match self {
594 MatchInfo::Enum(s) => &s.arms,
595 MatchInfo::Extern(s) => &s.arms,
596 MatchInfo::Value(s) => &s.arms,
597 }
598 }
599 pub fn arms_mut(&mut self) -> &mut [MatchArm<'db>] {
600 match self {
601 MatchInfo::Enum(s) => &mut s.arms,
602 MatchInfo::Extern(s) => &mut s.arms,
603 MatchInfo::Value(s) => &mut s.arms,
604 }
605 }
606 pub fn location(&self) -> &LocationId<'db> {
607 match self {
608 MatchInfo::Enum(s) => &s.location,
609 MatchInfo::Extern(s) => &s.location,
610 MatchInfo::Value(s) => &s.location,
611 }
612 }
613 pub fn location_mut(&mut self) -> &mut LocationId<'db> {
614 match self {
615 MatchInfo::Enum(s) => &mut s.location,
616 MatchInfo::Extern(s) => &mut s.location,
617 MatchInfo::Value(s) => &mut s.location,
618 }
619 }
620}
621
622#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
624pub enum DependencyType {
625 Call,
627 Cost,
629}
630
631#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
633pub enum LoweringStage {
634 Monomorphized,
636 PreOptimizations,
643 PostBaseline,
646 Final,
649}