use std::ops::{Deref, DerefMut};
use cairo_lang_defs::diagnostic_utils::StableLocation;
use cairo_lang_diagnostics::{Diagnostics, Maybe};
use cairo_lang_semantic as semantic;
use cairo_lang_semantic::{ConcreteEnumId, ConcreteVariant};
use cairo_lang_utils::ordered_hash_map::OrderedHashMap;
use id_arena::{Arena, Id};
use itertools::chain;
use num_bigint::BigInt;
pub mod blocks;
pub use blocks::BlockId;
use self::blocks::{FlatBlocks, StructuredBlocks};
use crate::diagnostic::LoweringDiagnostic;
pub type VariableId = Id<Variable>;
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
pub struct RefIndex(pub usize);
#[derive(Debug, PartialEq, Eq)]
pub struct StructuredLowered {
pub diagnostics: Diagnostics<LoweringDiagnostic>,
pub root: Maybe<BlockId>,
pub variables: Arena<Variable>,
pub blocks: StructuredBlocks,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct FlatLowered {
pub diagnostics: Diagnostics<LoweringDiagnostic>,
pub root: Maybe<BlockId>,
pub variables: Arena<Variable>,
pub blocks: FlatBlocks,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct StructuredBlock {
pub initial_refs: Vec<VariableId>,
pub inputs: Vec<VariableId>,
pub statements: Vec<StructuredStatement>,
pub end: StructuredBlockEnd,
}
#[derive(Clone, Debug, Default, PartialEq, Eq)]
pub struct VarRemapping {
pub remapping: OrderedHashMap<VariableId, VariableId>,
}
impl Deref for VarRemapping {
type Target = OrderedHashMap<VariableId, VariableId>;
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 enum StructuredBlockEnd {
Callsite(VarRemapping),
Return { refs: Vec<VariableId>, returns: Vec<VariableId> },
Panic { refs: Vec<VariableId>, data: VariableId },
Unreachable,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct FlatBlock {
pub inputs: Vec<VariableId>,
pub statements: Vec<Statement>,
pub end: FlatBlockEnd,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum FlatBlockEnd {
Callsite(VarRemapping),
Return(Vec<VariableId>),
Unreachable,
}
impl TryFrom<StructuredBlock> for FlatBlock {
type Error = ();
fn try_from(value: StructuredBlock) -> Result<Self, Self::Error> {
Ok(FlatBlock {
inputs: value.inputs,
statements: value.statements.into_iter().map(|s| s.statement).collect(),
end: value.end.try_into()?,
})
}
}
impl TryFrom<StructuredBlockEnd> for FlatBlockEnd {
type Error = ();
fn try_from(value: StructuredBlockEnd) -> Result<Self, Self::Error> {
Ok(match value {
StructuredBlockEnd::Callsite(vars) => FlatBlockEnd::Callsite(vars),
StructuredBlockEnd::Return { refs, returns } => {
FlatBlockEnd::Return(chain!(refs.iter(), returns.iter()).copied().collect())
}
StructuredBlockEnd::Panic { .. } => return Err(()),
StructuredBlockEnd::Unreachable => FlatBlockEnd::Unreachable,
})
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Variable {
pub droppable: bool,
pub duplicatable: bool,
pub ty: semantic::TypeId,
pub location: StableLocation,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct StructuredStatement {
pub statement: Statement,
pub ref_updates: OrderedHashMap<RefIndex, VariableId>,
}
impl From<Statement> for StructuredStatement {
fn from(statement: Statement) -> Self {
StructuredStatement { statement, ref_updates: Default::default() }
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum Statement {
Literal(StatementLiteral),
Call(StatementCall),
MatchExtern(StatementMatchExtern),
StructConstruct(StatementStructConstruct),
StructDestructure(StatementStructDestructure),
EnumConstruct(StatementEnumConstruct),
MatchEnum(StatementMatchEnum),
}
impl Statement {
pub fn inputs(&self) -> Vec<VariableId> {
match &self {
Statement::Literal(_stmt) => vec![],
Statement::Call(stmt) => stmt.inputs.clone(),
Statement::MatchExtern(stmt) => stmt.inputs.clone(),
Statement::StructConstruct(stmt) => stmt.inputs.clone(),
Statement::StructDestructure(stmt) => vec![stmt.input],
Statement::EnumConstruct(stmt) => vec![stmt.input],
Statement::MatchEnum(stmt) => vec![stmt.input],
}
}
pub fn outputs(&self) -> Vec<VariableId> {
match &self {
Statement::Literal(stmt) => vec![stmt.output],
Statement::Call(stmt) => stmt.outputs.clone(),
Statement::MatchExtern(_) => vec![],
Statement::StructConstruct(stmt) => vec![stmt.output],
Statement::StructDestructure(stmt) => stmt.outputs.clone(),
Statement::EnumConstruct(stmt) => vec![stmt.output],
Statement::MatchEnum(_) => vec![],
}
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct StatementLiteral {
pub value: BigInt,
pub output: VariableId,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct StatementCall {
pub function: semantic::FunctionId,
pub inputs: Vec<VariableId>,
pub outputs: Vec<VariableId>,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct StatementMatchExtern {
pub function: semantic::FunctionId,
pub inputs: Vec<VariableId>,
pub arms: Vec<(ConcreteVariant, BlockId)>,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct StatementEnumConstruct {
pub variant: ConcreteVariant,
pub input: VariableId,
pub output: VariableId,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct StatementMatchEnum {
pub concrete_enum_id: ConcreteEnumId,
pub input: VariableId,
pub arms: Vec<(ConcreteVariant, BlockId)>,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct StatementStructConstruct {
pub inputs: Vec<VariableId>,
pub output: VariableId,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct StatementStructDestructure {
pub input: VariableId,
pub outputs: Vec<VariableId>,
}