structs r a lot more sane in code, can now actually assign & stuff

This commit is contained in:
2025-04-08 20:00:16 -04:00
parent cb9a366f43
commit 26e7a4da4a
21 changed files with 405 additions and 197 deletions

View File

@@ -1,4 +1,7 @@
use crate::{common::FileSpan, ir::{Len, Size}};
use crate::{
common::FileSpan,
ir::{FieldID, Len, StructID, VarID},
};
use super::Type;
use std::{collections::HashMap, fmt::Debug};
@@ -13,25 +16,41 @@ pub struct FnDef {
#[derive(Clone)]
pub struct StructField {
pub name: String,
pub ty: Type,
pub offset: Len,
}
#[derive(Clone)]
pub struct StructDef {
pub name: String,
pub fields: HashMap<String, StructField>,
pub size: Size,
pub fields: Vec<StructField>,
pub field_map: HashMap<String, FieldID>,
pub origin: Origin,
}
#[derive(Clone)]
pub struct VarDef {
pub name: String,
pub parent: Option<FieldRef>,
pub ty: Type,
pub origin: Origin,
}
#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq)]
pub struct VarOffset {
pub id: VarID,
pub offset: Len,
}
#[derive(Debug, Clone, Copy, 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,
}
#[derive(Clone)]
pub struct DataDef {
pub ty: Type,
@@ -49,3 +68,15 @@ impl FnDef {
}
}
}
impl StructDef {
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(i), f))
}
}

View File

