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_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::diagnostic::LoweringDiagnostic;
32use crate::fmt::LoweredFormatter;
33use crate::ids::{FunctionId, LocationId, Signature};
34
35#[derive(Clone, Debug, Eq, Hash, PartialEq, HeapSize, salsa::Update)]
38pub struct Location<'db> {
39 pub stable_location: StableLocation<'db>,
41 pub notes: Vec<DiagnosticNote<'db>>,
45 pub inline_locations: Vec<StableLocation<'db>>,
47}
48impl<'db> Location<'db> {
49 pub fn new(stable_location: StableLocation<'db>) -> Self {
50 Self { stable_location, notes: vec![], inline_locations: vec![] }
51 }
52
53 pub fn with_note(mut self, note: DiagnosticNote<'db>) -> Self {
55 self.notes.push(note);
56 self
57 }
58
59 pub fn maybe_with_note(mut self, note: Option<DiagnosticNote<'db>>) -> Self {
61 let Some(note) = note else {
62 return self;
63 };
64 self.notes.push(note);
65 self
66 }
67
68 pub fn add_note_with_location(
70 self,
71 db: &'db dyn Database,
72 text: &str,
73 location: LocationId<'db>,
74 ) -> Self {
75 self.with_note(DiagnosticNote::with_location(
76 text.into(),
77 location.long(db).stable_location.span_in_file(db),
78 ))
79 }
80}
81
82impl<'db> DebugWithDb<'db> for Location<'db> {
83 type Db = dyn Database;
84 fn fmt(&self, f: &mut std::fmt::Formatter<'_>, db: &'db dyn Database) -> std::fmt::Result {
85 self.stable_location.span_in_file(db).fmt(f, db)?;
86
87 for note in &self.notes {
88 f.write_str("\nnote: ")?;
89 note.fmt(f, db)?;
90 }
91 Ok(())
92 }
93}
94
95impl<'db> LocationId<'db> {
96 pub fn inlined(self, db: &'db dyn Database, inlining_location: StableLocation<'db>) -> Self {
98 let mut location = self.long(db).clone();
99 location.inline_locations.push(inlining_location);
100 location.intern(db)
101 }
102 pub fn all_locations(self, db: &'db dyn Database) -> Vec<StableLocation<'db>> {
104 let location = self.long(db);
105 let mut all_locations = vec![location.stable_location];
106 all_locations.extend(location.inline_locations.iter().cloned());
107 all_locations
108 }
109}
110
111#[derive(Clone, Debug, PartialEq, Eq)]
112pub struct VariableMarker;
113pub type VariableId = Id<VariableMarker>;
114pub type VariableArena<'db> = Arena<Variable<'db>, DefaultArenaBehavior<VariableMarker>>;
115
116#[derive(Copy, Clone, Debug, Eq, PartialEq)]
136pub struct VarUsage<'db> {
137 pub var_id: VariableId,
138 pub location: LocationId<'db>,
139}
140
141#[derive(Clone, Debug, PartialEq, Eq)]
143pub struct Lowered<'db> {
144 pub diagnostics: Diagnostics<'db, LoweringDiagnostic<'db>>,
146 pub signature: Signature<'db>,
148 pub variables: VariableArena<'db>,
150 pub blocks: Blocks<'db>,
152 pub parameters: Vec<VariableId>,
154}
155
156unsafe impl<'db> salsa::Update for Lowered<'db> {
157 unsafe fn maybe_update(old_pointer: *mut Self, new_value: Self) -> bool {
158 let old_value = unsafe { &mut *old_pointer };
159 let res = unsafe {
160 Diagnostics::maybe_update(&mut old_value.diagnostics, new_value.diagnostics)
161 | Signature::maybe_update(&mut old_value.signature, new_value.signature)
162 } | (old_value.blocks != new_value.blocks);
163 if res {
164 old_value.variables = new_value.variables;
165 old_value.parameters = new_value.parameters;
166 old_value.blocks = new_value.blocks;
167 }
168 res
169 }
170}
171
172#[derive(Clone, Debug, Default, PartialEq, Eq)]
174pub struct VarRemapping<'db> {
175 pub remapping: OrderedHashMap<VariableId, VarUsage<'db>>,
177}
178impl<'db> Deref for VarRemapping<'db> {
179 type Target = OrderedHashMap<VariableId, VarUsage<'db>>;
180
181 fn deref(&self) -> &Self::Target {
182 &self.remapping
183 }
184}
185impl<'db> DerefMut for VarRemapping<'db> {
186 fn deref_mut(&mut self) -> &mut Self::Target {
187 &mut self.remapping
188 }
189}
190
191#[derive(Clone, Debug, PartialEq, Eq)]
193pub struct Block<'db> {
194 pub statements: Vec<Statement<'db>>,
196 pub end: BlockEnd<'db>,
198}
199impl<'db> Default for Block<'db> {
200 fn default() -> Self {
201 Self { statements: Default::default(), end: BlockEnd::NotSet }
202 }
203}
204impl<'db> Block<'db> {
205 pub fn is_set(&self) -> bool {
206 !matches!(self.end, BlockEnd::NotSet)
207 }
208}
209
210#[derive(Clone, Debug, PartialEq, Eq)]
212pub enum BlockEnd<'db> {
213 NotSet,
216 Return(Vec<VarUsage<'db>>, LocationId<'db>),
218 Panic(VarUsage<'db>),
220 Goto(BlockId, VarRemapping<'db>),
222 Match {
223 info: MatchInfo<'db>,
224 },
225}
226
227#[derive(Clone, Debug, PartialEq, Eq)]
229pub struct Variable<'db> {
230 pub ty: semantic::TypeId<'db>,
232 pub location: LocationId<'db>,
234 pub info: TypeInfo<'db>,
236}
237
238impl<'db> DebugWithDb<'db> for Variable<'db> {
239 type Db = LoweredFormatter<'db>;
240
241 fn fmt(&self, f: &mut std::fmt::Formatter<'_>, _ctx: &Self::Db) -> std::fmt::Result {
242 write!(f, "Variable({:?})", self.ty)
243 }
244}
245
246impl<'db> Variable<'db> {
247 pub fn new(
248 db: &'db dyn Database,
249 ctx: ImplLookupContextId<'db>,
250 ty: semantic::TypeId<'db>,
251 location: LocationId<'db>,
252 ) -> Self {
253 Self { ty, location, info: db.type_info(ctx, ty) }
254 }
255
256 pub fn with_default_context(
258 db: &'db dyn Database,
259 ty: semantic::TypeId<'db>,
260 location: LocationId<'db>,
261 ) -> Self {
262 Self {
263 ty,
264 location,
265 info: TypeInfo {
266 copyable: db.copyable(ty),
267 droppable: db.droppable(ty),
268 destruct_impl: Err(InferenceError::Ambiguity(Ambiguity::WillNotInfer(
269 concrete_destruct_trait(db, ty),
270 ))),
271 panic_destruct_impl: Err(InferenceError::Ambiguity(Ambiguity::WillNotInfer(
272 concrete_panic_destruct_trait(db, ty),
273 ))),
274 },
275 }
276 }
277}
278
279#[derive(Clone, Debug, PartialEq, Eq)]
281pub enum Statement<'db> {
282 Const(StatementConst<'db>),
284
285 Call(StatementCall<'db>),
287
288 StructConstruct(StatementStructConstruct<'db>),
290 StructDestructure(StatementStructDestructure<'db>),
291
292 EnumConstruct(StatementEnumConstruct<'db>),
294
295 Snapshot(StatementSnapshot<'db>),
296 Desnap(StatementDesnap<'db>),
297}
298impl<'db> Statement<'db> {
299 pub fn inputs(&self) -> &[VarUsage<'db>] {
300 match &self {
301 Statement::Const(_stmt) => &[],
302 Statement::Call(stmt) => stmt.inputs.as_slice(),
303 Statement::StructConstruct(stmt) => stmt.inputs.as_slice(),
304 Statement::StructDestructure(stmt) => std::slice::from_ref(&stmt.input),
305 Statement::EnumConstruct(stmt) => std::slice::from_ref(&stmt.input),
306 Statement::Snapshot(stmt) => std::slice::from_ref(&stmt.input),
307 Statement::Desnap(stmt) => std::slice::from_ref(&stmt.input),
308 }
309 }
310
311 pub fn inputs_mut(&mut self) -> &mut [VarUsage<'db>] {
312 match self {
313 Statement::Const(_stmt) => &mut [],
314 Statement::Call(stmt) => stmt.inputs.as_mut_slice(),
315 Statement::StructConstruct(stmt) => stmt.inputs.as_mut_slice(),
316 Statement::StructDestructure(stmt) => std::slice::from_mut(&mut stmt.input),
317 Statement::EnumConstruct(stmt) => std::slice::from_mut(&mut stmt.input),
318 Statement::Snapshot(stmt) => std::slice::from_mut(&mut stmt.input),
319 Statement::Desnap(stmt) => std::slice::from_mut(&mut stmt.input),
320 }
321 }
322
323 pub fn outputs(&self) -> &[VariableId] {
324 match self {
325 Statement::Const(stmt) => std::slice::from_ref(&stmt.output),
326 Statement::Call(stmt) => stmt.outputs.as_slice(),
327 Statement::StructConstruct(stmt) => std::slice::from_ref(&stmt.output),
328 Statement::StructDestructure(stmt) => stmt.outputs.as_slice(),
329 Statement::EnumConstruct(stmt) => std::slice::from_ref(&stmt.output),
330 Statement::Snapshot(stmt) => stmt.outputs.as_slice(),
331 Statement::Desnap(stmt) => std::slice::from_ref(&stmt.output),
332 }
333 }
334
335 pub fn outputs_mut(&mut self) -> &mut [VariableId] {
336 match self {
337 Statement::Const(stmt) => std::slice::from_mut(&mut stmt.output),
338 Statement::Call(stmt) => stmt.outputs.as_mut_slice(),
339 Statement::StructConstruct(stmt) => std::slice::from_mut(&mut stmt.output),
340 Statement::StructDestructure(stmt) => stmt.outputs.as_mut_slice(),
341 Statement::EnumConstruct(stmt) => std::slice::from_mut(&mut stmt.output),
342 Statement::Snapshot(stmt) => stmt.outputs.as_mut_slice(),
343 Statement::Desnap(stmt) => std::slice::from_mut(&mut stmt.output),
344 }
345 }
346 pub fn location(&self) -> Option<LocationId<'db>> {
347 match &self {
349 Statement::Const(_) => None,
350 Statement::Call(stmt) => Some(stmt.location),
351 Statement::StructConstruct(_) => None,
352 Statement::StructDestructure(stmt) => Some(stmt.input.location),
353 Statement::EnumConstruct(stmt) => Some(stmt.input.location),
354 Statement::Snapshot(stmt) => Some(stmt.input.location),
355 Statement::Desnap(stmt) => Some(stmt.input.location),
356 }
357 }
358 pub fn location_mut(&mut self) -> Option<&mut LocationId<'db>> {
359 match self {
360 Statement::Const(_) => None,
361 Statement::Call(stmt) => Some(&mut stmt.location),
362 Statement::StructConstruct(_) => None,
363 Statement::StructDestructure(stmt) => Some(&mut stmt.input.location),
364 Statement::EnumConstruct(stmt) => Some(&mut stmt.input.location),
365 Statement::Snapshot(stmt) => Some(&mut stmt.input.location),
366 Statement::Desnap(stmt) => Some(&mut stmt.input.location),
367 }
368 }
369}
370
371#[derive(Clone, Debug, PartialEq, Eq)]
373pub struct StatementConst<'db> {
374 pub value: ConstValueId<'db>,
376 pub output: VariableId,
378 pub boxed: bool,
380}
381impl<'db> StatementConst<'db> {
382 pub fn new(value: ConstValueId<'db>, output: VariableId, boxed: bool) -> Self {
384 Self { value, output, boxed }
385 }
386 pub fn new_flat(value: ConstValueId<'db>, output: VariableId) -> Self {
388 Self::new(value, output, false)
389 }
390 pub fn new_boxed(value: ConstValueId<'db>, output: VariableId) -> Self {
392 Self::new(value, output, true)
393 }
394}
395
396#[derive(Clone, Debug, PartialEq, Eq)]
398pub struct StatementCall<'db> {
399 pub function: FunctionId<'db>,
401 pub inputs: Vec<VarUsage<'db>>,
403 pub with_coupon: bool,
406 pub outputs: Vec<VariableId>,
408 pub is_specialization_base_call: bool,
410 pub location: LocationId<'db>,
412}
413
414#[derive(Clone, Debug, PartialEq, Eq)]
417pub struct StatementEnumConstruct<'db> {
418 pub variant: ConcreteVariant<'db>,
419 pub input: VarUsage<'db>,
421 pub output: VariableId,
423}
424
425#[derive(Clone, Debug, PartialEq, Eq)]
427pub struct StatementStructConstruct<'db> {
428 pub inputs: Vec<VarUsage<'db>>,
429 pub output: VariableId,
431}
432
433#[derive(Clone, Debug, PartialEq, Eq)]
436pub struct StatementStructDestructure<'db> {
437 pub input: VarUsage<'db>,
439 pub outputs: Vec<VariableId>,
441}
442
443#[derive(Clone, Debug, PartialEq, Eq)]
445pub struct StatementSnapshot<'db> {
446 pub input: VarUsage<'db>,
447 pub outputs: [VariableId; 2],
448}
449impl<'db> StatementSnapshot<'db> {
450 pub fn new(
451 input: VarUsage<'db>,
452 output_original: VariableId,
453 output_snapshot: VariableId,
454 ) -> Self {
455 Self { input, outputs: [output_original, output_snapshot] }
456 }
457 pub fn original(&self) -> VariableId {
458 self.outputs[0]
459 }
460 pub fn snapshot(&self) -> VariableId {
461 self.outputs[1]
462 }
463}
464
465#[derive(Clone, Debug, PartialEq, Eq)]
467pub struct StatementDesnap<'db> {
468 pub input: VarUsage<'db>,
469 pub output: VariableId,
471}
472
473#[derive(Clone, Debug, PartialEq, Eq)]
475pub struct MatchArm<'db> {
476 pub arm_selector: MatchArmSelector<'db>,
478
479 pub block_id: BlockId,
481
482 pub var_ids: Vec<VariableId>,
484}
485
486#[derive(Clone, Debug, PartialEq, Eq)]
489pub struct MatchExternInfo<'db> {
490 pub function: FunctionId<'db>,
493 pub inputs: Vec<VarUsage<'db>>,
495 pub arms: Vec<MatchArm<'db>>,
498 pub location: LocationId<'db>,
500}
501
502#[derive(Clone, Debug, PartialEq, Eq)]
504pub struct MatchEnumInfo<'db> {
505 pub concrete_enum_id: ConcreteEnumId<'db>,
506 pub input: VarUsage<'db>,
508 pub arms: Vec<MatchArm<'db>>,
511 pub location: LocationId<'db>,
513}
514#[derive(Clone, Debug, PartialEq, Eq)]
517pub struct MatchEnumValue<'db> {
518 pub num_of_arms: usize,
519
520 pub input: VarUsage<'db>,
522 pub arms: Vec<MatchArm<'db>>,
524 pub location: LocationId<'db>,
526}
527
528#[derive(Clone, Debug, PartialEq, Eq)]
529pub enum MatchInfo<'db> {
530 Enum(MatchEnumInfo<'db>),
531 Extern(MatchExternInfo<'db>),
532 Value(MatchEnumValue<'db>),
533}
534impl<'db> MatchInfo<'db> {
535 pub fn inputs(&self) -> &[VarUsage<'db>] {
536 match self {
537 MatchInfo::Enum(s) => std::slice::from_ref(&s.input),
538 MatchInfo::Extern(s) => s.inputs.as_slice(),
539 MatchInfo::Value(s) => std::slice::from_ref(&s.input),
540 }
541 }
542
543 pub fn inputs_mut(&mut self) -> &mut [VarUsage<'db>] {
544 match self {
545 MatchInfo::Enum(s) => std::slice::from_mut(&mut s.input),
546 MatchInfo::Extern(s) => s.inputs.as_mut_slice(),
547 MatchInfo::Value(s) => std::slice::from_mut(&mut s.input),
548 }
549 }
550 pub fn arms(&self) -> &[MatchArm<'db>] {
551 match self {
552 MatchInfo::Enum(s) => &s.arms,
553 MatchInfo::Extern(s) => &s.arms,
554 MatchInfo::Value(s) => &s.arms,
555 }
556 }
557 pub fn arms_mut(&mut self) -> &mut [MatchArm<'db>] {
558 match self {
559 MatchInfo::Enum(s) => &mut s.arms,
560 MatchInfo::Extern(s) => &mut s.arms,
561 MatchInfo::Value(s) => &mut s.arms,
562 }
563 }
564 pub fn location(&self) -> &LocationId<'db> {
565 match self {
566 MatchInfo::Enum(s) => &s.location,
567 MatchInfo::Extern(s) => &s.location,
568 MatchInfo::Value(s) => &s.location,
569 }
570 }
571 pub fn location_mut(&mut self) -> &mut LocationId<'db> {
572 match self {
573 MatchInfo::Enum(s) => &mut s.location,
574 MatchInfo::Extern(s) => &mut s.location,
575 MatchInfo::Value(s) => &mut s.location,
576 }
577 }
578}
579
580#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
582pub enum DependencyType {
583 Call,
585 Cost,
587}
588
589#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
591pub enum LoweringStage {
592 Monomorphized,
594 PreOptimizations,
601 PostBaseline,
604 Final,
607}