trudging through the snow

This commit is contained in:
2025-05-06 23:27:30 -04:00
parent 9368d6dcd0
commit 0016ede873
29 changed files with 1375 additions and 1085 deletions

View File

@@ -1,14 +1,16 @@
use crate::{compiler::arch::riscv::*, ir::UIdent};
use std::fmt::Debug;
pub type RV64Instruction = LinkerInstruction<RegRef, UIdent>;
use crate::{compiler::arch::riscv::*, ir::IdentID};
pub type RV64Instruction<V = IdentID> = LinkerInstruction<RegRef<V>, V>;
#[derive(Copy, Clone)]
pub enum RegRef {
Var(UIdent),
Reg(Reg),
pub enum RegRef<V = IdentID, R = Reg> {
Var(V),
Reg(R),
}
impl std::fmt::Debug for RegRef {
impl<V: Debug, R: Debug> Debug for RegRef<V, R> {
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 {
}
}
}

View File

@@ -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<RegRef>,
args: Vec<RegRef<IdentID, String>>,
}
#[derive(Clone)]
pub enum RegRef {
Var(VarID),
Reg(String),
}

View File

@@ -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<RV64Instruction>,
instructions: Vec<RV64Instruction<VarID>>,
inputs: Vec<(Reg, VarID)>,
outputs: Vec<(Reg, VarID)>,
},

View File

@@ -1,6 +1,7 @@
mod func;
mod program;
mod symbol;
mod res;
pub use func::*;
pub use program::*;

View File

