//! This module contains relevant code for [`Pass`] and shared code between //! passes. Passes can be performed on Reid MIR to e.g. typecheck the code. use std::collections::HashMap; use std::convert::Infallible; use std::error::Error as STDError; use crate::codegen::intrinsics::form_intrinsic_binops; use crate::error_raporting::ReidError; use super::*; #[derive(thiserror::Error, Debug, Clone)] pub enum SimplePassError { #[error("Function not defined: {0}")] FunctionAlreadyDefined(String), #[error("Variable not defined: {0}")] VariableAlreadyDefined(String), } #[derive(Debug, Clone, Eq, PartialOrd, Ord)] pub struct Error { pub metadata: Metadata, pub kind: TErr, } impl std::fmt::Display for Error { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "Error at {}: {}", self.metadata, self.kind) } } impl STDError for Error { fn source(&self) -> Option<&(dyn STDError + 'static)> { self.kind.source() } } impl PartialEq for Error { fn eq(&self, other: &Self) -> bool { self.kind == other.kind && self.metadata.complete_overlap(&other.metadata) } } #[derive(Debug)] pub struct State { pub errors: Vec>, } impl State { fn new() -> State { State { errors: Default::default(), } } fn or_else + Clone + Copy>(&mut self, result: Result, default: U, meta: T) -> U { match result { Ok(t) => t, Err(e) => { self.errors.push(Error { metadata: meta.into(), kind: e, }); default } } } fn ok + Clone + Copy, U>(&mut self, result: Result, meta: T) -> Option { match result { Ok(v) => Some(v), Err(e) => { self.errors.push(Error { metadata: meta.into(), kind: e, }); None } } } } #[derive(Clone, Debug)] pub struct Storage(HashMap); impl Default for Storage { fn default() -> Self { Self(Default::default()) } } impl Storage { pub fn set(&mut self, key: Key, value: T) -> Result { if let Some(_) = self.0.get(&key) { Err(()) } else { self.0.insert(key, value.clone()); Ok(value) } } pub fn get(&self, key: &Key) -> Option<&T> { self.0.get(key) } pub fn iter(&self) -> impl Iterator { self.0.iter() } pub fn find(&self, key: &Key) -> Option<(&Key, &T)> { self.0.iter().find(|(k, _)| *k == key) } pub fn filter(&self, key: &Key) -> Vec<(&Key, &T)> { self.0.iter().filter(|(k, _)| *k == key).collect() } } pub type BinopMap = Storage; #[derive(Clone, Default, Debug)] pub struct Scope { pub binops: BinopMap, pub function_returns: Storage, pub variables: Storage, pub types: Storage, /// Hard Return type of this scope, if inside a function pub return_type_hint: Option, pub data: Data, } impl Scope { pub fn inner(&self) -> Scope { Scope { function_returns: self.function_returns.clone(), variables: self.variables.clone(), binops: self.binops.clone(), types: self.types.clone(), return_type_hint: self.return_type_hint.clone(), data: self.data.clone(), } } pub fn get_struct_type(&self, key: &CustomTypeKey) -> Option<&StructType> { let ty = self.types.get(&key)?; match &ty.kind { TypeDefinitionKind::Struct(struct_ty) => Some(struct_ty), } } pub fn find_type(&self, name: &String) -> Option<&CustomTypeKey> { self.types .0 .iter() .find(|(CustomTypeKey(n, _), _)| n == name) .map(|(key, _)| key) } } #[derive(Clone, Debug)] pub struct ScopeFunction { pub ret: TypeKind, pub params: Vec, } #[derive(Clone, Debug)] pub struct ScopeVariable { pub ty: TypeKind, pub mutable: bool, } #[derive(Clone, Debug, Eq)] pub struct ScopeBinopKey { pub params: (TypeKind, TypeKind), pub operator: BinaryOperator, } #[derive(Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)] pub enum CommutativeKind { True, False, Any, } impl PartialEq for ScopeBinopKey { fn eq(&self, other: &Self) -> bool { if self.operator != other.operator { return false; } if self.operator.is_commutative() != other.operator.is_commutative() { return false; } let operators_eq = self.params.0.narrow_into(&other.params.0).is_ok() && self.params.1.narrow_into(&other.params.1).is_ok(); let swapped_ops_eq = self.params.0.narrow_into(&other.params.1).is_ok() && self.params.1.narrow_into(&other.params.0).is_ok(); if self.operator.is_commutative() { operators_eq || swapped_ops_eq } else { operators_eq } } } impl std::hash::Hash for ScopeBinopKey { fn hash(&self, state: &mut H) { if self.operator.is_commutative() { let mut sorted = vec![&self.params.0, &self.params.1]; sorted.sort(); sorted.hash(state); self.operator.hash(state); } else { self.params.hash(state); } } } #[derive(Clone, Debug)] pub struct ScopeBinopDef { pub hands: (TypeKind, TypeKind), pub operator: BinaryOperator, pub return_ty: TypeKind, } impl ScopeBinopDef { pub fn narrow(&self, lhs: &TypeKind, rhs: &TypeKind) -> Option<(TypeKind, TypeKind, TypeKind)> { let lhs_ty = lhs.narrow_into(&self.hands.0); let rhs_ty = rhs.narrow_into(&self.hands.1); if let (Ok(lhs_ty), Ok(rhs_ty)) = (lhs_ty, rhs_ty) { Some((lhs_ty, rhs_ty, self.return_ty.clone())) } else { None } } } pub struct PassState<'st, 'sc, Data: Clone + Default, TError: STDError + Clone> { state: &'st mut State, pub scope: &'sc mut Scope, inner: Vec>, pub module_id: Option, } impl<'st, 'sc, Data: Clone + Default, TError: STDError + Clone> PassState<'st, 'sc, Data, TError> { fn from(state: &'st mut State, scope: &'sc mut Scope, module_id: Option) -> Self { PassState { state, scope, inner: Vec::new(), module_id: module_id, } } pub fn or_else + Clone + Copy>( &mut self, result: Result, default: U, meta: TMeta, ) -> U { self.state.or_else(result, default, meta) } pub fn ok + Clone + Copy, U>(&mut self, result: Result, meta: TMeta) -> Option { self.state.ok(result, meta) } pub fn note_errors + Clone>(&mut self, errors: &Vec, meta: TMeta) { for error in errors { self.ok::<_, Infallible>(Err(error.clone()), meta.clone().into()); } } pub fn inner(&mut self) -> PassState { self.inner.push(self.scope.inner()); let scope = self.inner.last_mut().unwrap(); PassState { state: self.state, scope, inner: Vec::new(), module_id: self.module_id, } } } pub type PassResult = Result<(), ReidError>; pub trait Pass { type Data: Clone + Default; type TError: STDError + Clone; fn context(&mut self, _context: &mut Context, mut _state: PassState) -> PassResult { Ok(()) } fn module(&mut self, _module: &mut Module, mut _state: PassState) -> PassResult { Ok(()) } fn function( &mut self, _function: &mut FunctionDefinition, mut _state: PassState, ) -> PassResult { Ok(()) } fn block(&mut self, _block: &mut Block, mut _state: PassState) -> PassResult { Ok(()) } fn stmt(&mut self, _stmt: &mut Statement, mut _state: PassState) -> PassResult { Ok(()) } fn expr(&mut self, _expr: &mut Expression, mut _state: PassState) -> PassResult { Ok(()) } } impl Context { pub fn pass(&mut self, pass: &mut T) -> Result, ReidError> { let mut state = State::new(); let mut scope = Scope::default(); pass.context(self, PassState::from(&mut state, &mut scope, None))?; for intrinsic in form_intrinsic_binops() { scope .binops .set( ScopeBinopKey { params: (intrinsic.lhs.1.clone(), intrinsic.rhs.1.clone()), operator: intrinsic.op, }, ScopeBinopDef { hands: (intrinsic.lhs.1.clone(), intrinsic.rhs.1.clone()), operator: intrinsic.op, return_ty: intrinsic.return_type.clone(), }, ) .ok(); } for (_, module) in &mut self.modules { module.pass(pass, &mut state, &mut scope.inner())?; } Ok(state) } } impl Module { fn pass(&mut self, pass: &mut T, state: &mut State, scope: &mut Scope) -> PassResult { for typedef in &self.typedefs { scope .types .set( CustomTypeKey(typedef.name.clone(), typedef.source_module), typedef.clone(), ) .ok(); } for binop in &self.binop_defs { scope .binops .set( ScopeBinopKey { params: (binop.lhs.1.clone(), binop.rhs.1.clone()), operator: binop.op, }, ScopeBinopDef { hands: (binop.lhs.1.clone(), binop.rhs.1.clone()), operator: binop.op, return_ty: binop.return_type.clone(), }, ) .ok(); } for function in &self.functions { scope .function_returns .set( function.name.clone(), ScopeFunction { ret: function.return_type.clone(), params: function.parameters.iter().cloned().map(|v| v.1).collect(), }, ) .ok(); } pass.module(self, PassState::from(state, scope, Some(self.module_id)))?; for function in &mut self.functions { function.pass(pass, state, &mut scope.inner(), self.module_id)?; } Ok(()) } } impl FunctionDefinition { fn pass( &mut self, pass: &mut T, state: &mut State, scope: &mut Scope, mod_id: SourceModuleId, ) -> PassResult { for param in &self.parameters { scope .variables .set( param.0.clone(), ScopeVariable { ty: param.1.clone(), mutable: false, }, ) .ok(); } pass.function(self, PassState::from(state, scope, Some(mod_id)))?; match &mut self.kind { FunctionDefinitionKind::Local(block, _) => { scope.return_type_hint = Some(self.return_type.clone()); block.pass(pass, state, scope, mod_id)?; } FunctionDefinitionKind::Extern(_) => {} FunctionDefinitionKind::Intrinsic(..) => {} }; Ok(()) } } impl Block { fn pass( &mut self, pass: &mut T, state: &mut State, scope: &mut Scope, mod_id: SourceModuleId, ) -> PassResult { let mut scope = scope.inner(); for statement in &mut self.statements { statement.pass(pass, state, &mut scope, mod_id)?; } pass.block(self, PassState::from(state, &mut scope, Some(mod_id))) } } impl Statement { fn pass( &mut self, pass: &mut T, state: &mut State, scope: &mut Scope, mod_id: SourceModuleId, ) -> PassResult { match &mut self.0 { StmtKind::Let(_, _, expression) => { expression.pass(pass, state, scope, mod_id)?; } StmtKind::Set(_, expression) => { expression.pass(pass, state, scope, mod_id)?; } StmtKind::Import(_) => {} StmtKind::Expression(expression) => { expression.pass(pass, state, scope, mod_id)?; } StmtKind::While(while_statement) => { while_statement.condition.pass(pass, state, scope, mod_id)?; while_statement.block.pass(pass, state, scope, mod_id)?; } } pass.stmt(self, PassState::from(state, scope, Some(mod_id)))?; match &mut self.0 { StmtKind::Let(variable_reference, mutable, _) => { scope .variables .set( variable_reference.1.clone(), ScopeVariable { ty: variable_reference.0.clone(), mutable: *mutable, }, ) .ok(); } StmtKind::Set(_, _) => {} StmtKind::Import(_) => {} StmtKind::Expression(_) => {} StmtKind::While(_) => {} }; Ok(()) } } impl Expression { fn pass( &mut self, pass: &mut T, state: &mut State, scope: &mut Scope, mod_id: SourceModuleId, ) -> PassResult { pass.expr(self, PassState::from(state, scope, Some(mod_id)))?; Ok(()) } }