use std::ops::{Deref, DerefMut};
use cairo_lang_debug::DebugWithDb;
use cairo_lang_defs::diagnostic_utils::StableLocation;
use cairo_lang_diagnostics::{DiagnosticNote, Diagnostics};
use cairo_lang_semantic as semantic;
use cairo_lang_semantic::items::imp::ImplLookupContext;
use cairo_lang_semantic::types::TypeInfo;
use cairo_lang_semantic::{ConcreteEnumId, ConcreteVariant};
use cairo_lang_utils::ordered_hash_map::OrderedHashMap;
use cairo_lang_utils::{Intern, LookupIntern};
use id_arena::{Arena, Id};
pub mod blocks;
pub use blocks::BlockId;
use semantic::MatchArmSelector;
use semantic::expr::inference::InferenceError;
use semantic::items::constant::ConstValue;
use semantic::items::imp::ImplId;
use self::blocks::FlatBlocks;
use crate::db::LoweringGroup;
use crate::diagnostic::LoweringDiagnostic;
use crate::ids::{FunctionId, LocationId, Signature};
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
pub struct Location {
pub stable_location: StableLocation,
pub notes: Vec<DiagnosticNote>,
pub inline_locations: Vec<StableLocation>,
}
impl Location {
pub fn new(stable_location: StableLocation) -> Self {
Self { stable_location, notes: vec![], inline_locations: vec![] }
}
pub fn with_note(mut self, note: DiagnosticNote) -> Self {
self.notes.push(note);
self
}
pub fn maybe_with_note(mut self, note: Option<DiagnosticNote>) -> Self {
let Some(note) = note else {
return self;
};
self.notes.push(note);
self
}
pub fn add_note_with_location(
self,
db: &dyn LoweringGroup,
text: &str,
location: LocationId,
) -> Self {
self.with_note(DiagnosticNote::with_location(
text.into(),
location.lookup_intern(db).stable_location.diagnostic_location(db.upcast()),
))
}
}
impl DebugWithDb<dyn LoweringGroup> for Location {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>, db: &dyn LoweringGroup) -> std::fmt::Result {
let files_db = db.upcast();
self.stable_location.diagnostic_location(db.upcast()).fmt(f, files_db)?;
for note in &self.notes {
f.write_str("\nnote: ")?;
note.fmt(f, files_db)?;
}
Ok(())
}
}
impl LocationId {
pub fn inlined(self, db: &dyn LoweringGroup, inlining_location: StableLocation) -> Self {
let mut location = self.lookup_intern(db);
location.inline_locations.push(inlining_location);
location.intern(db)
}
pub fn all_locations(self, db: &dyn LoweringGroup) -> Vec<StableLocation> {
let location = self.lookup_intern(db);
let mut all_locations = vec![location.stable_location];
all_locations.extend(location.inline_locations.iter().cloned());
all_locations
}
}
pub type VariableId = Id<Variable>;
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub struct VarUsage {
pub var_id: VariableId,
pub location: LocationId,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct FlatLowered {
pub diagnostics: Diagnostics<LoweringDiagnostic>,
pub signature: Signature,
pub variables: Arena<Variable>,
pub blocks: FlatBlocks,
pub parameters: Vec<VariableId>,
}
#[derive(Clone, Debug, Default, PartialEq, Eq)]
pub struct VarRemapping {
pub remapping: OrderedHashMap<VariableId, VarUsage>,
}
impl Deref for VarRemapping {
type Target = OrderedHashMap<VariableId, VarUsage>;
fn deref(&self) -> &Self::Target {
&self.remapping
}
}
impl DerefMut for VarRemapping {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.remapping
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct FlatBlock {
pub statements: Vec<Statement>,
pub end: FlatBlockEnd,
}
impl Default for FlatBlock {
fn default() -> Self {
Self { statements: Default::default(), end: FlatBlockEnd::NotSet }
}
}
impl FlatBlock {
pub fn is_set(&self) -> bool {
!matches!(self.end, FlatBlockEnd::NotSet)
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum FlatBlockEnd {
NotSet,
Return(Vec<VarUsage>, LocationId),
Panic(VarUsage),
Goto(BlockId, VarRemapping),
Match {
info: MatchInfo,
},
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Variable {
pub droppable: Result<ImplId, InferenceError>,
pub copyable: Result<ImplId, InferenceError>,
pub destruct_impl: Result<ImplId, InferenceError>,
pub panic_destruct_impl: Result<ImplId, InferenceError>,
pub ty: semantic::TypeId,
pub location: LocationId,
}
impl Variable {
pub fn new(
db: &dyn LoweringGroup,
ctx: ImplLookupContext,
ty: semantic::TypeId,
location: LocationId,
) -> Self {
let TypeInfo { droppable, copyable, destruct_impl, panic_destruct_impl } =
match db.type_info(ctx, ty) {
Ok(info) => info,
Err(diag_added) => TypeInfo {
droppable: Err(InferenceError::Reported(diag_added)),
copyable: Err(InferenceError::Reported(diag_added)),
destruct_impl: Err(InferenceError::Reported(diag_added)),
panic_destruct_impl: Err(InferenceError::Reported(diag_added)),
},
};
Self { copyable, droppable, destruct_impl, panic_destruct_impl, ty, location }
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum Statement {
Const(StatementConst),
Call(StatementCall),
StructConstruct(StatementStructConstruct),
StructDestructure(StatementStructDestructure),
EnumConstruct(StatementEnumConstruct),
Snapshot(StatementSnapshot),
Desnap(StatementDesnap),
}
impl Statement {
pub fn inputs(&self) -> &[VarUsage] {
match &self {
Statement::Const(_stmt) => &[],
Statement::Call(stmt) => stmt.inputs.as_slice(),
Statement::StructConstruct(stmt) => stmt.inputs.as_slice(),
Statement::StructDestructure(stmt) => std::slice::from_ref(&stmt.input),
Statement::EnumConstruct(stmt) => std::slice::from_ref(&stmt.input),
Statement::Snapshot(stmt) => std::slice::from_ref(&stmt.input),
Statement::Desnap(stmt) => std::slice::from_ref(&stmt.input),
}
}
pub fn inputs_mut(&mut self) -> &mut [VarUsage] {
match self {
Statement::Const(_stmt) => &mut [],
Statement::Call(stmt) => stmt.inputs.as_mut_slice(),
Statement::StructConstruct(stmt) => stmt.inputs.as_mut_slice(),
Statement::StructDestructure(stmt) => std::slice::from_mut(&mut stmt.input),
Statement::EnumConstruct(stmt) => std::slice::from_mut(&mut stmt.input),
Statement::Snapshot(stmt) => std::slice::from_mut(&mut stmt.input),
Statement::Desnap(stmt) => std::slice::from_mut(&mut stmt.input),
}
}
pub fn outputs(&self) -> &[VariableId] {
match &self {
Statement::Const(stmt) => std::slice::from_ref(&stmt.output),
Statement::Call(stmt) => stmt.outputs.as_slice(),
Statement::StructConstruct(stmt) => std::slice::from_ref(&stmt.output),
Statement::StructDestructure(stmt) => stmt.outputs.as_slice(),
Statement::EnumConstruct(stmt) => std::slice::from_ref(&stmt.output),
Statement::Snapshot(stmt) => stmt.outputs.as_slice(),
Statement::Desnap(stmt) => std::slice::from_ref(&stmt.output),
}
}
pub fn location(&self) -> Option<LocationId> {
match &self {
Statement::Const(_) => None,
Statement::Call(stmt) => Some(stmt.location),
Statement::StructConstruct(_) => None,
Statement::StructDestructure(stmt) => Some(stmt.input.location),
Statement::EnumConstruct(stmt) => Some(stmt.input.location),
Statement::Snapshot(stmt) => Some(stmt.input.location),
Statement::Desnap(stmt) => Some(stmt.input.location),
}
}
pub fn location_mut(&mut self) -> Option<&mut LocationId> {
match self {
Statement::Const(_) => None,
Statement::Call(stmt) => Some(&mut stmt.location),
Statement::StructConstruct(_) => None,
Statement::StructDestructure(stmt) => Some(&mut stmt.input.location),
Statement::EnumConstruct(stmt) => Some(&mut stmt.input.location),
Statement::Snapshot(stmt) => Some(&mut stmt.input.location),
Statement::Desnap(stmt) => Some(&mut stmt.input.location),
}
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct StatementConst {
pub value: ConstValue,
pub output: VariableId,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct StatementCall {
pub function: FunctionId,
pub inputs: Vec<VarUsage>,
pub with_coupon: bool,
pub outputs: Vec<VariableId>,
pub location: LocationId,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct StatementEnumConstruct {
pub variant: ConcreteVariant,
pub input: VarUsage,
pub output: VariableId,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct StatementStructConstruct {
pub inputs: Vec<VarUsage>,
pub output: VariableId,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct StatementStructDestructure {
pub input: VarUsage,
pub outputs: Vec<VariableId>,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct StatementSnapshot {
pub input: VarUsage,
outputs: [VariableId; 2],
}
impl StatementSnapshot {
pub fn new(input: VarUsage, output_original: VariableId, output_snapshot: VariableId) -> Self {
Self { input, outputs: [output_original, output_snapshot] }
}
pub fn original(&self) -> VariableId {
self.outputs[0]
}
pub fn snapshot(&self) -> VariableId {
self.outputs[1]
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct StatementDesnap {
pub input: VarUsage,
pub output: VariableId,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct MatchArm {
pub arm_selector: MatchArmSelector,
pub block_id: BlockId,
pub var_ids: Vec<VariableId>,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct MatchExternInfo {
pub function: FunctionId,
pub inputs: Vec<VarUsage>,
pub arms: Vec<MatchArm>,
pub location: LocationId,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct MatchEnumInfo {
pub concrete_enum_id: ConcreteEnumId,
pub input: VarUsage,
pub arms: Vec<MatchArm>,
pub location: LocationId,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct MatchEnumValue {
pub num_of_arms: usize,
pub input: VarUsage,
pub arms: Vec<MatchArm>,
pub location: LocationId,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum MatchInfo {
Enum(MatchEnumInfo),
Extern(MatchExternInfo),
Value(MatchEnumValue),
}
impl MatchInfo {
pub fn inputs(&self) -> &[VarUsage] {
match self {
MatchInfo::Enum(s) => std::slice::from_ref(&s.input),
MatchInfo::Extern(s) => s.inputs.as_slice(),
MatchInfo::Value(s) => std::slice::from_ref(&s.input),
}
}
pub fn inputs_mut(&mut self) -> &mut [VarUsage] {
match self {
MatchInfo::Enum(s) => std::slice::from_mut(&mut s.input),
MatchInfo::Extern(s) => s.inputs.as_mut_slice(),
MatchInfo::Value(s) => std::slice::from_mut(&mut s.input),
}
}
pub fn arms(&self) -> &[MatchArm] {
match self {
MatchInfo::Enum(s) => &s.arms,
MatchInfo::Extern(s) => &s.arms,
MatchInfo::Value(s) => &s.arms,
}
}
pub fn location(&self) -> &LocationId {
match self {
MatchInfo::Enum(s) => &s.location,
MatchInfo::Extern(s) => &s.location,
MatchInfo::Value(s) => &s.location,
}
}
pub fn location_mut(&mut self) -> &mut LocationId {
match self {
MatchInfo::Enum(s) => &mut s.location,
MatchInfo::Extern(s) => &mut s.location,
MatchInfo::Value(s) => &mut s.location,
}
}
}
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub enum DependencyType {
Call,
Cost,
}