@@ -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<LInstruction>,
stack: HashMap<VarID, Size>,
subvar_map: HashMap<VarID, VarOffset>,
struct_insts: HashMap<LStructInst, LStructInst>,
struct_insts: HashMap<StructInst, LStructInst>,
makes_call: bool,
loopp: Option<LoopCtx>,
}
@@ -127,44 +128,48 @@ impl<'a> LFunctionBuilder<'a> {
}
}
pub fn insert_instr(&mut self, i: &UInstrInst) -> Option<Option<String>> {
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<Len> {
pub fn field_offset(&mut self, p: &UProgram, sty: &StructInst, field: &str) -> Option<Len> {
let inst = self.struct_inst(p, sty);
Some(inst.offset(field)?)
}
pub fn size_of_type(&mut self, p: &UProgram, ty: &Type) -> Option<Size> {
pub fn size_of_type(&mut self, p: &UProgram, ty: &TypeID) -> Option<Size> {
// 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,

92
src/ir/lower/res.rs Normal file
View File

@@ -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<UInstrInst<Resolved>> {
Some(UInstrInst {
i: self.i.resolve(p)?,
origin: self.origin,
})
}
}
impl UInstruction {
pub fn resolve<'a>(&'a self, p: &'a UProgram) -> Option<UInstruction<Resolved>> {
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::<Option<_>>()?,
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::<Option<_>>()?,
},
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<AsmBlockArg<VarID>> {
Some(AsmBlockArg {
var: self.var.var(p)?,
reg: self.reg,
ty: self.ty,
})
}
}
impl RV64Instruction {
pub fn resolve(&self, p: &UProgram) -> Option<RV64Instruction<VarID>> {
self.try_map(|i| {
Some(match i {
RegRef::Var(v) => RegRef::Var(v.var(p)?),
RegRef::Reg(r) => RegRef::Reg(*r),
})
})
}
}

View File

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

View File

@@ -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::*;

View File

@@ -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<ResErr>) {
pub fn report_errs(p: &UProgram, output: &mut CompilerOutput, mut errs: Vec<ResErr>) {
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<ResErr>)
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<ResErr>)
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,

View File

@@ -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<S: ResStage = Unresolved> {
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<IdentID>,
dst: S::Var,
f: S::Func,
args: Vec<S::Var>,
},
AsmBlock {
instructions: Vec<RV64Instruction>,
args: Vec<AsmBlockArg>,
instructions: Vec<RV64Instruction<S::Var>>,
args: Vec<AsmBlockArg<S::Var>>,
},
Ret {
src: IdentID,
src: S::Var,
},
Construct {
dst: IdentID,
struc: IdentID,
fields: HashMap<String, IdentID>,
dst: S::Var,
struc: S::Struct,
fields: HashMap<String, S::Var>,
},
If {
cond: IdentID,
body: Vec<UInstrInst>,
cond: S::Var,
body: Vec<UInstrInst<S>>,
},
Loop {
body: Vec<UInstrInst>,
body: Vec<UInstrInst<S>>,
},
Break,
Continue,
}
#[derive(Clone)]
pub struct UInstrInst {
pub i: UInstruction,
pub struct UInstrInst<S: ResStage = Unresolved> {
pub i: UInstruction<S>,
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<V = IdentID> {
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(())
}
}

View File

@@ -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<String>;
@@ -16,7 +44,6 @@ pub type StructID = ID<UStruct>;
pub type DataID = ID<UData>;
pub type ModID = ID<UModule>;
#[derive(Clone)]
pub struct UFunc {
pub name: String,
pub origin: Origin,
@@ -26,12 +53,12 @@ pub struct UFunc {
pub instructions: Vec<UInstrInst>,
}
#[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<GenericID>,
}
#[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<String, VarID>,
}
/// eg. a::b::c::<T,U>.d.e
#[derive(Clone, Debug)]
/// a generic identifier for all (identifiable) kinds
/// eg. a::b::c.d.e
/// or a::Result<T,_>
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<TypeID>,
fields: Vec<MemberID>,
base: ResBase,
path: Vec<MemberIdent>,
},
PartialVar {
id: VarID,
fields: Vec<MemberID>,
},
Failed(ResErr),
Failed(Option<ResErr>),
Cooked,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct MemberID {
pub struct MemberIdent {
pub ty: MemberTy,
pub name: String,
pub gargs: Vec<TypeID>,
pub origin: Origin,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct ModPath {
pub id: ModID,
pub path: Vec<MemberID>,
#[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<String, Member>,
pub children: HashMap<String, ModID>,
pub parent: Option<ModID>,
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<GenericID>,
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<TypeID>,
}
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<VarID> {
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<Item = &UInstrInst> {
InstrIter::new(self.instructions.iter())
@@ -162,4 +335,3 @@ impl<'a> Iterator for InstrIter<'a> {
Some(next)
}
}

View File

@@ -1,8 +1,5 @@
use super::*;
use std::{
collections::HashMap,
ops::{Deref, DerefMut},
};
use std::collections::HashMap;
pub struct UProgram {
pub fns: Vec<UFunc>,
@@ -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<T>(v: &mut Vec<T>, t: T) -> ID<T> {
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<Type> {
&**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
}
}

View File

@@ -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::<UFunc>(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::<UVar, UVar>(dest, src, src);
}
res |= data.match_types(dst, f.ret, dst);
res |= data.match_types::<UVar, Type>(dst, f.ret, dst);
}
UInstruction::Mv { dst, src } => {
res |= data.match_types(dst, src, src);
res |= data.match_types::<UVar, UVar>(dst, src, src);
}
UInstruction::Ref { dst, src } => {
let dstid = data.res_ty_id::<UVar>(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::<Type, UVar>(dest_ty, src, src);
}
UInstruction::Deref { dst, src } => {
let srcid = data.res_ty_id::<UVar>(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::<UVar, Type>(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::<UVar, Type>(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::<Type, UVar>(ctx.ret, src, src);
}
UInstruction::Construct { dst, struc, fields } => {
let si = data.res_id::<UStruct>(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::<Type, UVar>(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::<UVar>(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<Item = TypeID>,
src: impl Iterator<Item = TypeID>,
) -> MatchRes {
@@ -353,30 +314,24 @@ struct ResData<'a> {
errs: Vec<ResErr>,
}
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<Dst: ResKind, Src: ResKind>(
&mut self,
dst: impl Resolvable<Type>,
src: impl Resolvable<Type>,
dst: impl Resolvable<Dst>,
src: impl Resolvable<Src>,
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<K: ResKind>(&mut self, x: impl Resolvable<K>) -> Result<K::Res, InstrRes> {
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<K>,
x: impl Resolvable<UVar>,
ctx: ResolveCtx<'b>,
) -> Option<TypeID>
where
K::Res: TypeIDed,
{
self.res_id::<K>(x, ctx).map(|i| i.type_id(&self.s))
) -> Option<TypeID> {
self.res_id::<UVar>(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<TypeID, InstrRes> {
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::<Type>(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<Type>,
errs: &mut Vec<ResErr>,
) -> Result<Res, Option<ResErr>> {
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<Type>,
changed: &mut bool,
errs: &mut Vec<ResErr>,
) -> Result<Res, InstrRes> {
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<K: ResKind> {
s: &mut Sources,
types: &mut Vec<Type>,
errs: &mut Vec<ResErr>,
changed: &mut bool,
) -> Result<K::Res, InstrRes>;
}
impl<T: TypeIDed> Resolvable<Type> for T {
fn try_res(
&self,
s: &mut Sources,
_: &mut Vec<Type>,
errs: &mut Vec<ResErr>,
) -> Result<TypeID, InstrRes> {
Ok(self.type_id(s))
}
}
impl IdentID {
pub fn resolve(self, s: &mut Sources) -> Result<Res, InstrRes> {
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<K: ResKind> Resolvable<K> for IdentID {
fn try_res(
&self,
s: &mut Sources,
types: &mut Vec<Type>,
errs: &mut Vec<ResErr>,
changed: &mut bool,
) -> Result<K::Res, InstrRes> {
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<K: ResKind> Resolvable<K> 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<K: ResKind> Resolvable<K> for &IdentID {
fn try_res(
&self,
s: &mut Sources,
types: &mut Vec<Type>,
errs: &mut Vec<ResErr>,
changed: &mut bool,
) -> Result<K::Res, InstrRes> {
Resolvable::<K>::try_res(*self, s, types, errs, changed)
}
}
#[derive(Debug, Clone)]
pub enum Res {
Var(VarID),
Fn(FnInst),
Struct(StructInst),
Type(TypeID),
impl Resolvable<UVar> for VarID {
fn try_res(
&self,
s: &mut Sources,
types: &mut Vec<Type>,
errs: &mut Vec<ResErr>,
changed: &mut bool,
) -> Result<<UVar as ResKind>::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<Type> for TypeID {
fn try_res(
&self,
s: &mut Sources,
types: &mut Vec<Type>,
errs: &mut Vec<ResErr>,
changed: &mut bool,
) -> Result<<Type as ResKind>::Res, InstrRes> {
Ok(*self)
}
}
@@ -646,8 +646,7 @@ pub trait ResKind {
types: &mut Vec<Type>,
s: &mut Sources,
origin: Origin,
errs: &mut Vec<ResErr>,
) -> Option<Self::Res>;
) -> Result<Self::Res, Res>;
}
impl ResKind for UFunc {
@@ -655,16 +654,10 @@ impl ResKind for UFunc {
fn ty() -> KindTy {
KindTy::Fn
}
fn from_res(
res: Res,
_: &mut Vec<Type>,
_: &mut Sources,
_: Origin,
_: &mut Vec<ResErr>,
) -> Option<Self::Res> {
fn from_res(res: Res, _: &mut Vec<Type>, _: &mut Sources, _: Origin) -> Result<Self::Res, Res> {
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<Type>,
s: &mut Sources,
origin: Origin,
errs: &mut Vec<ResErr>,
) -> Option<Self::Res> {
Some(match res {
Res::Fn(fty) => inst_fn_var(&fty, s.fns, origin, s.vars, types, s.generics, errs),
) -> Result<Self::Res, Res> {
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<Type>,
_: &mut Sources,
_: Origin,
_: &mut Vec<ResErr>,
) -> Option<Self::Res> {
fn from_res(res: Res, _: &mut Vec<Type>, _: &mut Sources, _: Origin) -> Result<Self::Res, Res> {
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<Type>,
s: &mut Sources,
_: Origin,
errs: &mut Vec<ResErr>,
) -> Option<Self::Res> {
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<Self::Res, Res> {
Ok(match res {
Res::Struct(si) => push_id(types, Type::Struct(si)),
Res::Type(id) => id,
_ => return Err(res),
})
}
}
impl<K: ResKind> Resolvable<K> for &IdentID {
fn try_res(
&self,
s: &mut Sources,
types: &mut Vec<Type>,
errs: &mut Vec<ResErr>,
) -> Result<K::Res, InstrRes> {
Resolvable::<K>::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<Type>) {}
}
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<Type>) {
inst_var(s.vars, s.structs, *self, types);
}
}
impl TypeIDed for DataID {
@@ -769,8 +745,8 @@ impl<T: TypeIDed> TypeIDed for &T {
}
}
impl FromResidual<std::result::Result<std::convert::Infallible, InstrRes>> for InstrRes {
fn from_residual(residual: std::result::Result<std::convert::Infallible, InstrRes>) -> Self {
impl FromResidual<Result<Infallible, InstrRes>> for InstrRes {
fn from_residual(residual: Result<Infallible, InstrRes>) -> Self {
match residual {
Ok(_) => unreachable!(),
Err(r) => r,
@@ -787,3 +763,11 @@ impl HasOrigin for &IdentID {
data.s.idents[*self].origin
}
}
impl FromResidual<Result<Infallible, MatchRes>> for MatchRes {
fn from_residual(residual: Result<Infallible, MatchRes>) -> Self {
match residual {
Ok(_) => unreachable!(),
Err(r) => r,
}
}
}

View File

@@ -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<TypeID>,
}
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
pub struct FnInst {
pub id: FnID,
/// assumed to be valid
pub gargs: Vec<TypeID>,
}
@@ -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<UVar>,
types: &mut Vec<Type>,
generics: &[UGeneric],
errs: &mut Vec<ResErr>,
) -> 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<Type>,
generics: &[UGeneric],
errs: &mut Vec<ResErr>,
) -> 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<UVar>,
types: &mut Vec<Type>,
generics: &[UGeneric],
errs: &mut Vec<ResErr>,
) -> 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<Type>,
pub fn inst_var(vars: &mut Vec<UVar>, structs: &[UStruct], id: VarID, types: &mut Vec<Type>) {
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<ResErr>,
) -> 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<ResErr>> {
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<Type>,
// will be needed when constraints are added
_generics: &[UGeneric],
errs: &mut Vec<ResErr>,
) -> Vec<TypeID> {
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<Type>) -> TypeID {
let gmap = inst_gmap(&def.gargs, &gargs);
inst_type(def.ty, types, &gmap)
}
pub fn inst_gmap(dst: &[GenericID], src: &[TypeID]) -> HashMap<GenericID, TypeID> {
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<Type>, gmap: &HashMap<GenericID, TypeID>) -> TypeID {
inst_type_ins(push_id, id, types, gmap)
}
pub fn inst_type_ins(
insert: impl Fn(&mut Vec<Type>, Type) -> TypeID,
id: TypeID,
types: &mut Vec<Type>,
gmap: &HashMap<GenericID, TypeID>,
) -> 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> = (&T, &U)