INITIAL GENERICS IMPL

This commit is contained in:
2025-04-15 03:21:57 -04:00
parent 993458f4be
commit 329b1d86ac
18 changed files with 607 additions and 381 deletions

View File

@@ -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<Len>,
order: HashMap<String, usize>,
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<LInstruction>,
stack: HashMap<VarID, Size>,
subvar_map: HashMap<VarID, VarOffset>,
struct_insts: HashMap<Type, StructInst>,
makes_call: bool,
loopp: Option<LoopCtx>,
}
@@ -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<VarOffset> {
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::<Vec<_>>();
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<Len> {
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<Size> {
// 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<Size> {
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;

View File

@@ -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
}
}
}

View File

@@ -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<FieldID, VarInst>,
fields: HashMap<String, VarInst>,
},
If {
cond: VarInst,

View File

@@ -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<VarID>,
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<StructField>,
pub field_map: HashMap<String, FieldID>,
pub fields: HashMap<String, StructField>,
pub generics: Vec<GenericID>,
pub origin: Origin,
}
#[derive(Clone)]
pub struct UGeneric {}
#[derive(Clone)]
pub struct UVar {
pub parent: Option<FieldRef>,
@@ -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<Item = (FieldID, &StructField)> {
self.fields
.iter()
.enumerate()
.map(|(i, f)| (FieldID::new(i), f))
pub fn flat_iter(&self) -> impl Iterator<Item = &UInstrInst> {
InstrIter::new(self.instructions.iter())
}
}
pub type StructID = ID<UStruct>;
pub type VarID = ID<UVar>;
pub type DataID = ID<UData>;
pub type FieldID = ID<StructField>;
pub struct InstrIter<'a> {
iters: Vec<core::slice::Iter<'a, UInstrInst>>,
}
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<Self::Item> {
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<Option<Self>> {
&mut program.$field
}
fn from_program(program: &UProgram) -> &Vec<Option<Self>> {
&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<UFunc>;
impl Kind for UFunc {
const INDEX: usize = 0;
fn from_program_mut(program: &mut UProgram) -> &mut Vec<Option<Self>> {
&mut program.fns
}
fn from_program(program: &UProgram) -> &Vec<Option<Self>> {
&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<Option<Self>> {
&mut program.vars
}
fn from_program(program: &UProgram) -> &Vec<Option<Self>> {
&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<Option<Self>> {
&mut program.structs
}
fn from_program(program: &UProgram) -> &Vec<Option<Self>> {
&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<Option<Self>> {
&mut program.data
}
fn from_program(program: &UProgram) -> &Vec<Option<Self>> {
&program.data
}
}
impl Named for UData {
const NAME: &str = "data";
}
impl Named for StructField {
const NAME: &str = "field";
}
pub type VarID = ID<UVar>;
pub type StructID = ID<UStruct>;
pub type DataID = ID<UData>;
pub type GenericID = ID<UGeneric>;
pub trait Kind {
const INDEX: usize;

View File

@@ -6,6 +6,7 @@ pub struct UProgram {
pub fns: Vec<Option<UFunc>>,
pub vars: Vec<Option<UVar>>,
pub structs: Vec<Option<UStruct>>,
pub types: Vec<Option<UGeneric>>,
pub data: Vec<Option<UData>>,
pub start: Option<FnID>,
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<Size> {
// 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<Size> {
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<VarOffset> {
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<Len> {
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<Item = (VarID, &UVar)> {
self.vars
.iter()

View File

@@ -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<Type> },
Generic { id: GenericID },
Fn { args: Vec<Type>, ret: Box<Type> },
Ref(Box<Type>),
Slice(Box<Type>),
Array(Box<Type>, 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<Option<UVar>>, 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<Option<UVar>>,
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<UVar>], id: VarID) -> &Type {
&vars[id.0]
pub fn get(vars: &[Option<UVar>], 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<UVar>], id: VarID, ty: Type) {
@@ -112,8 +198,11 @@ pub fn match_types(dest: &Type, src: &Type) -> Option<Type> {
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,

View File

@@ -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],
});
}