From 4586361000c1fa1d6f00342ebb5ade0a74527156 Mon Sep 17 00:00:00 2001 From: Shadow Cat Date: Wed, 7 May 2025 01:42:47 -0400 Subject: [PATCH] more wrestling --- src/ir/upper/ident.rs | 136 +++++ src/ir/upper/instr.rs | 25 +- src/ir/upper/kind.rs | 160 +----- src/ir/upper/mod.rs | 2 + src/ir/upper/program.rs | 51 +- src/ir/upper/resolve.rs | 773 ---------------------------- src/ir/upper/{ => resolve}/error.rs | 19 +- src/ir/upper/resolve/ident.rs | 174 +++++++ src/ir/upper/resolve/instantiate.rs | 158 ++++++ src/ir/upper/resolve/instr.rs | 138 +++++ src/ir/upper/resolve/matc.rs | 139 +++++ src/ir/upper/resolve/mod.rs | 349 +++++++++++++ src/ir/upper/ty.rs | 202 ++------ 13 files changed, 1193 insertions(+), 1133 deletions(-) create mode 100644 src/ir/upper/ident.rs delete mode 100644 src/ir/upper/resolve.rs rename src/ir/upper/{ => resolve}/error.rs (92%) create mode 100644 src/ir/upper/resolve/ident.rs create mode 100644 src/ir/upper/resolve/instantiate.rs create mode 100644 src/ir/upper/resolve/instr.rs create mode 100644 src/ir/upper/resolve/matc.rs create mode 100644 src/ir/upper/resolve/mod.rs diff --git a/src/ir/upper/ident.rs b/src/ir/upper/ident.rs new file mode 100644 index 0000000..cecfe04 --- /dev/null +++ b/src/ir/upper/ident.rs @@ -0,0 +1,136 @@ +use std::fmt::Display; + +use super::*; + +/// a generic identifier for all (identifiable) kinds +/// eg. a::b::c.d.e +/// or a::Result +pub struct UIdent { + pub status: IdentStatus, + pub origin: Origin, +} + +pub enum IdentStatus { + Res(Res), + Unres { + base: ResBase, + path: Vec, + }, + Failed(Option), + Cooked, +} + +pub struct MemberIdent { + pub ty: MemberTy, + pub name: String, + pub gargs: Vec, + pub origin: Origin, +} + +#[derive(Clone, Copy)] +pub enum MemberTy { + Member, + Field, +} + +impl MemberTy { + pub fn sep(&self) -> &'static str { + match self { + MemberTy::Member => "::", + MemberTy::Field => ".", + } + } +} + +impl Display for MemberTy { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.write_str(match self { + MemberTy::Member => "member", + MemberTy::Field => "field", + }) + } +} + +#[derive(Debug, Clone)] +pub enum Res { + Var(VarID), + Fn(FnInst), + Struct(StructInst), + Type(TypeID), + Generic(GenericID), + Module(ModID), +} + +impl Res { + pub fn kind(&self) -> KindTy { + match self { + Res::Var(..) => KindTy::Var, + Res::Fn(..) => KindTy::Fn, + Res::Struct(..) => KindTy::Struct, + Res::Type(..) => KindTy::Type, + Res::Module(..) => KindTy::Module, + Res::Generic(..) => KindTy::Generic, + } + } + + pub fn display_str(&self, p: &UProgram) -> String { + let name = match self { + Res::Var(id) => &p.vars[id].name, + Res::Fn(fi) => &p.fns[fi.id].name, + Res::Struct(si) => &p.structs[si.id].name, + Res::Type(id) => &p.type_name(id), + Res::Generic(id) => &p.generics[id].name, + Res::Module(id) => &p.modules[id].name, + }; + format!("{} '{}'", self.kind(), name) + } +} + +#[derive(Clone)] +pub enum ResBase { + Unvalidated(MemRes), + Validated(Res), +} + +impl ResBase { + pub fn display_str(&self, p: &UProgram) -> String { + match self { + ResBase::Unvalidated(uv) => uv.display_str(p), + ResBase::Validated(res) => res.display_str(p), + } + } +} + +#[derive(Clone)] +pub struct MemRes { + pub mem: Member, + pub origin: Origin, + pub gargs: Vec, +} + +impl MemRes { + pub fn display_str(&self, p: &UProgram) -> String { + self.mem.id.display_str(p) + } +} + +impl IdentID { + pub fn var(&self, p: &UProgram) -> Option { + match p.idents[self].status { + IdentStatus::Res(Res::Var(id)) => Some(id), + _ => None, + } + } + pub fn fun<'a>(&self, p: &'a UProgram) -> Option<&'a FnInst> { + match &p.idents[self].status { + IdentStatus::Res(Res::Fn(i)) => Some(&i), + _ => None, + } + } + pub fn struc<'a>(&self, p: &'a UProgram) -> Option<&'a StructInst> { + match &p.idents[self].status { + IdentStatus::Res(Res::Struct(i)) => Some(&i), + _ => None, + } + } +} diff --git a/src/ir/upper/instr.rs b/src/ir/upper/instr.rs index d27c95b..e00bab5 100644 --- a/src/ir/upper/instr.rs +++ b/src/ir/upper/instr.rs @@ -1,8 +1,31 @@ use std::collections::HashMap; -use super::{arch::riscv64::RV64Instruction, DataID, IdentID, Origin, ResStage, Unresolved}; +use super::{arch::riscv64::RV64Instruction, *}; use crate::compiler::arch::riscv::Reg; +pub trait ResStage { + type Var; + type Func; + type Struct; + type Type; +} + +pub struct Unresolved; +impl ResStage for Unresolved { + type Var = IdentID; + type Func = IdentID; + type Struct = IdentID; + type Type = IdentID; +} + +pub struct Resolved; +impl ResStage for Resolved { + type Var = VarID; + type Func = FnInst; + type Struct = StructInst; + type Type = TypeID; +} + pub enum UInstruction { Mv { dst: S::Var, diff --git a/src/ir/upper/kind.rs b/src/ir/upper/kind.rs index 6b55f79..072285d 100644 --- a/src/ir/upper/kind.rs +++ b/src/ir/upper/kind.rs @@ -1,6 +1,6 @@ //! all main IR Upper data structures stored in UProgram -use super::{FnInst, ResErr, StructInst, Type, UInstrInst, UInstruction, UProgram}; +use super::*; use crate::{ common::FileSpan, ir::{Len, ID}, @@ -10,29 +10,6 @@ use std::{ fmt::{Debug, Display}, }; -pub trait ResStage { - type Var; - type Func; - type Struct; - type Type; -} - -pub struct Unresolved; -impl ResStage for Unresolved { - type Var = IdentID; - type Func = IdentID; - type Struct = IdentID; - type Type = IdentID; -} - -pub struct Resolved; -impl ResStage for Resolved { - type Var = VarID; - type Func = FnInst; - type Struct = StructInst; - type Type = TypeID; -} - pub type NamePath = Vec; pub type FnID = ID; @@ -79,55 +56,6 @@ pub struct UVar { pub children: HashMap, } -/// a generic identifier for all (identifiable) kinds -/// eg. a::b::c.d.e -/// or a::Result -pub struct UIdent { - pub status: IdentStatus, - pub origin: Origin, -} - -pub enum IdentStatus { - Res(Res), - Unres { - base: ResBase, - path: Vec, - }, - Failed(Option), - Cooked, -} - -pub struct MemberIdent { - pub ty: MemberTy, - pub name: String, - pub gargs: Vec, - pub origin: Origin, -} - -#[derive(Clone, Copy)] -pub enum MemberTy { - Member, - Field, -} - -impl MemberTy { - pub fn sep(&self) -> &'static str { - match self { - MemberTy::Member => "::", - MemberTy::Field => ".", - } - } -} - -impl Display for MemberTy { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.write_str(match self { - MemberTy::Member => "member", - MemberTy::Field => "field", - }) - } -} - #[derive(Debug, Clone, Copy, Hash, Eq, PartialEq)] pub struct VarOffset { pub id: VarID, @@ -186,7 +114,7 @@ impl MemberID { MemberID::Fn(id) => &p.fns[id].name, MemberID::Struct(id) => &p.structs[id].name, MemberID::Module(id) => &p.modules[id].name, - MemberID::Type(id) => &p.type_name(id), + MemberID::Type(def) => &p.type_name(def.ty), }; format!("{} '{}'", self.kind(), name) } @@ -218,90 +146,6 @@ impl Display for KindTy { } } -#[derive(Debug, Clone)] -pub enum Res { - Var(VarID), - Fn(FnInst), - Struct(StructInst), - Type(TypeID), - Generic(GenericID), - Module(ModID), -} - -impl Res { - pub fn kind(&self) -> KindTy { - match self { - Res::Var(..) => KindTy::Var, - Res::Fn(..) => KindTy::Fn, - Res::Struct(..) => KindTy::Struct, - Res::Type(..) => KindTy::Type, - Res::Module(..) => KindTy::Module, - Res::Generic(..) => KindTy::Generic, - } - } - - pub fn display_str(&self, p: &UProgram) -> String { - let name = match self { - Res::Var(id) => &p.vars[id].name, - Res::Fn(fi) => &p.fns[fi.id].name, - Res::Struct(si) => &p.structs[si.id].name, - Res::Type(id) => &p.type_name(id), - Res::Generic(id) => &p.generics[id].name, - Res::Module(id) => &p.modules[id].name, - }; - format!("{} '{}'", self.kind(), name) - } -} - -#[derive(Clone)] -pub enum ResBase { - Unvalidated(MemRes), - Validated(Res), -} - -impl ResBase { - pub fn display_str(&self, p: &UProgram) -> String { - match self { - ResBase::Unvalidated(uv) => uv.display_str(p), - ResBase::Validated(res) => res.display_str(p), - } - } -} - -#[derive(Clone)] -pub struct MemRes { - pub mem: Member, - pub origin: Origin, - pub gargs: Vec, -} - -impl MemRes { - pub fn display_str(&self, p: &UProgram) -> String { - self.mem.id.display_str(p) - } -} - -impl IdentID { - pub fn var(&self, p: &UProgram) -> Option { - match p.idents[self].status { - IdentStatus::Res(Res::Var(id)) => Some(id), - _ => None, - } - } - pub fn fun<'a>(&self, p: &'a UProgram) -> Option<&'a FnInst> { - match &p.idents[self].status { - IdentStatus::Res(Res::Fn(i)) => Some(&i), - _ => None, - } - } - pub fn struc<'a>(&self, p: &'a UProgram) -> Option<&'a StructInst> { - match &p.idents[self].status { - IdentStatus::Res(Res::Struct(i)) => Some(&i), - _ => None, - } - } -} - impl UFunc { pub fn flat_iter(&self) -> impl Iterator { InstrIter::new(self.instructions.iter()) diff --git a/src/ir/upper/mod.rs b/src/ir/upper/mod.rs index 810e8a3..6f6d5dd 100644 --- a/src/ir/upper/mod.rs +++ b/src/ir/upper/mod.rs @@ -4,6 +4,7 @@ mod program; mod ty; mod resolve; mod error; +mod ident; use super::*; @@ -13,3 +14,4 @@ pub use program::*; pub use ty::*; pub use error::*; pub use resolve::*; +pub use ident::*; diff --git a/src/ir/upper/program.rs b/src/ir/upper/program.rs index 6ef5492..d43d0a2 100644 --- a/src/ir/upper/program.rs +++ b/src/ir/upper/program.rs @@ -24,7 +24,7 @@ impl UProgram { pub fn new() -> Self { let mut types = Vec::new(); let tc = TypeCache { - unit: push_id(&mut types, Type::Unit), + unit: push_id(&mut types, Type::Real(RType::Unit)), error: push_id(&mut types, Type::Error), }; Self { @@ -42,7 +42,7 @@ impl UProgram { } pub fn infer(&mut self) -> TypeID { - self.def_ty(Type::Infer) + self.def_ty(RType::Infer.ty()) } pub fn def_var(&mut self, v: UVar) -> VarID { @@ -79,31 +79,34 @@ impl UProgram { pub fn type_name(&self, ty: impl Typed) -> String { match ty.ty(self) { - Type::Struct(ty) => { - format!( - "{}{}", - self.structs[ty.id].name, - self.gparams_str(&ty.gargs) - ) - } - Type::FnRef(ty) => { - format!( - "fn{}({}) -> {}", - &self.gparams_str(&ty.gargs), - &self.type_list_str(self.fns[ty.id].args.iter().map(|v| self.vars[v].ty)), - &self.type_name(self.fns[ty.id].ret) - ) - } - Type::Ref(t) => format!("{}&", self.type_name(t)), - Type::Deref(t) => format!("{}^", self.type_name(t)), - Type::Bits(size) => format!("b{}", size), - Type::Array(t, len) => format!("[{}; {len}]", self.type_name(t)), - Type::Unit => "()".to_string(), - Type::Slice(t) => format!("&[{}]", self.type_name(t)), + Type::Real(rty) => match rty { + RType::Struct(ty) => { + format!( + "{}{}", + self.structs[ty.id].name, + self.gparams_str(&ty.gargs) + ) + } + RType::FnRef(ty) => { + format!( + "fn{}({}) -> {}", + &self.gparams_str(&ty.gargs), + &self.type_list_str(self.fns[ty.id].args.iter().map(|v| self.vars[v].ty)), + &self.type_name(self.fns[ty.id].ret) + ) + } + RType::Ref(t) => format!("{}&", self.type_name(t)), + RType::Deref(t) => format!("{}^", self.type_name(t)), + RType::Bits(size) => format!("b{}", size), + RType::Array(t, len) => format!("[{}; {len}]", self.type_name(t)), + RType::Unit => "()".to_string(), + RType::Slice(t) => format!("&[{}]", self.type_name(t)), + RType::Infer => "{inferred}".to_string(), + }, Type::Error => "{error}".to_string(), - Type::Infer => "{inferred}".to_string(), Type::Unres(_) => "{unresolved}".to_string(), Type::Generic(id) => self.generics[id].name.clone(), + Type::Ptr(id) => self.type_name(id), } } diff --git a/src/ir/upper/resolve.rs b/src/ir/upper/resolve.rs deleted file mode 100644 index 13516e1..0000000 --- a/src/ir/upper/resolve.rs +++ /dev/null @@ -1,773 +0,0 @@ -use super::{ - inst_fn_var, inst_type, inst_typedef, inst_var, push_id, report_errs, resolve_refs, validate_gargs, ControlFlowOp, DataID, FnInst, IdentID, IdentStatus, KindTy, MemberID, MemberTy, Origin, Res, ResBase, ResErr, StructInst, Type, TypeID, TypeMismatch, UData, UFunc, UGeneric, UIdent, UInstrInst, UInstruction, UModule, UProgram, UStruct, UVar, VarID -}; -use crate::{ - common::CompilerOutput, - ir::{MemRes, Member}, -}; -use std::{ - collections::HashSet, - convert::Infallible, - ops::{BitOrAssign, FromResidual}, -}; - -// dawg this file is way too long -// this is the omega file tho that's super cool - -impl UProgram { - pub fn resolve(&mut self, output: &mut CompilerOutput) { - let mut unfinished = Vec::new(); - let mut data = ResData { - unfinished: Vec::new(), - changed: false, - types: &mut self.types, - s: Sources { - idents: &mut self.idents, - vars: &mut self.vars, - fns: &self.fns, - structs: &self.structs, - generics: &self.generics, - data: &self.data, - modules: &self.modules, - }, - errs: Vec::new(), - }; - for (fid, f) in self.fns.iter().enumerate() { - for i in &f.instructions { - resolve_instr( - &mut data, - ResolveCtx { - ret: f.ret, - breakable: false, - i, - }, - ); - } - // this currently works bc expressions create temporary variables - // although you can't do things like loop {return 3} (need to analyze control flow) - if !matches!(data.types[f.ret], Type::Unit) - && f.instructions - .last() - .is_none_or(|i| !matches!(i.i, UInstruction::Ret { .. })) - { - data.errs.push(ResErr::NoReturn { fid }); - } - } - while !data.unfinished.is_empty() && data.changed { - data.changed = false; - std::mem::swap(&mut data.unfinished, &mut unfinished); - for ctx in unfinished.drain(..) { - resolve_instr(&mut data, ctx); - } - } - let errs = data.errs; - report_errs(self, output, errs); - } -} - -#[derive(Clone, Copy)] -struct ResolveCtx<'a> { - ret: TypeID, - breakable: bool, - i: &'a UInstrInst, -} - -pub fn resolve_instr<'a>(data: &mut ResData<'a>, ctx: ResolveCtx<'a>) -> Option<()> { - let mut res = InstrRes::Finished; - match &ctx.i.i { - UInstruction::Call { dst, f, args } => { - let fi = data.res_id::(f, ctx)?; - let f = &data.s.fns[fi.id]; - for (src, &dest) in args.iter().zip(&f.args) { - res |= data.match_types::(dest, src, src); - } - res |= data.match_types::(dst, f.ret, dst); - } - UInstruction::Mv { dst, src } => { - res |= data.match_types::(dst, src, src); - } - UInstruction::Ref { dst, src } => { - let dstid = data.res_var_ty(dst, ctx)?; - let Type::Ref(dest_ty) = data.types[dstid] else { - compiler_error() - }; - res |= data.match_types::(dest_ty, src, src); - } - UInstruction::Deref { dst, src } => { - let srcid = data.res_var_ty(src, ctx)?; - let Type::Ref(src_ty) = data.types[srcid] else { - let origin = src.origin(data); - data.errs.push(ResErr::CannotDeref { origin, ty: srcid }); - return None; - }; - res |= data.match_types::(dst, src_ty, src); - } - UInstruction::LoadData { dst, src } => { - let srcid = src.type_id(&data.s); - res |= data.match_types::(dst, srcid, dst); - } - UInstruction::LoadSlice { dst, src } => { - let dstid = data.res_var_ty(dst, ctx)?; - let srcid = src.type_id(&data.s); - let Type::Slice(dstty) = data.types[dstid] else { - compiler_error() - }; - let Type::Array(srcty, _) = data.types[srcid] else { - compiler_error() - }; - res |= data.match_types(dstty, srcty, dst); - } - UInstruction::AsmBlock { instructions, args } => { - // TODO - } - UInstruction::Ret { src } => { - res |= data.match_types::(ctx.ret, src, src); - } - UInstruction::Construct { dst, struc, fields } => { - let si = data.res_id::(dst, ctx)?; - let sid = si.id; - let st = &data.s.structs[sid]; - let mut used = HashSet::new(); - for (name, field) in &st.fields { - if let Some(src) = fields.get(name) { - used.insert(name); - res |= data.match_types::(field.ty, src, src); - } else { - let origin = dst.origin(data); - data.errs.push(ResErr::MissingField { - origin, - id: sid, - name: name.clone(), - }); - } - } - for (name, _) in fields { - if !used.contains(name) { - let origin = dst.origin(data); - data.errs.push(ResErr::UnknownStructField { - origin, - id: sid, - name: name.clone(), - }); - } - } - } - UInstruction::If { cond, body } => { - if let Some(id) = data.res_var_ty(cond, ctx) { - if !matches!(data.types[id], Type::Bits(64)) { - let origin = cond.origin(data); - data.errs.push(ResErr::CondType { origin, ty: id }); - } - } - for i in body { - resolve_instr( - data, - ResolveCtx { - ret: ctx.ret, - breakable: ctx.breakable, - i, - }, - ); - } - } - UInstruction::Loop { body } => { - for i in body { - resolve_instr( - data, - ResolveCtx { - ret: ctx.ret, - breakable: true, - i, - }, - ); - } - } - UInstruction::Break => { - if !ctx.breakable { - data.errs.push(ResErr::BadControlFlow { - op: ControlFlowOp::Break, - origin: ctx.i.origin, - }); - } - } - UInstruction::Continue => { - if !ctx.breakable { - data.errs.push(ResErr::BadControlFlow { - op: ControlFlowOp::Continue, - origin: ctx.i.origin, - }); - } - } - } - match res { - InstrRes::Finished => (), - InstrRes::Unfinished => data.unfinished.push(ctx), - } - return None; -} - -fn compiler_error() -> ! { - // TODO: this is probably a compiler error / should never happen - panic!("how could this happen to me (you)"); -} - -pub fn match_types(data: &mut ResData, dst: impl TypeIDed, src: impl TypeIDed) -> MatchRes { - let dstid = data.res_ty(&dst)?; - let srcid = data.res_ty(&src)?; - if dstid == srcid { - return MatchRes::Finished; - } - let error = || { - MatchRes::Error(vec![TypeMismatch { - dst: dstid, - src: srcid, - }]) - }; - match (data.types[dstid].clone(), data.types[srcid].clone()) { - (Type::Error, _) | (_, Type::Error) => MatchRes::Finished, - (Type::Infer, Type::Infer) => MatchRes::Unfinished, - (Type::Infer, x) => { - data.changed = true; - data.types[dstid] = x; - dst.finish(&mut data.s, data.types); - MatchRes::Finished - } - (x, Type::Infer) => { - data.changed = true; - data.types[srcid] = x; - src.finish(&mut data.s, data.types); - MatchRes::Finished - } - (Type::Struct(dest), Type::Struct(src)) => { - if dest.id != src.id { - return error(); - } - match_all(data, dest.gargs.iter().cloned(), src.gargs.iter().cloned()) - } - // ( - // Type::Fn { - // args: dst_args, - // ret: dst_ret, - // }, - // Type::Fn { - // args: src_args, - // ret: src_ret, - // }, - // ) => { - // let dst = dst_args.into_iter().chain(once(dst_ret)); - // let src = src_args.into_iter().chain(once(src_ret)); - // match_all(data, dst, src) - // } - (Type::Ref(dest), Type::Ref(src)) => match_types(data, dest, src), - (Type::Slice(dest), Type::Slice(src)) => match_types(data, dest, src), - (Type::Array(dest, dlen), Type::Array(src, slen)) => { - if dlen == slen { - match_types(data, dest, src) - } else { - error() - } - } - _ => error(), - } -} - -fn match_all( - data: &mut ResData, - dst: impl Iterator, - src: impl Iterator, -) -> MatchRes { - let mut finished = true; - let mut errors = Vec::new(); - for (dst, src) in dst.zip(src) { - match match_types(data, dst, src) { - MatchRes::Unfinished => finished = false, - MatchRes::Error(errs) => errors.extend(errs), - MatchRes::Finished => (), - } - } - if finished { - if errors.is_empty() { - MatchRes::Finished - } else { - MatchRes::Error(errors) - } - } else { - MatchRes::Unfinished - } -} - -struct Sources<'a> { - idents: &'a mut [UIdent], - vars: &'a mut Vec, - fns: &'a [UFunc], - structs: &'a [UStruct], - generics: &'a [UGeneric], - data: &'a [UData], - modules: &'a [UModule], -} - -struct ResData<'a> { - unfinished: Vec>, - changed: bool, - types: &'a mut Vec, - s: Sources<'a>, - errs: Vec, -} - -impl<'a> ResData<'a> { - pub fn match_types( - &mut self, - dst: impl Resolvable, - src: impl Resolvable, - origin: impl HasOrigin, - ) -> InstrRes - where - Dst::Res: TypeIDed, - Src::Res: TypeIDed, - { - let dst = dst - .try_res(&mut self.s, self.types, &mut self.errs, &mut self.changed)? - .type_id(&self.s); - let src = src - .try_res(&mut self.s, self.types, &mut self.errs, &mut self.changed)? - .type_id(&self.s); - let res = match_types(self, dst, src); - match res { - MatchRes::Unfinished => InstrRes::Unfinished, - MatchRes::Finished => InstrRes::Finished, - MatchRes::Error(es) => { - self.errs.push(ResErr::Type { - errs: es, - origin: origin.origin(self), - dst, - src, - }); - InstrRes::Finished - } - } - } - pub fn try_res_id(&mut self, x: impl Resolvable) -> Result { - x.try_res( - &mut self.s, - &mut self.types, - &mut self.errs, - &mut self.changed, - ) - } - pub fn res_var_ty<'b: 'a>( - &mut self, - x: impl Resolvable, - ctx: ResolveCtx<'b>, - ) -> Option { - self.res_id::(x, ctx).map(|i| i.type_id(&self.s)) - } - pub fn res_id<'b: 'a, K: ResKind>( - &mut self, - x: impl Resolvable, - ctx: ResolveCtx<'b>, - ) -> Option { - match self.try_res_id(x) { - Ok(id) => return Some(id), - Err(InstrRes::Unfinished) => self.unfinished.push(ctx), - Err(InstrRes::Finished) => (), - } - None - } - - pub fn res_ty(&mut self, x: &impl TypeIDed) -> Result { - let id = resolve_refs(self.types, x.type_id(&self.s)); - Ok(if let Type::Unres(ident) = self.types[id] { - match self.try_res_id::(ident) { - Ok(nid) => { - // this does NOT feel good lmao - self.types[id] = self.types[nid].clone(); - x.finish(&mut self.s, self.types); - nid - } - Err(res) => return Err(res), - } - } else { - id - }) - } -} - -pub enum MatchRes { - Unfinished, - Finished, - Error(Vec), -} - -#[derive(Debug, Clone, Copy)] -pub enum InstrRes { - Finished, - Unfinished, -} - -impl MemRes { - pub fn validate( - &self, - fns: &[UFunc], - structs: &[UStruct], - generics: &[UGeneric], - types: &mut Vec, - errs: &mut Vec, - ) -> Result> { - let no_gargs = || { - if self.gargs.len() > 0 { - Err(ResErr::GenericCount { - origin: self.origin, - expected: 0, - found: self.gargs.len(), - }) - } else { - Ok(()) - } - }; - Ok(match &self.mem.id { - &MemberID::Fn(id) => { - validate_gargs( - &fns[id].gargs, - &self.gargs, - generics, - types, - errs, - self.origin, - )?; - Res::Fn(FnInst { - id, - gargs: self.gargs.clone(), - }) - } - &MemberID::Struct(id) => { - validate_gargs( - &structs[id].gargs, - &self.gargs, - generics, - types, - errs, - self.origin, - )?; - Res::Struct(StructInst { - id, - gargs: self.gargs.clone(), - }) - } - &MemberID::Var(id) => { - no_gargs()?; - Res::Var(id) - } - &MemberID::Module(id) => { - no_gargs()?; - Res::Module(id) - } - MemberID::Type(def) => { - validate_gargs(&def.gargs, &self.gargs, generics, types, errs, self.origin)?; - inst_typedef(def, &self.gargs, types); - Res::Type(def.ty) - } - }) - } -} - -impl IdentID { - pub fn resolve( - self, - s: &mut Sources, - types: &mut Vec, - changed: &mut bool, - errs: &mut Vec, - ) -> Result { - let status = &mut s.idents[self].status; - // TOOD: there are some clones here that shouldn't be needed - Ok(match status { - IdentStatus::Res(r) => r.clone(), - IdentStatus::Unres { path, base } => { - while let Some(mem) = path.pop() { - let res = match base { - ResBase::Unvalidated(u) => { - match u.validate(s.fns, s.structs, s.generics, types, errs) { - Ok(res) => res, - Err(err) => { - *status = IdentStatus::Failed(err); - return Err(InstrRes::Finished); - } - } - } - ResBase::Validated(res) => res.clone(), - }; - *base = match (res, mem.ty) { - (Res::Module(id), MemberTy::Member) => { - let Some(m) = s.modules[id].members.get(&mem.name) else { - return Err(InstrRes::Unfinished); - }; - ResBase::Unvalidated(MemRes { - mem: m.clone(), - origin: mem.origin, - gargs: mem.gargs, - }) - } - (Res::Var(id), MemberTy::Field) => { - // trait resolution here - let Some(&child) = s.vars[id].children.get(&mem.name) else { - return Err(InstrRes::Unfinished); - }; - ResBase::Unvalidated(MemRes { - mem: Member { - id: MemberID::Var(child), - }, - origin: mem.origin, - gargs: mem.gargs, - }) - } - _ => { - *status = IdentStatus::Failed(Some(ResErr::UnknownMember { - origin: mem.origin, - ty: mem.ty, - name: mem.name.clone(), - parent: base.clone(), - })); - return Err(InstrRes::Finished); - } - }; - } - let res = match base { - ResBase::Unvalidated(u) => { - match u.validate(s.fns, s.structs, s.generics, types, errs) { - Ok(res) => res, - Err(err) => { - *status = IdentStatus::Failed(err); - return Err(InstrRes::Finished); - } - } - } - ResBase::Validated(res) => res.clone(), - }; - *status = IdentStatus::Res(res.clone()); - *changed = true; - res - } - IdentStatus::Cooked => return Err(InstrRes::Finished), - IdentStatus::Failed(_) => return Err(InstrRes::Finished), - }) - } -} - -impl BitOrAssign for InstrRes { - fn bitor_assign(&mut self, rhs: Self) { - match rhs { - InstrRes::Finished => (), - InstrRes::Unfinished => *self = InstrRes::Unfinished, - } - } -} - -impl FromResidual> for InstrRes { - fn from_residual(_: Option) -> Self { - Self::Unfinished - } -} - -trait Resolvable { - fn try_res( - &self, - s: &mut Sources, - types: &mut Vec, - errs: &mut Vec, - changed: &mut bool, - ) -> Result; -} - -impl Resolvable for IdentID { - fn try_res( - &self, - s: &mut Sources, - types: &mut Vec, - errs: &mut Vec, - changed: &mut bool, - ) -> Result { - let origin = s.idents[self].origin; - let res = self.resolve(s, types, changed, errs)?; - match K::from_res(res, types, s, origin) { - Ok(res) => Ok(res), - Err(res) => { - errs.push(ResErr::KindMismatch { - origin, - expected: K::ty(), - found: res, - }); - Err(InstrRes::Finished) - } - } - } -} - -impl Resolvable for &IdentID { - fn try_res( - &self, - s: &mut Sources, - types: &mut Vec, - errs: &mut Vec, - changed: &mut bool, - ) -> Result { - Resolvable::::try_res(*self, s, types, errs, changed) - } -} - -impl Resolvable for VarID { - fn try_res( - &self, - s: &mut Sources, - types: &mut Vec, - errs: &mut Vec, - changed: &mut bool, - ) -> Result<::Res, InstrRes> { - Ok(*self) - } -} - -impl Resolvable for TypeID { - fn try_res( - &self, - s: &mut Sources, - types: &mut Vec, - errs: &mut Vec, - changed: &mut bool, - ) -> Result<::Res, InstrRes> { - Ok(*self) - } -} - -pub trait ResKind { - type Res; - fn ty() -> KindTy; - fn from_res( - res: Res, - types: &mut Vec, - s: &mut Sources, - origin: Origin, - ) -> Result; -} - -impl ResKind for UFunc { - type Res = FnInst; - fn ty() -> KindTy { - KindTy::Fn - } - fn from_res(res: Res, _: &mut Vec, _: &mut Sources, _: Origin) -> Result { - match res { - Res::Fn(fi) => Ok(fi), - _ => Err(res), - } - } -} - -impl ResKind for UVar { - type Res = VarID; - fn ty() -> KindTy { - KindTy::Var - } - fn from_res( - res: Res, - types: &mut Vec, - s: &mut Sources, - origin: Origin, - ) -> Result { - Ok(match res { - Res::Fn(fty) => inst_fn_var(fty, s.fns, origin, s.vars, types), - Res::Var(id) => id, - _ => return Err(res), - }) - } -} - -impl ResKind for UStruct { - type Res = StructInst; - fn ty() -> KindTy { - KindTy::Struct - } - fn from_res(res: Res, _: &mut Vec, _: &mut Sources, _: Origin) -> Result { - match res { - Res::Struct(si) => Ok(si), - _ => Err(res), - } - } -} - -impl ResKind for Type { - type Res = TypeID; - fn ty() -> KindTy { - KindTy::Type - } - fn from_res( - res: Res, - types: &mut Vec, - s: &mut Sources, - _: Origin, - ) -> Result { - Ok(match res { - Res::Struct(si) => push_id(types, Type::Struct(si)), - Res::Type(id) => id, - _ => return Err(res), - }) - } -} - -pub trait TypeIDed { - fn type_id(&self, s: &Sources) -> TypeID; - fn finish(&self, s: &mut Sources, types: &mut Vec) {} -} - -impl TypeIDed for TypeID { - fn type_id(&self, _: &Sources) -> TypeID { - *self - } -} - -impl TypeIDed for VarID { - fn type_id(&self, s: &Sources) -> TypeID { - s.vars[self].ty - } - fn finish(&self, s: &mut Sources, types: &mut Vec) { - inst_var(s.vars, s.structs, *self, types); - } -} - -impl TypeIDed for DataID { - fn type_id(&self, s: &Sources) -> TypeID { - s.data[self].ty - } -} - -impl TypeIDed for &T { - fn type_id(&self, s: &Sources) -> TypeID { - (*self).type_id(s) - } -} - -impl FromResidual> for InstrRes { - fn from_residual(residual: Result) -> Self { - match residual { - Ok(_) => unreachable!(), - Err(r) => r, - } - } -} - -trait HasOrigin { - fn origin(&self, data: &ResData) -> Origin; -} - -impl HasOrigin for &IdentID { - fn origin(&self, data: &ResData) -> Origin { - data.s.idents[*self].origin - } -} -impl FromResidual> for MatchRes { - fn from_residual(residual: Result) -> Self { - match residual { - Ok(_) => unreachable!(), - Err(r) => r, - } - } -} diff --git a/src/ir/upper/error.rs b/src/ir/upper/resolve/error.rs similarity index 92% rename from src/ir/upper/error.rs rename to src/ir/upper/resolve/error.rs index 73c797b..56bb2d6 100644 --- a/src/ir/upper/error.rs +++ b/src/ir/upper/resolve/error.rs @@ -1,7 +1,10 @@ -use crate::common::{CompilerMsg, CompilerOutput}; +use crate::{ + common::{CompilerMsg, CompilerOutput}, + ir::RType, +}; use super::{ - IdentStatus, KindTy, MemRes, MemberTy, Origin, Res, ResBase, StructID, Type, TypeID, UProgram + IdentStatus, KindTy, MemberTy, Origin, Res, ResBase, StructID, Type, TypeID, UProgram, }; pub fn report_errs(p: &UProgram, output: &mut CompilerOutput, mut errs: Vec) { @@ -16,7 +19,11 @@ pub fn report_errs(p: &UProgram, output: &mut CompilerOutput, mut errs: Vec errs.push(err.clone()), + IdentStatus::Failed(err) => { + if let Some(err) = err { + errs.push(err.clone()) + } + } _ => (), } } @@ -113,11 +120,7 @@ pub fn report_errs(p: &UProgram, output: &mut CompilerOutput, mut errs: Vec output.err(CompilerMsg::new( - format!("Var {:?} is error type!", var.name), - var.origin, - )), - Type::Infer => output.err(CompilerMsg::new( + Type::Real(RType::Infer) => output.err(CompilerMsg::new( format!("Type of {:?} cannot be inferred", var.name), var.origin, )), diff --git a/src/ir/upper/resolve/ident.rs b/src/ir/upper/resolve/ident.rs new file mode 100644 index 0000000..8af4b78 --- /dev/null +++ b/src/ir/upper/resolve/ident.rs @@ -0,0 +1,174 @@ +use super::*; + +impl MemRes { + pub fn validate( + &self, + fns: &[UFunc], + structs: &[UStruct], + generics: &[UGeneric], + types: &mut Vec, + errs: &mut Vec, + ) -> Result> { + let no_gargs = || { + if self.gargs.len() > 0 { + Err(ResErr::GenericCount { + origin: self.origin, + expected: 0, + found: self.gargs.len(), + }) + } else { + Ok(()) + } + }; + Ok(match &self.mem.id { + &MemberID::Fn(id) => { + validate_gargs( + &fns[id].gargs, + &self.gargs, + generics, + types, + errs, + self.origin, + )?; + Res::Fn(FnInst { + id, + gargs: self.gargs.clone(), + }) + } + &MemberID::Struct(id) => { + validate_gargs( + &structs[id].gargs, + &self.gargs, + generics, + types, + errs, + self.origin, + )?; + Res::Struct(StructInst { + id, + gargs: self.gargs.clone(), + }) + } + &MemberID::Var(id) => { + no_gargs()?; + Res::Var(id) + } + &MemberID::Module(id) => { + no_gargs()?; + Res::Module(id) + } + MemberID::Type(def) => { + validate_gargs(&def.gargs, &self.gargs, generics, types, errs, self.origin)?; + inst_typedef(def, &self.gargs, types); + Res::Type(def.ty) + } + }) + } +} + +impl IdentID { + pub fn resolve( + self, + s: &mut Sources, + types: &mut Vec, + changed: &mut bool, + errs: &mut Vec, + ) -> Result { + let status = &mut s.idents[self].status; + // TOOD: there are some clones here that shouldn't be needed + Ok(match status { + IdentStatus::Res(r) => r.clone(), + IdentStatus::Unres { path, base } => { + while let Some(mem) = path.pop() { + let res = match base { + ResBase::Unvalidated(u) => { + match u.validate(s.fns, s.structs, s.generics, types, errs) { + Ok(res) => res, + Err(err) => { + *status = IdentStatus::Failed(err); + return Err(ResolveRes::Finished); + } + } + } + ResBase::Validated(res) => res.clone(), + }; + *base = match (res, mem.ty) { + (Res::Module(id), MemberTy::Member) => { + let Some(m) = s.modules[id].members.get(&mem.name) else { + return Err(ResolveRes::Unfinished); + }; + ResBase::Unvalidated(MemRes { + mem: m.clone(), + origin: mem.origin, + gargs: mem.gargs, + }) + } + (Res::Var(id), MemberTy::Field) => { + // trait resolution here + let Some(&child) = s.vars[id].children.get(&mem.name) else { + return Err(ResolveRes::Unfinished); + }; + ResBase::Unvalidated(MemRes { + mem: Member { + id: MemberID::Var(child), + }, + origin: mem.origin, + gargs: mem.gargs, + }) + } + _ => { + *status = IdentStatus::Failed(Some(ResErr::UnknownMember { + origin: mem.origin, + ty: mem.ty, + name: mem.name.clone(), + parent: base.clone(), + })); + return Err(ResolveRes::Finished); + } + }; + } + let res = match base { + ResBase::Unvalidated(u) => { + match u.validate(s.fns, s.structs, s.generics, types, errs) { + Ok(res) => res, + Err(err) => { + *status = IdentStatus::Failed(err); + return Err(ResolveRes::Finished); + } + } + } + ResBase::Validated(res) => res.clone(), + }; + *status = IdentStatus::Res(res.clone()); + *changed = true; + res + } + IdentStatus::Cooked => return Err(ResolveRes::Finished), + IdentStatus::Failed(_) => return Err(ResolveRes::Finished), + }) + } +} + +pub fn validate_gargs( + dst: &[GenericID], + src: &[TypeID], + generics: &[UGeneric], + types: &[Type], + errs: &mut Vec, + origin: Origin, +) -> Result<(), Option> { + if dst.len() != src.len() { + return Err(Some(ResErr::GenericCount { + origin, + expected: dst.len(), + found: src.len(), + })); + } + for (dst, src) in dst.iter().zip(src.iter()) { + let g = &generics[dst]; + let t = &types[src]; + // TODO: validate trait constraints + } + Ok(()) +} + diff --git a/src/ir/upper/resolve/instantiate.rs b/src/ir/upper/resolve/instantiate.rs new file mode 100644 index 0000000..0f9e11d --- /dev/null +++ b/src/ir/upper/resolve/instantiate.rs @@ -0,0 +1,158 @@ +use std::collections::HashMap; + +use super::*; + +pub fn inst_fn_var( + fi: FnInst, + fns: &[UFunc], + origin: Origin, + vars: &mut Vec, + types: &mut Vec, +) -> VarID { + let name = fns[fi.id].name.clone(); + let ty = push_id(types, RType::FnRef(fi).ty()); + push_id( + vars, + UVar { + name, + origin, + ty, + parent: None, + children: HashMap::new(), + }, + ) +} + +pub fn inst_struct_var( + si: StructInst, + structs: &[UStruct], + origin: Origin, + vars: &mut Vec, + types: &mut Vec, +) -> VarID { + let name = structs[si.id].name.clone(); + let ty = push_id(types, RType::Struct(si).ty()); + let id = push_id( + vars, + UVar { + name, + origin, + ty, + parent: None, + children: HashMap::new(), + }, + ); + inst_var(vars, structs, id, types); + id +} + +pub fn inst_var(vars: &mut Vec, structs: &[UStruct], id: VarID, types: &mut Vec) { + match real_type(types, vars[id].ty) { + RType::Struct(si) => { + let fields = &structs[si.id].fields; + let s_gargs = &structs[si.id].gargs; + let gmap = inst_gmap(s_gargs, &si.gargs); + let children = fields + .iter() + .map(|(name, f)| { + (name.clone(), { + let ty = inst_type(f.ty, types, &gmap); + let fid = push_id( + vars, + UVar { + name: name.clone(), + origin: f.origin, + ty, + parent: Some(id), + children: HashMap::new(), + }, + ); + inst_var(vars, structs, fid, types); + fid + }) + }) + .collect(); + vars[id].children = children; + } + _ => (), + } +} + +/// gargs assumed to be valid +pub fn inst_typedef(def: &TypeDef, gargs: &[TypeID], types: &mut Vec) -> TypeID { + let gmap = inst_gmap(&def.gargs, &gargs); + inst_type(def.ty, types, &gmap) +} + +pub fn inst_gmap(dst: &[GenericID], src: &[TypeID]) -> HashMap { + let mut gmap = HashMap::new(); + for (&gid, &tid) in dst.iter().zip(src) { + gmap.insert(gid, tid); + } + gmap +} + +pub fn inst_type(id: TypeID, types: &mut Vec, gmap: &HashMap) -> TypeID { + if gmap.len() == 0 { + return id; + } + match inst_type_(id, types, gmap) { + Some(new) => new, + None => id, + } +} + +fn inst_type_( + id: TypeID, + types: &mut Vec, + gmap: &HashMap, +) -> Option { + let ty = match types[id].clone() { + Type::Real(rty) => match rty { + RType::Bits(_) => return None, + RType::Struct(struct_ty) => RType::Struct(StructInst { + id: struct_ty.id, + gargs: inst_all(&struct_ty.gargs, types, gmap)?, + }), + RType::FnRef(fn_ty) => RType::FnRef(FnInst { + id: fn_ty.id, + gargs: inst_all(&fn_ty.gargs, types, gmap)?, + }), + RType::Ref(id) => RType::Ref(inst_type_(id, types, gmap)?), + RType::Slice(id) => RType::Slice(inst_type_(id, types, gmap)?), + RType::Array(id, len) => RType::Array(inst_type_(id, types, gmap)?, len), + RType::Unit => return None, + RType::Generic(gid) => { + return gmap + .get(&gid) + .map(|id| Some(*id)) + .unwrap_or_else(|| None) + } + RType::Infer => RType::Infer, + } + .ty(), + Type::Deref(id) => Type::Deref(inst_type_(id, types, gmap)?), + Type::Ptr(id) => Type::Ptr(inst_type_(id, types, gmap)?), + Type::Unres(mod_path) => Type::Unres(mod_path.clone()), + Type::Error => return None, + }; + Some(push_id(types, ty)) +} + +fn inst_all( + ids: &[TypeID], + types: &mut Vec, + gmap: &HashMap, +) -> Option> { + let mut vec = None; + for (i, &id) in ids.iter().enumerate() { + if let Some(id) = inst_type_(id, types, gmap) { + vec.get_or_insert_with(|| ids.iter().take(i).cloned().collect::>()) + .push(id); + } else if let Some(vec) = &mut vec { + vec.push(id) + } + } + vec +} + diff --git a/src/ir/upper/resolve/instr.rs b/src/ir/upper/resolve/instr.rs new file mode 100644 index 0000000..813b3a8 --- /dev/null +++ b/src/ir/upper/resolve/instr.rs @@ -0,0 +1,138 @@ +use std::collections::HashSet; + +use super::*; + +pub fn resolve_instr<'a>(data: &mut ResData<'a>, ctx: ResolveCtx<'a>) -> Option<()> { + let mut res = ResolveRes::Finished; + match &ctx.i.i { + UInstruction::Call { dst, f, args } => { + let fi = data.res_id::(f, ctx)?; + let f = &data.s.fns[fi.id]; + for (src, &dest) in args.iter().zip(&f.args) { + res |= data.match_types::(dest, src, src); + } + res |= data.match_types::(dst, f.ret, dst); + } + UInstruction::Mv { dst, src } => { + res |= data.match_types::(dst, src, src); + } + UInstruction::Ref { dst, src } => { + let dstty = data.res_var_ty(dst, ctx)?.0; + let &RType::Ref(dest_ty) = dstty else { + compiler_error() + }; + res |= data.match_types::(dest_ty, src, src); + } + UInstruction::Deref { dst, src } => { + let (srcty, srcid) = data.res_var_ty(src, ctx)?; + let &RType::Ref(src_ty) = srcty else { + let origin = src.origin(data); + data.errs.push(ResErr::CannotDeref { origin, ty: srcid }); + return None; + }; + res |= data.match_types::(dst, src_ty, src); + } + UInstruction::LoadData { dst, src } => { + let srcid = src.type_id(&data.s); + res |= data.match_types::(dst, srcid, dst); + } + UInstruction::LoadSlice { dst, src } => { + let (dstty, dstid) = data.res_var_ty(dst, ctx)?; + let &RType::Slice(dstty) = dstty else { + compiler_error() + }; + let srcid = src.type_id(&data.s); + let Type::Real(RType::Array(srcty, _)) = data.types[srcid] else { + compiler_error() + }; + res |= data.match_types(dstty, srcty, dst); + } + UInstruction::AsmBlock { instructions, args } => { + // TODO + } + UInstruction::Ret { src } => { + res |= data.match_types::(ctx.ret, src, src); + } + UInstruction::Construct { dst, struc, fields } => { + let si = data.res_id::(dst, ctx)?; + let sid = si.id; + let st = &data.s.structs[sid]; + let mut used = HashSet::new(); + for (name, field) in &st.fields { + if let Some(src) = fields.get(name) { + used.insert(name); + res |= data.match_types::(field.ty, src, src); + } else { + let origin = dst.origin(data); + data.errs.push(ResErr::MissingField { + origin, + id: sid, + name: name.clone(), + }); + } + } + for (name, _) in fields { + if !used.contains(name) { + let origin = dst.origin(data); + data.errs.push(ResErr::UnknownStructField { + origin, + id: sid, + name: name.clone(), + }); + } + } + } + UInstruction::If { cond, body } => { + if let Some(ty) = data.res_var_ty(cond, ctx) { + if !matches!(ty.0, RType::Bits(64)) { + let id = ty.1; + let origin = cond.origin(data); + data.errs.push(ResErr::CondType { origin, ty: id }); + } + } + for i in body { + resolve_instr( + data, + ResolveCtx { + ret: ctx.ret, + breakable: ctx.breakable, + i, + }, + ); + } + } + UInstruction::Loop { body } => { + for i in body { + resolve_instr( + data, + ResolveCtx { + ret: ctx.ret, + breakable: true, + i, + }, + ); + } + } + UInstruction::Break => { + if !ctx.breakable { + data.errs.push(ResErr::BadControlFlow { + op: ControlFlowOp::Break, + origin: ctx.i.origin, + }); + } + } + UInstruction::Continue => { + if !ctx.breakable { + data.errs.push(ResErr::BadControlFlow { + op: ControlFlowOp::Continue, + origin: ctx.i.origin, + }); + } + } + } + match res { + ResolveRes::Finished => (), + ResolveRes::Unfinished => data.unfinished.push(ctx), + } + return None; +} diff --git a/src/ir/upper/resolve/matc.rs b/src/ir/upper/resolve/matc.rs new file mode 100644 index 0000000..b5c1256 --- /dev/null +++ b/src/ir/upper/resolve/matc.rs @@ -0,0 +1,139 @@ +use super::*; + +pub fn match_types(data: &mut ResData, dst: impl TypeIDed, src: impl TypeIDed) -> MatchRes { + let dstid = dst.type_id(&data.s); + let srcid = src.type_id(&data.s); + let dstty = data.real_ty(&dst)?.clone(); + let srcty = data.real_ty(&src)?.clone(); + let error = || { + MatchRes::Error(vec![TypeMismatch { + dst: dstid, + src: srcid, + }]) + }; + match (dstty, srcty) { + // prefer changing dst over src + (RType::Infer, _) => { + data.changed = true; + data.types[dstid] = Type::Ptr(srcid); + dst.finish(&mut data.s, data.types); + MatchRes::Finished + } + (_, RType::Infer) => { + data.changed = true; + data.types[srcid] = Type::Ptr(dstid); + src.finish(&mut data.s, data.types); + MatchRes::Finished + } + (RType::Struct(dest), RType::Struct(src)) => { + if dest.id != src.id { + return error(); + } + match_all(data, dest.gargs.iter().cloned(), src.gargs.iter().cloned()) + } + // ( + // Type::Fn { + // args: dst_args, + // ret: dst_ret, + // }, + // Type::Fn { + // args: src_args, + // ret: src_ret, + // }, + // ) => { + // let dst = dst_args.into_iter().chain(once(dst_ret)); + // let src = src_args.into_iter().chain(once(src_ret)); + // match_all(data, dst, src) + // } + (RType::Ref(dest), RType::Ref(src)) => match_types(data, dest, src), + (RType::Slice(dest), RType::Slice(src)) => match_types(data, dest, src), + (RType::Array(dest, dlen), RType::Array(src, slen)) => { + if dlen == slen { + match_types(data, dest, src) + } else { + error() + } + } + _ => error(), + } +} + +fn match_all( + data: &mut ResData, + dst: impl Iterator, + src: impl Iterator, +) -> MatchRes { + let mut finished = true; + let mut errors = Vec::new(); + for (dst, src) in dst.zip(src) { + match match_types(data, dst, src) { + MatchRes::Unfinished => finished = false, + MatchRes::Error(errs) => errors.extend(errs), + MatchRes::Finished => (), + } + } + if finished { + if errors.is_empty() { + MatchRes::Finished + } else { + MatchRes::Error(errors) + } + } else { + MatchRes::Unfinished + } +} + +impl<'a> ResData<'a> { + pub fn match_types( + &mut self, + dst: impl Resolvable, + src: impl Resolvable, + origin: impl HasOrigin, + ) -> ResolveRes + where + Dst::Res: TypeIDed, + Src::Res: TypeIDed, + { + let dst = dst + .try_res(&mut self.s, self.types, &mut self.errs, &mut self.changed)? + .type_id(&self.s); + let src = src + .try_res(&mut self.s, self.types, &mut self.errs, &mut self.changed)? + .type_id(&self.s); + let res = match_types(self, dst, src); + match res { + MatchRes::Unfinished => ResolveRes::Unfinished, + MatchRes::Finished => ResolveRes::Finished, + MatchRes::Error(es) => { + self.errs.push(ResErr::Type { + errs: es, + origin: origin.origin(self), + dst, + src, + }); + ResolveRes::Finished + } + } + } + pub fn real_ty(&mut self, x: &impl TypeIDed) -> Result<&RType, MatchRes> { + real_type(self.types, x.type_id(&self.s)).map_err(|res| match res { + ResolveRes::Finished => MatchRes::Finished, + ResolveRes::Unfinished => MatchRes::Unfinished, + }) + } +} + +pub enum MatchRes { + Unfinished, + Finished, + Error(Vec), +} + +impl FromResidual> for MatchRes { + fn from_residual(residual: Result) -> Self { + match residual { + Ok(_) => unreachable!(), + Err(r) => r, + } + } +} diff --git a/src/ir/upper/resolve/mod.rs b/src/ir/upper/resolve/mod.rs new file mode 100644 index 0000000..852dc31 --- /dev/null +++ b/src/ir/upper/resolve/mod.rs @@ -0,0 +1,349 @@ +use super::*; +use crate::{ + common::CompilerOutput, + ir::{MemRes, Member}, +}; +use std::{ + convert::Infallible, + ops::{BitOrAssign, FromResidual}, +}; + +mod error; +mod instr; +mod matc; +mod ident; +mod instantiate; + +pub use error::*; +use instr::*; +use instantiate::*; + +impl UProgram { + pub fn resolve(&mut self, output: &mut CompilerOutput) { + let mut unfinished = Vec::new(); + let mut data = ResData { + unfinished: Vec::new(), + changed: false, + types: &mut self.types, + s: Sources { + idents: &mut self.idents, + vars: &mut self.vars, + fns: &self.fns, + structs: &self.structs, + generics: &self.generics, + data: &self.data, + modules: &self.modules, + }, + errs: Vec::new(), + }; + for (fid, f) in self.fns.iter().enumerate() { + for i in &f.instructions { + resolve_instr( + &mut data, + ResolveCtx { + ret: f.ret, + breakable: false, + i, + }, + ); + } + // this currently works bc expressions create temporary variables + // although you can't do things like loop {return 3} (need to analyze control flow) + if data.types[f.ret] != RType::Unit.ty() + && f.instructions + .last() + .is_none_or(|i| !matches!(i.i, UInstruction::Ret { .. })) + { + data.errs.push(ResErr::NoReturn { fid }); + } + } + while !data.unfinished.is_empty() && data.changed { + data.changed = false; + std::mem::swap(&mut data.unfinished, &mut unfinished); + for ctx in unfinished.drain(..) { + resolve_instr(&mut data, ctx); + } + } + let errs = data.errs; + report_errs(self, output, errs); + } +} + +#[derive(Clone, Copy)] +struct ResolveCtx<'a> { + ret: TypeID, + breakable: bool, + i: &'a UInstrInst, +} + +fn compiler_error() -> ! { + // TODO: this is probably a compiler error / should never happen + panic!("how could this happen to me (you)"); +} + +struct Sources<'a> { + idents: &'a mut [UIdent], + vars: &'a mut Vec, + fns: &'a [UFunc], + structs: &'a [UStruct], + generics: &'a [UGeneric], + data: &'a [UData], + modules: &'a [UModule], +} + +struct ResData<'a> { + unfinished: Vec>, + changed: bool, + types: &'a mut Vec, + s: Sources<'a>, + errs: Vec, +} + +impl<'a> ResData<'a> { + pub fn try_res_id(&mut self, x: impl Resolvable) -> Result { + x.try_res( + &mut self.s, + &mut self.types, + &mut self.errs, + &mut self.changed, + ) + } + pub fn res_var_ty<'b: 'a>( + &mut self, + x: impl Resolvable, + ctx: ResolveCtx<'b>, + ) -> Option<(&RType, TypeID)> { + let id = self.res_id::(x, ctx).map(|i| i.type_id(&self.s))?; + real_type(self.types, id).ok().map(|ty| (ty, id)) + } + pub fn res_id<'b: 'a, K: ResKind>( + &mut self, + x: impl Resolvable, + ctx: ResolveCtx<'b>, + ) -> Option { + match self.try_res_id(x) { + Ok(id) => return Some(id), + Err(ResolveRes::Unfinished) => self.unfinished.push(ctx), + Err(ResolveRes::Finished) => (), + } + None + } + +} + +#[derive(Debug, Clone, Copy)] +pub enum ResolveRes { + Finished, + Unfinished, +} + +impl BitOrAssign for ResolveRes { + fn bitor_assign(&mut self, rhs: Self) { + match rhs { + ResolveRes::Finished => (), + ResolveRes::Unfinished => *self = ResolveRes::Unfinished, + } + } +} + +impl FromResidual> for ResolveRes { + fn from_residual(_: Option) -> Self { + Self::Unfinished + } +} + +trait Resolvable { + fn try_res( + &self, + s: &mut Sources, + types: &mut Vec, + errs: &mut Vec, + changed: &mut bool, + ) -> Result; +} + +impl Resolvable for IdentID { + fn try_res( + &self, + s: &mut Sources, + types: &mut Vec, + errs: &mut Vec, + changed: &mut bool, + ) -> Result { + let origin = s.idents[self].origin; + let res = self.resolve(s, types, changed, errs)?; + match K::from_res(res, types, s, origin) { + Ok(res) => Ok(res), + Err(res) => { + errs.push(ResErr::KindMismatch { + origin, + expected: K::ty(), + found: res, + }); + Err(ResolveRes::Finished) + } + } + } +} + +impl Resolvable for &IdentID { + fn try_res( + &self, + s: &mut Sources, + types: &mut Vec, + errs: &mut Vec, + changed: &mut bool, + ) -> Result { + Resolvable::::try_res(*self, s, types, errs, changed) + } +} + +impl Resolvable for VarID { + fn try_res( + &self, + s: &mut Sources, + types: &mut Vec, + errs: &mut Vec, + changed: &mut bool, + ) -> Result<::Res, ResolveRes> { + Ok(*self) + } +} + +impl Resolvable for TypeID { + fn try_res( + &self, + s: &mut Sources, + types: &mut Vec, + errs: &mut Vec, + changed: &mut bool, + ) -> Result<::Res, ResolveRes> { + Ok(*self) + } +} + +pub trait ResKind { + type Res; + fn ty() -> KindTy; + fn from_res( + res: Res, + types: &mut Vec, + s: &mut Sources, + origin: Origin, + ) -> Result; +} + +impl ResKind for UFunc { + type Res = FnInst; + fn ty() -> KindTy { + KindTy::Fn + } + fn from_res(res: Res, _: &mut Vec, _: &mut Sources, _: Origin) -> Result { + match res { + Res::Fn(fi) => Ok(fi), + _ => Err(res), + } + } +} + +impl ResKind for UVar { + type Res = VarID; + fn ty() -> KindTy { + KindTy::Var + } + fn from_res( + res: Res, + types: &mut Vec, + s: &mut Sources, + origin: Origin, + ) -> Result { + Ok(match res { + Res::Fn(fty) => inst_fn_var(fty, s.fns, origin, s.vars, types), + Res::Var(id) => id, + _ => return Err(res), + }) + } +} + +impl ResKind for UStruct { + type Res = StructInst; + fn ty() -> KindTy { + KindTy::Struct + } + fn from_res(res: Res, _: &mut Vec, _: &mut Sources, _: Origin) -> Result { + match res { + Res::Struct(si) => Ok(si), + _ => Err(res), + } + } +} + +impl ResKind for Type { + type Res = TypeID; + fn ty() -> KindTy { + KindTy::Type + } + fn from_res( + res: Res, + types: &mut Vec, + s: &mut Sources, + _: Origin, + ) -> Result { + Ok(match res { + Res::Struct(si) => push_id(types, RType::Struct(si).ty()), + Res::Type(id) => id, + _ => return Err(res), + }) + } +} + +pub trait TypeIDed { + fn type_id(&self, s: &Sources) -> TypeID; + fn finish(&self, s: &mut Sources, types: &mut Vec) {} +} + +impl TypeIDed for TypeID { + fn type_id(&self, _: &Sources) -> TypeID { + *self + } +} + +impl TypeIDed for VarID { + fn type_id(&self, s: &Sources) -> TypeID { + s.vars[self].ty + } + fn finish(&self, s: &mut Sources, types: &mut Vec) { + inst_var(s.vars, s.structs, *self, types); + } +} + +impl TypeIDed for DataID { + fn type_id(&self, s: &Sources) -> TypeID { + s.data[self].ty + } +} + +impl TypeIDed for &T { + fn type_id(&self, s: &Sources) -> TypeID { + (*self).type_id(s) + } +} + +impl FromResidual> for ResolveRes { + fn from_residual(residual: Result) -> Self { + match residual { + Ok(_) => unreachable!(), + Err(r) => r, + } + } +} + +trait HasOrigin { + fn origin(&self, data: &ResData) -> Origin; +} + +impl HasOrigin for &IdentID { + fn origin(&self, data: &ResData) -> Origin { + data.s.idents[*self].origin + } +} diff --git a/src/ir/upper/ty.rs b/src/ir/upper/ty.rs index 2efcdd6..4b782fd 100644 --- a/src/ir/upper/ty.rs +++ b/src/ir/upper/ty.rs @@ -1,9 +1,4 @@ -use std::collections::HashMap; - -use super::{ - push_id, FnID, GenericID, IdentID, Len, Origin, ResErr, StructID, TypeDef, TypeID, UFunc, - UGeneric, UProgram, UStruct, UVar, VarID, -}; +use super::{FnID, GenericID, IdentID, Len, ResolveRes, StructID, TypeID, UProgram, VarID}; #[derive(Debug, Clone, Hash, Eq, PartialEq)] pub struct FieldRef { @@ -25,23 +20,36 @@ pub struct FnInst { pub gargs: Vec, } -#[derive(Clone)] +#[derive(Clone, PartialEq)] pub enum Type { + Real(RType), + Deref(TypeID), + Ptr(TypeID), + Unres(IdentID), + Error, +} + +/// "real" types +#[derive(Clone, PartialEq)] +pub enum RType { Bits(u32), Struct(StructInst), - FnRef(FnInst), // this can be added for constraints later (F: fn(...) -> ...) // Fn { args: Vec, ret: TypeID }, + // "fake" types + FnRef(FnInst), Ref(TypeID), - Deref(TypeID), Slice(TypeID), Array(TypeID, Len), Unit, - // "fake" types - Unres(IdentID), - Generic(GenericID), Infer, - Error, + Generic(GenericID), +} + +impl RType { + pub const fn ty(self) -> Type { + Type::Real(self) + } } impl Type { @@ -61,16 +69,16 @@ impl Type { impl TypeID { pub fn rf(self) -> Type { - Type::Ref(self) + RType::Ref(self).ty() } pub fn derf(self) -> Type { Type::Deref(self) } pub fn arr(self, len: Len) -> Type { - Type::Array(self, len) + RType::Array(self, len).ty() } pub fn slice(self) -> Type { - Type::Slice(self) + RType::Slice(self).ty() } } @@ -80,159 +88,15 @@ impl Type { } } -pub fn inst_fn_var( - fi: FnInst, - fns: &[UFunc], - origin: Origin, - vars: &mut Vec, - types: &mut Vec, -) -> VarID { - let name = fns[fi.id].name.clone(); - let ty = push_id(types, Type::FnRef(fi)); - push_id( - vars, - UVar { - name, - origin, - ty, - parent: None, - children: HashMap::new(), +pub fn real_type(types: &[Type], id: TypeID) -> Result<&RType, ResolveRes> { + match &types[id] { + Type::Real(rtype) => Ok(rtype), + &Type::Ptr(id) => real_type(types, id), + &Type::Deref(id) => match real_type(types, id)? { + &RType::Ref(id) => real_type(types, id), + _ => Err(ResolveRes::Finished), }, - ) -} - -pub fn inst_struct_var( - si: StructInst, - structs: &[UStruct], - origin: Origin, - vars: &mut Vec, - types: &mut Vec, -) -> VarID { - let name = structs[si.id].name.clone(); - let ty = push_id(types, Type::Struct(si)); - let id = push_id( - vars, - UVar { - name, - origin, - ty, - parent: None, - children: HashMap::new(), - }, - ); - inst_var(vars, structs, id, types); - id -} - -pub fn inst_var(vars: &mut Vec, structs: &[UStruct], id: VarID, types: &mut Vec) { - match &types[resolve_refs(types, vars[id].ty)] { - Type::Struct(si) => { - let fields = &structs[si.id].fields; - let s_gargs = &structs[si.id].gargs; - let gmap = inst_gmap(s_gargs, &si.gargs); - let children = fields - .iter() - .map(|(name, f)| { - (name.clone(), { - let ty = inst_type(f.ty, types, &gmap); - let fid = push_id( - vars, - UVar { - name: name.clone(), - origin: f.origin, - ty, - parent: Some(id), - children: HashMap::new(), - }, - ); - inst_var(vars, structs, fid, types); - fid - }) - }) - .collect(); - vars[id].children = children; - } - _ => (), + Type::Unres(_) => Err(ResolveRes::Unfinished), + Type::Error => Err(ResolveRes::Finished), } } - -pub fn resolve_refs(types: &[Type], id: TypeID) -> TypeID { - if let Type::Deref(rid) = types[id] - && let Type::Ref(nid) = types[rid] - { - nid - } else { - id - } -} - -pub fn validate_gargs( - dst: &[GenericID], - src: &[TypeID], - generics: &[UGeneric], - types: &[Type], - errs: &mut Vec, - origin: Origin, -) -> Result<(), Option> { - if dst.len() != src.len() { - return Err(Some(ResErr::GenericCount { - origin, - expected: dst.len(), - found: src.len(), - })); - } - for (dst, src) in dst.iter().zip(src.iter()) { - let g = &generics[dst]; - let t = &types[src]; - // TODO: validate trait constraints - } - Ok(()) -} - -/// gargs assumed to be valid -pub fn inst_typedef(def: &TypeDef, gargs: &[TypeID], types: &mut Vec) -> TypeID { - let gmap = inst_gmap(&def.gargs, &gargs); - inst_type(def.ty, types, &gmap) -} - -pub fn inst_gmap(dst: &[GenericID], src: &[TypeID]) -> HashMap { - let mut gmap = HashMap::new(); - for (&gid, &tid) in dst.iter().zip(src) { - gmap.insert(gid, tid); - } - gmap -} - -pub fn inst_type(id: TypeID, types: &mut Vec, gmap: &HashMap) -> TypeID { - let ty = match types[id].clone() { - Type::Bits(_) => return id, - Type::Struct(struct_ty) => Type::Struct(StructInst { - id: struct_ty.id, - gargs: struct_ty - .gargs - .iter() - .map(|id| inst_type(*id, types, gmap)) - .collect(), - }), - Type::FnRef(fn_ty) => Type::FnRef(FnInst { - id: fn_ty.id, - gargs: fn_ty - .gargs - .iter() - .map(|id| inst_type(*id, types, gmap)) - .collect(), - }), - Type::Ref(id) => Type::Ref(inst_type(id, types, gmap)), - Type::Deref(id) => Type::Deref(inst_type(id, types, gmap)), - Type::Slice(id) => Type::Slice(inst_type(id, types, gmap)), - Type::Array(id, len) => Type::Array(inst_type(id, types, gmap), len), - Type::Unit => Type::Unit, - Type::Unres(mod_path) => Type::Unres(mod_path.clone()), - Type::Generic(gid) => return gmap.get(&gid).cloned().unwrap_or_else(|| id), - Type::Infer => Type::Infer, - Type::Error => Type::Error, - }; - push_id(types, ty) -} - -// type Test = (&T, &U)