From 329b1d86ac8c0cd4c825244710a5eac461f1cdba Mon Sep 17 00:00:00 2001 From: shadow cat Date: Tue, 15 Apr 2025 03:21:57 -0400 Subject: [PATCH] INITIAL GENERICS IMPL --- Cargo.lock | 2 +- Cargo.toml | 4 - data/test.lang | 18 +++ src/ir/lower/program.rs | 245 ++++++++++++++++++++++++++++------- src/ir/upper/error.rs | 8 +- src/ir/upper/instr.rs | 6 +- src/ir/upper/kind.rs | 151 ++++++++++----------- src/ir/upper/program.rs | 57 +++----- src/ir/upper/ty.rs | 195 ++++++++++++++++++++-------- src/ir/upper/validate.rs | 59 ++++++--- src/parser/v3/lower/def.rs | 55 +------- src/parser/v3/lower/expr.rs | 29 +---- src/parser/v3/lower/func.rs | 13 +- src/parser/v3/lower/mod.rs | 1 + src/parser/v3/lower/struc.rs | 53 ++------ src/parser/v3/lower/ty.rs | 53 ++++++++ src/parser/v3/nodes/struc.rs | 18 ++- src/parser/v3/nodes/ty.rs | 21 ++- 18 files changed, 607 insertions(+), 381 deletions(-) create mode 100644 src/parser/v3/lower/ty.rs diff --git a/Cargo.lock b/Cargo.lock index afaae83..9065268 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,6 +1,6 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. -version = 3 +version = 4 [[package]] name = "lang" diff --git a/Cargo.toml b/Cargo.toml index 7541543..defb8fc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,7 +2,3 @@ name = "lang" version = "0.1.0" edition = "2021" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] diff --git a/data/test.lang b/data/test.lang index f780cae..7ad59ca 100644 --- a/data/test.lang +++ b/data/test.lang @@ -44,6 +44,7 @@ fn start() { print("test: 0x"); print_hex(31); println(""); + generic(); exit(0); } @@ -74,6 +75,23 @@ fn structer(test: Test) { println(""); } +struct GTest { + a: T, + b: U, +} + +fn generic() { + let gt = GTest { + a: 39, + b: "hello", + }; + print("generic: "); + print_dec(gt.a); + print(", "); + print(gt.b); + println(""); +} + fn thinger() { print("estamos jugando\n"); } diff --git a/src/ir/lower/program.rs b/src/ir/lower/program.rs index e4b5ed4..faaf802 100644 --- a/src/ir/lower/program.rs +++ b/src/ir/lower/program.rs @@ -1,10 +1,9 @@ use std::collections::HashMap; -use crate::ir::{AsmBlockArgType, UInstrInst, Size, SymbolSpace, UFunc, VarOffset}; +use crate::ir::{AsmBlockArgType, Size, SymbolSpace, UFunc, UInstrInst, VarOffset}; use super::{ - IRLFunction, LInstruction, Len, Symbol, SymbolSpaceBuilder, Type, UInstruction, UProgram, - VarID, + IRLFunction, LInstruction, Len, Symbol, SymbolSpaceBuilder, Type, UInstruction, UProgram, VarID, }; pub struct LProgram { @@ -43,12 +42,37 @@ impl LProgram { } } +pub struct StructInst { + offsets: Vec, + order: HashMap, + size: Size, +} + pub struct LFunctionBuilder<'a> { + data: LFunctionBuilderData<'a>, program: &'a UProgram, +} + +impl<'a> LFunctionBuilderData<'a> { + pub fn new(builder: &'a mut SymbolSpaceBuilder) -> Self { + Self { + instrs: Vec::new(), + struct_insts: HashMap::new(), + stack: HashMap::new(), + subvar_map: HashMap::new(), + makes_call: false, + builder, + loopp: None, + } + } +} + +pub struct LFunctionBuilderData<'a> { builder: &'a mut SymbolSpaceBuilder, instrs: Vec, stack: HashMap, subvar_map: HashMap, + struct_insts: HashMap, makes_call: bool, loopp: Option, } @@ -62,29 +86,32 @@ pub struct LoopCtx { impl<'a> LFunctionBuilder<'a> { pub fn new(program: &'a UProgram, builder: &'a mut SymbolSpaceBuilder) -> Self { Self { - instrs: Vec::new(), - stack: HashMap::new(), - subvar_map: HashMap::new(), - makes_call: false, + data: LFunctionBuilderData::new(builder), program, - builder, - loopp: None, } } pub fn alloc_stack(&mut self, i: VarID) -> Option<()> { - if self.program.size_of_var(i).expect("unsized type") == 0 { + if self + .data + .size_of_var(self.program, i) + .expect("unsized type") + == 0 + { return None; }; self.map_subvar(i); - let var = self.program.var_offset(i).expect("var offset"); - *self - .stack - .entry(var.id) - .or_insert(self.program.size_of_var(var.id).expect("unsized type")); + let var = self.data.var_offset(self.program, i).expect("var offset"); + if let None = self.stack.get(&var.id) { + let size = self + .data + .size_of_var(self.program, var.id) + .expect("unsized type"); + self.data.stack.insert(var.id, size); + } Some(()) } pub fn map_subvar(&mut self, i: VarID) { - let off = self.program.var_offset(i).expect("var offset"); + let off = self.data.var_offset(self.program, i).expect("var offset"); if off.id != i { self.subvar_map.insert(i, off); } @@ -112,7 +139,7 @@ impl<'a> LFunctionBuilder<'a> { UInstruction::LoadData { dest, src } => { self.alloc_stack(dest.id)?; let data = self.program.expect(*src); - let sym = self.builder.ro_data( + let sym = self.data.builder.ro_data( src, &data.content, Some(self.program.names.get(dest.id).to_string()), @@ -133,7 +160,7 @@ impl<'a> LFunctionBuilder<'a> { self.program.type_name(&data.ty) ))); }; - let sym = self.builder.ro_data( + let sym = self.data.builder.ro_data( src, &data.content, Some(self.program.names.get(dest.id).to_string()), @@ -168,7 +195,10 @@ impl<'a> LFunctionBuilder<'a> { self.makes_call = true; let fid = &self.program.fn_map[&f.id]; let sym = self.builder.func(fid); - let ret_size = self.program.size_of_var(dest.id).expect("unsized type"); + let ret_size = self + .data + .size_of_var(self.program, dest.id) + .expect("unsized type"); let dest = if ret_size > 0 { Some((dest.id, ret_size)) } else { @@ -181,7 +211,12 @@ impl<'a> LFunctionBuilder<'a> { .iter() .map(|a| { self.map_subvar(a.id); - (a.id, self.program.size_of_var(a.id).expect("unsized type")) + ( + a.id, + self.data + .size_of_var(self.program, a.id) + .expect("unsized type"), + ) }) .collect(), }; @@ -210,31 +245,32 @@ impl<'a> LFunctionBuilder<'a> { } UInstruction::Ret { src } => { self.map_subvar(src.id); - self.instrs.push(LInstruction::Ret { - src: if self.program.size_of_var(src.id).expect("unsized var") == 0 { - None - } else { - Some(src.id) - }, - }) + let src = if self + .data + .size_of_var(self.program, src.id) + .expect("unsized var") + == 0 + { + None + } else { + Some(src.id) + }; + self.data.instrs.push(LInstruction::Ret { src }) } UInstruction::Construct { dest, fields } => { self.alloc_stack(dest.id)?; - let ty = &self.program.expect(dest.id).ty; - let &Type::Struct { id, ref args } = ty else { - return Some(Some(format!( - "Failed to contruct type {}", - self.program.type_name(ty) - ))); - }; - for (&fid, var) in fields { + for (field, var) in fields { self.map_subvar(var.id); - self.instrs.push(LInstruction::Mv { + let i = LInstruction::Mv { dest: dest.id, src: var.id, - dest_offset: self.program.field_offset(id, fid).expect("field offset"), + dest_offset: self + .data + .field_offset(self.program, dest.id, field) + .expect("field offset"), src_offset: 0, - }) + }; + self.instrs.push(i) } } UInstruction::If { cond, body } => { @@ -266,35 +302,146 @@ impl<'a> LFunctionBuilder<'a> { self.loopp = old; } UInstruction::Break => { - self.instrs.push(LInstruction::Jump( - self.loopp.expect("Tried to break outside of loop").bot, + self.data.instrs.push(LInstruction::Jump( + self.data.loopp.expect("Tried to break outside of loop").bot, )); } UInstruction::Continue => { - self.instrs.push(LInstruction::Jump( - self.loopp.expect("Tried to break outside of loop").top, + self.data.instrs.push(LInstruction::Jump( + self.data.loopp.expect("Tried to break outside of loop").top, )); } }; Some(None) } - pub fn finish(self, f: &UFunc) -> IRLFunction { + pub fn finish(mut self, f: &UFunc) -> IRLFunction { IRLFunction { - instructions: self.instrs, - makes_call: self.makes_call, args: f .args .iter() - .map(|a| (*a, self.program.size_of_var(*a).expect("unsized type"))) + .map(|a| { + ( + *a, + self.data + .size_of_var(self.program, *a) + .expect("unsized type"), + ) + }) .collect(), - ret_size: self.program.size_of_type(&f.ret).expect("unsized type"), - stack: self.stack, - subvar_map: self.subvar_map, + ret_size: self + .data + .size_of_type(self.program, &f.ret) + .expect("unsized type"), + instructions: self.data.instrs, + makes_call: self.data.makes_call, + stack: self.data.stack, + subvar_map: self.data.subvar_map, } } } +impl LFunctionBuilderData<'_> { + pub fn var_offset(&mut self, p: &UProgram, var: VarID) -> Option { + let mut current = VarOffset { id: var, offset: 0 }; + while let Some(parent) = &p.get(current.id)?.parent { + current.id = parent.var; + current.offset += self.field_offset(p, parent.var, &parent.field)?; + } + Some(current) + } + pub fn addr_size(&self) -> Size { + 64 + } + pub fn struct_inst(&mut self, p: &UProgram, ty: &Type) -> Option<&StructInst> { + // normally I'd let Some(..) here and return, but polonius does not exist :grief: + if self.struct_insts.get(ty).is_none() { + let Type::Struct { id, args } = ty else { + return None; + }; + let struc = p.get(*id)?; + let mut sizes = struc + .fields + .iter() + .map(|(n, f)| { + let ty = if let Type::Generic { id } = &f.ty { + struc + .generics + .iter() + .enumerate() + .find_map(|(i, g)| if *g == *id { args.get(i) } else { None }) + .unwrap_or(&f.ty) + } else { + &f.ty + }; + (n, self.size_of_type(p, ty).expect("unsized type")) + }) + .collect::>(); + sizes.sort_by(|(n1, s1, ..), (n2, s2, ..)| s1.cmp(s2).then_with(|| n1.cmp(n2))); + let mut offset = 0; + let mut offsets = Vec::new(); + let mut order = HashMap::new(); + for (i, (name, size)) in sizes.iter().rev().enumerate() { + // TODO: alignment!!! + order.insert(name.to_string(), i); + offsets.push(offset); + offset += size; + } + self.struct_insts.insert( + ty.clone(), + StructInst { + offsets, + order, + size: offset, + }, + ); + } + self.struct_insts.get(ty) + } + + pub fn field_offset(&mut self, p: &UProgram, var: VarID, field: &str) -> Option { + let ty = &p.get(var)?.ty; + let inst = self.struct_inst(p, ty)?; + let i = *inst.order.get(field)?; + Some(*inst.offsets.get(i)?) + } + + pub fn size_of_type(&mut self, p: &UProgram, ty: &Type) -> Option { + // TODO: target matters + Some(match ty { + Type::Bits(b) => *b, + Type::Struct { id, args } => self.struct_inst(p, ty)?.size, + Type::Generic { id } => return None, + Type::Fn { args, ret } => todo!(), + Type::Ref(_) => self.addr_size(), + Type::Array(ty, len) => self.size_of_type(p, ty)? * len, + Type::Slice(_) => self.addr_size() * 2, + Type::Infer => return None, + Type::Error => return None, + Type::Unit => 0, + Type::Placeholder => return None, + }) + } + + pub fn size_of_var(&mut self, p: &UProgram, var: VarID) -> Option { + self.size_of_type(p, &p.get(var)?.ty) + } +} + +impl<'a> std::ops::Deref for LFunctionBuilder<'a> { + type Target = LFunctionBuilderData<'a>; + + fn deref(&self) -> &Self::Target { + &self.data + } +} + +impl<'a> std::ops::DerefMut for LFunctionBuilder<'a> { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.data + } +} + impl std::ops::Deref for LProgram { type Target = SymbolSpace; diff --git a/src/ir/upper/error.rs b/src/ir/upper/error.rs index 0a9ebe1..cedd404 100644 --- a/src/ir/upper/error.rs +++ b/src/ir/upper/error.rs @@ -3,9 +3,12 @@ use crate::common::{CompilerMsg, CompilerOutput, FileSpan}; use super::{Type, UProgram}; impl CompilerOutput { - pub fn check_assign(&mut self, p: &UProgram, src: &Type, dest: &Type, span: FileSpan) -> bool { + pub fn check_assign(&mut self, p: &UProgram, src: &Type, dest: &Type, span: FileSpan) { // TODO: spans if src != dest { + if !src.is_real() || !dest.is_real() { + return; + } self.err(CompilerMsg { msg: format!( "Cannot assign type '{}' to '{}'", @@ -14,9 +17,6 @@ impl CompilerOutput { ), spans: vec![span], }); - true - } else { - false } } } diff --git a/src/ir/upper/instr.rs b/src/ir/upper/instr.rs index 3e1d0a4..a27197c 100644 --- a/src/ir/upper/instr.rs +++ b/src/ir/upper/instr.rs @@ -1,7 +1,7 @@ use std::{collections::HashMap, fmt::Write}; -use super::{arch::riscv64::RV64Instruction, inst::VarInst, DataID, FnID, UInstrInst, UFunc}; -use crate::{compiler::arch::riscv::Reg, ir::FieldID, util::Padder}; +use super::{arch::riscv64::RV64Instruction, inst::VarInst, DataID, FnID, UFunc, UInstrInst}; +use crate::{compiler::arch::riscv::Reg, util::Padder}; pub enum UInstruction { Mv { @@ -38,7 +38,7 @@ pub enum UInstruction { }, Construct { dest: VarInst, - fields: HashMap, + fields: HashMap, }, If { cond: VarInst, diff --git a/src/ir/upper/kind.rs b/src/ir/upper/kind.rs index 3720519..d62dcd7 100644 --- a/src/ir/upper/kind.rs +++ b/src/ir/upper/kind.rs @@ -3,11 +3,9 @@ use crate::{ ir::{Len, Named, ID}, }; -use super::{Type, UInstrInst, UProgram}; +use super::{Type, UInstrInst, UInstruction, UProgram}; use std::{collections::HashMap, fmt::Debug}; -pub const NAMED_KINDS: usize = 4; - pub struct UFunc { pub args: Vec, pub ret: Type, @@ -17,17 +15,19 @@ pub struct UFunc { #[derive(Clone)] pub struct StructField { - pub name: String, pub ty: Type, } #[derive(Clone)] pub struct UStruct { - pub fields: Vec, - pub field_map: HashMap, + pub fields: HashMap, + pub generics: Vec, pub origin: Origin, } +#[derive(Clone)] +pub struct UGeneric {} + #[derive(Clone)] pub struct UVar { pub parent: Option, @@ -41,13 +41,10 @@ pub struct VarOffset { pub offset: Len, } -#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq)] +#[derive(Debug, Clone, Hash, Eq, PartialEq)] pub struct FieldRef { pub var: VarID, - // this is technically redundant bc you can get it from the var... - // but it makes things a lot easier, and you'd have to recheck the fields anyways - pub struc: StructID, - pub field: FieldID, + pub field: String, } #[derive(Clone)] @@ -70,84 +67,68 @@ impl UFunc { ret: Box::new(self.ret.clone()), } } -} - -impl UStruct { - pub fn field(&self, id: FieldID) -> &StructField { - &self.fields[id.0] - } - pub fn get_field(&self, name: &str) -> Option<&StructField> { - self.field_map.get(name).map(|id| self.field(*id)) - } - pub fn iter_fields(&self) -> impl Iterator { - self.fields - .iter() - .enumerate() - .map(|(i, f)| (FieldID::new(i), f)) + pub fn flat_iter(&self) -> impl Iterator { + InstrIter::new(self.instructions.iter()) } } -pub type StructID = ID; -pub type VarID = ID; -pub type DataID = ID; -pub type FieldID = ID; +pub struct InstrIter<'a> { + iters: Vec>, +} + +impl<'a> InstrIter<'a> { + pub fn new(iter: core::slice::Iter<'a, UInstrInst>) -> Self { + Self { iters: vec![iter] } + } +} + +impl<'a> Iterator for InstrIter<'a> { + type Item = &'a UInstrInst; + + fn next(&mut self) -> Option { + let iter = self.iters.last_mut()?; + let Some(next) = iter.next() else { + self.iters.pop(); + return self.next(); + }; + match &next.i { + UInstruction::Loop { body } => self.iters.push(body.iter()), + UInstruction::If { cond: _, body } => self.iters.push(body.iter()), + _ => (), + } + Some(next) + } +} + +macro_rules! impl_kind { + ($struc:ty, $idx:expr, $field:ident, $name:expr) => { + impl Kind for $struc { + const INDEX: usize = $idx; + fn from_program_mut(program: &mut UProgram) -> &mut Vec> { + &mut program.$field + } + fn from_program(program: &UProgram) -> &Vec> { + &program.$field + } + } + impl Named for $struc { + const NAME: &str = $name; + } + }; +} + +impl_kind!(UFunc, 0, fns, "func"); +impl_kind!(UVar, 1, vars, "var"); +impl_kind!(UStruct, 2, structs, "struct"); +impl_kind!(UGeneric, 3, types, "type"); +impl_kind!(UData, 4, data, "data"); +pub const NAMED_KINDS: usize = 5; + pub type FnID = ID; - -impl Kind for UFunc { - const INDEX: usize = 0; - fn from_program_mut(program: &mut UProgram) -> &mut Vec> { - &mut program.fns - } - fn from_program(program: &UProgram) -> &Vec> { - &program.fns - } -} -impl Named for UFunc { - const NAME: &str = "func"; -} - -impl Kind for UVar { - const INDEX: usize = 1; - fn from_program_mut(program: &mut UProgram) -> &mut Vec> { - &mut program.vars - } - fn from_program(program: &UProgram) -> &Vec> { - &program.vars - } -} -impl Named for UVar { - const NAME: &str = "var"; -} - -impl Kind for UStruct { - const INDEX: usize = 2; - fn from_program_mut(program: &mut UProgram) -> &mut Vec> { - &mut program.structs - } - fn from_program(program: &UProgram) -> &Vec> { - &program.structs - } -} -impl Named for UStruct { - const NAME: &str = "struct"; -} - -impl Kind for UData { - const INDEX: usize = 3; - fn from_program_mut(program: &mut UProgram) -> &mut Vec> { - &mut program.data - } - fn from_program(program: &UProgram) -> &Vec> { - &program.data - } -} -impl Named for UData { - const NAME: &str = "data"; -} - -impl Named for StructField { - const NAME: &str = "field"; -} +pub type VarID = ID; +pub type StructID = ID; +pub type DataID = ID; +pub type GenericID = ID; pub trait Kind { const INDEX: usize; diff --git a/src/ir/upper/program.rs b/src/ir/upper/program.rs index 5325460..73420a0 100644 --- a/src/ir/upper/program.rs +++ b/src/ir/upper/program.rs @@ -6,6 +6,7 @@ pub struct UProgram { pub fns: Vec>, pub vars: Vec>, pub structs: Vec>, + pub types: Vec>, pub data: Vec>, pub start: Option, pub names: NameMap, @@ -46,6 +47,7 @@ impl UProgram { fns: Vec::new(), vars: Vec::new(), structs: Vec::new(), + types: Vec::new(), data: Vec::new(), start: None, names: NameMap::new(), @@ -87,27 +89,6 @@ impl UProgram { pub fn get_fn_var(&self, id: VarID) -> Option<&UFunc> { self.fns[self.fn_map.get(&id)?.0].as_ref() } - pub fn size_of_type(&self, ty: &Type) -> Option { - // TODO: target matters - Some(match ty { - Type::Bits(b) => *b, - Type::Struct { id, args } => self.structs[id.0] - .as_ref()? - .fields - .iter() - .try_fold(0, |sum, f| Some(sum + self.size_of_type(&f.ty)?))?, - Type::Fn { args, ret } => todo!(), - Type::Ref(_) => 64, - Type::Array(ty, len) => self.size_of_type(ty)? * len, - Type::Slice(_) => 128, - Type::Infer => return None, - Type::Error => return None, - Type::Unit => 0, - }) - } - pub fn size_of_var(&self, var: VarID) -> Option { - self.size_of_type(&self.get(var)?.ty) - } pub fn temp_subvar(&mut self, origin: Origin, ty: Type, parent: FieldRef) -> VarInst { self.temp_var_inner(origin, ty, Some(parent)) } @@ -145,6 +126,22 @@ impl UProgram { id } + pub fn field_type<'a>(&'a self, sty: &'a Type, field: &str) -> Option<&'a Type> { + let Type::Struct { id, args } = sty else { + return None; + }; + let struc = self.get(*id)?; + let field = struc.fields.get(field)?; + if let Type::Generic { id } = field.ty { + for (i, g) in struc.generics.iter().enumerate() { + if *g == id { + return Some(&args[i]) + } + } + } + Some(&field.ty) + } + pub fn type_name(&self, ty: &Type) -> String { let mut str = String::new(); match ty { @@ -176,10 +173,12 @@ impl UProgram { } Type::Error => str += "{error}", Type::Infer => str += "{inferred}", + Type::Generic { id } => str += self.names.get(*id), Type::Bits(size) => str += &format!("b{}", size), Type::Array(t, len) => str += &format!("[{}; {len}]", self.type_name(t)), Type::Unit => str += "()", Type::Slice(t) => str += &format!("&[{}]", self.type_name(t)), + Type::Placeholder => str += "{placeholder}", } str } @@ -192,22 +191,6 @@ impl UProgram { last.insert(name, Idents::new(id.into())); } } - pub fn var_offset(&self, var: VarID) -> Option { - let mut current = VarOffset { id: var, offset: 0 }; - while let Some(parent) = self.get(current.id)?.parent { - current.id = parent.var; - current.offset += self.field_offset(parent.struc, parent.field)?; - } - Some(current) - } - pub fn field_offset(&self, struct_id: StructID, field: FieldID) -> Option { - let struc = self.get(struct_id)?; - let mut offset = 0; - for i in 0..field.0 { - offset += self.size_of_type(&struc.fields[i].ty)?; - } - Some(offset) - } pub fn iter_vars(&self) -> impl Iterator { self.vars .iter() diff --git a/src/ir/upper/ty.rs b/src/ir/upper/ty.rs index 9168d85..52236a9 100644 --- a/src/ir/upper/ty.rs +++ b/src/ir/upper/ty.rs @@ -1,15 +1,19 @@ -use super::{Len, StructID, UInstruction, UProgram, UVar, VarID}; +use std::collections::{HashMap, HashSet}; -#[derive(Clone, PartialEq)] +use super::{GenericID, Len, StructID, UInstruction, UProgram, UVar, VarID}; + +#[derive(Clone, PartialEq, Eq, Hash)] pub enum Type { Bits(u32), Struct { id: StructID, args: Vec }, + Generic { id: GenericID }, Fn { args: Vec, ret: Box }, Ref(Box), Slice(Box), Array(Box, Len), Infer, Error, + Placeholder, Unit, } @@ -23,81 +27,163 @@ impl Type { pub fn slice(self) -> Self { Self::Slice(Box::new(self)) } + pub fn is_real(&self) -> bool { + !matches!(self, Self::Error | Self::Placeholder | Self::Infer) + } } impl UProgram { pub fn resolve_types(&mut self) { // I LOVE RUST let mut vars = self.vars.clone(); - for f in self.fns.iter().flatten() { - for i in &f.instructions { - self.resolve_instr_types(&mut vars, &i.i); + for (i, f) in self.iter_fns() { + let mut redo_iter = Vec::new(); + let mut ph_vars = Vec::new(); + let mut redo_new = Vec::new(); + for i in f.flat_iter() { + if let Err(id) = self.resolve_instr_types(&mut vars, &i.i) { + redo_iter.push(i); + ph_vars.push(id); + } + } + while !redo_iter.is_empty() { + let mut new_ph = Vec::new(); + for id in &ph_vars { + let i = id.0; + let Some(var) = &vars[i] else { + continue; + }; + if var.ty == Type::Placeholder + && let Some(parent) = var.parent.as_ref() + { + let pty = &vars[parent.var.0].as_ref().unwrap().ty; + if let Some(ft) = self.field_type(pty, &parent.field) { + vars[i].as_mut().unwrap().ty = ft.clone(); + } else { + new_ph.push(parent.var); + } + } + } + ph_vars = new_ph; + for &i in &redo_iter { + if let Err(id) = self.resolve_instr_types(&mut vars, &i.i) { + redo_new.push(i); + ph_vars.push(id); + } + } + std::mem::swap(&mut redo_iter, &mut redo_new); + redo_new.clear(); } } self.vars = vars; } - pub fn resolve_instr_types(&self, vars: &mut Vec>, i: &UInstruction) { - match &i { - UInstruction::Call { dest, f, args } => { - let ret = self.get_fn_var(f.id).expect("bruh").ret.clone(); - vars[dest.id.0].as_mut().expect("bruh").ty = ret; - } - UInstruction::Mv { dest, src } => { - let dest_ty = get(vars, dest.id); - let src_ty = get(vars, src.id); - if let Some(ty) = match_types(dest_ty, src_ty) { - set(vars, dest.id, ty.clone()); - set(vars, src.id, ty); + pub fn resolve_instr_types( + &self, + vars: &mut Vec>, + i: &UInstruction, + ) -> Result<(), VarID> { + 'outer: { + match &i { + UInstruction::Call { dest, f, args } => { + let fun = self.get_fn_var(f.id).expect("bruh"); + vars[dest.id.0].as_mut().expect("bruh").ty = fun.ret.clone(); + for (src, &dest) in args.iter().zip(&fun.args) { + let dest_ty = get(vars, dest)?; + let src_ty = get(vars, src.id)?; + if let Some(ty) = match_types(dest_ty, src_ty) { + set(vars, dest, ty.clone()); + set(vars, src.id, ty); + } + } } - } - UInstruction::Ref { dest, src } => { - let dest_ty = get(vars, dest.id); - let src_ty = get(vars, src.id); - if let Type::Ref(dest_ty) = dest_ty { + UInstruction::Mv { dest, src } => { + let dest_ty = get(vars, dest.id)?; + let src_ty = get(vars, src.id)?; + if let Some(ty) = match_types(dest_ty, src_ty) { + set(vars, dest.id, ty.clone()); + set(vars, src.id, ty); + } + } + UInstruction::Ref { dest, src } => { + let dest_ty = get(vars, dest.id)?; + let src_ty = get(vars, src.id)?; + let Type::Ref(dest_ty) = dest_ty else { + break 'outer; + }; if let Some(ty) = match_types(dest_ty, src_ty) { set(vars, dest.id, ty.clone().rf()); set(vars, src.id, ty); } } - } - UInstruction::LoadData { dest, src } => { - // TODO - } - UInstruction::LoadSlice { dest, src } => { - // TODO - } - UInstruction::LoadFn { dest, src } => { - // TODO - } - UInstruction::AsmBlock { instructions, args } => { - // TODO - } - UInstruction::Ret { .. } => {} - UInstruction::Construct { dest, fields } => { - // TODO - } - UInstruction::If { cond, body } => { - for i in body { - self.resolve_instr_types(vars, &i.i); + UInstruction::LoadData { dest, src } => { + // TODO } - } - UInstruction::Loop { body } => { - for i in body { - self.resolve_instr_types(vars, &i.i); + UInstruction::LoadSlice { dest, src } => { + // TODO } + UInstruction::LoadFn { dest, src } => { + // TODO + } + UInstruction::AsmBlock { instructions, args } => { + // TODO + } + UInstruction::Ret { .. } => {} + UInstruction::Construct { dest, fields } => { + let dest_ty = get(vars, dest.id)?; + let Type::Struct { id, args } = dest_ty else { + break 'outer; + }; + let id = *id; + let Some(struc) = self.get(id) else { + break 'outer; + }; + let mut new = HashMap::new(); + for (name, field) in &struc.fields { + let Some(src) = fields.get(name) else { + continue; + }; + let src_ty = get(vars, src.id)?; + if let Some(ty) = match_types(&field.ty, src_ty) { + if let Type::Generic { id } = field.ty { + new.insert(id, ty.clone()); + } + set(vars, src.id, ty); + } + } + let mut args: Vec<_> = struc + .generics + .iter() + .map(|&id| Type::Generic { id }) + .collect(); + for (i, g) in struc.generics.iter().enumerate() { + if let Some(ty) = new.remove(g) { + args[i] = ty; + } + } + // for arg in &args { + // println!("{:?}", self.type_name(arg)); + // } + set(vars, dest.id, Type::Struct { id, args }); + } + UInstruction::If { cond, body } => {} + UInstruction::Loop { body } => {} + UInstruction::Break => {} + UInstruction::Continue => {} } - UInstruction::Break => {} - UInstruction::Continue => {} } + Ok(()) } } -pub fn get(vars: &[Option], id: VarID) -> &Type { - &vars[id.0] +pub fn get(vars: &[Option], id: VarID) -> Result<&Type, VarID> { + let var = vars[id.0] .as_ref() - .expect("PARTIAL BORROWING WOULD BE REALLY COOL") - .ty + .expect("PARTIAL BORROWING WOULD BE REALLY COOL"); + if var.ty == Type::Placeholder { + return Err(id); + } + Ok(&var.ty) } pub fn set(vars: &mut [Option], id: VarID, ty: Type) { @@ -112,8 +198,11 @@ pub fn match_types(dest: &Type, src: &Type) -> Option { return None; } match (dest, src) { - (Type::Error, x) | (x, Type::Error) => None, + (Type::Error, _) | (_, Type::Error) => None, + (Type::Placeholder, _) | (_, Type::Placeholder) => None, (Type::Infer, x) | (x, Type::Infer) => Some(x.clone()), + // TODO: handle constraints? + (Type::Generic { id }, x) | (x, Type::Generic { id }) => Some(x.clone()), ( Type::Struct { id: dest_id, diff --git a/src/ir/upper/validate.rs b/src/ir/upper/validate.rs index fea2172..a4c2994 100644 --- a/src/ir/upper/validate.rs +++ b/src/ir/upper/validate.rs @@ -21,6 +21,23 @@ impl UProgram { spans: vec![var.origin], }); } + if var.ty == Type::Placeholder { + output.err(CompilerMsg { + msg: format!("Var {:?} still placeholder!", id), + spans: vec![var.origin], + }); + } + if let Some(parent) = &var.parent { + let pty = &self.get(parent.var).unwrap().ty; + if let Some(ft) = self.field_type(pty, &parent.field) { + output.check_assign(self, &var.ty, ft, var.origin); + } else { + output.err(CompilerMsg { + msg: format!("invalid parent!"), + spans: vec![var.origin], + }); + } + } } output } @@ -81,14 +98,15 @@ impl UProgram { } UInstruction::AsmBlock { instructions, args } => { for arg in args { - if let Some(size) = self.size_of_var(arg.var.id) - && size != 64 - { - output.err(CompilerMsg { - msg: format!("asm block args must be size 64, is size {}", size), - spans: vec![arg.var.span], - }); - } + // TODO: validate size with enabled targets + // if let Some(size) = self.size_of_var(arg.var.id) + // && size != 64 + // { + // output.err(CompilerMsg { + // msg: format!("asm block args must be size 64, is size {}", size), + // spans: vec![arg.var.span], + // }); + // } } } UInstruction::Ret { src } => { @@ -98,24 +116,35 @@ impl UProgram { } UInstruction::Construct { dest, fields } => { let dest_def = self.expect(dest.id); - let tyid = match &dest_def.ty { - Type::Struct { id, args } => *id, + let (tyid, args) = match &dest_def.ty { + Type::Struct { id, args } => (*id, args), _ => { output.err(CompilerMsg { - msg: "uhh type is not struct".to_string(), + msg: format!( + "Type {} cannot be constructed", + self.type_name(&dest_def.ty) + ), spans: vec![dest.span], }); continue; } }; let def = self.expect(tyid); - for (id, field) in def.iter_fields() { - if let Some(var) = fields.get(&id) { + for (name, field) in &def.fields { + if let Some(var) = fields.get(name) { + let mut sty = &field.ty; + if let Type::Generic { id } = sty { + for (g, a) in def.generics.iter().zip(args) { + if *g == *id { + sty = a; + } + } + } let ety = &self.expect(var.id).ty; - output.check_assign(self, &field.ty, ety, var.span); + output.check_assign(self, ety, sty, var.span); } else { output.err(CompilerMsg { - msg: format!("field '{}' missing from struct", field.name), + msg: format!("field '{}' missing from struct", name), spans: vec![dest.span], }); } diff --git a/src/parser/v3/lower/def.rs b/src/parser/v3/lower/def.rs index ef3b004..3d68735 100644 --- a/src/parser/v3/lower/def.rs +++ b/src/parser/v3/lower/def.rs @@ -1,6 +1,6 @@ -use crate::ir::{Type, UProgram, UStruct, UVar, VarInst}; +use crate::ir::{Type, UProgram, UVar, VarInst}; -use super::{CompilerMsg, CompilerOutput, FileSpan, Node, PType, PVarDef}; +use super::{CompilerOutput, Node, PVarDef}; impl Node { pub fn lower(&self, program: &mut UProgram, output: &mut CompilerOutput) -> Option { @@ -27,54 +27,3 @@ impl Node { }) } } - -impl Node { - pub fn lower(&self, namespace: &mut UProgram, output: &mut CompilerOutput) -> Type { - self.as_ref() - .map(|t| t.lower(namespace, output, self.span)) - .unwrap_or(Type::Error) - } -} - -impl PType { - pub fn lower( - &self, - namespace: &mut UProgram, - output: &mut CompilerOutput, - span: FileSpan, - ) -> Type { - let Some(name) = self.name.as_ref() else { - return Type::Error; - }; - match namespace - .get_idents(name) - .and_then(|ids| ids.get::()) - { - Some(id) => { - let args = self - .args - .iter() - .map(|n| n.lower(namespace, output)) - .collect(); - Type::Struct { id, args } - } - None => { - if let Ok(num) = name.parse::() { - Type::Bits(num) - } else { - match name.as_str() { - "slice" => { - let inner = self.args[0].lower(namespace, output); - Type::Slice(Box::new(inner)) - } - "_" => Type::Infer, - _ => { - output.err(CompilerMsg::from_span(span, "Type not found".to_string())); - Type::Error - } - } - } - } - } - } -} diff --git a/src/parser/v3/lower/expr.rs b/src/parser/v3/lower/expr.rs index 29e6544..e7f6ab5 100644 --- a/src/parser/v3/lower/expr.rs +++ b/src/parser/v3/lower/expr.rs @@ -58,35 +58,16 @@ impl FnLowerable for PExpr { PExpr::BinaryOp(op, e1, e2) => { let res1 = e1.lower(ctx)?; if *op == PInfixOp::Access { - let sty = &ctx.program.expect(res1.id).ty; - let Type::Struct { - id: struct_id, - args, - } = sty - else { - ctx.err(format!( - "Type {:?} has no fields", - ctx.program.type_name(sty) - )); - return None; - }; - let struc = ctx.program.expect(*struct_id); let Some(box PExpr::Ident(ident)) = &e2.inner else { - ctx.err("Field accesses must be identifiers".to_string()); + ctx.err("Field accessors must be identifiers".to_string()); return None; }; - let fname = &ident.as_ref()?.0; - let Some(&field) = struc.field_map.get(fname) else { - ctx.err(format!("Field '{fname}' not in struct")); - return None; - }; - let fdef = struc.field(field); + let fname = ident.as_ref()?.0.clone(); ctx.temp_subvar( - fdef.ty.clone(), + Type::Placeholder, FieldRef { var: res1.id, - struc: *struct_id, - field, + field: fname, }, ) } else { @@ -147,7 +128,7 @@ impl FnLowerable for PExpr { .program .get_fn_var(fe.id) .map(|f| f.ret.clone()) - .unwrap_or(Type::Error); + .unwrap_or(Type::Placeholder); let temp = ctx.temp(ty); ctx.push(UInstruction::Call { dest: temp, diff --git a/src/parser/v3/lower/func.rs b/src/parser/v3/lower/func.rs index 117547e..4d72d57 100644 --- a/src/parser/v3/lower/func.rs +++ b/src/parser/v3/lower/func.rs @@ -1,6 +1,9 @@ use super::{CompilerMsg, CompilerOutput, FileSpan, FnLowerable, Node, PFunction}; use crate::{ - ir::{FieldRef, FnID, Idents, Type, UFunc, UInstrInst, UInstruction, UProgram, UVar, VarInst}, + ir::{ + FieldRef, FnID, Idents, InstrIter, Type, UFunc, UInstrInst, UInstruction, UProgram, UVar, + VarInst, + }, parser, }; @@ -24,8 +27,7 @@ impl PFunction { name.to_string(), Some(UVar { parent: None, - // this gets replaced with the correct type later - ty: Type::Error, + ty: Type::Placeholder, origin: self.header.span, }), ); @@ -62,11 +64,12 @@ impl PFunction { }); } let origin = self.header.span; + let instructions = ctx.instructions; let f = UFunc { origin, args, ret, - instructions: ctx.instructions, + instructions, }; p.expect_mut(p.inv_fn_map[id.0]).ty = f.ty(p); p.write(id, f) @@ -115,7 +118,7 @@ impl FnLowerCtx<'_> { self.program.temp_subvar(self.span, ty, parent) } pub fn push(&mut self, i: UInstruction) { - self.instructions.push(UInstrInst { i, span: self.span }); + self.push_at(i, self.span); } pub fn push_at(&mut self, i: UInstruction, span: FileSpan) { self.instructions.push(UInstrInst { i, span }); diff --git a/src/parser/v3/lower/mod.rs b/src/parser/v3/lower/mod.rs index 7aa67aa..d560485 100644 --- a/src/parser/v3/lower/mod.rs +++ b/src/parser/v3/lower/mod.rs @@ -6,6 +6,7 @@ mod expr; mod func; mod module; mod struc; +mod ty; use super::*; diff --git a/src/parser/v3/lower/struc.rs b/src/parser/v3/lower/struc.rs index 389e067..b52bfef 100644 --- a/src/parser/v3/lower/struc.rs +++ b/src/parser/v3/lower/struc.rs @@ -1,8 +1,6 @@ -use std::collections::HashMap; - use crate::{ common::{CompilerOutput, FileSpan}, - ir::{FieldID, StructField, StructID, Type, UInstruction, UProgram, UStruct, VarInst}, + ir::{StructField, StructID, UInstruction, UProgram, UStruct, VarInst}, parser::{Node, PConstruct, PConstructFields, PStruct, PStructFields}, }; @@ -12,18 +10,6 @@ impl FnLowerable for PConstruct { type Output = VarInst; fn lower(&self, ctx: &mut FnLowerCtx) -> Option { let ty = self.name.lower(ctx.program, ctx.output); - let field_map = match ty { - Type::Struct { id, .. } => ctx.program.expect(id), - _ => { - ctx.err(format!( - "Type {} cannot be constructed", - ctx.program.type_name(&ty) - )); - return None; - } - } - .field_map - .clone(); let fields = match &self.fields { PConstructFields::Named(nodes) => nodes .iter() @@ -31,15 +17,7 @@ impl FnLowerable for PConstruct { let def = n.as_ref()?; let name = def.name.as_ref()?.to_string(); let expr = def.val.as_ref()?.lower(ctx)?; - let Some(&field) = field_map.get(&name) else { - ctx.err(format!( - "Struct {} has no field {}", - ctx.program.type_name(&ty), - name - )); - return None; - }; - Some((field, expr)) + Some((name, expr)) }) .collect(), PConstructFields::Tuple(nodes) => nodes @@ -48,15 +26,7 @@ impl FnLowerable for PConstruct { .flat_map(|(i, n)| { let expr = n.as_ref()?.lower(ctx)?; let name = format!("{i}"); - let Some(&field) = field_map.get(&name) else { - ctx.err(format!( - "Struct {} has no field {}", - ctx.program.type_name(&ty), - name - )); - return None; - }; - Some((field, expr)) + Some((name, expr)) }) .collect(), PConstructFields::None => Default::default(), @@ -75,7 +45,12 @@ impl PStruct { output: &mut CompilerOutput, span: FileSpan, ) -> Option<()> { - let mut field_map = HashMap::new(); + p.push(); + let generics = self + .generics + .iter() + .flat_map(|a| a.as_ref()?.lower(p)) + .collect(); let fields = match &self.fields { PStructFields::Named(nodes) => nodes .iter() @@ -98,21 +73,17 @@ impl PStruct { PStructFields::None => vec![], } .into_iter() - .enumerate() - .map(|(i, (name, ty))| { - let id = FieldID::new(i); - field_map.insert(name.clone(), id); - StructField { name, ty } - }) + .map(|(name, ty)| (name, StructField { ty })) .collect(); p.write( id, UStruct { origin: span, - field_map, + generics, fields, }, ); + p.pop(); Some(()) } } diff --git a/src/parser/v3/lower/ty.rs b/src/parser/v3/lower/ty.rs new file mode 100644 index 0000000..0242531 --- /dev/null +++ b/src/parser/v3/lower/ty.rs @@ -0,0 +1,53 @@ +use crate::{ + ir::{GenericID, Type, UGeneric, UProgram, UStruct}, + parser::PGenericDef, +}; + +use super::{CompilerMsg, CompilerOutput, FileSpan, Node, PType}; + +impl Node { + pub fn lower(&self, namespace: &mut UProgram, output: &mut CompilerOutput) -> Type { + self.as_ref() + .map(|t| t.lower(namespace, output, self.span)) + .unwrap_or(Type::Error) + } +} + +impl PType { + pub fn lower(&self, p: &mut UProgram, output: &mut CompilerOutput, span: FileSpan) -> Type { + let Some(name) = self.name.as_ref() else { + return Type::Error; + }; + let ids = p.get_idents(name); + // TODO: should generics always take precedence? + if let Some(id) = ids.and_then(|ids| ids.get::()) { + Type::Generic { id } + } else if let Some(id) = ids.and_then(|ids| ids.get::()) { + let args = self.args.iter().map(|n| n.lower(p, output)).collect(); + Type::Struct { id, args } + } else if let Ok(num) = name.parse::() { + Type::Bits(num) + } else { + match name.as_str() { + "slice" => { + let inner = self.args[0].lower(p, output); + Type::Slice(Box::new(inner)) + } + "_" => Type::Infer, + _ => { + output.err(CompilerMsg::from_span(span, "Type not found".to_string())); + Type::Error + } + } + } + } +} + +impl PGenericDef { + pub fn lower(&self, p: &mut UProgram) -> Option { + let Some(name) = self.name.as_ref() else { + return None; + }; + Some(p.def_searchable(name.to_string(), Some(UGeneric {}))) + } +} diff --git a/src/parser/v3/nodes/struc.rs b/src/parser/v3/nodes/struc.rs index 88382f7..7c0a694 100644 --- a/src/parser/v3/nodes/struc.rs +++ b/src/parser/v3/nodes/struc.rs @@ -3,13 +3,14 @@ use std::fmt::Debug; use crate::parser::ParsableWith; use super::{ - util::parse_list, CompilerMsg, Keyword, Node, PExpr, PFieldDef, PIdent, PType, PVarDef, - Parsable, ParseResult, ParserCtx, Symbol, + util::parse_list, CompilerMsg, Keyword, Node, PExpr, PFieldDef, PIdent, PType, PGenericDef, + PVarDef, Parsable, ParseResult, ParserCtx, Symbol, }; #[derive(Debug)] pub struct PStruct { pub name: Node, + pub generics: Vec>, pub fields: PStructFields, } @@ -37,7 +38,15 @@ impl Parsable for PStruct { fn parse(ctx: &mut ParserCtx) -> ParseResult { ctx.expect_kw(Keyword::Struct)?; let name = ctx.parse()?; - let next = ctx.expect_peek()?; + let mut next = ctx.expect_peek()?; + let args = if next.is_symbol(Symbol::OpenAngle) { + ctx.next(); + let res = parse_list(ctx, Symbol::CloseAngle)?; + next = ctx.expect_peek()?; + res + } else { + Vec::new() + }; let fields = if next.is_symbol(Symbol::Semicolon) { ctx.next(); PStructFields::None @@ -52,10 +61,11 @@ impl Parsable for PStruct { ctx.err(msg); return ParseResult::Recover(PStruct { name, + generics: args, fields: PStructFields::None, }); }; - ParseResult::Ok(PStruct { name, fields }) + ParseResult::Ok(PStruct { name, generics: args, fields }) } } diff --git a/src/parser/v3/nodes/ty.rs b/src/parser/v3/nodes/ty.rs index 8cbba0b..23d8fef 100644 --- a/src/parser/v3/nodes/ty.rs +++ b/src/parser/v3/nodes/ty.rs @@ -1,14 +1,16 @@ use std::fmt::Debug; -use super::{ - util::parse_list, CompilerMsg, Node, PIdent, Parsable, ParseResult, ParserCtx, Symbol, Token, -}; +use super::{util::parse_list, Node, PIdent, Parsable, ParseResult, ParserCtx, Symbol}; pub struct PType { pub name: Node, pub args: Vec>, } +pub struct PGenericDef { + pub name: Node, +} + impl Parsable for PType { fn parse(ctx: &mut ParserCtx) -> ParseResult { let next = ctx.expect_peek()?; @@ -35,6 +37,12 @@ impl Parsable for PType { } } +impl Parsable for PGenericDef { + fn parse(ctx: &mut ParserCtx) -> ParseResult { + ParseResult::Ok(Self { name: ctx.parse()? }) + } +} + impl Debug for PType { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "{:?}", self.name)?; @@ -46,3 +54,10 @@ impl Debug for PType { Ok(()) } } + +impl Debug for PGenericDef { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{:?}", self.name)?; + Ok(()) + } +}