1use 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::corelib::{concrete_destruct_trait, concrete_panic_destruct_trait};
14use cairo_lang_semantic::expr::inference::InferenceError;
15use cairo_lang_semantic::expr::inference::solver::Ambiguity;
16use cairo_lang_semantic::items::constant::ConstValueId;
17use cairo_lang_semantic::items::imp::ImplLookupContextId;
18use cairo_lang_semantic::types::{TypeInfo, TypesSemantic};
19use cairo_lang_semantic::{ConcreteEnumId, ConcreteVariant};
20use cairo_lang_utils::Intern;
21use cairo_lang_utils::ordered_hash_map::OrderedHashMap;
22use id_arena::{Arena, DefaultArenaBehavior, Id};
23
24pub mod blocks;
25pub use blocks::BlockId;
26use salsa::Database;
27use semantic::MatchArmSelector;
28
29use self::blocks::Blocks;
30use crate::diagnostic::LoweringDiagnostic;
31use crate::fmt::LoweredFormatter;
32use crate::ids::{FunctionId, LocationId, Signature};
33
34#[derive(Clone, Debug, Eq, Hash, PartialEq, salsa::Update)]
37pub struct Location<'db> {
38 pub stable_location: StableLocation<'db>,
40 pub notes: Vec<DiagnosticNote<'db>>,
44 pub inline_locations: Vec<StableLocation<'db>>,
46}
47impl<'db> Location<'db> {
48 pub fn new(stable_location: StableLocation<'db>) -> Self {
49 Self { stable_location, notes: vec![], inline_locations: vec![] }
50 }
51
52 pub fn with_note(mut self, note: DiagnosticNote<'db>) -> Self {
54 self.notes.push(note);
55 self
56 }
57
58 pub fn maybe_with_note(mut self, note: Option<DiagnosticNote<'db>>) -> Self {
60 let Some(note) = note else {
61 return self;
62 };
63 self.notes.push(note);
64 self
65 }
66
67 pub fn add_note_with_location(
69 self,
70 db: &'db dyn Database,
71 text: &str,
72 location: LocationId<'db>,
73 ) -> Self {
74 self.with_note(DiagnosticNote::with_location(
75 text.into(),
76 location.long(db).stable_location.span_in_file(db),
77 ))
78 }
79}
80
81impl<'db> DebugWithDb<'db> for Location<'db> {
82 type Db = dyn Database;
83 fn fmt(&self, f: &mut std::fmt::Formatter<'_>, db: &'db dyn Database) -> std::fmt::Result {
84 self.stable_location.span_in_file(db).fmt(f, db)?;
85
86 for note in &self.notes {
87 f.write_str("\nnote: ")?;
88 note.fmt(f, db)?;
89 }
90 Ok(())
91 }
92}
93
94impl<'db> LocationId<'db> {
95 pub fn inlined(self, db: &'db dyn Database, inlining_location: StableLocation<'db>) -> Self {
97 let mut location = self.long(db).clone();
98 location.inline_locations.push(inlining_location);
99 location.intern(db)
100 }
101 pub fn all_locations(self, db: &'db dyn Database) -> Vec<StableLocation<'db>> {
103 let location = self.long(db);
104 let mut all_locations = vec![location.stable_location];
105 all_locations.extend(location.inline_locations.iter().cloned());
106 all_locations
107 }
108}
109
110#[derive(Clone, Debug, PartialEq, Eq)]
111pub struct VariableMarker;
112pub type VariableId = Id<VariableMarker>;
113pub type VariableArena<'db> = Arena<Variable<'db>, DefaultArenaBehavior<VariableMarker>>;
114
115#[derive(Copy, Clone, Debug, Eq, PartialEq)]
135pub struct VarUsage<'db> {
136 pub var_id: VariableId,
137 pub location: LocationId<'db>,
138}
139
140#[derive(Clone, Debug, PartialEq, Eq)]
142pub struct Lowered<'db> {
143 pub diagnostics: Diagnostics<'db, LoweringDiagnostic<'db>>,
145 pub signature: Signature<'db>,
147 pub variables: VariableArena<'db>,
149 pub blocks: Blocks<'db>,
151 pub parameters: Vec<VariableId>,
153}
154
155unsafe impl<'db> salsa::Update for Lowered<'db> {
156 unsafe fn maybe_update(old_pointer: *mut Self, new_value: Self) -> bool {
157 let old_value = unsafe { &mut *old_pointer };
158 let res = unsafe {
159 Diagnostics::maybe_update(&mut old_value.diagnostics, new_value.diagnostics)
160 | Signature::maybe_update(&mut old_value.signature, new_value.signature)
161 } | (old_value.blocks != new_value.blocks);
162 if res {
163 old_value.variables = new_value.variables;
164 old_value.parameters = new_value.parameters;
165 old_value.blocks = new_value.blocks;
166 }
167 res
168 }
169}
170
171#[derive(Clone, Debug, Default, PartialEq, Eq)]
173pub struct VarRemapping<'db> {
174 pub remapping: OrderedHashMap<VariableId, VarUsage<'db>>,
176}
177impl<'db> Deref for VarRemapping<'db> {
178 type Target = OrderedHashMap<VariableId, VarUsage<'db>>;
179
180 fn deref(&self) -> &Self::Target {
181 &self.remapping
182 }
183}
184impl<'db> DerefMut for VarRemapping<'db> {
185 fn deref_mut(&mut self) -> &mut Self::Target {
186 &mut self.remapping
187 }
188}
189
190#[derive(Clone, Debug, PartialEq, Eq)]
192pub struct Block<'db> {
193 pub statements: Vec<Statement<'db>>,
195 pub end: BlockEnd<'db>,
197}
198impl<'db> Default for Block<'db> {
199 fn default() -> Self {
200 Self { statements: Default::default(), end: BlockEnd::NotSet }
201 }
202}
203impl<'db> Block<'db> {
204 pub fn is_set(&self) -> bool {
205 !matches!(self.end, BlockEnd::NotSet)
206 }
207}
208
209#[derive(Clone, Debug, PartialEq, Eq)]
211pub enum BlockEnd<'db> {
212 NotSet,
215 Return(Vec<VarUsage<'db>>, LocationId<'db>),
217 Panic(VarUsage<'db>),
219 Goto(BlockId, VarRemapping<'db>),
221 Match {
222 info: MatchInfo<'db>,
223 },
224}
225
226#[derive(Clone, Debug, PartialEq, Eq)]
228pub struct Variable<'db> {
229 pub ty: semantic::TypeId<'db>,
231 pub location: LocationId<'db>,
233 pub info: TypeInfo<'db>,
235}
236
237impl<'db> DebugWithDb<'db> for Variable<'db> {
238 type Db = LoweredFormatter<'db>;
239
240 fn fmt(&self, f: &mut std::fmt::Formatter<'_>, _ctx: &Self::Db) -> std::fmt::Result {
241 write!(f, "Variable({:?})", self.ty)
242 }
243}
244
245impl<'db> Variable<'db> {
246 pub fn new(
247 db: &'db dyn Database,
248 ctx: ImplLookupContextId<'db>,
249 ty: semantic::TypeId<'db>,
250 location: LocationId<'db>,
251 ) -> Self {
252 Self { ty, location, info: db.type_info(ctx, ty) }
253 }
254
255 pub fn with_default_context(
257 db: &'db dyn Database,
258 ty: semantic::TypeId<'db>,
259 location: LocationId<'db>,
260 ) -> Self {
261 Self {
262 ty,
263 location,
264 info: TypeInfo {
265 copyable: db.copyable(ty),
266 droppable: db.droppable(ty),
267 destruct_impl: Err(InferenceError::Ambiguity(Ambiguity::WillNotInfer(
268 concrete_destruct_trait(db, ty),
269 ))),
270 panic_destruct_impl: Err(InferenceError::Ambiguity(Ambiguity::WillNotInfer(
271 concrete_panic_destruct_trait(db, ty),
272 ))),
273 },
274 }
275 }
276}
277
278#[derive(Clone, Debug, PartialEq, Eq)]
280pub enum Statement<'db> {
281 Const(StatementConst<'db>),
283
284 Call(StatementCall<'db>),
286
287 StructConstruct(StatementStructConstruct<'db>),
289 StructDestructure(StatementStructDestructure<'db>),
290
291 EnumConstruct(StatementEnumConstruct<'db>),
293
294 Snapshot(StatementSnapshot<'db>),
295 Desnap(StatementDesnap<'db>),
296}
297impl<'db> Statement<'db> {
298 pub fn inputs(&self) -> &[VarUsage<'db>] {
299 match &self {
300 Statement::Const(_stmt) => &[],
301 Statement::Call(stmt) => stmt.inputs.as_slice(),
302 Statement::StructConstruct(stmt) => stmt.inputs.as_slice(),
303 Statement::StructDestructure(stmt) => std::slice::from_ref(&stmt.input),
304 Statement::EnumConstruct(stmt) => std::slice::from_ref(&stmt.input),
305 Statement::Snapshot(stmt) => std::slice::from_ref(&stmt.input),
306 Statement::Desnap(stmt) => std::slice::from_ref(&stmt.input),
307 }
308 }
309
310 pub fn inputs_mut(&mut self) -> &mut [VarUsage<'db>] {
311 match self {
312 Statement::Const(_stmt) => &mut [],
313 Statement::Call(stmt) => stmt.inputs.as_mut_slice(),
314 Statement::StructConstruct(stmt) => stmt.inputs.as_mut_slice(),
315 Statement::StructDestructure(stmt) => std::slice::from_mut(&mut stmt.input),
316 Statement::EnumConstruct(stmt) => std::slice::from_mut(&mut stmt.input),
317 Statement::Snapshot(stmt) => std::slice::from_mut(&mut stmt.input),
318 Statement::Desnap(stmt) => std::slice::from_mut(&mut stmt.input),
319 }
320 }
321
322 pub fn outputs(&self) -> &[VariableId] {
323 match self {
324 Statement::Const(stmt) => std::slice::from_ref(&stmt.output),
325 Statement::Call(stmt) => stmt.outputs.as_slice(),
326 Statement::StructConstruct(stmt) => std::slice::from_ref(&stmt.output),
327 Statement::StructDestructure(stmt) => stmt.outputs.as_slice(),
328 Statement::EnumConstruct(stmt) => std::slice::from_ref(&stmt.output),
329 Statement::Snapshot(stmt) => stmt.outputs.as_slice(),
330 Statement::Desnap(stmt) => std::slice::from_ref(&stmt.output),
331 }
332 }
333
334 pub fn outputs_mut(&mut self) -> &mut [VariableId] {
335 match self {
336 Statement::Const(stmt) => std::slice::from_mut(&mut stmt.output),
337 Statement::Call(stmt) => stmt.outputs.as_mut_slice(),
338 Statement::StructConstruct(stmt) => std::slice::from_mut(&mut stmt.output),
339 Statement::StructDestructure(stmt) => stmt.outputs.as_mut_slice(),
340 Statement::EnumConstruct(stmt) => std::slice::from_mut(&mut stmt.output),
341 Statement::Snapshot(stmt) => stmt.outputs.as_mut_slice(),
342 Statement::Desnap(stmt) => std::slice::from_mut(&mut stmt.output),
343 }
344 }
345 pub fn location(&self) -> Option<LocationId<'db>> {
346 match &self {
348 Statement::Const(_) => None,
349 Statement::Call(stmt) => Some(stmt.location),
350 Statement::StructConstruct(_) => None,
351 Statement::StructDestructure(stmt) => Some(stmt.input.location),
352 Statement::EnumConstruct(stmt) => Some(stmt.input.location),
353 Statement::Snapshot(stmt) => Some(stmt.input.location),
354 Statement::Desnap(stmt) => Some(stmt.input.location),
355 }
356 }
357 pub fn location_mut(&mut self) -> Option<&mut LocationId<'db>> {
358 match self {
359 Statement::Const(_) => None,
360 Statement::Call(stmt) => Some(&mut stmt.location),
361 Statement::StructConstruct(_) => None,
362 Statement::StructDestructure(stmt) => Some(&mut stmt.input.location),
363 Statement::EnumConstruct(stmt) => Some(&mut stmt.input.location),
364 Statement::Snapshot(stmt) => Some(&mut stmt.input.location),
365 Statement::Desnap(stmt) => Some(&mut stmt.input.location),
366 }
367 }
368}
369
370#[derive(Clone, Debug, PartialEq, Eq)]
372pub struct StatementConst<'db> {
373 pub value: ConstValueId<'db>,
375 pub output: VariableId,
377 pub boxed: bool,
379}
380impl<'db> StatementConst<'db> {
381 pub fn new(value: ConstValueId<'db>, output: VariableId, boxed: bool) -> Self {
383 Self { value, output, boxed }
384 }
385 pub fn new_flat(value: ConstValueId<'db>, output: VariableId) -> Self {
387 Self::new(value, output, false)
388 }
389 pub fn new_boxed(value: ConstValueId<'db>, output: VariableId) -> Self {
391 Self::new(value, output, true)
392 }
393}
394
395#[derive(Clone, Debug, PartialEq, Eq)]
397pub struct StatementCall<'db> {
398 pub function: FunctionId<'db>,
400 pub inputs: Vec<VarUsage<'db>>,
402 pub with_coupon: bool,
405 pub outputs: Vec<VariableId>,
407 pub is_specialization_base_call: bool,
409 pub location: LocationId<'db>,
411}
412
413#[derive(Clone, Debug, PartialEq, Eq)]
416pub struct StatementEnumConstruct<'db> {
417 pub variant: ConcreteVariant<'db>,
418 pub input: VarUsage<'db>,
420 pub output: VariableId,
422}
423
424#[derive(Clone, Debug, PartialEq, Eq)]
426pub struct StatementStructConstruct<'db> {
427 pub inputs: Vec<VarUsage<'db>>,
428 pub output: VariableId,
430}
431
432#[derive(Clone, Debug, PartialEq, Eq)]
435pub struct StatementStructDestructure<'db> {
436 pub input: VarUsage<'db>,
438 pub outputs: Vec<VariableId>,
440}
441
442#[derive(Clone, Debug, PartialEq, Eq)]
444pub struct StatementSnapshot<'db> {
445 pub input: VarUsage<'db>,
446 pub outputs: [VariableId; 2],
447}
448impl<'db> StatementSnapshot<'db> {
449 pub fn new(
450 input: VarUsage<'db>,
451 output_original: VariableId,
452 output_snapshot: VariableId,
453 ) -> Self {
454 Self { input, outputs: [output_original, output_snapshot] }
455 }
456 pub fn original(&self) -> VariableId {
457 self.outputs[0]
458 }
459 pub fn snapshot(&self) -> VariableId {
460 self.outputs[1]
461 }
462}
463
464#[derive(Clone, Debug, PartialEq, Eq)]
466pub struct StatementDesnap<'db> {
467 pub input: VarUsage<'db>,
468 pub output: VariableId,
470}
471
472#[derive(Clone, Debug, PartialEq, Eq)]
474pub struct MatchArm<'db> {
475 pub arm_selector: MatchArmSelector<'db>,
477
478 pub block_id: BlockId,
480
481 pub var_ids: Vec<VariableId>,
483}
484
485#[derive(Clone, Debug, PartialEq, Eq)]
488pub struct MatchExternInfo<'db> {
489 pub function: FunctionId<'db>,
492 pub inputs: Vec<VarUsage<'db>>,
494 pub arms: Vec<MatchArm<'db>>,
497 pub location: LocationId<'db>,
499}
500
501#[derive(Clone, Debug, PartialEq, Eq)]
503pub struct MatchEnumInfo<'db> {
504 pub concrete_enum_id: ConcreteEnumId<'db>,
505 pub input: VarUsage<'db>,
507 pub arms: Vec<MatchArm<'db>>,
510 pub location: LocationId<'db>,
512}
513#[derive(Clone, Debug, PartialEq, Eq)]
516pub struct MatchEnumValue<'db> {
517 pub num_of_arms: usize,
518
519 pub input: VarUsage<'db>,
521 pub arms: Vec<MatchArm<'db>>,
523 pub location: LocationId<'db>,
525}
526
527#[derive(Clone, Debug, PartialEq, Eq)]
528pub enum MatchInfo<'db> {
529 Enum(MatchEnumInfo<'db>),
530 Extern(MatchExternInfo<'db>),
531 Value(MatchEnumValue<'db>),
532}
533impl<'db> MatchInfo<'db> {
534 pub fn inputs(&self) -> &[VarUsage<'db>] {
535 match self {
536 MatchInfo::Enum(s) => std::slice::from_ref(&s.input),
537 MatchInfo::Extern(s) => s.inputs.as_slice(),
538 MatchInfo::Value(s) => std::slice::from_ref(&s.input),
539 }
540 }
541
542 pub fn inputs_mut(&mut self) -> &mut [VarUsage<'db>] {
543 match self {
544 MatchInfo::Enum(s) => std::slice::from_mut(&mut s.input),
545 MatchInfo::Extern(s) => s.inputs.as_mut_slice(),
546 MatchInfo::Value(s) => std::slice::from_mut(&mut s.input),
547 }
548 }
549 pub fn arms(&self) -> &[MatchArm<'db>] {
550 match self {
551 MatchInfo::Enum(s) => &s.arms,
552 MatchInfo::Extern(s) => &s.arms,
553 MatchInfo::Value(s) => &s.arms,
554 }
555 }
556 pub fn arms_mut(&mut self) -> &mut [MatchArm<'db>] {
557 match self {
558 MatchInfo::Enum(s) => &mut s.arms,
559 MatchInfo::Extern(s) => &mut s.arms,
560 MatchInfo::Value(s) => &mut s.arms,
561 }
562 }
563 pub fn location(&self) -> &LocationId<'db> {
564 match self {
565 MatchInfo::Enum(s) => &s.location,
566 MatchInfo::Extern(s) => &s.location,
567 MatchInfo::Value(s) => &s.location,
568 }
569 }
570 pub fn location_mut(&mut self) -> &mut LocationId<'db> {
571 match self {
572 MatchInfo::Enum(s) => &mut s.location,
573 MatchInfo::Extern(s) => &mut s.location,
574 MatchInfo::Value(s) => &mut s.location,
575 }
576 }
577}
578
579#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
581pub enum DependencyType {
582 Call,
584 Cost,
586}
587
588#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
590pub enum LoweringStage {
591 Monomorphized,
593 PreOptimizations,
600 PostBaseline,
603 Final,
606}