@@ -3,7 +3,7 @@ use std::{collections::HashMap, fmt::Write};
use super::{
arch::riscv64::RV64Instruction, inst::VarInst, DataID, FnID, IRUInstrInst, Type, VarID,
};
use crate::{compiler::arch::riscv::Reg, util::Padder};
use crate::{compiler::arch::riscv::Reg, ir::FieldID, util::Padder};
pub struct IRUFunction {
pub name: String,
@@ -33,11 +33,6 @@ pub enum IRUInstruction {
dest: VarInst,
src: FnID,
},
Access {
dest: VarInst,
src: VarInst,
field: String,
},
Call {
dest: VarInst,
f: VarInst,
@@ -52,7 +47,7 @@ pub enum IRUInstruction {
},
Construct {
dest: VarInst,
fields: HashMap<String, VarInst>,
fields: HashMap<FieldID, VarInst>,
},
If {
cond: VarInst,
@@ -62,6 +57,7 @@ pub enum IRUInstruction {
body: Vec<IRUInstrInst>,
},
Break,
Continue,
}
#[derive(Debug)]
@@ -95,7 +91,6 @@ impl std::fmt::Debug for IRUInstruction {
}
Self::Ret { src } => f.debug_struct("Ret").field("src", src).finish()?,
Self::Construct { dest, fields } => write!(f, "{dest:?} <- {fields:?}")?,
Self::Access { dest, src, field } => write!(f, "{dest:?} <- {src:?}.{field}")?,
Self::If { cond, body } => {
write!(f, "if {cond:?}:")?;
if !body.is_empty() {
@@ -125,6 +120,7 @@ impl std::fmt::Debug for IRUInstruction {
}
}
Self::Break => write!(f, "break")?,
Self::Continue => write!(f, "continue")?,
}
Ok(())
}

View File

@@ -1,13 +1,11 @@
use std::{collections::HashMap, fmt::Debug};
use crate::common::FileSpan;
use super::{inst::VarInst, *};
pub struct IRUProgram {
pub fn_defs: Vec<FnDef>,
pub var_defs: Vec<VarDef>,
pub type_defs: Vec<StructDef>,
pub struct_defs: Vec<StructDef>,
pub data_defs: Vec<DataDef>,
pub fns: Vec<Option<IRUFunction>>,
pub data: Vec<Vec<u8>>,
@@ -21,7 +19,7 @@ impl IRUProgram {
Self {
fn_defs: Vec::new(),
var_defs: Vec::new(),
type_defs: Vec::new(),
struct_defs: Vec::new(),
data_defs: Vec::new(),
data: Vec::new(),
fn_map: HashMap::new(),
@@ -57,8 +55,8 @@ impl IRUProgram {
pub fn get_fn_var(&self, id: VarID) -> Option<&FnDef> {
Some(&self.fn_defs[self.fn_map.get(&id)?.0])
}
pub fn get_struct(&self, id: TypeID) -> &StructDef {
&self.type_defs[id.0]
pub fn get_struct(&self, id: StructID) -> &StructDef {
&self.struct_defs[id.0]
}
pub fn alias_fn(&mut self, name: &str, id: FnID) {
self.insert(name, Ident::Fn(id));
@@ -80,9 +78,11 @@ impl IRUProgram {
pub fn size_of_type(&self, ty: &Type) -> Option<Size> {
// TODO: target matters
Some(match ty {
Type::Concrete(id) => self.type_defs[id.0].size,
Type::Bits(b) => *b,
Type::Generic { base, args } => todo!(),
Type::Struct { id, args } => self.struct_defs[id.0]
.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,
@@ -92,12 +92,21 @@ impl IRUProgram {
Type::Unit => 0,
})
}
pub fn struct_layout() {}
pub fn size_of_var(&self, var: VarID) -> Option<Size> {
self.size_of_type(&self.var_defs[var.0].ty)
}
pub fn temp_subvar(&mut self, origin: Origin, ty: Type, parent: FieldRef) -> VarInst {
self.temp_var_inner(origin, ty, Some(parent))
}
pub fn temp_var(&mut self, origin: Origin, ty: Type) -> VarInst {
self.temp_var_inner(origin, ty, None)
}
fn temp_var_inner(&mut self, origin: Origin, ty: Type, parent: Option<FieldRef>) -> VarInst {
let v = self.def_var(VarDef {
name: format!("temp{}", self.temp),
parent,
origin,
ty,
});
@@ -107,11 +116,13 @@ impl IRUProgram {
span: origin,
}
}
pub fn def_fn(&mut self, def: FnDef) -> FnID {
let i = self.fn_defs.len();
let id = FnID(i);
let var_def = VarDef {
name: def.name.clone(),
parent: None,
origin: def.origin,
ty: def.ty(),
};
@@ -126,11 +137,11 @@ impl IRUProgram {
id
}
pub fn def_type(&mut self, def: StructDef) -> TypeID {
let i = self.type_defs.len();
let id = TypeID(i);
pub fn def_struct(&mut self, def: StructDef) -> StructID {
let i = self.struct_defs.len();
let id = StructID(i);
self.insert(&def.name, Ident::Type(id));
self.type_defs.push(def);
self.struct_defs.push(def);
id
}
pub fn def_data(&mut self, def: DataDef, bytes: Vec<u8>) -> DataID {
@@ -142,10 +153,7 @@ impl IRUProgram {
pub fn type_name(&self, ty: &Type) -> String {
let mut str = String::new();
match ty {
Type::Concrete(t) => {
str += &self.get_struct(*t).name;
}
Type::Generic { base, args } => {
Type::Struct { id: base, args } => {
str += &self.get_struct(*base).name;
if let Some(arg) = args.first() {
str = str + "<" + &self.type_name(arg);
@@ -201,13 +209,29 @@ impl IRUProgram {
.enumerate()
.flat_map(|(i, f)| Some((FnID(i), f.as_ref()?)))
}
pub fn var_offset(&self, var: VarID) -> Option<VarOffset> {
let mut current = VarOffset { id: var, offset: 0 };
while let Some(parent) = self.var_defs[current.id.0].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(struct_id);
let mut offset = 0;
for i in 0..field.0 {
offset += self.size_of_type(&struc.fields[i].ty)?;
}
Some(offset)
}
}
#[derive(Debug, Clone, Copy)]
pub enum Ident {
Var(VarID),
Fn(FnID),
Type(TypeID),
Type(StructID),
}
#[derive(Debug, Clone, Copy)]
@@ -215,7 +239,7 @@ pub struct Idents {
pub latest: Ident,
pub var: Option<VarID>,
pub func: Option<FnID>,
pub ty: Option<TypeID>,
pub struc: Option<StructID>,
}
impl Idents {
@@ -224,7 +248,7 @@ impl Idents {
latest,
var: None,
func: None,
ty: None,
struc: None,
};
s.insert(latest);
s
@@ -238,7 +262,7 @@ impl Idents {
Ident::Fn(f) => {
self.func = Some(f);
}
Ident::Type(t) => self.ty = Some(t),
Ident::Type(t) => self.struc = Some(t),
}
}
}

View File

@@ -1,10 +1,9 @@
use super::{IRUProgram, Len, TypeID};
use super::{IRUProgram, Len, StructID};
#[derive(Clone, PartialEq)]
pub enum Type {
Concrete(TypeID),
Bits(u32),
Generic { base: TypeID, args: Vec<Type> },
Struct { id: StructID, args: Vec<Type> },
Fn { args: Vec<Type>, ret: Box<Type> },
Ref(Box<Type>),
Slice(Box<Type>),
@@ -26,6 +25,7 @@ impl Type {
}
}
// should impl instead
pub fn resolve_types(ns: &IRUProgram) {
for (i, f) in ns.iter_fns() {
for inst in &f.instructions {

View File

@@ -80,8 +80,8 @@ impl IRUProgram {
}
IRUInstruction::Construct { dest, fields } => {
let dest_def = self.get_var(dest.id);
let tyid = match dest_def.ty {
Type::Concrete(id) => id,
let tyid = match &dest_def.ty {
Type::Struct { id, args } => *id,
_ => {
output.err(CompilerMsg {
msg: "uhh type is not struct".to_string(),
@@ -91,44 +91,17 @@ impl IRUProgram {
}
};
let def = self.get_struct(tyid);
for (name, field) in &def.fields {
if let Some(var) = fields.get(name) {
for (id, field) in def.iter_fields() {
if let Some(var) = fields.get(&id) {
let ety = &self.get_var(var.id).ty;
output.check_assign(self, &field.ty, ety, var.span);
} else {
output.err(CompilerMsg {
msg: format!("field '{name}' missing from struct"),
msg: format!("field '{}' missing from struct", field.name),
spans: vec![dest.span],
});
}
}
for name in fields.keys() {
if !def.fields.contains_key(name) {
output.err(CompilerMsg {
msg: format!("field '{name}' not in struct"),
spans: vec![dest.span],
});
}
}
}
IRUInstruction::Access { dest, src, field } => {
let dest_def = self.get_var(dest.id);
let src_def = self.get_var(src.id);
let tyid = match src_def.ty {
Type::Concrete(id) => id,
_ => {
output.err(CompilerMsg {
msg: "uhh type is not struct".to_string(),
spans: vec![dest.span],
});
continue;
}
};
let def = self.get_struct(tyid);
let field = def.fields.get(field).expect(
"already validated during parse lowering... probably shouldn't be?",
);
output.check_assign(self, &field.ty, &dest_def.ty, i.span);
}
IRUInstruction::If { cond, body } => {
let cond = self.get_var(cond.id);
@@ -147,6 +120,15 @@ impl IRUProgram {
}
// TODO
}
IRUInstruction::Continue => {
if !breakable {
output.err(CompilerMsg {
msg: "Can't continue here (outside of loop)".to_string(),
spans: vec![i.span],
});
}
// TODO
}
}
}
if needs_ret && no_ret && *ret != Type::Unit {