diff --git a/ideas b/ideas index 712e779..6b99cb7 100644 --- a/ideas +++ b/ideas @@ -1,21 +1,27 @@ -struct +move names into separate vec with origins +make struct fields a vec, resolve into index -Type - structinst - bits - -but type is already typeinst? - -for each var, create list of constraints on type - then just need to iterate through constraints to determine type - keep doing passes for vars that depend on the type of other vars - really need to make subvar for each field of struct var so 2 different "a.b" refer to same thing - makes borrow checking easier - do dependency cycles exist? - for global vars yes, in functions no - but if function returns are inferrable (even if just "impl Trait"), then needed in functions? -every kind has an origin, should make separate like names? +inner values that auto generate map function: + enum Thing { + A(T), + B(T, T), + C + } +or + #[derive(Map(T))] + enum Thing { ... } +{([< +std::Option:(u32)::Some(3) +func:(u32)("hello", test, 3); +std::Option:[u32]::Some(3) +func:[T]("hello", test, 3); +std::Option::::Some(3) +func::(3) +std.Option.[u32].Some(3) +func.[T]("hello", test, 3); +std::Option:::Some(3) +func:(3) + -TYPE IDS!!!!! MY SAVIOR!!! MY GOAT!!! diff --git a/src/compiler/arch/riscv/asm.rs b/src/compiler/arch/riscv/asm.rs index 9194fc3..123a554 100644 --- a/src/compiler/arch/riscv/asm.rs +++ b/src/compiler/arch/riscv/asm.rs @@ -68,6 +68,83 @@ pub enum LinkerInstruction { imm: i32, }, } +impl LinkerInstruction { + pub fn map(&self, r: impl Fn(&R) -> R2) -> LinkerInstruction { + self.try_map(|v| Some(r(v))).unwrap() + } + pub fn try_map(&self, r: impl Fn(&R) -> Option) -> Option> { + use LinkerInstruction as I; + Some(match self { + Self::ECall => I::ECall, + Self::EBreak => I::EBreak, + &Self::Li { ref dest, imm } => I::Li { dest: r(dest)?, imm }, + Self::Mv { ref dest, src } => I::Mv { + dest: r(dest)?, + src: r(src)?, + }, + Self::La { .. } => todo!(), + &Self::Load { + width, + ref dest, + ref base, + offset, + } => I::Load { + width, + dest: r(dest)?, + offset, + base: r(base)?, + }, + &Self::Store { + width, + ref src, + ref base, + offset, + } => I::Store { + width, + src: r(src)?, + offset, + base: r(base)?, + }, + &Self::Op { + op, + funct, + ref dest, + ref src1, + ref src2, + } => I::Op { + op, + funct, + dest: r(dest)?, + src1: r(src1)?, + src2: r(src2)?, + }, + &Self::OpImm { op, ref dest, ref src, imm } => I::OpImm { + op, + dest: r(dest)?, + src: r(src)?, + imm, + }, + &Self::OpImmF7 { + op, + funct, + ref dest, + ref src, + imm, + } => I::OpImmF7 { + op, + funct, + dest: r(dest)?, + src: r(src)?, + imm, + }, + Self::Ret => I::Ret, + Self::Call(..) => todo!(), + Self::Jal { .. } => todo!(), + Self::J(..) => todo!(), + Self::Branch { .. } => todo!(), + }) + } +} pub fn addi(dest: Reg, src: Reg, imm: BitsI32<11, 0>) -> RawInstruction { opi(op32i::ADD, dest, src, imm.to_u()) diff --git a/src/compiler/arch/riscv/compile.rs b/src/compiler/arch/riscv/compile.rs index 393207c..81184f2 100644 --- a/src/compiler/arch/riscv/compile.rs +++ b/src/compiler/arch/riscv/compile.rs @@ -2,10 +2,7 @@ use std::collections::HashMap; use crate::{ compiler::{arch::riscv::Reg, debug::DebugInfo, UnlinkedFunction, UnlinkedProgram}, - ir::{ - arch::riscv64::{RV64Instruction as AI, RegRef}, - LInstruction as IRI, LProgram, Len, Size, - }, + ir::{arch::riscv64::RegRef, LInstruction as IRI, LProgram, Len, Size, VarID}, }; use super::{LinkerInstruction as LI, *}; @@ -103,8 +100,8 @@ pub fn compile(program: &LProgram) -> UnlinkedProgram
  • { irli.push((v.len(), format!("{i:?}"))); match i { IRI::Mv { - dest, - dest_offset, + dst: dest, + dst_offset: dest_offset, src, src_offset, } => { @@ -119,11 +116,15 @@ pub fn compile(program: &LProgram) -> UnlinkedProgram
  • { s, ); } - IRI::Ref { dest, src } => { + IRI::Ref { dst: dest, src } => { v.push(LI::addi(t0, sp, stack[src])); v.push(LI::sd(t0, stack[dest], sp)); } - IRI::LoadAddr { dest, offset, src } => { + IRI::LoadAddr { + dst: dest, + offset, + src, + } => { v.extend([ LI::La { dest: t0, @@ -133,7 +134,7 @@ pub fn compile(program: &LProgram) -> UnlinkedProgram
  • { ]); } IRI::LoadData { - dest, + dst: dest, offset, src, len, @@ -144,7 +145,7 @@ pub fn compile(program: &LProgram) -> UnlinkedProgram
  • { }); mov_mem(&mut v, t0, 0, sp, stack[dest] + *offset as i32, t1, *len); } - IRI::Call { dest, f, args } => { + IRI::Call { dst: dest, f, args } => { let mut offset = 0; if let Some((dest, s)) = dest { offset -= align(s); @@ -166,82 +167,14 @@ pub fn compile(program: &LProgram) -> UnlinkedProgram
  • { for (reg, var) in inputs { v.push(LI::ld(*reg, stack[var], sp)); } - fn r(rr: RegRef) -> Reg { + fn r(rr: &RegRef) -> Reg { match rr { RegRef::Var(..) => todo!(), - RegRef::Reg(reg) => reg, + RegRef::Reg(reg) => *reg, } } for i in instructions { - match *i { - AI::ECall => v.push(LI::ECall), - AI::EBreak => v.push(LI::EBreak), - AI::Li { dest, imm } => v.push(LI::Li { dest: r(dest), imm }), - AI::Mv { dest, src } => v.push(LI::Mv { - dest: r(dest), - src: r(src), - }), - AI::La { .. } => todo!(), - AI::Load { - width, - dest, - base, - offset, - } => v.push(LI::Load { - width, - dest: r(dest), - offset, - base: r(base), - }), - AI::Store { - width, - src, - base, - offset, - } => v.push(LI::Store { - width, - src: r(src), - offset, - base: r(base), - }), - AI::Op { - op, - funct, - dest, - src1, - src2, - } => v.push(LI::Op { - op, - funct, - dest: r(dest), - src1: r(src1), - src2: r(src2), - }), - AI::OpImm { op, dest, src, imm } => v.push(LI::OpImm { - op, - dest: r(dest), - src: r(src), - imm, - }), - AI::OpImmF7 { - op, - funct, - dest, - src, - imm, - } => v.push(LI::OpImmF7 { - op, - funct, - dest: r(dest), - src: r(src), - imm, - }), - AI::Ret => v.push(LI::Ret), - AI::Call(..) => todo!(), - AI::Jal { .. } => todo!(), - AI::J(..) => todo!(), - AI::Branch { .. } => todo!(), - } + v.push(i.map(|v| r(v))); } for (reg, var) in outputs { v.push(LI::sd(*reg, stack[var], sp)); diff --git a/src/ir/arch/riscv64.rs b/src/ir/arch/riscv64.rs index b9da0a8..63d623b 100644 --- a/src/ir/arch/riscv64.rs +++ b/src/ir/arch/riscv64.rs @@ -1,14 +1,16 @@ -use crate::{compiler::arch::riscv::*, ir::UIdent}; +use std::fmt::Debug; -pub type RV64Instruction = LinkerInstruction; +use crate::{compiler::arch::riscv::*, ir::IdentID}; + +pub type RV64Instruction = LinkerInstruction, V>; #[derive(Copy, Clone)] -pub enum RegRef { - Var(UIdent), - Reg(Reg), +pub enum RegRef { + Var(V), + Reg(R), } -impl std::fmt::Debug for RegRef { +impl Debug for RegRef { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { Self::Var(v) => write!(f, "{{{:?}}}", v), @@ -16,4 +18,3 @@ impl std::fmt::Debug for RegRef { } } } - diff --git a/src/ir/asm.rs b/src/ir/asm.rs index 3c104fc..9c448d8 100644 --- a/src/ir/asm.rs +++ b/src/ir/asm.rs @@ -1,16 +1,7 @@ -use crate::compiler::arch::riscv::Reg; - -use super::VarID; +use super::{arch::riscv64::RegRef, IdentID}; #[derive(Clone)] pub struct IRAsmInstruction { op: String, - args: Vec, + args: Vec>, } - -#[derive(Clone)] -pub enum RegRef { - Var(VarID), - Reg(String), -} - diff --git a/src/ir/lower/func.rs b/src/ir/lower/func.rs index ee6431e..b5e602f 100644 --- a/src/ir/lower/func.rs +++ b/src/ir/lower/func.rs @@ -1,5 +1,5 @@ use super::*; -use crate::compiler::arch::riscv::Reg; +use crate::{compiler::arch::riscv::Reg, ir::arch::riscv64::RegRef}; use arch::riscv64::RV64Instruction; use std::collections::HashMap; @@ -16,33 +16,33 @@ pub struct IRLFunction { #[derive(Debug)] pub enum LInstruction { Mv { - dest: VarID, - dest_offset: Size, + dst: VarID, + dst_offset: Size, src: VarID, src_offset: Size, }, Ref { - dest: VarID, + dst: VarID, src: VarID, }, LoadAddr { - dest: VarID, + dst: VarID, offset: Size, src: Symbol, }, LoadData { - dest: VarID, + dst: VarID, offset: Size, src: Symbol, len: Len, }, Call { - dest: Option<(VarID, Size)>, + dst: Option<(VarID, Size)>, f: Symbol, args: Vec<(VarID, Size)>, }, AsmBlock { - instructions: Vec, + instructions: Vec>, inputs: Vec<(Reg, VarID)>, outputs: Vec<(Reg, VarID)>, }, diff --git a/src/ir/lower/mod.rs b/src/ir/lower/mod.rs index 5559d58..9f3b373 100644 --- a/src/ir/lower/mod.rs +++ b/src/ir/lower/mod.rs @@ -1,6 +1,7 @@ mod func; mod program; mod symbol; +mod res; pub use func::*; pub use program::*; diff --git a/src/ir/lower/program.rs b/src/ir/lower/program.rs index d89e367..632b4d8 100644 --- a/src/ir/lower/program.rs +++ b/src/ir/lower/program.rs @@ -1,10 +1,11 @@ use std::collections::HashMap; -use crate::ir::{AsmBlockArgType, Size, LStructInst, SymbolSpace, Type, UFunc, UInstrInst, VarOffset}; -LStructInst use super::{ IRLFunction, LInstruction, Len, Symbol, SymbolSpaceBuilder, UInstruction, UProgram, VarID, }; +use crate::ir::{ + AsmBlockArgType, Size, StructInst, SymbolSpace, Type, TypeID, UFunc, UInstrInst, VarOffset, +}; pub struct LProgram { sym_space: SymbolSpace, @@ -22,7 +23,7 @@ impl LProgram { let mut ssbuilder = SymbolSpaceBuilder::with_entries(&[start]); let entry = ssbuilder.func(&start); while let Some((sym, i)) = ssbuilder.pop_fn() { - let f = p.fns[i.0].as_ref().unwrap(); + let f = &p.fns[i.0]; let mut fbuilder = LFunctionBuilder::new(p, &mut ssbuilder); for i in &f.instructions { fbuilder.insert_instr(i); @@ -31,7 +32,7 @@ impl LProgram { fbuilder.instrs.push(LInstruction::Ret { src: None }); } let res = fbuilder.finish(f); - ssbuilder.write_fn(sym, res, Some(p.names.path(i).to_string())); + ssbuilder.write_fn(sym, res, Some(f.name.clone())); } let sym_space = ssbuilder.finish().expect("we failed the mission"); Ok(Self { sym_space, entry }) @@ -82,7 +83,7 @@ pub struct LFunctionBuilderData<'a> { instrs: Vec, stack: HashMap, subvar_map: HashMap, - struct_insts: HashMap, + struct_insts: HashMap, makes_call: bool, loopp: Option, } @@ -127,44 +128,48 @@ impl<'a> LFunctionBuilder<'a> { } } pub fn insert_instr(&mut self, i: &UInstrInst) -> Option> { - match &i.i { - UInstruction::Mv { dst: dest, src } => { - self.alloc_stack(dest.id)?; - self.map_subvar(src.id); + match i + .i + .resolve(self.program) + .expect("failed to resolve during lowering") + { + UInstruction::Mv { dst, src } => { + self.alloc_stack(dst)?; + self.map_subvar(src); self.instrs.push(LInstruction::Mv { - dest: dest.id, - dest_offset: 0, - src: src.id, + dst, + dst_offset: 0, + src, src_offset: 0, }); } - UInstruction::Ref { dst: dest, src } => { - self.alloc_stack(dest.id)?; - self.map_subvar(src.id); - self.instrs.push(LInstruction::Ref { - dest: dest.id, - src: src.id, - }); + UInstruction::Ref { dst, src } => { + self.alloc_stack(dst)?; + self.map_subvar(src); + self.instrs.push(LInstruction::Ref { dst, src }); } - UInstruction::LoadData { dst: dest, src } => { - self.alloc_stack(dest.id)?; - let data = self.program.expect(*src); + UInstruction::Deref { dst, src } => { + todo!() + } + UInstruction::LoadData { dst, src } => { + self.alloc_stack(dst)?; + let data = &self.program.data[src]; let sym = self.data.builder.ro_data( src, &data.content, - Some(self.program.names.path(dest.id).to_string()), + Some(&self.program.data[src].name), ); self.instrs.push(LInstruction::LoadData { - dest: dest.id, + dst, offset: 0, len: data.content.len() as Len, src: sym, }); } - UInstruction::LoadSlice { dst: dest, src } => { - self.alloc_stack(dest.id)?; - let data = self.program.expect(*src); - let Type::Array(_, len) = &data.ty else { + UInstruction::LoadSlice { dst, src } => { + self.alloc_stack(dst)?; + let data = &self.program.data[src]; + let Type::Array(_, len) = &self.program.types[data.ty] else { return Some(Some(format!( "tried to load {} as slice", self.program.type_name(&data.ty) @@ -173,10 +178,10 @@ impl<'a> LFunctionBuilder<'a> { let sym = self.data.builder.ro_data( src, &data.content, - Some(self.program.names.path(dest.id).to_string()), + Some(&self.program.data[src].name), ); self.instrs.push(LInstruction::LoadAddr { - dest: dest.id, + dst, offset: 0, src: sym, }); @@ -185,46 +190,36 @@ impl<'a> LFunctionBuilder<'a> { .builder .anon_ro_data(&(*len as u64).to_le_bytes(), Some(format!("len: {}", len))); self.instrs.push(LInstruction::LoadData { - dest: dest.id, + dst, offset: 8, len: 8, src: sym, }); } - UInstruction::LoadFn { dst: dest, src } => { - self.alloc_stack(dest.id)?; - let sym = self.builder.func(src); - self.instrs.push(LInstruction::LoadAddr { - dest: dest.id, - offset: 0, - src: sym, - }); - } - UInstruction::Call { dst: dest, f, args } => { - self.alloc_stack(dest.id); + UInstruction::Call { dst, f, args } => { + self.alloc_stack(dst); self.makes_call = true; - let fid = &self.program.fn_var.fun(f.id).expect("a"); - let sym = self.builder.func(fid); + let sym = self.builder.func(f.id); let ret_size = self .data - .size_of_var(self.program, dest.id) + .size_of_var(self.program, dst) .expect("unsized type"); - let dest = if ret_size > 0 { - Some((dest.id, ret_size)) + let dst = if ret_size > 0 { + Some((dst, ret_size)) } else { None }; let call = LInstruction::Call { - dest, + dst, f: sym, args: args - .iter() - .map(|a| { - self.map_subvar(a.id); + .into_iter() + .map(|id| { + self.map_subvar(id); ( - a.id, + id, self.data - .size_of_var(self.program, a.id) + .size_of_var(self.program, id) .expect("unsized type"), ) }) @@ -238,12 +233,12 @@ impl<'a> LFunctionBuilder<'a> { for a in args { match a.ty { AsmBlockArgType::In => { - self.map_subvar(a.var.id); - inputs.push((a.reg, a.var.id)) + self.map_subvar(a.var); + inputs.push((a.reg, a.var)) } AsmBlockArgType::Out => { - self.alloc_stack(a.var.id)?; - outputs.push((a.reg, a.var.id)); + self.alloc_stack(a.var)?; + outputs.push((a.reg, a.var)); } } } @@ -254,33 +249,33 @@ impl<'a> LFunctionBuilder<'a> { }) } UInstruction::Ret { src } => { - self.map_subvar(src.id); + self.map_subvar(src); let src = if self .data - .size_of_var(self.program, src.id) + .size_of_var(self.program, src) .expect("unsized var") == 0 { None } else { - Some(src.id) + Some(src) }; self.data.instrs.push(LInstruction::Ret { src }) } - UInstruction::Construct { dst: dest, fields } => { - let sty = &self.program.expect_type(dest.id); - let Type::Struct(sty) = sty else { - panic!("bruh htis aint' a struct"); - }; - self.alloc_stack(dest.id)?; - for (field, var) in fields { - self.map_subvar(var.id); + UInstruction::Construct { + dst, + ref struc, + ref fields, + } => { + self.alloc_stack(dst)?; + for (field, &src) in fields { + self.map_subvar(src); let i = LInstruction::Mv { - dest: dest.id, - src: var.id, - dest_offset: self + dst, + src, + dst_offset: self .data - .field_offset(self.program, sty, field) + .field_offset(self.program, struc, field) .expect("field offset"), src_offset: 0, }; @@ -288,14 +283,11 @@ impl<'a> LFunctionBuilder<'a> { } } UInstruction::If { cond, body } => { - self.map_subvar(cond.id); + self.map_subvar(cond); let sym = self.builder.reserve(); - self.instrs.push(LInstruction::Branch { - to: *sym, - cond: cond.id, - }); + self.instrs.push(LInstruction::Branch { to: *sym, cond }); for i in body { - self.insert_instr(i); + self.insert_instr(&i); } self.instrs.push(LInstruction::Mark(*sym)); } @@ -374,9 +366,9 @@ impl LFunctionBuilderData<'_> { Some(VarOffset { id: var, offset }) } pub fn addr_size(&self) -> Size { - 64StructTy + 64 } - pub fn struct_inst(&mut self, p: &UProgram, ty: &LStructInst) -> &LStructInst { + pub fn struct_inst(&mut self, p: &UProgram, ty: &StructInst) -> &LStructInst { // normally I'd let Some(..) here and return, but polonius does not exist :grief: if self.struct_insts.get(ty).is_none() { let LStructInst { id, args } = ty; @@ -423,18 +415,20 @@ impl LFunctionBuilderData<'_> { self.struct_insts.get(ty).unwrap() } - pub fn field_offset(&mut self, p: &UProgram, sty: &LStructInst, field: &str) -> Option { + pub fn field_offset(&mut self, p: &UProgram, sty: &StructInst, field: &str) -> Option { let inst = self.struct_inst(p, sty); Some(inst.offset(field)?) } - pub fn size_of_type(&mut self, p: &UProgram, ty: &Type) -> Option { + pub fn size_of_type(&mut self, p: &UProgram, ty: &TypeID) -> Option { // TODO: target matters - Some(match p.follow_type(ty)? { + Some(match &p.types[ty] { Type::Bits(b) => *b, Type::Struct(ty) => self.struct_inst(p, ty).size, - Type::Generic { id } => return None, - Type::Fn { args, ret } => todo!(), + Type::Generic(id) => return None, + // function references are resolved at compile time into direct calls, + // so they don't have any size as arguments + Type::FnRef(fi) => 0, Type::Ref(_) => self.addr_size(), Type::Array(ty, len) => self.size_of_type(p, ty)? * len, Type::Slice(_) => self.addr_size() * 2, diff --git a/src/ir/lower/res.rs b/src/ir/lower/res.rs new file mode 100644 index 0000000..2794746 --- /dev/null +++ b/src/ir/lower/res.rs @@ -0,0 +1,92 @@ +use crate::ir::{ + arch::riscv64::{RV64Instruction, RegRef}, + AsmBlockArg, Resolved, UInstrInst, UInstruction, UProgram, VarID, +}; + +impl UInstrInst { + pub fn resolve<'a>(&'a self, p: &'a UProgram) -> Option> { + Some(UInstrInst { + i: self.i.resolve(p)?, + origin: self.origin, + }) + } +} + +impl UInstruction { + pub fn resolve<'a>(&'a self, p: &'a UProgram) -> Option> { + use UInstruction as I; + Some(match self { + I::Mv { dst, src } => I::Mv { + dst: dst.var(p)?, + src: src.var(p)?, + }, + I::Ref { dst, src } => I::Ref { + dst: dst.var(p)?, + src: src.var(p)?, + }, + I::Deref { dst, src } => I::Deref { + dst: dst.var(p)?, + src: src.var(p)?, + }, + I::LoadData { dst, src } => I::LoadData { + dst: dst.var(p)?, + src: *src, + }, + I::LoadSlice { dst, src } => I::LoadSlice { + dst: dst.var(p)?, + src: *src, + }, + I::Call { dst, f, args } => I::Call { + dst: dst.var(p)?, + f: f.fun(p)?.clone(), + args: args.iter().map(|i| i.var(p)).try_collect()?, + }, + I::AsmBlock { instructions, args } => I::AsmBlock { + instructions: instructions + .iter() + .map(|i| i.resolve(p)) + .collect::>()?, + args: args.iter().map(|a| a.resolve(p)).try_collect()?, + }, + I::Ret { src } => I::Ret { src: src.var(p)? }, + I::Construct { dst, struc, fields } => I::Construct { + dst: dst.var(p)?, + struc: struc.struc(p)?.clone(), + fields: fields + .iter() + .map(|(name, ident)| ident.var(p).map(|i| (name.clone(), i))) + .collect::>()?, + }, + I::If { cond, body } => I::If { + cond: cond.var(p)?, + body: body.iter().map(|i| i.resolve(p)).try_collect()?, + }, + I::Loop { body } => I::Loop { + body: body.iter().map(|i| i.resolve(p)).try_collect()?, + }, + I::Break => I::Break, + I::Continue => I::Continue, + }) + } +} + +impl AsmBlockArg { + pub fn resolve(&self, p: &UProgram) -> Option> { + Some(AsmBlockArg { + var: self.var.var(p)?, + reg: self.reg, + ty: self.ty, + }) + } +} + +impl RV64Instruction { + pub fn resolve(&self, p: &UProgram) -> Option> { + self.try_map(|i| { + Some(match i { + RegRef::Var(v) => RegRef::Var(v.var(p)?), + RegRef::Reg(r) => RegRef::Reg(*r), + }) + }) + } +} diff --git a/src/ir/lower/symbol.rs b/src/ir/lower/symbol.rs index 7d29d09..8bc282e 100644 --- a/src/ir/lower/symbol.rs +++ b/src/ir/lower/symbol.rs @@ -62,7 +62,7 @@ impl SymbolSpaceBuilder { pub fn with_entries(entries: &[FnID]) -> SymbolSpaceBuilder { let mut s = Self::new(); for e in entries { - s.func(e); + s.func(*e); } s } @@ -73,24 +73,24 @@ impl SymbolSpaceBuilder { let sym = self.reserve(); self.write_ro_data(sym, data.to_vec(), label) } - pub fn ro_data(&mut self, id: &DataID, data: &[u8], label: Option) -> Symbol { - match self.data_map.get(id) { + pub fn ro_data(&mut self, id: DataID, data: &[u8], label: Option<&str>) -> Symbol { + match self.data_map.get(&id) { Some(s) => *s, None => { let sym = self.reserve(); - self.data_map.insert(*id, *sym); - self.write_ro_data(sym, data.to_vec(), label) + self.data_map.insert(id, *sym); + self.write_ro_data(sym, data.to_vec(), label.map(|l| l.to_string())) } } } - pub fn func(&mut self, id: &FnID) -> Symbol { - match self.fn_map.get(id) { + pub fn func(&mut self, id: FnID) -> Symbol { + match self.fn_map.get(&id) { Some(s) => *s, None => { let wsym = self.reserve(); let sym = *wsym; - self.unwritten_fns.push((wsym, *id)); - self.fn_map.insert(*id, sym); + self.unwritten_fns.push((wsym, id)); + self.fn_map.insert(id, sym); sym } } diff --git a/src/ir/mod.rs b/src/ir/mod.rs index 2883df1..2a33319 100644 --- a/src/ir/mod.rs +++ b/src/ir/mod.rs @@ -1,3 +1,9 @@ +//! the IR is split into 2 layers: upper and lower +//! upper handles all of the main language features like types, +//! and the lower is a very concrete format that can be easily +//! translated to assembly and will probably also include +//! the majority of optimization, but not sure + mod upper; mod lower; mod id; @@ -7,3 +13,4 @@ pub mod arch; pub use upper::*; pub use lower::*; pub use id::*; + diff --git a/src/ir/upper/error.rs b/src/ir/upper/error.rs index 8388c80..73c797b 100644 --- a/src/ir/upper/error.rs +++ b/src/ir/upper/error.rs @@ -1,11 +1,25 @@ -use crate::{ - common::{CompilerMsg, CompilerOutput}, - ir::ID, +use crate::common::{CompilerMsg, CompilerOutput}; + +use super::{ + IdentStatus, KindTy, MemRes, MemberTy, Origin, Res, ResBase, StructID, Type, TypeID, UProgram }; -use super::{KindTy, Origin, Res, StructID, TypeID, UProgram}; - -pub fn report_errs(p: &UProgram, output: &mut CompilerOutput, errs: Vec) { +pub fn report_errs(p: &UProgram, output: &mut CompilerOutput, mut errs: Vec) { + for ident in &p.idents { + match &ident.status { + IdentStatus::Unres { path, base } => { + let mem = path.last().unwrap(); + errs.push(ResErr::UnknownMember { + ty: mem.ty, + name: mem.name.clone(), + origin: mem.origin, + parent: base.clone(), + }) + } + IdentStatus::Failed(err) => errs.push(err.clone()), + _ => (), + } + } for err in errs { match err { ResErr::Type { @@ -56,7 +70,7 @@ pub fn report_errs(p: &UProgram, output: &mut CompilerOutput, errs: Vec) origin, )); } - ResErr::UnknownField { origin, id, name } => { + ResErr::UnknownStructField { origin, id, name } => { output.err(CompilerMsg::new( format!("Unknown field '{name}' in struct '{}'", p.structs[id].name), origin, @@ -83,47 +97,48 @@ pub fn report_errs(p: &UProgram, output: &mut CompilerOutput, errs: Vec) found, expected, } => output.err(CompilerMsg::new( - { - let name = match &found { - Res::Fn(fty) => &p.fns[fty.id].name, - Res::Type(id) => &p.type_name(id), - Res::Var(id) => &p.vars[id].name, - Res::Struct(sty) => &p.structs[sty.id].name, - }; - format!( - "Expected {}, found {} '{}'", - expected.str(), - found.kind_str(), - name - ) - }, + format!("Expected {expected}, found {}", found.display_str(p)), origin, )), - ResErr::UnexpectedField { origin } => { - output.err(CompilerMsg::new(format!("Unexpected fields here"), origin)) - } + ResErr::UnknownMember { + origin, + ty, + name, + parent, + } => output.err(CompilerMsg::new( + format!("Unknown {ty} {name} of {}", parent.display_str(p)), + origin, + )), + } + } + for var in &p.vars { + match &p.types[var.ty] { + Type::Error => output.err(CompilerMsg::new( + format!("Var {:?} is error type!", var.name), + var.origin, + )), + Type::Infer => output.err(CompilerMsg::new( + format!("Type of {:?} cannot be inferred", var.name), + var.origin, + )), + _ => (), } } } -#[derive(Debug, Clone)] +#[derive(Clone)] pub enum ResErr { - UnknownModule { - origin: Origin, - name: String, - }, UnknownMember { origin: Origin, + ty: MemberTy, name: String, + parent: ResBase, }, KindMismatch { origin: Origin, expected: KindTy, found: Res, }, - UnexpectedField { - origin: Origin, - }, GenericCount { origin: Origin, expected: usize, @@ -153,7 +168,7 @@ pub enum ResErr { id: StructID, name: String, }, - UnknownField { + UnknownStructField { origin: Origin, id: StructID, name: String, diff --git a/src/ir/upper/instr.rs b/src/ir/upper/instr.rs index 79596fc..d27c95b 100644 --- a/src/ir/upper/instr.rs +++ b/src/ir/upper/instr.rs @@ -1,153 +1,71 @@ -use std::{collections::HashMap, fmt::Write}; +use std::collections::HashMap; -use super::{arch::riscv64::RV64Instruction, DataID, FnID, Origin, UFunc, UIdent, IdentID}; -use crate::{compiler::arch::riscv::Reg, util::Padder}; +use super::{arch::riscv64::RV64Instruction, DataID, IdentID, Origin, ResStage, Unresolved}; +use crate::compiler::arch::riscv::Reg; -#[derive(Clone)] -pub enum UInstruction { +pub enum UInstruction { Mv { - dst: IdentID, - src: IdentID, + dst: S::Var, + src: S::Var, }, Ref { - dst: IdentID, - src: IdentID, + dst: S::Var, + src: S::Var, }, Deref { - dst: IdentID, - src: IdentID, + dst: S::Var, + src: S::Var, }, LoadData { - dst: IdentID, + dst: S::Var, src: DataID, }, LoadSlice { - dst: IdentID, + dst: S::Var, src: DataID, }, - LoadFn { - dst: IdentID, - src: FnID, - }, Call { - dst: IdentID, - f: IdentID, - args: Vec, + dst: S::Var, + f: S::Func, + args: Vec, }, AsmBlock { - instructions: Vec, - args: Vec, + instructions: Vec>, + args: Vec>, }, Ret { - src: IdentID, + src: S::Var, }, Construct { - dst: IdentID, - struc: IdentID, - fields: HashMap, + dst: S::Var, + struc: S::Struct, + fields: HashMap, }, If { - cond: IdentID, - body: Vec, + cond: S::Var, + body: Vec>, }, Loop { - body: Vec, + body: Vec>, }, Break, Continue, } -#[derive(Clone)] -pub struct UInstrInst { - pub i: UInstruction, +pub struct UInstrInst { + pub i: UInstruction, pub origin: Origin, } -impl std::fmt::Debug for UInstrInst { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{:?}", self.i) - } -} - #[derive(Debug, Clone)] -pub struct AsmBlockArg { - pub var: IdentID, +pub struct AsmBlockArg { + pub var: V, pub reg: Reg, pub ty: AsmBlockArgType, } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Copy)] pub enum AsmBlockArgType { In, Out, } - -impl std::fmt::Debug for UInstruction { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - Self::Mv { dst: dest, src } => write!(f, "{dest:?} <- {src:?}")?, - Self::Ref { dst: dest, src } => write!(f, "{dest:?} <- {src:?}&")?, - Self::Deref { dst: dest, src } => write!(f, "{dest:?} <- {src:?}^")?, - Self::LoadData { dst: dest, src } => write!(f, "{dest:?} <- {src:?}")?, - Self::LoadFn { dst: dest, src } => write!(f, "{dest:?} <- {src:?}")?, - Self::LoadSlice { dst: dest, src } => write!(f, "{dest:?} <- &[{src:?}]")?, - Self::Call { - dst: dest, - f: func, - args, - } => write!(f, "{dest:?} <- {func:?}({args:?})")?, - Self::AsmBlock { args, instructions } => write!(f, "asm {args:?} {instructions:#?}")?, - Self::Ret { src } => f.debug_struct("Ret").field("src", src).finish()?, - Self::Construct { dst: dest, struc, fields } => write!(f, "{dest:?} <- {struc:?}{fields:?}")?, - Self::If { cond, body } => { - write!(f, "if {cond:?}:")?; - if !body.is_empty() { - f.write_str("{\n ")?; - let mut padder = Padder::new(f); - for i in body { - // they don't expose wrap_buf :grief: - padder.write_str(&format!("{i:?};\n"))?; - } - f.write_char('}')?; - } else { - f.write_str("{}")?; - } - } - Self::Loop { body } => { - write!(f, "loop:")?; - if !body.is_empty() { - f.write_str("{\n ")?; - let mut padder = Padder::new(f); - for i in body { - // they don't expose wrap_buf :grief: - padder.write_str(&format!("{i:?};\n"))?; - } - f.write_char('}')?; - } else { - f.write_str("{}")?; - } - } - Self::Break => write!(f, "break")?, - Self::Continue => write!(f, "continue")?, - } - Ok(()) - } -} - -impl std::fmt::Debug for UFunc { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{:?}", self.args)?; - if !self.instructions.is_empty() { - f.write_str("{\n ")?; - let mut padder = Padder::new(f); - for i in &self.instructions { - // they don't expose wrap_buf :grief: - padder.write_str(&format!("{i:?};\n"))?; - } - f.write_char('}')?; - } else { - f.write_str("{}")?; - } - Ok(()) - } -} diff --git a/src/ir/upper/kind.rs b/src/ir/upper/kind.rs index 6f1088d..6b55f79 100644 --- a/src/ir/upper/kind.rs +++ b/src/ir/upper/kind.rs @@ -1,9 +1,37 @@ -use super::{FnInst, ResErr, StructInst, Type, UInstrInst, UInstruction}; +//! all main IR Upper data structures stored in UProgram + +use super::{FnInst, ResErr, StructInst, Type, UInstrInst, UInstruction, UProgram}; use crate::{ common::FileSpan, ir::{Len, ID}, }; -use std::{collections::HashMap, fmt::Debug}; +use std::{ + collections::HashMap, + 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; @@ -16,7 +44,6 @@ pub type StructID = ID; pub type DataID = ID; pub type ModID = ID; -#[derive(Clone)] pub struct UFunc { pub name: String, pub origin: Origin, @@ -26,12 +53,12 @@ pub struct UFunc { pub instructions: Vec, } -#[derive(Clone)] pub struct StructField { pub ty: TypeID, + pub origin: Origin, + // pub vis: Visibility } -#[derive(Clone)] pub struct UStruct { pub name: String, pub origin: Origin, @@ -39,13 +66,11 @@ pub struct UStruct { pub gargs: Vec, } -#[derive(Clone)] pub struct UGeneric { pub name: String, pub origin: Origin, } -#[derive(Clone)] pub struct UVar { pub name: String, pub origin: Origin, @@ -54,43 +79,53 @@ pub struct UVar { pub children: HashMap, } -/// eg. a::b::c::.d.e -#[derive(Clone, Debug)] +/// 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, } -#[derive(Clone, Debug)] pub enum IdentStatus { - Var(VarID), - Struct(StructInst), - Fn(FnInst), - Type(TypeID), + Res(Res), Unres { - path: ModPath, - mem: MemberID, - gargs: Vec, - fields: Vec, + base: ResBase, + path: Vec, }, - PartialVar { - id: VarID, - fields: Vec, - }, - Failed(ResErr), + Failed(Option), Cooked, } -#[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub struct MemberID { +pub struct MemberIdent { + pub ty: MemberTy, pub name: String, + pub gargs: Vec, pub origin: Origin, } -#[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub struct ModPath { - pub id: ModID, - pub path: Vec, +#[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)] @@ -110,25 +145,163 @@ pub struct UData { pub struct UModule { pub name: String, pub members: HashMap, - pub children: HashMap, pub parent: Option, + pub func: FnID, } #[derive(Clone)] pub struct Member { - pub id: MemberTy, + pub id: MemberID, // pub visibility: Visibility } #[derive(Clone)] -pub enum MemberTy { +pub enum MemberID { Fn(FnID), Struct(StructID), Var(VarID), + Module(ModID), + Type(TypeDef), +} + +#[derive(Clone)] +pub struct TypeDef { + pub gargs: Vec, + pub ty: TypeID, +} + +impl MemberID { + pub fn kind(&self) -> KindTy { + match self { + MemberID::Fn(_) => KindTy::Fn, + MemberID::Struct(_) => KindTy::Struct, + MemberID::Var(_) => KindTy::Var, + MemberID::Module(_) => KindTy::Module, + MemberID::Type(_) => KindTy::Type, + } + } + pub fn display_str(&self, p: &UProgram) -> String { + let name = match self { + MemberID::Var(id) => &p.vars[id].name, + 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), + }; + format!("{} '{}'", self.kind(), name) + } } pub type Origin = FileSpan; +// "effective" (externally visible) kinds +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum KindTy { + Type, + Var, + Struct, + Fn, + Module, + Generic, +} + +impl Display for KindTy { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.write_str(match self { + KindTy::Type => "type", + KindTy::Var => "variable", + KindTy::Fn => "function", + KindTy::Struct => "struct", + KindTy::Module => "module", + KindTy::Generic => "generic", + }) + } +} + +#[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()) @@ -162,4 +335,3 @@ impl<'a> Iterator for InstrIter<'a> { Some(next) } } - diff --git a/src/ir/upper/program.rs b/src/ir/upper/program.rs index f7472b5..6ef5492 100644 --- a/src/ir/upper/program.rs +++ b/src/ir/upper/program.rs @@ -1,8 +1,5 @@ use super::*; -use std::{ - collections::HashMap, - ops::{Deref, DerefMut}, -}; +use std::collections::HashMap; pub struct UProgram { pub fns: Vec, @@ -23,12 +20,6 @@ pub struct TypeCache { pub error: TypeID, } -pub struct UModuleBuilder<'a> { - pub p: &'a mut UProgram, - pub module: ModID, - pub temp: usize, -} - impl UProgram { pub fn new() -> Self { let mut types = Vec::new(); @@ -66,7 +57,7 @@ impl UProgram { push_id(&mut self.types, t) } - pub fn def_var_inst(&mut self, i: UIdent) -> IdentID { + pub fn def_ident(&mut self, i: UIdent) -> IdentID { push_id(&mut self.idents, i) } @@ -82,10 +73,18 @@ impl UProgram { push_id(&mut self.structs, s) } + pub fn def_module(&mut self, m: UModule) -> ModID { + push_id(&mut self.modules, m) + } + 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)) + format!( + "{}{}", + self.structs[ty.id].name, + self.gparams_str(&ty.gargs) + ) } Type::FnRef(ty) => { format!( @@ -97,13 +96,13 @@ impl UProgram { } Type::Ref(t) => format!("{}&", self.type_name(t)), Type::Deref(t) => format!("{}^", self.type_name(t)), - Type::Unres(_) => "{unresolved}".to_string(), 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::Error => "{error}".to_string(), Type::Infer => "{inferred}".to_string(), + Type::Unres(_) => "{unresolved}".to_string(), Type::Generic(id) => self.generics[id].name.clone(), } } @@ -138,34 +137,6 @@ pub fn push_id(v: &mut Vec, t: T) -> ID { id } -impl<'a> UModuleBuilder<'a> { - pub fn new(program: &'a mut UProgram, id: ModID) -> Self { - Self { - p: program, - module: id, - temp: 0, - } - } - pub fn temp_var(&mut self, origin: Origin, ty: impl Typable) -> IdentID { - self.temp_var_inner(origin, ty) - } - fn temp_var_inner(&mut self, origin: Origin, ty: impl Typable) -> IdentID { - let var = UVar { - name: format!("temp{}", self.temp), - ty: ty.ty(self), - origin, - parent: None, - children: HashMap::new(), - }; - let id = self.p.def_var(var); - self.temp += 1; - self.def_var_inst(UIdent { - status: IdentStatus::Var(id), - origin, - }) - } -} - // I'm done with names... pub trait Typed { fn ty<'a>(&'a self, p: &'a UProgram) -> &'a Type; @@ -194,33 +165,3 @@ impl Typed for &Box { &**self } } - -pub trait Typable { - fn ty(self, p: &mut UProgram) -> TypeID; -} - -impl Typable for Type { - fn ty(self, p: &mut UProgram) -> TypeID { - p.def_ty(self) - } -} - -impl Typable for TypeID { - fn ty(self, p: &mut UProgram) -> TypeID { - self - } -} - -impl Deref for UModuleBuilder<'_> { - type Target = UProgram; - - fn deref(&self) -> &Self::Target { - self.p - } -} - -impl DerefMut for UModuleBuilder<'_> { - fn deref_mut(&mut self) -> &mut Self::Target { - self.p - } -} diff --git a/src/ir/upper/resolve.rs b/src/ir/upper/resolve.rs index 92fa051..13516e1 100644 --- a/src/ir/upper/resolve.rs +++ b/src/ir/upper/resolve.rs @@ -1,11 +1,9 @@ use super::{ - inst_fn_ty, inst_struct_ty, report_errs, ControlFlowOp, DataID, FnInst, IdentID, IdentStatus, - MemberTy, Origin, ResErr, StructInst, Type, TypeID, TypeMismatch, UData, UFunc, UGeneric, - UIdent, UInstrInst, UInstruction, UModule, UProgram, UStruct, UVar, VarID, + 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::{CompilerMsg, CompilerOutput}, - ir::{inst_fn_var, ID}, + common::CompilerOutput, + ir::{MemRes, Member}, }; use std::{ collections::HashSet, @@ -14,6 +12,7 @@ use std::{ }; // 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) { @@ -61,41 +60,8 @@ impl UProgram { resolve_instr(&mut data, ctx); } } - let mut errs = data.errs; - for ident in &self.idents { - match &ident.status { - IdentStatus::Unres { - path, - mem, - gargs, - fields, - } => errs.push(ResErr::UnknownModule { - origin: path.path[0].origin, - name: path.path[0].name.clone(), - }), - IdentStatus::PartialVar { id, fields } => todo!(), - IdentStatus::Failed(err) => errs.push(err.clone()), - _ => (), - } - } + let errs = data.errs; report_errs(self, output, errs); - for var in &self.vars { - match &self.types[var.ty] { - Type::Error => output.err(CompilerMsg::new( - format!("Var {:?} is error type!", var.name), - var.origin, - )), - Type::Infer => output.err(CompilerMsg::new( - format!("Type of {:?} cannot be inferred", var.name), - var.origin, - )), - Type::Unres(_) => output.err(CompilerMsg::new( - format!("Var {:?} type still unresolved!", var.name), - var.origin, - )), - _ => (), - } - } } } @@ -112,36 +78,37 @@ pub fn resolve_instr<'a>(data: &mut ResData<'a>, ctx: ResolveCtx<'a>) -> Option< 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); + for (src, &dest) in args.iter().zip(&f.args) { + res |= data.match_types::(dest, src, src); } - res |= data.match_types(dst, f.ret, dst); + res |= data.match_types::(dst, f.ret, dst); } UInstruction::Mv { dst, src } => { - res |= data.match_types(dst, src, src); + res |= data.match_types::(dst, src, src); } UInstruction::Ref { dst, src } => { - let dstid = data.res_ty_id::(dst, ctx)?; + 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); + res |= data.match_types::(dest_ty, src, src); } UInstruction::Deref { dst, src } => { - let srcid = data.res_ty_id::(src, ctx)?; + 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); + res |= data.match_types::(dst, src_ty, src); } UInstruction::LoadData { dst, src } => { - res |= data.match_types(dst, src, dst); + let srcid = src.type_id(&data.s); + res |= data.match_types::(dst, srcid, dst); } UInstruction::LoadSlice { dst, src } => { - let dstid = data.res_id(src, ctx)?; - let srcid = data.res_id(src, ctx)?; + 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() }; @@ -150,14 +117,11 @@ pub fn resolve_instr<'a>(data: &mut ResData<'a>, ctx: ResolveCtx<'a>) -> Option< }; res |= data.match_types(dstty, srcty, dst); } - UInstruction::LoadFn { dst, src } => { - // TODO: validate size with enabled targets - } UInstruction::AsmBlock { instructions, args } => { // TODO } UInstruction::Ret { src } => { - res |= data.match_types(ctx.ret, src, src); + res |= data.match_types::(ctx.ret, src, src); } UInstruction::Construct { dst, struc, fields } => { let si = data.res_id::(dst, ctx)?; @@ -167,7 +131,7 @@ pub fn resolve_instr<'a>(data: &mut ResData<'a>, ctx: ResolveCtx<'a>) -> Option< for (name, field) in &st.fields { if let Some(src) = fields.get(name) { used.insert(name); - res |= data.match_types(field.ty, src, src); + res |= data.match_types::(field.ty, src, src); } else { let origin = dst.origin(data); data.errs.push(ResErr::MissingField { @@ -180,7 +144,7 @@ pub fn resolve_instr<'a>(data: &mut ResData<'a>, ctx: ResolveCtx<'a>) -> Option< for (name, _) in fields { if !used.contains(name) { let origin = dst.origin(data); - data.errs.push(ResErr::UnknownField { + data.errs.push(ResErr::UnknownStructField { origin, id: sid, name: name.clone(), @@ -189,7 +153,7 @@ pub fn resolve_instr<'a>(data: &mut ResData<'a>, ctx: ResolveCtx<'a>) -> Option< } } UInstruction::If { cond, body } => { - if let Some(id) = data.res_ty_id::(cond, ctx) { + 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 }); @@ -247,24 +211,31 @@ fn compiler_error() -> ! { panic!("how could this happen to me (you)"); } -pub fn match_types(data: &mut TypeResData, dst: impl TypeIDed, src: impl TypeIDed) -> MatchRes { - let dst = data.res_id(dst); - let src = data.res_id(src); - if dst == src { +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, src }]); - match (data.types[dst].clone(), data.types[src].clone()) { + 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[dst] = 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[src] = x; + data.changed = true; + data.types[srcid] = x; + src.finish(&mut data.s, data.types); MatchRes::Finished } (Type::Struct(dest), Type::Struct(src)) => { @@ -300,18 +271,8 @@ pub fn match_types(data: &mut TypeResData, dst: impl TypeIDed, src: impl TypeIDe } } -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 - } -} - fn match_all( - data: &mut TypeResData, + data: &mut ResData, dst: impl Iterator, src: impl Iterator, ) -> MatchRes { @@ -353,30 +314,24 @@ struct ResData<'a> { errs: Vec, } -struct TypeResData<'a> { - changed: &'a mut bool, - types: &'a mut [Type], - sources: &'a Sources<'a>, -} - impl<'a> ResData<'a> { - pub fn match_types( + pub fn match_types( &mut self, - dst: impl Resolvable, - src: impl Resolvable, + dst: impl Resolvable, + src: impl Resolvable, origin: impl HasOrigin, - ) -> InstrRes { - let dst = dst.try_res(&mut self.s, self.types, &mut self.errs)?; - let src = src.try_res(&mut self.s, self.types, &mut self.errs)?; - let res = match_types( - &mut TypeResData { - changed: &mut self.changed, - types: self.types, - sources: &self.s, - }, - dst, - src, - ); + ) -> 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, @@ -392,17 +347,19 @@ 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) + x.try_res( + &mut self.s, + &mut self.types, + &mut self.errs, + &mut self.changed, + ) } - pub fn res_ty_id<'b: 'a, K: ResKind>( + pub fn res_var_ty<'b: 'a>( &mut self, - x: impl Resolvable, + x: impl Resolvable, ctx: ResolveCtx<'b>, - ) -> Option - where - K::Res: TypeIDed, - { - self.res_id::(x, ctx).map(|i| i.type_id(&self.s)) + ) -> Option { + self.res_id::(x, ctx).map(|i| i.type_id(&self.s)) } pub fn res_id<'b: 'a, K: ResKind>( &mut self, @@ -416,11 +373,22 @@ impl<'a> ResData<'a> { } None } -} -impl TypeResData<'_> { - pub fn res_id(&self, x: impl TypeIDed) -> TypeID { - resolve_refs(self.types, x.type_id(self.sources)) + 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 + }) } } @@ -436,6 +404,155 @@ pub enum InstrRes { 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 { @@ -457,134 +574,23 @@ trait Resolvable { s: &mut Sources, types: &mut Vec, errs: &mut Vec, + changed: &mut bool, ) -> Result; } -impl Resolvable for T { - fn try_res( - &self, - s: &mut Sources, - _: &mut Vec, - errs: &mut Vec, - ) -> Result { - Ok(self.type_id(s)) - } -} - -impl IdentID { - pub fn resolve(self, s: &mut Sources) -> Result { - let ident = &mut s.idents[self]; - Ok(match &mut ident.status { - IdentStatus::Var(id) => Res::Var(*id), - IdentStatus::Struct(sty) => Res::Struct(sty.clone()), - IdentStatus::Fn(fty) => Res::Fn(fty.clone()), - IdentStatus::Type(ty) => Res::Type(*ty), - IdentStatus::Unres { - path, - mem, - gargs, - fields, - } => { - let mut mid = path.id; - let mut count = 0; - for mem in &path.path { - let Some(&child) = s.modules[mid].children.get(&mem.name) else { - break; - }; - count += 1; - mid = child; - } - path.path.drain(0..count); - path.id = mid; - if path.path.len() != 0 { - return Err(InstrRes::Unfinished); - } - let Some(mem) = s.modules[mid].members.get(&mem.name) else { - return Err(InstrRes::Unfinished); - }; - match mem.id { - MemberTy::Fn(id) => { - if fields.len() > 0 { - ident.status = IdentStatus::Failed(ResErr::UnexpectedField { - origin: ident.origin, - }); - return Err(InstrRes::Finished); - } - let fty = FnInst { - id, - gargs: gargs.clone(), - }; - ident.status = IdentStatus::Fn(fty.clone()); - Res::Fn(fty) - } - MemberTy::Struct(id) => { - if fields.len() > 0 { - ident.status = IdentStatus::Failed(ResErr::UnexpectedField { - origin: ident.origin, - }); - return Err(InstrRes::Finished); - } - let sty = StructInst { - id, - gargs: gargs.clone(), - }; - ident.status = IdentStatus::Struct(sty.clone()); - Res::Struct(sty) - } - MemberTy::Var(id) => { - if !gargs.is_empty() { - ident.status = IdentStatus::Failed(ResErr::GenericCount { - origin: ident.origin, - expected: 0, - found: gargs.len(), - }); - return Err(InstrRes::Finished); - } - ident.status = IdentStatus::PartialVar { - id, - fields: fields.clone(), - }; - return self.resolve(s); - } - } - } - IdentStatus::PartialVar { id, fields } => { - let mut fiter = fields.iter(); - let mut next = fiter.next(); - let mut count = 0; - while let Some(mem) = next - && let Some(&cid) = s.vars[*id].children.get(&mem.name) - { - *id = cid; - next = fiter.next(); - count += 1; - } - fields.drain(0..count); - if fields.len() != 0 { - return Err(InstrRes::Unfinished); - } - let id = *id; - ident.status = IdentStatus::Var(id); - Res::Var(id) - } - IdentStatus::Cooked => return Err(InstrRes::Finished), - IdentStatus::Failed(_) => return Err(InstrRes::Finished), - }) - } -} - 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)?; - match K::from_res(res.clone(), types, s, origin, errs) { - Some(res) => Ok(res), - None => { + 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(), @@ -596,45 +602,39 @@ impl Resolvable for IdentID { } } -// "effective" (externally visible) kinds -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub enum KindTy { - Type, - Var, - Struct, - Fn, -} - -impl KindTy { - pub fn str(&self) -> &'static str { - match self { - KindTy::Type => "type", - KindTy::Var => "variable", - KindTy::Fn => "function", - KindTy::Struct => "struct", - } +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) } } -#[derive(Debug, Clone)] -pub enum Res { - Var(VarID), - Fn(FnInst), - Struct(StructInst), - Type(TypeID), +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 Res { - pub fn kind(&self) -> KindTy { - match self { - Res::Var(..) => KindTy::Var, - Res::Fn(..) => KindTy::Fn, - Res::Struct(..) => KindTy::Struct, - Res::Type(..) => KindTy::Type, - } - } - pub fn kind_str(&self) -> &'static str { - self.kind().str() +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) } } @@ -646,8 +646,7 @@ pub trait ResKind { types: &mut Vec, s: &mut Sources, origin: Origin, - errs: &mut Vec, - ) -> Option; + ) -> Result; } impl ResKind for UFunc { @@ -655,16 +654,10 @@ impl ResKind for UFunc { fn ty() -> KindTy { KindTy::Fn } - fn from_res( - res: Res, - _: &mut Vec, - _: &mut Sources, - _: Origin, - _: &mut Vec, - ) -> Option { + fn from_res(res: Res, _: &mut Vec, _: &mut Sources, _: Origin) -> Result { match res { - Res::Fn(fi) => Some(fi), - _ => None, + Res::Fn(fi) => Ok(fi), + _ => Err(res), } } } @@ -679,13 +672,11 @@ impl ResKind for UVar { types: &mut Vec, s: &mut Sources, origin: Origin, - errs: &mut Vec, - ) -> Option { - Some(match res { - Res::Fn(fty) => inst_fn_var(&fty, s.fns, origin, s.vars, types, s.generics, errs), + ) -> Result { + Ok(match res { + Res::Fn(fty) => inst_fn_var(fty, s.fns, origin, s.vars, types), Res::Var(id) => id, - Res::Struct(_) => return None, - Res::Type(_) => return None, + _ => return Err(res), }) } } @@ -695,16 +686,10 @@ impl ResKind for UStruct { fn ty() -> KindTy { KindTy::Struct } - fn from_res( - res: Res, - _: &mut Vec, - _: &mut Sources, - _: Origin, - _: &mut Vec, - ) -> Option { + fn from_res(res: Res, _: &mut Vec, _: &mut Sources, _: Origin) -> Result { match res { - Res::Struct(si) => Some(si), - _ => None, + Res::Struct(si) => Ok(si), + _ => Err(res), } } } @@ -719,30 +704,18 @@ impl ResKind for Type { types: &mut Vec, s: &mut Sources, _: Origin, - errs: &mut Vec, - ) -> Option { - Some(match res { - Res::Fn(fty) => inst_fn_ty(&fty, s.fns, types, s.generics, errs), - Res::Var(id) => id.type_id(s), - Res::Struct(si) => inst_struct_ty(&si, s.structs, types, s.generics, errs), + ) -> Result { + Ok(match res { + Res::Struct(si) => push_id(types, Type::Struct(si)), Res::Type(id) => id, + _ => return Err(res), }) } } -impl Resolvable for &IdentID { - fn try_res( - &self, - s: &mut Sources, - types: &mut Vec, - errs: &mut Vec, - ) -> Result { - Resolvable::::try_res(*self, s, types, errs) - } -} - pub trait TypeIDed { fn type_id(&self, s: &Sources) -> TypeID; + fn finish(&self, s: &mut Sources, types: &mut Vec) {} } impl TypeIDed for TypeID { @@ -755,6 +728,9 @@ 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 { @@ -769,8 +745,8 @@ impl TypeIDed for &T { } } -impl FromResidual> for InstrRes { - fn from_residual(residual: std::result::Result) -> Self { +impl FromResidual> for InstrRes { + fn from_residual(residual: Result) -> Self { match residual { Ok(_) => unreachable!(), Err(r) => r, @@ -787,3 +763,11 @@ impl HasOrigin for &IdentID { 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/ty.rs b/src/ir/upper/ty.rs index 66c75a4..2efcdd6 100644 --- a/src/ir/upper/ty.rs +++ b/src/ir/upper/ty.rs @@ -1,8 +1,8 @@ use std::collections::HashMap; use super::{ - push_id, FnID, GenericID, Len, ModPath, Origin, ResErr, StructID, TypeID, UFunc, UGeneric, - UProgram, UStruct, UVar, VarID, + push_id, FnID, GenericID, IdentID, Len, Origin, ResErr, StructID, TypeDef, TypeID, UFunc, + UGeneric, UProgram, UStruct, UVar, VarID, }; #[derive(Debug, Clone, Hash, Eq, PartialEq)] @@ -14,12 +14,14 @@ pub struct FieldRef { #[derive(Debug, Clone, Eq, PartialEq, Hash)] pub struct StructInst { pub id: StructID, + /// assumed to be valid pub gargs: Vec, } #[derive(Debug, Clone, Eq, PartialEq, Hash)] pub struct FnInst { pub id: FnID, + /// assumed to be valid pub gargs: Vec, } @@ -36,7 +38,7 @@ pub enum Type { Array(TypeID, Len), Unit, // "fake" types - Unres(ModPath), + Unres(IdentID), Generic(GenericID), Infer, Error, @@ -79,16 +81,14 @@ impl Type { } pub fn inst_fn_var( - fi: &FnInst, + fi: FnInst, fns: &[UFunc], origin: Origin, vars: &mut Vec, types: &mut Vec, - generics: &[UGeneric], - errs: &mut Vec, ) -> VarID { - let ty = inst_fn_ty(fi, fns, types, generics, errs); let name = fns[fi.id].name.clone(); + let ty = push_id(types, Type::FnRef(fi)); push_id( vars, UVar { @@ -101,33 +101,16 @@ pub fn inst_fn_var( ) } -pub fn inst_fn_ty( - fi: &FnInst, - fns: &[UFunc], - types: &mut Vec, - generics: &[UGeneric], - errs: &mut Vec, -) -> TypeID { - let f = &fns[fi.id]; - let ty = Type::FnRef(FnInst { - id: fi.id, - gargs: inst_generics(&f.gargs, &fi.gargs, types, generics, errs), - }); - push_id(types, ty) -} - pub fn inst_struct_var( - si: &StructInst, + si: StructInst, structs: &[UStruct], origin: Origin, vars: &mut Vec, types: &mut Vec, - generics: &[UGeneric], - errs: &mut Vec, ) -> VarID { - let ty = inst_struct_ty(si, structs, types, generics, errs); let name = structs[si.id].name.clone(); - push_id( + let ty = push_id(types, Type::Struct(si)); + let id = push_id( vars, UVar { name, @@ -136,68 +119,91 @@ pub fn inst_struct_var( parent: None, children: HashMap::new(), }, - ) + ); + inst_var(vars, structs, id, types); + id } -pub fn inst_struct_ty( - si: &StructInst, - structs: &[UStruct], - types: &mut Vec, +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; + } + _ => (), + } +} + +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, -) -> TypeID { - let s = &structs[si.id]; - let ty = Type::Struct(StructInst { - id: si.id, - gargs: inst_generics(&s.gargs, &si.gargs, types, generics, errs), - }); - push_id(types, ty) + 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(()) } -pub fn inst_generics( - source: &[GenericID], - args: &[TypeID], - types: &mut Vec, - // will be needed when constraints are added - _generics: &[UGeneric], - errs: &mut Vec, -) -> Vec { - if source.len() != args.len() { - // don't want unequal lengths to be inferred further - return source.iter().map(|_| push_id(types, Type::Error)).collect(); - } - let mut gargs = Vec::new(); +/// 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 in source { - let id = push_id(types, Type::Error); - gmap.insert(gid, id); - gargs.push(id); + for (&gid, &tid) in dst.iter().zip(src) { + gmap.insert(gid, tid); } - for (gid, &ty) in source.iter().zip(args) { - inst_type_ins( - |types, ty| { - let id = gmap[gid]; - types[id] = ty; - id - }, - ty, - types, - &gmap, - ); - } - gargs + gmap } pub fn inst_type(id: TypeID, types: &mut Vec, gmap: &HashMap) -> TypeID { - inst_type_ins(push_id, id, types, gmap) -} - -pub fn inst_type_ins( - insert: impl Fn(&mut Vec, Type) -> TypeID, - 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 { @@ -226,5 +232,7 @@ pub fn inst_type_ins( Type::Infer => Type::Infer, Type::Error => Type::Error, }; - insert(types, ty) + push_id(types, ty) } + +// type Test = (&T, &U) diff --git a/src/main.rs b/src/main.rs index f32ad8a..440a16d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,6 +2,7 @@ #![feature(try_trait_v2)] #![feature(trait_alias)] #![feature(let_chains)] +#![feature(iterator_try_collect)] // dawg what #![feature(str_as_str)] diff --git a/src/parser/v3/lower/arch/riscv64.rs b/src/parser/v3/lower/arch/riscv64.rs index e9ead4b..caadad5 100644 --- a/src/parser/v3/lower/arch/riscv64.rs +++ b/src/parser/v3/lower/arch/riscv64.rs @@ -12,7 +12,7 @@ impl RV64Instruction { let args = &inst.args[..]; let opstr = &**inst.op.inner.as_ref()?; // TODO: surely this can be abstracted... - let opi = |ctx: &mut FnLowerCtx<'_>, op: Funct3| -> Option { + let opi = |ctx: &mut FnLowerCtx<'_, '_, '_>, op: Funct3| -> Option { let [dest, src, imm] = args else { ctx.err(format!("{opstr} requires 3 arguments")); return None; @@ -22,7 +22,7 @@ impl RV64Instruction { let imm = i32_from_arg(imm, ctx)?; Some(Self::OpImm { op, dest, src, imm }) }; - let op = |ctx: &mut FnLowerCtx<'_>, op: Funct3, funct: Funct7| -> Option { + let op = |ctx: &mut FnLowerCtx<'_, '_, '_>, op: Funct3, funct: Funct7| -> Option { let [dest, src1, src2] = args else { ctx.err(format!("{opstr} requires 3 arguments")); return None; @@ -174,7 +174,7 @@ pub fn arg_to_var(node: &Node, ctx: &mut FnLowerCtx) -> Option ); return None; }; - ctx.var(node) + ctx.ident(node) } impl RegRef { @@ -184,7 +184,7 @@ impl RegRef { let reg = Reg::from_ident(node, ctx)?; Self::Reg(reg) } - PAsmArg::Ref(node) => Self::Var(ctx.var(node)?), + PAsmArg::Ref(node) => Self::Var(ctx.ident(node)?), }) } } diff --git a/src/parser/v3/lower/asm.rs b/src/parser/v3/lower/asm.rs index be8b14c..68f8f7f 100644 --- a/src/parser/v3/lower/asm.rs +++ b/src/parser/v3/lower/asm.rs @@ -1,8 +1,7 @@ use crate::{ compiler::arch::riscv::Reg, ir::{ - arch::riscv64::RV64Instruction, AsmBlockArg, AsmBlockArgType, Type, UInstruction, UIdent, - IdentID, + arch::riscv64::RV64Instruction, AsmBlockArg, AsmBlockArgType, IdentID, Type, UInstruction, }, parser::PAsmBlockArg, }; diff --git a/src/parser/v3/lower/block.rs b/src/parser/v3/lower/block.rs index d8a875f..cd8be59 100644 --- a/src/parser/v3/lower/block.rs +++ b/src/parser/v3/lower/block.rs @@ -1,5 +1,5 @@ use crate::{ - ir::{Type, UInstruction, UVar, UIdent, IdentID}, + ir::{IdentID, Type, UIdent, UInstruction, UVar}, parser::{PConstStatement, PStatementLike}, }; @@ -8,6 +8,7 @@ use super::{FnLowerCtx, FnLowerable, Import, PBlock, PStatement}; impl FnLowerable for PBlock { type Output = IdentID; fn lower(&self, ctx: &mut FnLowerCtx) -> Option { + ctx.ident_stack.push(); let mut last = None; let mut statements = Vec::new(); let mut fn_nodes = Vec::new(); @@ -27,64 +28,58 @@ impl FnLowerable for PBlock { }, } } - ctx.b.push(); // then lower imports for i_n in &import_nodes { if let Some(i) = i_n.as_ref() { let name = &i.0; - let path = ctx.b.path_for(name); + let path = ctx.path_for(name); let import = Import(path.clone()); if ctx.imports.insert(import) { - ctx.b.def_searchable::( - name, - Some(UVar { - ty: Type::Module(path), - }), - i_n.origin, - ); + ctx.def_var(UVar { + name: name.clone(), + ty: Type::Module(path), + origin: i_n.origin, + }); } } } // then lower const things let mut structs = Vec::new(); for s in &struct_nodes { - structs.push(s.lower_name(ctx.b)); + structs.push(s.lower(ctx.ctx)); } for (s, id) in struct_nodes.iter().zip(structs) { if let Some(id) = id { - s.lower(id, ctx.b, ctx.output); + s.lower(ctx.ctx); } } let mut fns = Vec::new(); for f in &fn_nodes { - fns.push(f.lower_name(ctx.b)); + fns.push(f.lower(ctx.ctx)); } for (f, id) in fn_nodes.iter().zip(fns) { if let Some(id) = id { - f.lower(id, ctx.b, ctx.imports, ctx.output) + f.lower(ctx.ctx); } } // then lower statements for s in statements { last = s.lower(ctx); } - ctx.b.pop(); + ctx.ident_stack.pop(); last } } impl FnLowerable for PStatement { - type Output = UIdent; - fn lower(&self, ctx: &mut FnLowerCtx) -> Option { + type Output = IdentID; + fn lower(&self, ctx: &mut FnLowerCtx) -> Option { match self { PStatement::Let(def, e) => { - let def = def.lower(ctx.b, ctx.output)?; + let def = def.lower(ctx.ctx)?; let res = e.lower(ctx); if let Some(res) = res { - ctx.push(UInstruction::Mv { - dst: def, - src: res, - }); + ctx.push(UInstruction::Mv { dst: def, src: res }); } None } diff --git a/src/parser/v3/lower/def.rs b/src/parser/v3/lower/def.rs index 2293f3e..efe0bd0 100644 --- a/src/parser/v3/lower/def.rs +++ b/src/parser/v3/lower/def.rs @@ -1,18 +1,23 @@ -use crate::ir::{UProgram, UVar, VarID, UIdent}; +use std::collections::HashMap; -use super::{CompilerOutput, Node, PVarDef}; +use crate::ir::{UVar, VarID}; + +use super::{ModuleLowerCtx, Node, PVarDef}; impl Node { - pub fn lower(&self, program: &mut UProgram, output: &mut CompilerOutput) -> Option { + pub fn lower(&self, ctx: &mut ModuleLowerCtx) -> Option { let s = self.as_ref()?; - let name = s.name.as_ref().map_or("{error}", |v| v); + let name = s.name.as_ref().map_or("{error}", |v| v).to_string(); let ty = match &s.ty { - Some(ty) => ty.lower(program, output), - None => program.infer(self.origin), + Some(ty) => ty.lower(ctx), + None => ctx.infer(), }; - Some(UIdent { - id: program.def_searchable(name, Some(UVar { ty }), self.origin), + Some(ctx.def_var(UVar { + name, + ty, origin: self.origin, - }) + parent: None, + children: HashMap::new(), + })) } } diff --git a/src/parser/v3/lower/expr.rs b/src/parser/v3/lower/expr.rs index ac506a8..3b0dc19 100644 --- a/src/parser/v3/lower/expr.rs +++ b/src/parser/v3/lower/expr.rs @@ -1,6 +1,6 @@ use super::{func::FnLowerCtx, FnLowerable, PExpr, PostfixOp}; use crate::{ - ir::{Type, UData, UInstruction, UIdent, IdentID}, + ir::{IdentID, IdentStatus, MemRes, Member, MemberID, MemberIdent, Type, UData, UInstruction}, parser::InfixOp, }; @@ -9,22 +9,49 @@ impl FnLowerable for PExpr { fn lower(&self, ctx: &mut FnLowerCtx) -> Option { let mut e = self; let mut path = Vec::new(); - while let PExpr::Member(node, ident) = e { - e = if let Some(t) = node.as_ref() { - path.push(ident); - &**t - } else { - return None; - }; + let mut gargs = None; + loop { + match e { + PExpr::Member(node, ty, ident) => { + e = if let Some(t) = node.as_ref() { + ctx.origin = node.origin; + path.push((ty, ident, gargs.unwrap_or_default())); + &**t + } else { + return None; + }; + } + PExpr::Generic(node, nodes) => match gargs { + None => gargs = Some(nodes.iter().map(|t| t.lower(ctx)).collect::>()), + Some(_) => { + // this should cover the more specific area of ::<...> + // but too lazy rn + ctx.err("Cannot specify generics here".to_string()); + return None; + } + }, + _ => break, + } } + while let PExpr::Member(node, ty, ident) = e {} + if path.len() > 0 { + // UIdent { + // origin: ctx.origin, + // status: IdentStatus::Unres { base: (), path: () }, + // } + } + let origin = ctx.origin; Some(match e { PExpr::Lit(l) => match l { super::PLiteral::String(s) => { - let dst = ctx.temp_var(ctx.origin, Type::Bits(8).slice(ctx.p)); + let sty = Type::Bits(8).slice(ctx.p); + let dst = ctx.temp_var(origin, sty); let data = s.as_bytes().to_vec(); + let dty = Type::Bits(8).arr(ctx.ctx.p, data.len() as u32); + let dty = ctx.def_ty(dty); let src = ctx.def_data(UData { name: format!("string \"{}\"", s.replace("\n", "\\n")), - ty: ctx.def_ty(Type::Bits(8).arr(ctx.b.p, data.len() as u32)), + ty: dty, content: data, }); ctx.push(UInstruction::LoadSlice { dst, src }); @@ -32,7 +59,7 @@ impl FnLowerable for PExpr { } super::PLiteral::Char(c) => { let ty = ctx.def_ty(Type::Bits(8)); - let dst = ctx.temp_var(ctx.origin, ty.clone()); + let dst = ctx.temp_var(origin, ty.clone()); let src = ctx.def_data(UData { name: format!("char '{c}'"), ty, @@ -44,7 +71,7 @@ impl FnLowerable for PExpr { super::PLiteral::Number(n) => { // TODO: temp let ty = ctx.def_ty(Type::Bits(64)); - let dst = ctx.temp_var(ctx.origin, ty.clone()); + let dst = ctx.temp_var(origin, ty.clone()); let src = ctx.def_data(UData { name: format!("num {n:?}"), ty, @@ -53,9 +80,9 @@ impl FnLowerable for PExpr { ctx.push(UInstruction::LoadData { dst, src }); dst } - super::PLiteral::Unit => ctx.temp_var(ctx.origin, Type::Unit), + super::PLiteral::Unit => ctx.temp_var(origin, Type::Unit), }, - PExpr::Ident(i) => ctx.var(i), + PExpr::Ident(i) => ctx.ident(i), PExpr::BinaryOp(op, e1, e2) => match op { InfixOp::Add => todo!(), InfixOp::Sub => todo!(), @@ -77,7 +104,7 @@ impl FnLowerable for PExpr { let res = e.lower(ctx)?; match op { PostfixOp::Ref => { - let ty = Type::Ref(ctx.b.infer()); + let ty = Type::Ref(ctx.ctx.infer()); let dest = ctx.temp(ty); ctx.push(UInstruction::Ref { dst: dest, @@ -86,7 +113,7 @@ impl FnLowerable for PExpr { dest } PostfixOp::Deref => { - let ty = Type::Deref(ctx.b.infer()); + let ty = Type::Deref(ctx.ctx.infer()); let dst = ctx.temp(ty); ctx.push(UInstruction::Deref { dst, src: res }); dst @@ -121,20 +148,20 @@ impl FnLowerable for PExpr { } PExpr::If(cond, body) => { let cond = cond.lower(ctx)?; - ctx.var_stack.push(); + ctx.ident_stack.push(); let mut body_ctx = ctx.branch(); body.lower(&mut body_ctx); let body = body_ctx.instructions; - ctx.var_stack.pop(); + ctx.ident_stack.pop(); ctx.push(UInstruction::If { cond, body }); return None; } PExpr::Loop(body) => { - ctx.var_stack.push(); + ctx.ident_stack.push(); let mut body_ctx = ctx.branch(); body.lower(&mut body_ctx); let body = body_ctx.instructions; - ctx.var_stack.pop(); + ctx.ident_stack.pop(); ctx.push(UInstruction::Loop { body }); return None; } @@ -146,13 +173,34 @@ impl FnLowerable for PExpr { ctx.push(UInstruction::Continue); return None; } - PExpr::Member(e, name) => { - ctx.err("Can't access a member here".to_string()); + PExpr::Member(e, ty, name) => { + let id = e.lower(ctx)?; + let name_str = name.as_ref()?.0; + let cur = &mut ctx.p.idents[id]; + match cur.status { + IdentStatus::Res(res) => { + cur.status = IdentStatus::Unres { + base: MemRes { + mem: Member { + id: MemberID + }, + origin: (), + gargs: (), + }, + path: (), + } + } + IdentStatus::Unres { base, path } => path.push(MemberIdent { + ty: *ty, + name: name_str, + origin: name.origin, + gargs: Vec::new(), + }), + IdentStatus::Failed(res_err) => return None, + IdentStatus::Cooked => return None, + } return None; } - PExpr::Field(e, name) => { - todo!() - } }) } } diff --git a/src/parser/v3/lower/func.rs b/src/parser/v3/lower/func.rs index 75efa99..1b14b67 100644 --- a/src/parser/v3/lower/func.rs +++ b/src/parser/v3/lower/func.rs @@ -1,74 +1,68 @@ -use std::{ - collections::HashMap, - ops::{Deref, DerefMut}, -}; +use std::ops::{Deref, DerefMut}; -use super::{CompilerMsg, CompilerOutput, FileSpan, FnLowerable, Imports, Node, PFunction}; +use super::{CompilerMsg, FileSpan, ModuleLowerCtx, Node, PFunction, Typable}; use crate::{ ir::{ - FnID, GenericID, ModPath, Origin, Typable, Type, UFunc, UInstrInst, UInstruction, - UModuleBuilder, VarID, UIdent, IdentID, IdentStatus, + FnID, IdentID, IdentStatus, MemRes, Member, MemberID, MemberIdent, MemberPath, MemberTy, + Origin, Res, Type, UFunc, UIdent, UInstrInst, UInstruction, }, parser, - util::NameStack, }; impl Node { - pub fn lower( - &self, - b: &mut UModuleBuilder, - imports: &mut Imports, - output: &mut CompilerOutput, - ) -> Option { - self.as_ref() - .map(|s| s.lower(b, imports, output, self.origin)) - .flatten() + pub fn lower(&self, ctx: &mut ModuleLowerCtx) -> Option { + self.as_ref().map(|s| s.lower(ctx, self.origin)).flatten() } } impl PFunction { - pub fn lower( - &self, - b: &mut UModuleBuilder, - imports: &mut Imports, - output: &mut CompilerOutput, - origin: Origin, - ) -> Option { + pub fn lower(&self, ctx: &mut ModuleLowerCtx, origin: Origin) -> Option { let header = self.header.as_ref()?; let name = header.name.as_ref()?.0.clone(); let (generics, args, ret) = if let Some(header) = self.header.as_ref() { ( - header.gargs.iter().flat_map(|a| a.lower(b)).collect(), + header + .gargs + .iter() + .flat_map(|a| a.lower(ctx).map(|g| (g.0, g.1, a.origin))) + .collect(), header .args .iter() - .flat_map(|a| Some(a.lower(b, output)?)) + .flat_map(|a| Some(a.lower(ctx)?)) .collect(), match &header.ret { - Some(ty) => ty.lower(b, output), - None => b.def_ty(Type::Unit), + Some(ty) => ty.lower(ctx), + None => ctx.def_ty(Type::Unit), }, ) } else { - (Vec::new(), Vec::new(), b.tc.error) + (Vec::new(), Vec::new(), ctx.tc.error) }; let gargs = generics.iter().map(|g| g.1).collect(); - let generics = generics.into_iter().collect(); + let generics = generics + .into_iter() + .map(|g| { + ( + g.0, + ctx.def_ident(UIdent { + status: IdentStatus::Res(Res::Generic(g.1)), + origin: g.2, + }), + ) + }) + .collect::>(); + ctx.ident_stack.extend(generics.into_iter()); let instructions = { - let mut var_stack = NameStack::new(); - let mut ctx = FnLowerCtx { + let mut fctx = FnLowerCtx { instructions: Vec::new(), - var_stack: &mut var_stack, - b, - output, + ctx, origin: self.body.origin, - generics: &generics, - imports, }; - let res = self.body.lower(&mut ctx); - let mut instructions = ctx.instructions; + let res = self.body.lower(&mut fctx); + let mut instructions = fctx.instructions; if let Some(src) = res { - let origin = b.idents[src].origin; + let origin = ctx.idents[src].origin; instructions.push(UInstrInst { origin, i: UInstruction::Ret { src }, @@ -84,35 +78,37 @@ impl PFunction { ret, instructions, }; - Some(b.def_fn(f)) + Some(ctx.def_fn(f)) } } pub struct FnLowerCtx<'a, 'b> { - pub b: &'a mut UModuleBuilder<'b>, + pub ctx: &'a mut ModuleLowerCtx<'b>, pub instructions: Vec, - pub output: &'a mut CompilerOutput, pub origin: FileSpan, - pub imports: &'a mut Imports, - pub var_stack: &'a mut NameStack, - pub generics: &'a HashMap, } impl<'a, 'b> FnLowerCtx<'a, 'b> { - pub fn var(&mut self, node: &Node) -> IdentID { + pub fn ident(&mut self, node: &Node) -> IdentID { let inst = UIdent { status: if let Some(n) = node.as_ref() { - if let Some(&var) = self.var_stack.search(&n.0) { - IdentStatus::Var(var) + if let Some(&res) = self.ident_stack.search(&n.0) { + return res; } else { IdentStatus::Unres { - path: ModPath { - id: self.b.module, - path: Vec::new(), + path: vec![MemberIdent { + ty: MemberTy::Member, + name: n.0.clone(), + origin: node.origin, + gargs: Vec::new(), + }], + base: MemRes { + mem: Member { + id: MemberID::Module(self.module), + }, + origin: self.origin, + gargs: Vec::new(), }, - name: n.0.clone(), - gargs: Vec::new(), - fields: Vec::new(), } } } else { @@ -120,16 +116,17 @@ impl<'a, 'b> FnLowerCtx<'a, 'b> { }, origin: node.origin, }; - self.def_var_inst(inst) + self.def_ident(inst) } pub fn err(&mut self, msg: String) { - self.output.err(CompilerMsg::new(msg, self.origin)) + let origin = self.origin; + self.output.err(CompilerMsg::new(msg, origin)) } pub fn err_at(&mut self, span: FileSpan, msg: String) { self.output.err(CompilerMsg::new(msg, span)) } pub fn temp(&mut self, ty: T) -> IdentID { - self.b.temp_var(self.origin, ty) + self.ctx.temp_var(self.origin, ty) } pub fn push(&mut self, i: UInstruction) { self.push_at(i, self.origin); @@ -139,27 +136,46 @@ impl<'a, 'b> FnLowerCtx<'a, 'b> { } pub fn branch<'c>(&'c mut self) -> FnLowerCtx<'c, 'b> { FnLowerCtx { - b: self.b, + ctx: self.ctx, instructions: Vec::new(), - generics: self.generics, - var_stack: self.var_stack, - output: self.output, origin: self.origin, - imports: self.imports, } } } impl<'b> Deref for FnLowerCtx<'_, 'b> { - type Target = UModuleBuilder<'b>; + type Target = ModuleLowerCtx<'b>; fn deref(&self) -> &Self::Target { - self.b + self.ctx } } impl DerefMut for FnLowerCtx<'_, '_> { fn deref_mut(&mut self) -> &mut Self::Target { - self.b + self.ctx + } +} + +pub trait FnLowerable { + type Output; + fn lower(&self, ctx: &mut FnLowerCtx) -> Option; +} + +impl FnLowerable for Node { + type Output = T::Output; + fn lower(&self, ctx: &mut FnLowerCtx) -> Option { + let old_span = ctx.origin; + ctx.origin = self.origin; + let res = self.as_ref()?.lower(ctx); + ctx.origin = old_span; + res + } +} + +impl FnLowerable for Box { + type Output = T::Output; + fn lower(&self, ctx: &mut FnLowerCtx) -> Option { + self.as_ref().lower(ctx) } } diff --git a/src/parser/v3/lower/mod.rs b/src/parser/v3/lower/mod.rs index 98b65b8..6eb1e90 100644 --- a/src/parser/v3/lower/mod.rs +++ b/src/parser/v3/lower/mod.rs @@ -4,65 +4,131 @@ mod block; mod def; mod expr; mod func; +mod map; mod struc; mod ty; -mod map; + +use std::{ + collections::HashMap, + ops::{Deref, DerefMut}, +}; use super::*; -use crate::ir::{Type, UFunc, UModuleBuilder}; +use crate::{ + ir::{ + IdentID, IdentStatus, ModID, Origin, Res, Type, TypeID, UFunc, UIdent, UModule, UProgram, + UVar, + }, + util::NameStack, +}; +pub use func::{FnLowerCtx, FnLowerable}; impl PModule { pub fn lower( &self, path: Vec, - p: &mut UModuleBuilder, + p: &mut UProgram, imports: &mut Imports, output: &mut CompilerOutput, - ) { + ) -> ModID { let name = path.last().unwrap().clone(); - p.set_module(path); - let fid = p.def_searchable(&name, None, self.block.origin); - p.push_name(&name); - let mut fctx = FnLowerCtx { - b: p, + let f = UFunc { + name: name.clone(), + args: Vec::new(), + instructions: Vec::new(), + gargs: Vec::new(), + ret: p.def_ty(Type::Unit), + origin: self.block.origin, + }; + let fid = p.def_fn(f); + let mid = p.def_module(UModule { + name, + members: HashMap::new(), + parent: None, + func: fid, + }); + let mut ctx = ModuleLowerCtx { + p, + output, + module: mid, + temp: 0, + ident_stack: NameStack::new(), + }; + let mut fctx = FnLowerCtx { + ctx: &mut ctx, instructions: Vec::new(), - output, origin: self.block.origin, - imports, }; self.block.lower(&mut fctx); - let f = UFunc { - args: Vec::new(), - instructions: fctx.instructions, - ret: Type::Unit, + p.fns[fid].instructions = fctx.instructions; + mid + } +} + +pub struct ModuleLowerCtx<'a> { + pub p: &'a mut UProgram, + pub output: &'a mut CompilerOutput, + pub module: ModID, + pub temp: usize, + pub ident_stack: NameStack, +} + +impl<'a> ModuleLowerCtx<'a> { + pub fn new(program: &'a mut UProgram, output: &'a mut CompilerOutput, id: ModID) -> Self { + Self { + p: program, + output, + module: id, + temp: 0, + ident_stack: NameStack::new(), + } + } + pub fn temp_var(&mut self, origin: Origin, ty: impl Typable) -> IdentID { + self.temp_var_inner(origin, ty) + } + fn temp_var_inner(&mut self, origin: Origin, ty: impl Typable) -> IdentID { + let var = UVar { + name: format!("temp{}", self.temp), + ty: ty.ty(self), + origin, + parent: None, + children: HashMap::new(), }; - p.write(fid, f); - p.pop_name(); + let id = self.p.def_var(var); + self.temp += 1; + self.def_ident(UIdent { + status: IdentStatus::Res(Res::Var(id)), + origin, + }) } } -pub use func::FnLowerCtx; -use import::Imports; - -pub trait FnLowerable { - type Output; - fn lower(&self, ctx: &mut FnLowerCtx) -> Option; +pub trait Typable { + fn ty(self, p: &mut UProgram) -> TypeID; } -impl FnLowerable for Node { - type Output = T::Output; - fn lower(&self, ctx: &mut FnLowerCtx) -> Option { - let old_span = ctx.origin; - ctx.origin = self.origin; - let res = self.as_ref()?.lower(ctx); - ctx.origin = old_span; - res +impl Typable for Type { + fn ty(self, p: &mut UProgram) -> TypeID { + p.def_ty(self) } } -impl FnLowerable for Box { - type Output = T::Output; - fn lower(&self, ctx: &mut FnLowerCtx) -> Option { - self.as_ref().lower(ctx) +impl Typable for TypeID { + fn ty(self, p: &mut UProgram) -> TypeID { + self + } +} + +impl Deref for ModuleLowerCtx<'_> { + type Target = UProgram; + + fn deref(&self) -> &Self::Target { + self.p + } +} + +impl DerefMut for ModuleLowerCtx<'_> { + fn deref_mut(&mut self) -> &mut Self::Target { + self.p } } diff --git a/src/parser/v3/lower/struc.rs b/src/parser/v3/lower/struc.rs index 8870613..62d4077 100644 --- a/src/parser/v3/lower/struc.rs +++ b/src/parser/v3/lower/struc.rs @@ -1,18 +1,16 @@ use crate::{ - common::{CompilerOutput, FileSpan}, - ir::{StructField, StructID, UModuleBuilder, UProgram, UStruct}, - parser::{Node, PStruct, PStructFields}, + common::FileSpan, + ir::{StructField, StructID, UStruct}, + parser::{PStruct, PStructFields}, }; +use super::ModuleLowerCtx; + impl PStruct { - pub fn lower( - &self, - id: StructID, - b: &mut UModuleBuilder, - output: &mut CompilerOutput, - span: FileSpan, - ) -> Option<()> { - let generics = self.generics.iter().flat_map(|a| a.lower(b)).collect(); + pub fn lower(&self, ctx: &mut ModuleLowerCtx, span: FileSpan) -> Option { + ctx.ident_stack.push(); + let gmap: Vec<_> = self.generics.iter().flat_map(|a| a.lower(ctx)).collect(); + let gargs = gmap.iter().map(|(_, id)| *id).collect(); let fields = match &self.fields { PStructFields::Named(nodes) => nodes .iter() @@ -20,7 +18,7 @@ impl PStruct { let def = n.as_ref()?; let name = def.name.as_ref()?.to_string(); let tynode = def.ty.as_ref()?; - let ty = tynode.lower(b, output); + let ty = tynode.lower(ctx); Some((name, ty)) }) .collect(), @@ -28,7 +26,7 @@ impl PStruct { .iter() .enumerate() .flat_map(|(i, n)| { - let ty = n.as_ref()?.lower(b, output, span); + let ty = n.as_ref()?.lower(ctx, span); Some((format!("{i}"), ty)) }) .collect(), @@ -38,21 +36,12 @@ impl PStruct { .map(|(name, ty)| (name, StructField { ty })) .collect(); let name = self.name.as_ref()?.to_string(); - b.def_data(UStruct { + ctx.ident_stack.pop(); + Some(ctx.def_struct(UStruct { name, - gargs: generics, + gargs, fields, origin: span, - }); - Some(()) - } -} - -impl Node { - pub fn lower(&self, id: StructID, p: &mut UProgram, output: &mut CompilerOutput) -> Option<()> { - let s = self.as_ref()?; - let name = s.name.as_ref()?; - s.lower(id, p, output, self.origin); - Some(()) + })) } } diff --git a/src/parser/v3/lower/ty.rs b/src/parser/v3/lower/ty.rs index 88dd54a..229c880 100644 --- a/src/parser/v3/lower/ty.rs +++ b/src/parser/v3/lower/ty.rs @@ -1,76 +1,76 @@ use crate::{ - ir::{GenericID, MemberID, ModPath, Type, TypeID, UGeneric, UModuleBuilder, UProgram}, + ir::{GenericID, MemberIdent, MemberPath, Type, TypeID, UGeneric, UProgram}, parser::PGenericDef, }; -use super::{CompilerOutput, FileSpan, Node, PType}; +use super::{FileSpan, ModuleLowerCtx, Node, PType}; impl Node> { - pub fn lower(&self, p: &mut UModuleBuilder, output: &mut CompilerOutput) -> TypeID { + pub fn lower(&self, ctx: &mut ModuleLowerCtx) -> TypeID { self.as_ref() - .map(|t| t.lower(p, output, self.origin)) - .unwrap_or(p.error) + .map(|t| t.lower(ctx, self.origin)) + .unwrap_or(ctx.tc.error) } } impl Node { - pub fn lower(&self, p: &mut UModuleBuilder, output: &mut CompilerOutput) -> TypeID { + pub fn lower(&self, ctx: &mut ModuleLowerCtx) -> TypeID { self.as_ref() - .map(|t| t.lower(p, output, self.origin)) - .unwrap_or(p.error) + .map(|t| t.lower(ctx, self.origin)) + .unwrap_or(ctx.tc.error) } } -fn test() {} - impl PType { - pub fn lower( - &self, - p: &mut UModuleBuilder, - output: &mut CompilerOutput, - mut origin: FileSpan, - ) -> TypeID { + pub fn lower(&self, ctx: &mut ModuleLowerCtx, mut origin: FileSpan) -> TypeID { let mut ty = self; let mut path = Vec::new(); while let PType::Member(node, ident) = ty { ty = if let Some(t) = node.as_ref() { let Some(name) = ident.as_ref() else { - return p.error; + return ctx.tc.error; }; origin = node.origin; - path.push(MemberID { + path.push(MemberIdent { name: name.0.clone(), origin: ident.origin, }); &**t } else { - return p.error; + return ctx.tc.error; }; } if !path.is_empty() { let PType::Ident(id) = ty else { - return p.error; + return ctx.tc.error; }; - path.push(MemberID { + path.push(MemberIdent { name: id.0.clone(), origin, }); - let ty = Type::Unres(ModPath { id: p.module, path }); - return p.def_ty(ty); + path.reverse(); + let ty = Type::Unres(MemberPath { + id: ctx.module, + path, + }); + return ctx.def_ty(ty); } let ty = match ty { PType::Member(_, _) => unreachable!(), PType::Ident(node) => { - path.push(MemberID { + path.push(MemberIdent { name: node.0.clone(), origin, }); path.reverse(); - Type::Unres(ModPath { id: p.module, path }) + Type::Unres(MemberPath { + id: ctx.module, + path, + }) } - PType::Ref(node) => node.lower(p, output).rf(), + PType::Ref(node) => node.lower(ctx).rf(), PType::Generic(node, nodes) => todo!(), }; - p.def_ty(ty) + ctx.def_ty(ty) } } diff --git a/src/parser/v3/nodes/expr.rs b/src/parser/v3/nodes/expr.rs index 73501c7..5101319 100644 --- a/src/parser/v3/nodes/expr.rs +++ b/src/parser/v3/nodes/expr.rs @@ -1,12 +1,12 @@ use std::fmt::{Debug, Write}; -use crate::{common::FilePos, parser::NodeParsableWith}; +use crate::{common::FilePos, ir::MemberTy, parser::NodeParsableWith}; use super::{ op::{InfixOp, PostfixOp}, util::parse_list, - CompilerMsg, Keyword, Node, PAsmBlock, PBlock, PIdent, PLiteral, PMap, Parsable, ParseResult, - ParserCtx, Symbol, + CompilerMsg, Keyword, Node, PAsmBlock, PBlock, PIdent, PLiteral, PMap, PType, Parsable, + ParseResult, ParserCtx, Symbol, }; type BoxNode = Node>; @@ -19,8 +19,8 @@ pub enum PExpr { Block(Node), Call(BoxNode, Vec>), Group(BoxNode), - Field(BoxNode, Node), - Member(BoxNode, Node), + Member(BoxNode, MemberTy, Node), + Generic(BoxNode, Vec>), AsmBlock(Node), Construct(BoxNode, Node), If(BoxNode, BoxNode), @@ -65,16 +65,25 @@ impl PExpr { e1 = Self::Call(Node::new(e1, span).bx(), args); continue; } else if next.is_symbol(Symbol::OpenCurly) { + ctx.next(); let map = ctx.parse()?; e1 = Self::Construct(Node::new(e1, span).bx(), map); continue; } else if next.is_symbol(Symbol::Dot) { + ctx.next(); let field = ctx.parse()?; - e1 = Self::Field(Node::new(e1, span).bx(), field); + e1 = Self::Member(Node::new(e1, span).bx(), MemberTy::Field, field); continue; } else if next.is_symbol(Symbol::DoubleColon) { - let field = ctx.parse()?; - e1 = Self::Member(Node::new(e1, span).bx(), field); + ctx.next(); + if ctx.peek().is_some_and(|i| i.is_symbol(Symbol::OpenAngle)) { + ctx.next(); + let gargs = parse_list(ctx, Symbol::CloseAngle)?; + e1 = Self::Generic(Node::new(e1, span).bx(), gargs); + } else { + let field = ctx.parse()?; + e1 = Self::Member(Node::new(e1, span).bx(), MemberTy::Member, field); + } continue; } else if let Some(op) = PostfixOp::from_token(next) { ctx.next(); @@ -185,13 +194,13 @@ impl Debug for PExpr { PExpr::PostfixOp(e, op) => write!(f, "({:?}{})", e, op.str())?, PExpr::Group(inner) => inner.fmt(f)?, PExpr::AsmBlock(inner) => inner.fmt(f)?, - PExpr::Construct(node, inner) => inner.fmt(f)?, + PExpr::Construct(node, inner) => write!(f, "{:?}{:?}", node, inner)?, PExpr::If(cond, res) => write!(f, "if {cond:?} then {res:?}")?, PExpr::Loop(res) => write!(f, "loop -> {res:?}")?, PExpr::Break => write!(f, "break")?, PExpr::Continue => write!(f, "continue")?, - PExpr::Field(e1, name) => write!(f, "{:?}.{:?}", e1, name)?, - PExpr::Member(e1, name) => write!(f, "{:?}::{:?}", e1, name)?, + PExpr::Member(e1, ty, name) => write!(f, "{:?}{}{:?}", e1, ty.sep(), name)?, + PExpr::Generic(e1, gargs) => write!(f, "{:?}<{:?}>", e1, gargs)?, } Ok(()) } diff --git a/src/util/name_stack.rs b/src/util/name_stack.rs index 00e1079..f42a863 100644 --- a/src/util/name_stack.rs +++ b/src/util/name_stack.rs @@ -1,23 +1,45 @@ use std::collections::HashMap; -pub struct NameStack(Vec>); +pub struct NameStack { + base: HashMap, + levels: Vec>, +} impl NameStack { pub fn new() -> Self { - Self(vec![HashMap::new()]) + Self { + base: HashMap::new(), + levels: Vec::new(), + } } pub fn search(&self, name: &str) -> Option<&T> { - for level in self.0.iter().rev() { + for level in self.levels.iter().rev() { if let Some(v) = level.get(name) { return Some(v); } } - None + self.base.get(name) } pub fn push(&mut self) { - self.0.push(HashMap::new()); + self.levels.push(HashMap::new()); } pub fn pop(&mut self) { - self.0.pop(); + self.levels.pop(); + } + fn cur(&mut self) -> &mut HashMap { + self.levels.last_mut().unwrap_or(&mut self.base) + } + pub fn insert(&mut self, name: String, v: T) -> bool { + let cur = self.cur(); + if cur.contains_key(&name) { + return true; + } + cur.insert(name, v); + false + } + pub fn extend(&mut self, iter: impl Iterator) { + for (name, v) in iter { + self.insert(name, v); + } } }