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

40
ideas
View File

@@ -1,21 +1,27 @@
struct
move names into separate vec with origins
make struct fields a vec, resolve into index
Type
structinst
bits
but type is already typeinst?
for each var, create list of constraints on type
then just need to iterate through constraints to determine type
keep doing passes for vars that depend on the type of other vars
really need to make subvar for each field of struct var so 2 different "a.b" refer to same thing
makes borrow checking easier
do dependency cycles exist?
for global vars yes, in functions no
but if function returns are inferrable (even if just "impl Trait"), then needed in functions?
every kind has an origin, should make separate like names?
inner values that auto generate map function:
enum Thing<inner T> {
A(T),
B(T, T),
C
}
or
#[derive(Map(T))]
enum Thing<T> { ... }
{([<
std::Option:(u32)::Some(3)
func:(u32)("hello", test, 3);
std::Option:[u32]::Some(3)
func:[T]("hello", test, 3);
std::Option::<u32>::Some(3)
func::<u32>(3)
std.Option.[u32].Some(3)
func.[T]("hello", test, 3);
std::Option:<u32>::Some(3)
func:<u32>(3)
TYPE IDS!!!!! MY SAVIOR!!! MY GOAT!!!

View File

@@ -68,6 +68,83 @@ pub enum LinkerInstruction<R = Reg, S = Symbol> {
imm: i32,
},
}
impl<R, S> LinkerInstruction<R, S> {
pub fn map<R2, S2>(&self, r: impl Fn(&R) -> R2) -> LinkerInstruction<R2, S2> {
self.try_map(|v| Some(r(v))).unwrap()
}
pub fn try_map<R2, S2>(&self, r: impl Fn(&R) -> Option<R2>) -> Option<LinkerInstruction<R2, S2>> {
use LinkerInstruction as I;
Some(match self {
Self::ECall => I::ECall,
Self::EBreak => I::EBreak,
&Self::Li { ref dest, imm } => I::Li { dest: r(dest)?, imm },
Self::Mv { ref dest, src } => I::Mv {
dest: r(dest)?,
src: r(src)?,
},
Self::La { .. } => todo!(),
&Self::Load {
width,
ref dest,
ref base,
offset,
} => I::Load {
width,
dest: r(dest)?,
offset,
base: r(base)?,
},
&Self::Store {
width,
ref src,
ref base,
offset,
} => I::Store {
width,
src: r(src)?,
offset,
base: r(base)?,
},
&Self::Op {
op,
funct,
ref dest,
ref src1,
ref src2,
} => I::Op {
op,
funct,
dest: r(dest)?,
src1: r(src1)?,
src2: r(src2)?,
},
&Self::OpImm { op, ref dest, ref src, imm } => I::OpImm {
op,
dest: r(dest)?,
src: r(src)?,
imm,
},
&Self::OpImmF7 {
op,
funct,
ref dest,
ref src,
imm,
} => I::OpImmF7 {
op,
funct,
dest: r(dest)?,
src: r(src)?,
imm,
},
Self::Ret => I::Ret,
Self::Call(..) => todo!(),
Self::Jal { .. } => todo!(),
Self::J(..) => todo!(),
Self::Branch { .. } => todo!(),
})
}
}
pub fn addi(dest: Reg, src: Reg, imm: BitsI32<11, 0>) -> RawInstruction {
opi(op32i::ADD, dest, src, imm.to_u())

View File

@@ -2,10 +2,7 @@ use std::collections::HashMap;
use crate::{
compiler::{arch::riscv::Reg, debug::DebugInfo, UnlinkedFunction, UnlinkedProgram},
ir::{
arch::riscv64::{RV64Instruction as AI, RegRef},
LInstruction as IRI, LProgram, Len, Size,
},
ir::{arch::riscv64::RegRef, LInstruction as IRI, LProgram, Len, Size, VarID},
};
use super::{LinkerInstruction as LI, *};
@@ -103,8 +100,8 @@ pub fn compile(program: &LProgram) -> UnlinkedProgram<LI> {
irli.push((v.len(), format!("{i:?}")));
match i {
IRI::Mv {
dest,
dest_offset,
dst: dest,
dst_offset: dest_offset,
src,
src_offset,
} => {
@@ -119,11 +116,15 @@ pub fn compile(program: &LProgram) -> UnlinkedProgram<LI> {
s,
);
}
IRI::Ref { dest, src } => {
IRI::Ref { dst: dest, src } => {
v.push(LI::addi(t0, sp, stack[src]));
v.push(LI::sd(t0, stack[dest], sp));
}
IRI::LoadAddr { dest, offset, src } => {
IRI::LoadAddr {
dst: dest,
offset,
src,
} => {
v.extend([
LI::La {
dest: t0,
@@ -133,7 +134,7 @@ pub fn compile(program: &LProgram) -> UnlinkedProgram<LI> {
]);
}
IRI::LoadData {
dest,
dst: dest,
offset,
src,
len,
@@ -144,7 +145,7 @@ pub fn compile(program: &LProgram) -> UnlinkedProgram<LI> {
});
mov_mem(&mut v, t0, 0, sp, stack[dest] + *offset as i32, t1, *len);
}
IRI::Call { dest, f, args } => {
IRI::Call { dst: dest, f, args } => {
let mut offset = 0;
if let Some((dest, s)) = dest {
offset -= align(s);
@@ -166,82 +167,14 @@ pub fn compile(program: &LProgram) -> UnlinkedProgram<LI> {
for (reg, var) in inputs {
v.push(LI::ld(*reg, stack[var], sp));
}
fn r(rr: RegRef) -> Reg {
fn r(rr: &RegRef<VarID>) -> Reg {
match rr {
RegRef::Var(..) => todo!(),
RegRef::Reg(reg) => reg,
RegRef::Reg(reg) => *reg,
}
}
for i in instructions {
match *i {
AI::ECall => v.push(LI::ECall),
AI::EBreak => v.push(LI::EBreak),
AI::Li { dest, imm } => v.push(LI::Li { dest: r(dest), imm }),
AI::Mv { dest, src } => v.push(LI::Mv {
dest: r(dest),
src: r(src),
}),
AI::La { .. } => todo!(),
AI::Load {
width,
dest,
base,
offset,
} => v.push(LI::Load {
width,
dest: r(dest),
offset,
base: r(base),
}),
AI::Store {
width,
src,
base,
offset,
} => v.push(LI::Store {
width,
src: r(src),
offset,
base: r(base),
}),
AI::Op {
op,
funct,
dest,
src1,
src2,
} => v.push(LI::Op {
op,
funct,
dest: r(dest),
src1: r(src1),
src2: r(src2),
}),
AI::OpImm { op, dest, src, imm } => v.push(LI::OpImm {
op,
dest: r(dest),
src: r(src),
imm,
}),
AI::OpImmF7 {
op,
funct,
dest,
src,
imm,
} => v.push(LI::OpImmF7 {
op,
funct,
dest: r(dest),
src: r(src),
imm,
}),
AI::Ret => v.push(LI::Ret),
AI::Call(..) => todo!(),
AI::Jal { .. } => todo!(),
AI::J(..) => todo!(),
AI::Branch { .. } => todo!(),
}
v.push(i.map(|v| r(v)));
}
for (reg, var) in outputs {
v.push(LI::sd(*reg, stack[var], sp));

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)

View File

@@ -2,6 +2,7 @@
#![feature(try_trait_v2)]
#![feature(trait_alias)]
#![feature(let_chains)]
#![feature(iterator_try_collect)]
// dawg what
#![feature(str_as_str)]

View File

@@ -12,7 +12,7 @@ impl RV64Instruction {
let args = &inst.args[..];
let opstr = &**inst.op.inner.as_ref()?;
// TODO: surely this can be abstracted...
let opi = |ctx: &mut FnLowerCtx<'_>, op: Funct3| -> Option<Self> {
let opi = |ctx: &mut FnLowerCtx<'_, '_, '_>, op: Funct3| -> Option<Self> {
let [dest, src, imm] = args else {
ctx.err(format!("{opstr} requires 3 arguments"));
return None;
@@ -22,7 +22,7 @@ impl RV64Instruction {
let imm = i32_from_arg(imm, ctx)?;
Some(Self::OpImm { op, dest, src, imm })
};
let op = |ctx: &mut FnLowerCtx<'_>, op: Funct3, funct: Funct7| -> Option<Self> {
let op = |ctx: &mut FnLowerCtx<'_, '_, '_>, op: Funct3, funct: Funct7| -> Option<Self> {
let [dest, src1, src2] = args else {
ctx.err(format!("{opstr} requires 3 arguments"));
return None;
@@ -174,7 +174,7 @@ pub fn arg_to_var(node: &Node<PAsmArg>, ctx: &mut FnLowerCtx) -> Option<UIdent>
);
return None;
};
ctx.var(node)
ctx.ident(node)
}
impl RegRef {
@@ -184,7 +184,7 @@ impl RegRef {
let reg = Reg::from_ident(node, ctx)?;
Self::Reg(reg)
}
PAsmArg::Ref(node) => Self::Var(ctx.var(node)?),
PAsmArg::Ref(node) => Self::Var(ctx.ident(node)?),
})
}
}

View File

@@ -1,8 +1,7 @@
use crate::{
compiler::arch::riscv::Reg,
ir::{
arch::riscv64::RV64Instruction, AsmBlockArg, AsmBlockArgType, Type, UInstruction, UIdent,
IdentID,
arch::riscv64::RV64Instruction, AsmBlockArg, AsmBlockArgType, IdentID, Type, UInstruction,
},
parser::PAsmBlockArg,
};

View File

@@ -1,5 +1,5 @@
use crate::{
ir::{Type, UInstruction, UVar, UIdent, IdentID},
ir::{IdentID, Type, UIdent, UInstruction, UVar},
parser::{PConstStatement, PStatementLike},
};
@@ -8,6 +8,7 @@ use super::{FnLowerCtx, FnLowerable, Import, PBlock, PStatement};
impl FnLowerable for PBlock {
type Output = IdentID;
fn lower(&self, ctx: &mut FnLowerCtx) -> Option<IdentID> {
ctx.ident_stack.push();
let mut last = None;
let mut statements = Vec::new();
let mut fn_nodes = Vec::new();
@@ -27,64 +28,58 @@ impl FnLowerable for PBlock {
},
}
}
ctx.b.push();
// then lower imports
for i_n in &import_nodes {
if let Some(i) = i_n.as_ref() {
let name = &i.0;
let path = ctx.b.path_for(name);
let path = ctx.path_for(name);
let import = Import(path.clone());
if ctx.imports.insert(import) {
ctx.b.def_searchable::<UVar>(
name,
Some(UVar {
ty: Type::Module(path),
}),
i_n.origin,
);
ctx.def_var(UVar {
name: name.clone(),
ty: Type::Module(path),
origin: i_n.origin,
});
}
}
}
// then lower const things
let mut structs = Vec::new();
for s in &struct_nodes {
structs.push(s.lower_name(ctx.b));
structs.push(s.lower(ctx.ctx));
}
for (s, id) in struct_nodes.iter().zip(structs) {
if let Some(id) = id {
s.lower(id, ctx.b, ctx.output);
s.lower(ctx.ctx);
}
}
let mut fns = Vec::new();
for f in &fn_nodes {
fns.push(f.lower_name(ctx.b));
fns.push(f.lower(ctx.ctx));
}
for (f, id) in fn_nodes.iter().zip(fns) {
if let Some(id) = id {
f.lower(id, ctx.b, ctx.imports, ctx.output)
f.lower(ctx.ctx);
}
}
// then lower statements
for s in statements {
last = s.lower(ctx);
}
ctx.b.pop();
ctx.ident_stack.pop();
last
}
}
impl FnLowerable for PStatement {
type Output = UIdent;
fn lower(&self, ctx: &mut FnLowerCtx) -> Option<UIdent> {
type Output = IdentID;
fn lower(&self, ctx: &mut FnLowerCtx) -> Option<IdentID> {
match self {
PStatement::Let(def, e) => {
let def = def.lower(ctx.b, ctx.output)?;
let def = def.lower(ctx.ctx)?;
let res = e.lower(ctx);
if let Some(res) = res {
ctx.push(UInstruction::Mv {
dst: def,
src: res,
});
ctx.push(UInstruction::Mv { dst: def, src: res });
}
None
}

View File

@@ -1,18 +1,23 @@
use crate::ir::{UProgram, UVar, VarID, UIdent};
use std::collections::HashMap;
use super::{CompilerOutput, Node, PVarDef};
use crate::ir::{UVar, VarID};
use super::{ModuleLowerCtx, Node, PVarDef};
impl Node<PVarDef> {
pub fn lower(&self, program: &mut UProgram, output: &mut CompilerOutput) -> Option<VarID> {
pub fn lower(&self, ctx: &mut ModuleLowerCtx) -> Option<VarID> {
let s = self.as_ref()?;
let name = s.name.as_ref().map_or("{error}", |v| v);
let name = s.name.as_ref().map_or("{error}", |v| v).to_string();
let ty = match &s.ty {
Some(ty) => ty.lower(program, output),
None => program.infer(self.origin),
Some(ty) => ty.lower(ctx),
None => ctx.infer(),
};
Some(UIdent {
id: program.def_searchable(name, Some(UVar { ty }), self.origin),
Some(ctx.def_var(UVar {
name,
ty,
origin: self.origin,
})
parent: None,
children: HashMap::new(),
}))
}
}

View File

@@ -1,6 +1,6 @@
use super::{func::FnLowerCtx, FnLowerable, PExpr, PostfixOp};
use crate::{
ir::{Type, UData, UInstruction, UIdent, IdentID},
ir::{IdentID, IdentStatus, MemRes, Member, MemberID, MemberIdent, Type, UData, UInstruction},
parser::InfixOp,
};
@@ -9,22 +9,49 @@ impl FnLowerable for PExpr {
fn lower(&self, ctx: &mut FnLowerCtx) -> Option<IdentID> {
let mut e = self;
let mut path = Vec::new();
while let PExpr::Member(node, ident) = e {
e = if let Some(t) = node.as_ref() {
path.push(ident);
&**t
} else {
return None;
};
let mut gargs = None;
loop {
match e {
PExpr::Member(node, ty, ident) => {
e = if let Some(t) = node.as_ref() {
ctx.origin = node.origin;
path.push((ty, ident, gargs.unwrap_or_default()));
&**t
} else {
return None;
};
}
PExpr::Generic(node, nodes) => match gargs {
None => gargs = Some(nodes.iter().map(|t| t.lower(ctx)).collect::<Vec<_>>()),
Some(_) => {
// this should cover the more specific area of ::<...>
// but too lazy rn
ctx.err("Cannot specify generics here".to_string());
return None;
}
},
_ => break,
}
}
while let PExpr::Member(node, ty, ident) = e {}
if path.len() > 0 {
// UIdent {
// origin: ctx.origin,
// status: IdentStatus::Unres { base: (), path: () },
// }
}
let origin = ctx.origin;
Some(match e {
PExpr::Lit(l) => match l {
super::PLiteral::String(s) => {
let dst = ctx.temp_var(ctx.origin, Type::Bits(8).slice(ctx.p));
let sty = Type::Bits(8).slice(ctx.p);
let dst = ctx.temp_var(origin, sty);
let data = s.as_bytes().to_vec();
let dty = Type::Bits(8).arr(ctx.ctx.p, data.len() as u32);
let dty = ctx.def_ty(dty);
let src = ctx.def_data(UData {
name: format!("string \"{}\"", s.replace("\n", "\\n")),
ty: ctx.def_ty(Type::Bits(8).arr(ctx.b.p, data.len() as u32)),
ty: dty,
content: data,
});
ctx.push(UInstruction::LoadSlice { dst, src });
@@ -32,7 +59,7 @@ impl FnLowerable for PExpr {
}
super::PLiteral::Char(c) => {
let ty = ctx.def_ty(Type::Bits(8));
let dst = ctx.temp_var(ctx.origin, ty.clone());
let dst = ctx.temp_var(origin, ty.clone());
let src = ctx.def_data(UData {
name: format!("char '{c}'"),
ty,
@@ -44,7 +71,7 @@ impl FnLowerable for PExpr {
super::PLiteral::Number(n) => {
// TODO: temp
let ty = ctx.def_ty(Type::Bits(64));
let dst = ctx.temp_var(ctx.origin, ty.clone());
let dst = ctx.temp_var(origin, ty.clone());
let src = ctx.def_data(UData {
name: format!("num {n:?}"),
ty,
@@ -53,9 +80,9 @@ impl FnLowerable for PExpr {
ctx.push(UInstruction::LoadData { dst, src });
dst
}
super::PLiteral::Unit => ctx.temp_var(ctx.origin, Type::Unit),
super::PLiteral::Unit => ctx.temp_var(origin, Type::Unit),
},
PExpr::Ident(i) => ctx.var(i),
PExpr::Ident(i) => ctx.ident(i),
PExpr::BinaryOp(op, e1, e2) => match op {
InfixOp::Add => todo!(),
InfixOp::Sub => todo!(),
@@ -77,7 +104,7 @@ impl FnLowerable for PExpr {
let res = e.lower(ctx)?;
match op {
PostfixOp::Ref => {
let ty = Type::Ref(ctx.b.infer());
let ty = Type::Ref(ctx.ctx.infer());
let dest = ctx.temp(ty);
ctx.push(UInstruction::Ref {
dst: dest,
@@ -86,7 +113,7 @@ impl FnLowerable for PExpr {
dest
}
PostfixOp::Deref => {
let ty = Type::Deref(ctx.b.infer());
let ty = Type::Deref(ctx.ctx.infer());
let dst = ctx.temp(ty);
ctx.push(UInstruction::Deref { dst, src: res });
dst
@@ -121,20 +148,20 @@ impl FnLowerable for PExpr {
}
PExpr::If(cond, body) => {
let cond = cond.lower(ctx)?;
ctx.var_stack.push();
ctx.ident_stack.push();
let mut body_ctx = ctx.branch();
body.lower(&mut body_ctx);
let body = body_ctx.instructions;
ctx.var_stack.pop();
ctx.ident_stack.pop();
ctx.push(UInstruction::If { cond, body });
return None;
}
PExpr::Loop(body) => {
ctx.var_stack.push();
ctx.ident_stack.push();
let mut body_ctx = ctx.branch();
body.lower(&mut body_ctx);
let body = body_ctx.instructions;
ctx.var_stack.pop();
ctx.ident_stack.pop();
ctx.push(UInstruction::Loop { body });
return None;
}
@@ -146,13 +173,34 @@ impl FnLowerable for PExpr {
ctx.push(UInstruction::Continue);
return None;
}
PExpr::Member(e, name) => {
ctx.err("Can't access a member here".to_string());
PExpr::Member(e, ty, name) => {
let id = e.lower(ctx)?;
let name_str = name.as_ref()?.0;
let cur = &mut ctx.p.idents[id];
match cur.status {
IdentStatus::Res(res) => {
cur.status = IdentStatus::Unres {
base: MemRes {
mem: Member {
id: MemberID
},
origin: (),
gargs: (),
},
path: (),
}
}
IdentStatus::Unres { base, path } => path.push(MemberIdent {
ty: *ty,
name: name_str,
origin: name.origin,
gargs: Vec::new(),
}),
IdentStatus::Failed(res_err) => return None,
IdentStatus::Cooked => return None,
}
return None;
}
PExpr::Field(e, name) => {
todo!()
}
})
}
}

View File

@@ -1,74 +1,68 @@
use std::{
collections::HashMap,
ops::{Deref, DerefMut},
};
use std::ops::{Deref, DerefMut};
use super::{CompilerMsg, CompilerOutput, FileSpan, FnLowerable, Imports, Node, PFunction};
use super::{CompilerMsg, FileSpan, ModuleLowerCtx, Node, PFunction, Typable};
use crate::{
ir::{
FnID, GenericID, ModPath, Origin, Typable, Type, UFunc, UInstrInst, UInstruction,
UModuleBuilder, VarID, UIdent, IdentID, IdentStatus,
FnID, IdentID, IdentStatus, MemRes, Member, MemberID, MemberIdent, MemberPath, MemberTy,
Origin, Res, Type, UFunc, UIdent, UInstrInst, UInstruction,
},
parser,
util::NameStack,
};
impl Node<PFunction> {
pub fn lower(
&self,
b: &mut UModuleBuilder,
imports: &mut Imports,
output: &mut CompilerOutput,
) -> Option<FnID> {
self.as_ref()
.map(|s| s.lower(b, imports, output, self.origin))
.flatten()
pub fn lower(&self, ctx: &mut ModuleLowerCtx) -> Option<FnID> {
self.as_ref().map(|s| s.lower(ctx, self.origin)).flatten()
}
}
impl PFunction {
pub fn lower(
&self,
b: &mut UModuleBuilder,
imports: &mut Imports,
output: &mut CompilerOutput,
origin: Origin,
) -> Option<FnID> {
pub fn lower(&self, ctx: &mut ModuleLowerCtx, origin: Origin) -> Option<FnID> {
let header = self.header.as_ref()?;
let name = header.name.as_ref()?.0.clone();
let (generics, args, ret) = if let Some(header) = self.header.as_ref() {
(
header.gargs.iter().flat_map(|a| a.lower(b)).collect(),
header
.gargs
.iter()
.flat_map(|a| a.lower(ctx).map(|g| (g.0, g.1, a.origin)))
.collect(),
header
.args
.iter()
.flat_map(|a| Some(a.lower(b, output)?))
.flat_map(|a| Some(a.lower(ctx)?))
.collect(),
match &header.ret {
Some(ty) => ty.lower(b, output),
None => b.def_ty(Type::Unit),
Some(ty) => ty.lower(ctx),
None => ctx.def_ty(Type::Unit),
},
)
} else {
(Vec::new(), Vec::new(), b.tc.error)
(Vec::new(), Vec::new(), ctx.tc.error)
};
let gargs = generics.iter().map(|g| g.1).collect();
let generics = generics.into_iter().collect();
let generics = generics
.into_iter()
.map(|g| {
(
g.0,
ctx.def_ident(UIdent {
status: IdentStatus::Res(Res::Generic(g.1)),
origin: g.2,
}),
)
})
.collect::<Vec<_>>();
ctx.ident_stack.extend(generics.into_iter());
let instructions = {
let mut var_stack = NameStack::new();
let mut ctx = FnLowerCtx {
let mut fctx = FnLowerCtx {
instructions: Vec::new(),
var_stack: &mut var_stack,
b,
output,
ctx,
origin: self.body.origin,
generics: &generics,
imports,
};
let res = self.body.lower(&mut ctx);
let mut instructions = ctx.instructions;
let res = self.body.lower(&mut fctx);
let mut instructions = fctx.instructions;
if let Some(src) = res {
let origin = b.idents[src].origin;
let origin = ctx.idents[src].origin;
instructions.push(UInstrInst {
origin,
i: UInstruction::Ret { src },
@@ -84,35 +78,37 @@ impl PFunction {
ret,
instructions,
};
Some(b.def_fn(f))
Some(ctx.def_fn(f))
}
}
pub struct FnLowerCtx<'a, 'b> {
pub b: &'a mut UModuleBuilder<'b>,
pub ctx: &'a mut ModuleLowerCtx<'b>,
pub instructions: Vec<UInstrInst>,
pub output: &'a mut CompilerOutput,
pub origin: FileSpan,
pub imports: &'a mut Imports,
pub var_stack: &'a mut NameStack<VarID>,
pub generics: &'a HashMap<String, GenericID>,
}
impl<'a, 'b> FnLowerCtx<'a, 'b> {
pub fn var(&mut self, node: &Node<parser::PIdent>) -> IdentID {
pub fn ident(&mut self, node: &Node<parser::PIdent>) -> IdentID {
let inst = UIdent {
status: if let Some(n) = node.as_ref() {
if let Some(&var) = self.var_stack.search(&n.0) {
IdentStatus::Var(var)
if let Some(&res) = self.ident_stack.search(&n.0) {
return res;
} else {
IdentStatus::Unres {
path: ModPath {
id: self.b.module,
path: Vec::new(),
path: vec![MemberIdent {
ty: MemberTy::Member,
name: n.0.clone(),
origin: node.origin,
gargs: Vec::new(),
}],
base: MemRes {
mem: Member {
id: MemberID::Module(self.module),
},
origin: self.origin,
gargs: Vec::new(),
},
name: n.0.clone(),
gargs: Vec::new(),
fields: Vec::new(),
}
}
} else {
@@ -120,16 +116,17 @@ impl<'a, 'b> FnLowerCtx<'a, 'b> {
},
origin: node.origin,
};
self.def_var_inst(inst)
self.def_ident(inst)
}
pub fn err(&mut self, msg: String) {
self.output.err(CompilerMsg::new(msg, self.origin))
let origin = self.origin;
self.output.err(CompilerMsg::new(msg, origin))
}
pub fn err_at(&mut self, span: FileSpan, msg: String) {
self.output.err(CompilerMsg::new(msg, span))
}
pub fn temp<T: Typable>(&mut self, ty: T) -> IdentID {
self.b.temp_var(self.origin, ty)
self.ctx.temp_var(self.origin, ty)
}
pub fn push(&mut self, i: UInstruction) {
self.push_at(i, self.origin);
@@ -139,27 +136,46 @@ impl<'a, 'b> FnLowerCtx<'a, 'b> {
}
pub fn branch<'c>(&'c mut self) -> FnLowerCtx<'c, 'b> {
FnLowerCtx {
b: self.b,
ctx: self.ctx,
instructions: Vec::new(),
generics: self.generics,
var_stack: self.var_stack,
output: self.output,
origin: self.origin,
imports: self.imports,
}
}
}
impl<'b> Deref for FnLowerCtx<'_, 'b> {
type Target = UModuleBuilder<'b>;
type Target = ModuleLowerCtx<'b>;
fn deref(&self) -> &Self::Target {
self.b
self.ctx
}
}
impl DerefMut for FnLowerCtx<'_, '_> {
fn deref_mut(&mut self) -> &mut Self::Target {
self.b
self.ctx
}
}
pub trait FnLowerable {
type Output;
fn lower(&self, ctx: &mut FnLowerCtx) -> Option<Self::Output>;
}
impl<T: FnLowerable> FnLowerable for Node<T> {
type Output = T::Output;
fn lower(&self, ctx: &mut FnLowerCtx) -> Option<T::Output> {
let old_span = ctx.origin;
ctx.origin = self.origin;
let res = self.as_ref()?.lower(ctx);
ctx.origin = old_span;
res
}
}
impl<T: FnLowerable> FnLowerable for Box<T> {
type Output = T::Output;
fn lower(&self, ctx: &mut FnLowerCtx) -> Option<T::Output> {
self.as_ref().lower(ctx)
}
}

View File

@@ -4,65 +4,131 @@ mod block;
mod def;
mod expr;
mod func;
mod map;
mod struc;
mod ty;
mod map;
use std::{
collections::HashMap,
ops::{Deref, DerefMut},
};
use super::*;
use crate::ir::{Type, UFunc, UModuleBuilder};
use crate::{
ir::{
IdentID, IdentStatus, ModID, Origin, Res, Type, TypeID, UFunc, UIdent, UModule, UProgram,
UVar,
},
util::NameStack,
};
pub use func::{FnLowerCtx, FnLowerable};
impl PModule {
pub fn lower(
&self,
path: Vec<String>,
p: &mut UModuleBuilder,
p: &mut UProgram,
imports: &mut Imports,
output: &mut CompilerOutput,
) {
) -> ModID {
let name = path.last().unwrap().clone();
p.set_module(path);
let fid = p.def_searchable(&name, None, self.block.origin);
p.push_name(&name);
let mut fctx = FnLowerCtx {
b: p,
let f = UFunc {
name: name.clone(),
args: Vec::new(),
instructions: Vec::new(),
gargs: Vec::new(),
ret: p.def_ty(Type::Unit),
origin: self.block.origin,
};
let fid = p.def_fn(f);
let mid = p.def_module(UModule {
name,
members: HashMap::new(),
parent: None,
func: fid,
});
let mut ctx = ModuleLowerCtx {
p,
output,
module: mid,
temp: 0,
ident_stack: NameStack::new(),
};
let mut fctx = FnLowerCtx {
ctx: &mut ctx,
instructions: Vec::new(),
output,
origin: self.block.origin,
imports,
};
self.block.lower(&mut fctx);
let f = UFunc {
args: Vec::new(),
instructions: fctx.instructions,
ret: Type::Unit,
p.fns[fid].instructions = fctx.instructions;
mid
}
}
pub struct ModuleLowerCtx<'a> {
pub p: &'a mut UProgram,
pub output: &'a mut CompilerOutput,
pub module: ModID,
pub temp: usize,
pub ident_stack: NameStack<IdentID>,
}
impl<'a> ModuleLowerCtx<'a> {
pub fn new(program: &'a mut UProgram, output: &'a mut CompilerOutput, id: ModID) -> Self {
Self {
p: program,
output,
module: id,
temp: 0,
ident_stack: NameStack::new(),
}
}
pub fn temp_var(&mut self, origin: Origin, ty: impl Typable) -> IdentID {
self.temp_var_inner(origin, ty)
}
fn temp_var_inner(&mut self, origin: Origin, ty: impl Typable) -> IdentID {
let var = UVar {
name: format!("temp{}", self.temp),
ty: ty.ty(self),
origin,
parent: None,
children: HashMap::new(),
};
p.write(fid, f);
p.pop_name();
let id = self.p.def_var(var);
self.temp += 1;
self.def_ident(UIdent {
status: IdentStatus::Res(Res::Var(id)),
origin,
})
}
}
pub use func::FnLowerCtx;
use import::Imports;
pub trait FnLowerable {
type Output;
fn lower(&self, ctx: &mut FnLowerCtx) -> Option<Self::Output>;
pub trait Typable {
fn ty(self, p: &mut UProgram) -> TypeID;
}
impl<T: FnLowerable> FnLowerable for Node<T> {
type Output = T::Output;
fn lower(&self, ctx: &mut FnLowerCtx) -> Option<T::Output> {
let old_span = ctx.origin;
ctx.origin = self.origin;
let res = self.as_ref()?.lower(ctx);
ctx.origin = old_span;
res
impl Typable for Type {
fn ty(self, p: &mut UProgram) -> TypeID {
p.def_ty(self)
}
}
impl<T: FnLowerable> FnLowerable for Box<T> {
type Output = T::Output;
fn lower(&self, ctx: &mut FnLowerCtx) -> Option<T::Output> {
self.as_ref().lower(ctx)
impl Typable for TypeID {
fn ty(self, p: &mut UProgram) -> TypeID {
self
}
}
impl Deref for ModuleLowerCtx<'_> {
type Target = UProgram;
fn deref(&self) -> &Self::Target {
self.p
}
}
impl DerefMut for ModuleLowerCtx<'_> {
fn deref_mut(&mut self) -> &mut Self::Target {
self.p
}
}

View File

@@ -1,18 +1,16 @@
use crate::{
common::{CompilerOutput, FileSpan},
ir::{StructField, StructID, UModuleBuilder, UProgram, UStruct},
parser::{Node, PStruct, PStructFields},
common::FileSpan,
ir::{StructField, StructID, UStruct},
parser::{PStruct, PStructFields},
};
use super::ModuleLowerCtx;
impl PStruct {
pub fn lower(
&self,
id: StructID,
b: &mut UModuleBuilder,
output: &mut CompilerOutput,
span: FileSpan,
) -> Option<()> {
let generics = self.generics.iter().flat_map(|a| a.lower(b)).collect();
pub fn lower(&self, ctx: &mut ModuleLowerCtx, span: FileSpan) -> Option<StructID> {
ctx.ident_stack.push();
let gmap: Vec<_> = self.generics.iter().flat_map(|a| a.lower(ctx)).collect();
let gargs = gmap.iter().map(|(_, id)| *id).collect();
let fields = match &self.fields {
PStructFields::Named(nodes) => nodes
.iter()
@@ -20,7 +18,7 @@ impl PStruct {
let def = n.as_ref()?;
let name = def.name.as_ref()?.to_string();
let tynode = def.ty.as_ref()?;
let ty = tynode.lower(b, output);
let ty = tynode.lower(ctx);
Some((name, ty))
})
.collect(),
@@ -28,7 +26,7 @@ impl PStruct {
.iter()
.enumerate()
.flat_map(|(i, n)| {
let ty = n.as_ref()?.lower(b, output, span);
let ty = n.as_ref()?.lower(ctx, span);
Some((format!("{i}"), ty))
})
.collect(),
@@ -38,21 +36,12 @@ impl PStruct {
.map(|(name, ty)| (name, StructField { ty }))
.collect();
let name = self.name.as_ref()?.to_string();
b.def_data(UStruct {
ctx.ident_stack.pop();
Some(ctx.def_struct(UStruct {
name,
gargs: generics,
gargs,
fields,
origin: span,
});
Some(())
}
}
impl Node<PStruct> {
pub fn lower(&self, id: StructID, p: &mut UProgram, output: &mut CompilerOutput) -> Option<()> {
let s = self.as_ref()?;
let name = s.name.as_ref()?;
s.lower(id, p, output, self.origin);
Some(())
}))
}
}

View File

@@ -1,76 +1,76 @@
use crate::{
ir::{GenericID, MemberID, ModPath, Type, TypeID, UGeneric, UModuleBuilder, UProgram},
ir::{GenericID, MemberIdent, MemberPath, Type, TypeID, UGeneric, UProgram},
parser::PGenericDef,
};
use super::{CompilerOutput, FileSpan, Node, PType};
use super::{FileSpan, ModuleLowerCtx, Node, PType};
impl Node<Box<PType>> {
pub fn lower(&self, p: &mut UModuleBuilder, output: &mut CompilerOutput) -> TypeID {
pub fn lower(&self, ctx: &mut ModuleLowerCtx) -> TypeID {
self.as_ref()
.map(|t| t.lower(p, output, self.origin))
.unwrap_or(p.error)
.map(|t| t.lower(ctx, self.origin))
.unwrap_or(ctx.tc.error)
}
}
impl Node<PType> {
pub fn lower(&self, p: &mut UModuleBuilder, output: &mut CompilerOutput) -> TypeID {
pub fn lower(&self, ctx: &mut ModuleLowerCtx) -> TypeID {
self.as_ref()
.map(|t| t.lower(p, output, self.origin))
.unwrap_or(p.error)
.map(|t| t.lower(ctx, self.origin))
.unwrap_or(ctx.tc.error)
}
}
fn test() {}
impl PType {
pub fn lower(
&self,
p: &mut UModuleBuilder,
output: &mut CompilerOutput,
mut origin: FileSpan,
) -> TypeID {
pub fn lower(&self, ctx: &mut ModuleLowerCtx, mut origin: FileSpan) -> TypeID {
let mut ty = self;
let mut path = Vec::new();
while let PType::Member(node, ident) = ty {
ty = if let Some(t) = node.as_ref() {
let Some(name) = ident.as_ref() else {
return p.error;
return ctx.tc.error;
};
origin = node.origin;
path.push(MemberID {
path.push(MemberIdent {
name: name.0.clone(),
origin: ident.origin,
});
&**t
} else {
return p.error;
return ctx.tc.error;
};
}
if !path.is_empty() {
let PType::Ident(id) = ty else {
return p.error;
return ctx.tc.error;
};
path.push(MemberID {
path.push(MemberIdent {
name: id.0.clone(),
origin,
});
let ty = Type::Unres(ModPath { id: p.module, path });
return p.def_ty(ty);
path.reverse();
let ty = Type::Unres(MemberPath {
id: ctx.module,
path,
});
return ctx.def_ty(ty);
}
let ty = match ty {
PType::Member(_, _) => unreachable!(),
PType::Ident(node) => {
path.push(MemberID {
path.push(MemberIdent {
name: node.0.clone(),
origin,
});
path.reverse();
Type::Unres(ModPath { id: p.module, path })
Type::Unres(MemberPath {
id: ctx.module,
path,
})
}
PType::Ref(node) => node.lower(p, output).rf(),
PType::Ref(node) => node.lower(ctx).rf(),
PType::Generic(node, nodes) => todo!(),
};
p.def_ty(ty)
ctx.def_ty(ty)
}
}

View File

@@ -1,12 +1,12 @@
use std::fmt::{Debug, Write};
use crate::{common::FilePos, parser::NodeParsableWith};
use crate::{common::FilePos, ir::MemberTy, parser::NodeParsableWith};
use super::{
op::{InfixOp, PostfixOp},
util::parse_list,
CompilerMsg, Keyword, Node, PAsmBlock, PBlock, PIdent, PLiteral, PMap, Parsable, ParseResult,
ParserCtx, Symbol,
CompilerMsg, Keyword, Node, PAsmBlock, PBlock, PIdent, PLiteral, PMap, PType, Parsable,
ParseResult, ParserCtx, Symbol,
};
type BoxNode = Node<Box<PExpr>>;
@@ -19,8 +19,8 @@ pub enum PExpr {
Block(Node<PBlock>),
Call(BoxNode, Vec<Node<PExpr>>),
Group(BoxNode),
Field(BoxNode, Node<PIdent>),
Member(BoxNode, Node<PIdent>),
Member(BoxNode, MemberTy, Node<PIdent>),
Generic(BoxNode, Vec<Node<PType>>),
AsmBlock(Node<PAsmBlock>),
Construct(BoxNode, Node<PMap>),
If(BoxNode, BoxNode),
@@ -65,16 +65,25 @@ impl PExpr {
e1 = Self::Call(Node::new(e1, span).bx(), args);
continue;
} else if next.is_symbol(Symbol::OpenCurly) {
ctx.next();
let map = ctx.parse()?;
e1 = Self::Construct(Node::new(e1, span).bx(), map);
continue;
} else if next.is_symbol(Symbol::Dot) {
ctx.next();
let field = ctx.parse()?;
e1 = Self::Field(Node::new(e1, span).bx(), field);
e1 = Self::Member(Node::new(e1, span).bx(), MemberTy::Field, field);
continue;
} else if next.is_symbol(Symbol::DoubleColon) {
let field = ctx.parse()?;
e1 = Self::Member(Node::new(e1, span).bx(), field);
ctx.next();
if ctx.peek().is_some_and(|i| i.is_symbol(Symbol::OpenAngle)) {
ctx.next();
let gargs = parse_list(ctx, Symbol::CloseAngle)?;
e1 = Self::Generic(Node::new(e1, span).bx(), gargs);
} else {
let field = ctx.parse()?;
e1 = Self::Member(Node::new(e1, span).bx(), MemberTy::Member, field);
}
continue;
} else if let Some(op) = PostfixOp::from_token(next) {
ctx.next();
@@ -185,13 +194,13 @@ impl Debug for PExpr {
PExpr::PostfixOp(e, op) => write!(f, "({:?}{})", e, op.str())?,
PExpr::Group(inner) => inner.fmt(f)?,
PExpr::AsmBlock(inner) => inner.fmt(f)?,
PExpr::Construct(node, inner) => inner.fmt(f)?,
PExpr::Construct(node, inner) => write!(f, "{:?}{:?}", node, inner)?,
PExpr::If(cond, res) => write!(f, "if {cond:?} then {res:?}")?,
PExpr::Loop(res) => write!(f, "loop -> {res:?}")?,
PExpr::Break => write!(f, "break")?,
PExpr::Continue => write!(f, "continue")?,
PExpr::Field(e1, name) => write!(f, "{:?}.{:?}", e1, name)?,
PExpr::Member(e1, name) => write!(f, "{:?}::{:?}", e1, name)?,
PExpr::Member(e1, ty, name) => write!(f, "{:?}{}{:?}", e1, ty.sep(), name)?,
PExpr::Generic(e1, gargs) => write!(f, "{:?}<{:?}>", e1, gargs)?,
}
Ok(())
}

View File

@@ -1,23 +1,45 @@
use std::collections::HashMap;
pub struct NameStack<T>(Vec<HashMap<String, T>>);
pub struct NameStack<T> {
base: HashMap<String, T>,
levels: Vec<HashMap<String, T>>,
}
impl<T> NameStack<T> {
pub fn new() -> Self {
Self(vec![HashMap::new()])
Self {
base: HashMap::new(),
levels: Vec::new(),
}
}
pub fn search(&self, name: &str) -> Option<&T> {
for level in self.0.iter().rev() {
for level in self.levels.iter().rev() {
if let Some(v) = level.get(name) {
return Some(v);
}
}
None
self.base.get(name)
}
pub fn push(&mut self) {
self.0.push(HashMap::new());
self.levels.push(HashMap::new());
}
pub fn pop(&mut self) {
self.0.pop();
self.levels.pop();
}
fn cur(&mut self) -> &mut HashMap<String, T> {
self.levels.last_mut().unwrap_or(&mut self.base)
}
pub fn insert(&mut self, name: String, v: T) -> bool {
let cur = self.cur();
if cur.contains_key(&name) {
return true;
}
cur.insert(name, v);
false
}
pub fn extend(&mut self, iter: impl Iterator<Item = (String, T)>) {
for (name, v) in iter {
self.insert(name, v);
}
}
}