the light is getting closer

This commit is contained in:
2025-05-04 04:12:56 -04:00
parent 5f36be9de9
commit 6583d47ef8
23 changed files with 1261 additions and 740 deletions

View File

@@ -79,6 +79,20 @@ impl<T> IndexMut<&ID<T>> for Vec<T> {
}
}
impl<T> Index<&mut ID<T>> for Vec<T> {
type Output = T;
fn index(&self, i: &mut ID<T>) -> &Self::Output {
&self[i.0]
}
}
impl<T> IndexMut<&mut ID<T>> for Vec<T> {
fn index_mut(&mut self, i: &mut ID<T>) -> &mut Self::Output {
&mut self[i.0]
}
}
impl<T> Index<ID<T>> for [T] {
type Output = T;
@@ -106,3 +120,17 @@ impl<T> IndexMut<&ID<T>> for [T] {
&mut self[i.0]
}
}
impl<T> Index<&mut ID<T>> for [T] {
type Output = T;
fn index(&self, i: &mut ID<T>) -> &Self::Output {
&self[i.0]
}
}
impl<T> IndexMut<&mut ID<T>> for [T] {
fn index_mut(&mut self, i: &mut ID<T>) -> &mut Self::Output {
&mut self[i.0]
}
}

View File

@@ -1,9 +1,7 @@
use std::collections::HashMap;
use crate::ir::{
AsmBlockArgType, FnID, Size, GenericTy, SymbolSpace, Type, UFunc, UInstrInst, VarOffset,
};
use crate::ir::{AsmBlockArgType, Size, StructTy, SymbolSpace, Type, UFunc, UInstrInst, VarOffset};
StructTy
use super::{
IRLFunction, LInstruction, Len, Symbol, SymbolSpaceBuilder, UInstruction, UProgram, VarID,
};
@@ -16,11 +14,15 @@ pub struct LProgram {
// NOTE: there are THREE places here where I specify size (8)
impl LProgram {
pub fn create(p: &UProgram, start: FnID) -> Result<Self, String> {
pub fn create(p: &UProgram) -> Result<Self, String> {
let start = p
.names
.id::<UFunc>(&[], "crate")
.ok_or("no start method found")?;
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];
let f = p.fns[i.0].as_ref().unwrap();
let mut fbuilder = LFunctionBuilder::new(p, &mut ssbuilder);
for i in &f.instructions {
fbuilder.insert_instr(i);
@@ -29,7 +31,7 @@ impl LProgram {
fbuilder.instrs.push(LInstruction::Ret { src: None });
}
let res = fbuilder.finish(f);
ssbuilder.write_fn(sym, res, Some(f.name.clone()));
ssbuilder.write_fn(sym, res, Some(p.names.path(i).to_string()));
}
let sym_space = ssbuilder.finish().expect("we failed the mission");
Ok(Self { sym_space, entry })
@@ -372,9 +374,9 @@ impl LFunctionBuilderData<'_> {
Some(VarOffset { id: var, offset })
}
pub fn addr_size(&self) -> Size {
64
64StructTy
}
pub fn struct_inst(&mut self, p: &UProgram, ty: &GenericTy) -> &StructInst {
pub fn struct_inst(&mut self, p: &UProgram, ty: &StructTy) -> &StructInst {
// normally I'd let Some(..) here and return, but polonius does not exist :grief:
if self.struct_insts.get(ty).is_none() {
let StructInst { id, args } = ty;

View File

@@ -1,31 +0,0 @@
// #[derive(Debug, Clone, Copy)]
// pub struct Ident {
// id: usize,
// kind: usize,
// }
//
// // this isn't really a map... but also keeps track of "side data"
// #[derive(Debug, Clone, Copy)]
// pub struct Idents {
// pub latest: Ident,
// pub kinds: [Option<usize>; NAMED_KINDS],
// }
//
// impl Idents {
// pub fn new(latest: Ident) -> Self {
// let mut s = Self {
// latest,
// kinds: [None; NAMED_KINDS],
// };
// s.insert(latest);
// s
// }
// pub fn insert(&mut self, i: Ident) {
// self.latest = i;
// self.kinds[i.kind] = Some(i.id);
// }
// pub fn get(&self) -> Option<ID<K>> {
// self.kinds[K::INDEX].map(|i| i.into())
// }
// }

186
src/ir/upper/error.rs Normal file
View File

@@ -0,0 +1,186 @@
use crate::{
common::{CompilerMsg, CompilerOutput},
ir::ID,
};
use super::{KindTy, Origin, StructID, TypeID, UProgram};
pub fn report_errs(p: &UProgram, output: &mut CompilerOutput, errs: Vec<ResErr>) {
for err in errs {
match err {
ResErr::Type {
dst,
src,
errs,
origin,
} => {
let mut msg = type_assign_err(p, dst, src);
for inner in errs {
if inner.dst != dst && inner.src != src {
msg.push_str("\n ");
msg.push_str(&type_assign_err(p, inner.dst, inner.src));
}
}
output.err(CompilerMsg::new(msg, origin));
}
ResErr::NotCallable { origin, ty } => {
output.err(CompilerMsg::new(
format!("Cannot call type '{}'", p.type_name(ty)),
origin,
));
}
ResErr::CannotDeref { origin, ty } => {
output.err(CompilerMsg::new(
format!("Cannot dereference type '{}'", p.type_name(ty)),
origin,
));
}
ResErr::CondType { origin, ty } => {
output.err(CompilerMsg::new(
format!("Condition types must be '64'; found '{}'", p.type_name(ty)),
origin,
));
}
ResErr::BadControlFlow { origin, op } => {
output.err(CompilerMsg::new(
format!("Cannot {} here (outside of loop)", op.str()),
origin,
));
}
ResErr::MissingField { origin, id, name } => {
output.err(CompilerMsg::new(
format!(
"Missing field '{name}' in creation of struct '{}'",
p.structs[id].name
),
origin,
));
}
ResErr::UnknownField { origin, id, name } => {
output.err(CompilerMsg::new(
format!("Unknown field '{name}' in struct '{}'", p.structs[id].name),
origin,
));
}
ResErr::NoReturn { fid } => output.err(CompilerMsg::new(
format!("Function must return a value"),
p.fns[fid].origin,
)),
ResErr::GenericCount {
origin,
expected,
found,
} => output.err(CompilerMsg::new(
if expected == 0 {
format!("No generic arguments expected")
} else {
format!("Expected {expected} generic arguments, found {found}")
},
origin,
)),
ResErr::KindMismatch {
origin,
found,
expected,
id,
} => output.err(CompilerMsg::new(
{
let name = match found {
KindTy::Type => &p.type_name(ID::new(id)),
KindTy::Var => &p.vars[id].name,
KindTy::Struct => &p.structs[id].name,
};
format!(
"Expected {}, found {} '{}'",
expected.str(),
found.str(),
name
)
},
origin,
)),
ResErr::UnexpectedField { origin } => {
output.err(CompilerMsg::new(format!("Unexpected fields here"), origin))
}
}
}
}
pub enum ResErr {
KindMismatch {
origin: Origin,
expected: KindTy,
found: KindTy,
id: usize,
},
UnexpectedField {
origin: Origin,
},
GenericCount {
origin: Origin,
expected: usize,
found: usize,
},
NotCallable {
origin: Origin,
ty: TypeID,
},
CannotDeref {
origin: Origin,
ty: TypeID,
},
CondType {
origin: Origin,
ty: TypeID,
},
NoReturn {
fid: usize,
},
BadControlFlow {
op: ControlFlowOp,
origin: Origin,
},
MissingField {
origin: Origin,
id: StructID,
name: String,
},
UnknownField {
origin: Origin,
id: StructID,
name: String,
},
Type {
dst: TypeID,
src: TypeID,
errs: Vec<TypeMismatch>,
origin: Origin,
},
}
pub enum ControlFlowOp {
Break,
Continue,
}
impl ControlFlowOp {
pub fn str(&self) -> &'static str {
match self {
ControlFlowOp::Break => "break",
ControlFlowOp::Continue => "continue",
}
}
}
pub struct TypeMismatch {
pub dst: TypeID,
pub src: TypeID,
}
pub fn type_assign_err(p: &UProgram, dst: TypeID, src: TypeID) -> String {
format!(
"Cannot assign type {} to {}",
p.type_name(src),
p.type_name(dst)
)
}

View File

@@ -1,35 +0,0 @@
use crate::ir::VarID;
use std::fmt::Debug;
use super::{MemberID, ModPath, Origin, UInstruction};
#[derive(Clone)]
pub struct VarInst {
pub status: VarStatus,
pub origin: Origin,
}
#[derive(Clone)]
pub enum VarStatus {
Res(VarID),
Unres { mid: ModPath, fields: Vec<MemberID> },
Partial { v: VarID, fields: Vec<MemberID> },
}
#[derive(Clone)]
pub struct VarParent {
id: VarID,
path: Vec<String>,
}
#[derive(Clone)]
pub struct UInstrInst {
pub i: UInstruction,
pub origin: Origin,
}
impl Debug for UInstrInst {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{:?}", self.i)
}
}

View File

@@ -1,52 +1,53 @@
use std::{collections::HashMap, fmt::Write};
use super::{arch::riscv64::RV64Instruction, inst::VarInst, DataID, FnID, UFunc, UInstrInst};
use super::{arch::riscv64::RV64Instruction, DataID, FnID, Origin, UFunc, VarInst, VarInstID};
use crate::{compiler::arch::riscv::Reg, util::Padder};
#[derive(Clone)]
pub enum UInstruction {
Mv {
dst: VarInst,
src: VarInst,
dst: VarInstID,
src: VarInstID,
},
Ref {
dst: VarInst,
src: VarInst,
dst: VarInstID,
src: VarInstID,
},
Deref {
dst: VarInst,
src: VarInst,
dst: VarInstID,
src: VarInstID,
},
LoadData {
dst: VarInst,
dst: VarInstID,
src: DataID,
},
LoadSlice {
dst: VarInst,
dst: VarInstID,
src: DataID,
},
LoadFn {
dst: VarInst,
dst: VarInstID,
src: FnID,
},
Call {
dst: VarInst,
f: VarInst,
args: Vec<VarInst>,
dst: VarInstID,
f: VarInstID,
args: Vec<VarInstID>,
},
AsmBlock {
instructions: Vec<RV64Instruction>,
args: Vec<AsmBlockArg>,
},
Ret {
src: VarInst,
src: VarInstID,
},
Construct {
dst: VarInst,
fields: HashMap<String, VarInst>,
dst: VarInstID,
struc: VarInstID,
fields: HashMap<String, VarInstID>,
},
If {
cond: VarInst,
cond: VarInstID,
body: Vec<UInstrInst>,
},
Loop {
@@ -56,9 +57,21 @@ pub enum UInstruction {
Continue,
}
#[derive(Clone)]
pub struct UInstrInst {
pub i: UInstruction,
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: VarInst,
pub var: VarInstID,
pub reg: Reg,
pub ty: AsmBlockArgType,
}
@@ -85,7 +98,7 @@ impl std::fmt::Debug for UInstruction {
} => 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, fields } => write!(f, "{dest:?} <- {fields:?}")?,
Self::Construct { dst: dest, struc, fields } => write!(f, "{dest:?} <- {struc:?}{fields:?}")?,
Self::If { cond, body } => {
write!(f, "if {cond:?}:")?;
if !body.is_empty() {

View File

@@ -7,19 +7,76 @@ use std::{collections::HashMap, fmt::Debug};
pub type NamePath = Vec<String>;
// "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",
}
}
}
pub trait Kind {
fn ty() -> KindTy;
}
impl Kind for UFunc {
fn ty() -> KindTy {
KindTy::Fn
}
}
impl Kind for UVar {
fn ty() -> KindTy {
KindTy::Var
}
}
impl Kind for UStruct {
fn ty() -> KindTy {
KindTy::Struct
}
}
impl Kind for Type {
fn ty() -> KindTy {
KindTy::Type
}
}
pub type FnID = ID<UFunc>;
pub type VarID = ID<UVar>;
pub type VarInstID = ID<VarInst>;
pub type TypeID = ID<Type>;
pub type GenericID = ID<UGeneric>;
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,
pub args: Vec<VarID>,
pub gargs: Vec<TypeID>,
pub gargs: Vec<GenericID>,
pub ret: TypeID,
pub instructions: Vec<UInstrInst>,
}
#[derive(Clone)]
pub struct StructField {
pub ty: Type,
pub ty: TypeID,
}
#[derive(Clone)]
@@ -27,7 +84,7 @@ pub struct UStruct {
pub name: String,
pub origin: Origin,
pub fields: HashMap<String, StructField>,
pub generics: Vec<GenericID>,
pub gargs: Vec<GenericID>,
}
#[derive(Clone)]
@@ -45,15 +102,40 @@ pub struct UVar {
pub children: Vec<VarID>,
}
#[derive(Clone, PartialEq, Eq, Hash)]
/// these are more like "expressions", need to find good name
/// eg. a::b::c::<T,U>.d.e
#[derive(Clone, Debug)]
pub struct VarInst {
pub status: VarStatus,
pub origin: Origin,
}
#[derive(Clone, Debug)]
pub enum VarStatus {
Var(VarID),
Struct(StructID, Vec<TypeID>),
Unres {
path: ModPath,
name: String,
gargs: Vec<TypeID>,
fields: Vec<MemberID>,
},
Partial {
v: VarID,
fields: Vec<MemberID>,
},
Cooked,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct MemberID {
pub name: String,
pub origin: Origin,
}
#[derive(Clone, PartialEq, Eq, Hash)]
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct ModPath {
pub m: ModID,
pub id: ModID,
pub path: Vec<MemberID>,
}
@@ -66,25 +148,26 @@ pub struct VarOffset {
#[derive(Clone)]
pub struct UData {
pub name: String,
pub ty: Type,
pub ty: TypeID,
pub content: Vec<u8>,
}
#[derive(Clone)]
pub struct UModule {
pub name: String,
pub members: HashMap<String, MemberID>,
pub members: HashMap<String, Member>,
pub children: HashMap<String, ModID>,
pub parent: Option<ModID>,
}
pub struct ModMissing {
pub import_all: Vec<ModID>,
pub vars: Vec<VarID>,
#[derive(Clone)]
pub struct Member {
pub id: MemberTy,
// pub visibility: Visibility
}
#[derive(Clone)]
pub enum Member {
pub enum MemberTy {
Fn(FnID),
Struct(StructID),
Var(VarID),
@@ -126,12 +209,13 @@ impl<'a> Iterator for InstrIter<'a> {
}
}
pub const NAMED_KINDS: usize = 5;
pub type FnID = ID<UFunc>;
pub type VarID = ID<UVar>;
pub type TypeID = ID<Type>;
pub type GenericID = ID<UGeneric>;
pub type StructID = ID<UStruct>;
pub type DataID = ID<UData>;
pub type ModID = ID<Option<UModule>>;
impl VarInst {
pub fn id(&self) -> Option<VarID> {
match &self.status {
VarStatus::Var(id) => Some(*id),
VarStatus::Unres { .. } => None,
VarStatus::Partial { .. } => None,
VarStatus::Cooked => None,
}
}
}

View File

@@ -1,17 +1,14 @@
mod assoc;
mod inst;
mod instr;
mod kind;
mod program;
mod ty;
mod validate;
mod resolve;
mod error;
use super::*;
use assoc::*;
pub use inst::*;
pub use instr::*;
pub use kind::*;
pub use program::*;
pub use ty::*;
pub use error::*;

View File

@@ -7,14 +7,18 @@ use std::{
pub struct UProgram {
pub fns: Vec<UFunc>,
pub structs: Vec<UStruct>,
pub modules: Vec<Option<UModule>>,
pub modules: Vec<UModule>,
pub data: Vec<UData>,
pub generics: Vec<UGeneric>,
pub vars: Vec<UVar>,
pub vars_insts: Vec<VarInst>,
pub types: Vec<Type>,
pub unres_vars: Vec<VarID>,
pub unres_tys: Vec<TypeID>,
pub vfmap: HashMap<VarID, FnID>,
pub tc: TypeCache,
}
pub struct TypeCache {
pub unit: TypeID,
pub error: TypeID,
}
@@ -28,196 +32,164 @@ pub struct UModuleBuilder<'a> {
impl UProgram {
pub fn new() -> Self {
let mut types = Vec::new();
let unit = Self::push_id(&mut types, Type::Unit);
let error = Self::push_id(&mut types, Type::Error);
let tc = TypeCache {
unit: push_id(&mut types, Type::Unit),
error: push_id(&mut types, Type::Error),
};
Self {
fns: Vec::new(),
vars: Vec::new(),
vars_insts: Vec::new(),
structs: Vec::new(),
types: Vec::new(),
generics: Vec::new(),
data: Vec::new(),
modules: Vec::new(),
unres_vars: Vec::new(),
unres_tys: Vec::new(),
error,
unit,
vfmap: HashMap::new(),
tc,
}
}
pub fn inst_type(&mut self, ty: TypeID, gs: Vec<TypeID>) -> TypeID {
let ty = match &self.types[ty] {
Type::Ref(id) => Type::Ref(self.inst_type(*id, gs)),
Type::Generic(id) => return gs[id.0],
Type::Bits(b) => Type::Bits(*b),
Type::Struct(struct_ty) => Type::Struct(struct_ty.clone()),
Type::Fn() => todo!(),
Type::Deref(id) => todo!(),
Type::Slice(id) => todo!(),
Type::Array(id, _) => todo!(),
Type::Unit => todo!(),
Type::Unres(mod_path) => todo!(),
Type::Infer => todo!(),
Type::Error => todo!(),
};
self.def_ty(ty)
}
pub fn infer(&mut self) -> TypeID {
self.def_ty(Type::Infer)
}
pub fn set_type(id: TypeID, ty: Type) {}
pub fn def_var(&mut self, v: UVar) -> VarID {
Self::push_id(&mut self.vars, v)
push_id(&mut self.vars, v)
}
pub fn def_fn(&mut self, f: UFunc) -> FnID {
Self::push_id(&mut self.fns, f)
push_id(&mut self.fns, f)
}
pub fn def_ty(&mut self, t: Type) -> TypeID {
Self::push_id(&mut self.types, t)
push_id(&mut self.types, t)
}
pub fn def_var_inst(&mut self, i: VarInst) -> VarInstID {
push_id(&mut self.vars_insts, i)
}
pub fn def_generic(&mut self, g: UGeneric) -> GenericID {
Self::push_id(&mut self.generics, g)
push_id(&mut self.generics, g)
}
pub fn def_data(&mut self, d: UData) -> DataID {
Self::push_id(&mut self.data, d)
push_id(&mut self.data, d)
}
pub fn def_struct(&mut self, s: UStruct) -> StructID {
Self::push_id(&mut self.structs, s)
push_id(&mut self.structs, s)
}
fn push_id<T>(v: &mut Vec<T>, t: T) -> ID<T> {
let id = ID::new(v.len());
v.push(t);
id
}
pub fn type_name<T: Typer>(&self, ty: T) -> String {
let mut str = String::new();
pub fn type_name(&self, ty: impl Typed) -> String {
match ty.ty(self) {
Type::Struct(ty) => {
str += &self.structs[ty.id].name;
let args = &ty.args;
if let Some(arg) = args.first() {
str = str + "<" + &self.type_name(arg);
format!("{}{}", self.structs[ty.id].name, self.gparams_str(&ty.args))
}
for arg in args.iter().skip(1) {
str = str + ", " + &self.type_name(arg);
Type::FnRef(ty) => {
format!(
"fn{}({}) -> {}",
&self.gparams_str(&ty.args),
&self.type_list_str(self.fns[ty.id].args.iter().map(|v| self.vars[v].ty)),
&self.type_name(self.fns[ty.id].ret)
)
}
if !args.is_empty() {
str += ">";
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::Generic(id) => self.generics[id].name.clone(),
}
}
Type::Fn { args, ret } => {
str += "fn(";
if let Some(arg) = args.first() {
pub fn type_list_str(&self, mut args: impl Iterator<Item = TypeID>) -> String {
let mut str = String::new();
if let Some(arg) = args.next() {
str += &self.type_name(arg);
}
for arg in args.iter().skip(1) {
for arg in args {
str = str + ", " + &self.type_name(arg);
}
str += ") -> ";
str += &self.type_name(ret);
str
}
Type::Ref(t) => {
str += &self.type_name(t);
str += "&";
pub fn gparams_str(&self, args: &[TypeID]) -> String {
let mut str = String::new();
if !args.is_empty() {
str += "<";
}
Type::Deref(t) => {
str += &self.type_name(t);
str += "^";
}
Type::Unres(_) => {
str += "{unresolved}";
}
Type::Bits(size) => str += &format!("b{}", size),
Type::Array(t, len) => str += &format!("[{}; {len}]", self.type_name(t)),
Type::Unit => str += "()",
Type::Slice(t) => str += &format!("&[{}]", self.type_name(t)),
Type::Error => str += "{error}",
Type::Infer => str += "{inferred}",
Type::Placeholder => str += "{placeholder}",
str += &self.type_list_str(args.iter().cloned());
if !args.is_empty() {
str += ">";
}
str
}
}
impl<'a> UModuleBuilder<'a> {
pub fn new(program: &'a mut UProgram, id: ModID, error: TypeID) -> Self {
Self {
p: program,
module: id,
error,
name_stack: Vec::new(),
temp: 0,
}
}
pub fn push(&mut self) {
self.name_stack.push(HashMap::new());
}
pub fn pop(&mut self) {
self.name_stack.pop();
}
pub fn get_idents(&self, name: &str) -> Option<Idents> {
for map in self.name_stack.iter().rev() {
let res = map.get(name);
if res.is_some() {
return res.cloned();
}
}
None
}
pub fn def_var(&mut self, name: &str, v: UVar, origin: Origin) -> VarID {
let id = self.p.def_var(name, v, origin);
self.name_on_stack(id, name.to_string());
pub fn push_id<T>(v: &mut Vec<T>, t: T) -> ID<T> {
let id = ID::new(v.len());
v.push(t);
id
}
pub fn temp_var<T: Typable>(&mut self, origin: Origin, ty: T) -> VarInst {
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) -> VarInstID {
self.temp_var_inner(origin, ty)
}
fn temp_var_inner<T: Typable>(&mut self, origin: Origin, ty: T) -> VarInst {
let t = self.temp;
let v = self
.p
.def_var(&format!("temp{}", t), UVar { ty: ty.ty(self) }, origin);
fn temp_var_inner(&mut self, origin: Origin, ty: impl Typable) -> VarInstID {
let var = UVar {
name: format!("temp{}", self.temp),
ty: ty.ty(self),
origin,
parent: None,
children: Vec::new(),
};
let id = self.p.def_var(var);
self.temp += 1;
VarInst { id: v, origin }
self.def_var_inst(VarInst {
status: VarStatus::Var(id),
origin,
})
}
}
// I'm done with names...
pub trait Typer {
pub trait Typed {
fn ty<'a>(&'a self, p: &'a UProgram) -> &'a Type;
}
impl Typer for &Type {
impl Typed for &Type {
fn ty(&self, _: &UProgram) -> &Type {
self
}
}
impl Typer for TypeID {
impl Typed for TypeID {
fn ty<'a>(&'a self, p: &'a UProgram) -> &'a Type {
&p.types[self]
}
}
impl Typer for &TypeID {
impl Typed for &TypeID {
fn ty<'a>(&'a self, p: &'a UProgram) -> &'a Type {
&p.types[*self]
}
}
impl Typer for &Box<Type> {
impl Typed for &Box<Type> {
fn ty<'a>(&'a self, _: &'a UProgram) -> &'a Type {
&**self
}

View File

@@ -1,60 +1,67 @@
use super::{Origin, GenericTy, Type, TypeID, TypeIDed, UInstruction, UProgram, UStruct, UVar};
use crate::common::{CompilerMsg, CompilerOutput};
use std::{collections::HashMap, ops::BitOrAssign};
use super::{
report_errs, ControlFlowOp, DataID, Kind, MemberTy, Origin, ResErr, Type, TypeID, TypeMismatch, UData, UFunc, UGeneric, UInstrInst, UInstruction, UModule, UProgram, UStruct, UVar, VarID, VarInst, VarInstID, VarStatus
};
use crate::{
common::{CompilerMsg, CompilerOutput},
ir::{inst_fn_var, inst_struct_ty, KindTy, ID},
};
use std::{
collections::HashSet,
convert::Infallible,
ops::{BitOrAssign, FromResidual},
};
// dawg this file is way too long
impl UProgram {
pub fn resolve(&mut self, output: &mut CompilerOutput) {
let mut unfinished = Vec::new();
let mut unfinished_new = Vec::new();
let data = &mut ResData {
let mut data = ResData {
unfinished: Vec::new(),
changed: false,
types: &mut self.types,
vars: &self.vars,
s: Sources {
insts: &mut self.vars_insts,
vars: &mut self.vars,
fns: &self.fns,
structs: &self.structs,
generics: &self.generics,
data: &self.data,
modules: &self.modules,
},
errs: Vec::new(),
};
for f in &self.fns {
for i in f.flat_iter() {
if resolve_instr(data, &i.i).unfinished() {
unfinished.push(i);
for (fid, f) in self.fns.iter().enumerate() {
for i in &f.instructions {
resolve_instr(
&mut data,
ResolveCtx {
ret: f.ret,
breakable: false,
i,
},
);
}
// this currently works bc expressions create temporary variables
// although you can't do things like loop {return 3} (need to analyze control flow)
if !matches!(data.types[f.ret], Type::Unit) {
if f.instructions
.last()
.is_none_or(|i| !matches!(i.i, UInstruction::Ret { .. }))
{
data.errs.push(ResErr::NoReturn { fid });
}
}
}
while !unfinished.is_empty() && data.changed {
while !data.unfinished.is_empty() && data.changed {
data.changed = false;
for &i in &unfinished {
if resolve_instr(data, &i.i).unfinished() {
unfinished_new.push(i);
}
}
std::mem::swap(&mut unfinished, &mut unfinished_new);
unfinished_new.clear();
}
for err in &data.errs {
match err {
&ResErr::Type {
dst,
src,
ref errs,
origin,
} => {
let mut msg = type_assign_err(self, dst, src);
for inner in errs {
if inner.dst != dst && inner.src != src {
msg.push_str("\n ");
msg.push_str(&type_assign_err(self, inner.dst, inner.src));
}
}
output.err(CompilerMsg::new(msg, origin));
}
&ResErr::NotCallable { origin, ty } => {
output.err(CompilerMsg::new(
format!("Cannot call type {}", self.type_name(ty)),
origin,
));
}
std::mem::swap(&mut data.unfinished, &mut unfinished);
for ctx in unfinished.drain(..) {
resolve_instr(&mut data, ctx);
}
}
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(
@@ -75,132 +82,234 @@ impl UProgram {
}
}
pub fn type_assign_err(p: &mut UProgram, dst: TypeID, src: TypeID) -> String {
format!(
"Cannot assign type {} to {}",
p.type_name(src),
p.type_name(dst)
)
#[derive(Clone, Copy)]
struct ResolveCtx<'a> {
ret: TypeID,
breakable: bool,
i: &'a UInstrInst,
}
pub fn resolve_instr(data: &mut ResData, i: &UInstruction) -> InstrRes {
let mut uf = InstrRes::Finished;
match &i {
pub fn resolve_instr<'a>(data: &mut ResData<'a>, ctx: ResolveCtx<'a>) -> Option<()> {
let mut res = InstrRes::Finished;
match &ctx.i.i {
UInstruction::Call { dst, f, args } => {
let ftyid = data.vars[f.id].ty;
let fty = &data.types[ftyid];
let Type::Fn { args: fargs, ret } = fty else {
data.errs.push(ResErr::NotCallable {
origin: f.origin,
ty: ftyid,
});
return InstrRes::Finished;
let fty = data.res_id(f, ctx)?;
let Type::FnRef(ftyy) = data.types[fty].clone() else {
let origin = f.origin(data);
data.errs.push(ResErr::NotCallable { origin, ty: fty });
return None;
};
uf |= data.match_types(dst, ret, dst.origin);
for (src, dest) in args.iter().zip(fargs) {
uf |= data.match_types(dest, src, src.origin);
let f = &data.s.fns[ftyy.id];
for (src, dest) in args.iter().zip(&f.args) {
res |= data.match_types(dest, src, src);
}
res |= data.match_types(dst, f.ret, dst);
}
UInstruction::Mv { dst, src } => {
uf |= data.match_types(dst, src, src.origin);
res |= data.match_types(dst, src, src);
}
UInstruction::Ref { dst, src } => {
let Type::Ref(dest_ty) = data.types[data.vars[dst.id].ty] else {
// TODO: this is probably a compiler error / should never happen
panic!("how could this happen to me (you)");
let dstid = data.res_id(dst, ctx)?;
let Type::Ref(dest_ty) = data.types[dstid] else {
compiler_error()
};
uf |= data.match_types(dest_ty, src, src.origin);
res |= data.match_types(dest_ty, src, src);
}
UInstruction::Deref { dst, src } => {
let srcid = data.res_id(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);
}
UInstruction::LoadData { dst, src } => {
// TODO
res |= data.match_types(dst, src, dst);
}
UInstruction::LoadSlice { dst, src } => {
// TODO
let dstid = data.res_id(src, ctx)?;
let srcid = data.res_id(src, ctx)?;
let Type::Slice(dstty) = data.types[dstid] else {
compiler_error()
};
let Type::Array(srcty, _) = data.types[srcid] else {
compiler_error()
};
res |= data.match_types(dstty, srcty, dst);
}
UInstruction::LoadFn { dst, src } => {
// TODO
// TODO: validate size with enabled targets
}
UInstruction::AsmBlock { instructions, args } => {
// TODO
}
UInstruction::Ret { .. } => {}
UInstruction::Construct { dst, fields } => {
let dest_ty = get(vars, dst.id)?;
let Type::Struct(sty) = dest_ty else {};
let id = sty.id;
let Some(struc) = get(id) else {};
let mut new = HashMap::new();
for (name, field) in &struc.fields {
let Some(src) = fields.get(name) else {
continue;
UInstruction::Ret { src } => {
res |= data.match_types(ctx.ret, src, src);
}
UInstruction::Construct { dst, struc, fields } => {
let id = data.res_id(dst, ctx, KindTy::Struct)?;
let Type::Struct(sty) = &data.types[id] else {
return None;
};
let src_ty = get(vars, src.id)?;
if let Some(ty) = match_types(vars, types, &field.ty, src_ty) {
if let Type::Generic { id } = field.ty {
new.insert(id, ty.clone());
}
set(vars, src.id, ty);
let sid = sty.id;
let st = &data.s.structs[sid];
let mut used = HashSet::new();
for (name, field) in &st.fields {
if let Some(src) = fields.get(name) {
used.insert(name);
res |= data.match_types(field.ty, src, src);
} else {
let origin = dst.origin(data);
data.errs.push(ResErr::MissingField {
origin,
id: sid,
name: name.clone(),
});
}
}
let mut args: Vec<_> = struc
.generics
.iter()
.map(|&id| Type::Generic { id })
.collect();
for (i, g) in struc.generics.iter().enumerate() {
if let Some(ty) = new.remove(g) {
args[i] = ty;
for (name, _) in fields {
if !used.contains(name) {
let origin = dst.origin(data);
data.errs.push(ResErr::UnknownField {
origin,
id: sid,
name: name.clone(),
});
}
}
set(vars, dst.id, Type::Struct(GenericTy { id, args }));
}
UInstruction::If { cond, body } => {
if let Some(id) = data.res_id(cond, ctx, KindTy::Var) {
if !matches!(data.types[id], Type::Bits(64)) {
let origin = cond.origin(data);
data.errs.push(ResErr::CondType { origin, ty: id });
}
}
for i in body {
uf |= resolve_instr(data, &i.i);
resolve_instr(
data,
ResolveCtx {
ret: ctx.ret,
breakable: ctx.breakable,
i,
},
);
}
}
UInstruction::Loop { body } => {}
UInstruction::Break => {}
UInstruction::Continue => {}
UInstruction::Loop { body } => {
for i in body {
resolve_instr(
data,
ResolveCtx {
ret: ctx.ret,
breakable: true,
i,
},
);
}
uf
}
UInstruction::Break => {
if !ctx.breakable {
data.errs.push(ResErr::BadControlFlow {
op: ControlFlowOp::Break,
origin: ctx.i.origin,
});
}
}
UInstruction::Continue => {
if !ctx.breakable {
data.errs.push(ResErr::BadControlFlow {
op: ControlFlowOp::Continue,
origin: ctx.i.origin,
});
}
}
}
match res {
InstrRes::Finished => (),
InstrRes::Unfinished => data.unfinished.push(ctx),
}
return None;
}
pub fn match_types<T1: TypeIDed, T2: TypeIDed>(
data: &mut TypeResData,
dst: T1,
src: T2,
) -> MatchRes {
let dst = dst.type_id(data.vars);
let src = src.type_id(data.vars);
fn compiler_error() -> ! {
// TODO: this is probably a compiler error / should never happen
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 {
return MatchRes::Finished;
}
let error = || MatchRes::Error(vec![TypeMismatch { dst, src }]);
match (&data.types[dst], &data.types[src]) {
match (data.types[dst].clone(), data.types[src].clone()) {
(Type::Error, _) | (_, Type::Error) => MatchRes::Finished,
(Type::Placeholder, _) | (_, Type::Placeholder) => MatchRes::Unfinished,
(Type::Infer, Type::Infer) => MatchRes::Unfinished,
(Type::Infer, x) => {
*data.changed = true;
data.types[dst] = x.clone();
data.types[dst] = x;
MatchRes::Finished
}
(x, Type::Infer) => {
*data.changed = true;
data.types[src] = x.clone();
data.types[src] = x;
MatchRes::Finished
}
(Type::Struct(dest), Type::Struct(src)) => {
if dest.id != src.id {
return error();
}
match_all(data, dest.args.iter().cloned(), src.args.iter().cloned())
}
// (
// Type::Fn {
// args: dst_args,
// ret: dst_ret,
// },
// Type::Fn {
// args: src_args,
// ret: src_ret,
// },
// ) => {
// let dst = dst_args.into_iter().chain(once(dst_ret));
// let src = src_args.into_iter().chain(once(src_ret));
// match_all(data, dst, src)
// }
(Type::Ref(dest), Type::Ref(src)) => match_types(data, dest, src),
(Type::Slice(dest), Type::Slice(src)) => match_types(data, dest, src),
(Type::Array(dest, dlen), Type::Array(src, slen)) => {
if dlen == slen {
match_types(data, dest, src)
} else {
error()
}
}
_ => error(),
}
}
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,
dst: impl Iterator<Item = TypeID>,
src: impl Iterator<Item = TypeID>,
) -> MatchRes {
let mut finished = true;
let mut errors = Vec::new();
let dargs = dest.args.clone();
let sargs = dest.args.clone();
for (darg, sarg) in dargs.iter().zip(&sargs) {
match match_types(data, darg, sarg) {
for (dst, src) in dst.zip(src) {
match match_types(data, dst, src) {
MatchRes::Unfinished => finished = false,
MatchRes::Error(errs) => errors.extend(errs),
MatchRes::Finished => (),
@@ -216,75 +325,45 @@ pub fn match_types<T1: TypeIDed, T2: TypeIDed>(
MatchRes::Unfinished
}
}
(
Type::Fn {
args: dest_args,
ret: dest_ret,
},
Type::Fn {
args: src_args,
ret: src_ret,
},
) => {
// TODO
MatchRes::Finished
}
(&Type::Ref(dest), &Type::Ref(src)) => match_types(data, dest, src),
(&Type::Slice(dest), &Type::Slice(src)) => match_types(data, dest, src),
(&Type::Array(dest, dlen), &Type::Array(src, slen)) => {
if dlen == slen {
match_types(data, dest, src)
} else {
error()
}
}
_ => error(),
}
struct Sources<'a> {
insts: &'a mut [VarInst],
vars: &'a mut Vec<UVar>,
fns: &'a [UFunc],
structs: &'a [UStruct],
generics: &'a [UGeneric],
data: &'a [UData],
modules: &'a [UModule],
}
struct ResData<'a> {
unfinished: Vec<ResolveCtx<'a>>,
changed: bool,
types: &'a mut [Type],
vars: &'a [UVar],
structs: &'a [UStruct],
types: &'a mut Vec<Type>,
s: Sources<'a>,
errs: Vec<ResErr>,
}
struct TypeResData<'a> {
changed: &'a mut bool,
types: &'a mut [Type],
vars: &'a [UVar],
structs: &'a [UStruct],
}
enum ResErr {
NotCallable {
origin: Origin,
ty: TypeID,
},
Type {
dst: TypeID,
src: TypeID,
errs: Vec<TypeMismatch>,
origin: Origin,
},
sources: &'a Sources<'a>,
}
impl<'a> ResData<'a> {
pub fn match_types<T1: TypeIDed, T2: TypeIDed>(
&'a mut self,
dst: T1,
src: T2,
origin: Origin,
pub fn match_types(
&mut self,
dst: impl ResID<Type>,
src: impl ResID<Type>,
origin: impl HasOrigin,
) -> InstrRes {
let dst = dst.type_id(self.vars);
let src = src.type_id(self.vars);
let dst = dst.try_id(&mut self.s, self.types, &mut self.errs, KindTy::Type)?;
let src = src.try_id(&mut self.s, self.types, &mut self.errs, KindTy::Type)?;
let res = match_types(
&mut TypeResData {
changed: &mut self.changed,
types: self.types,
vars: self.vars,
structs: self.structs,
sources: &self.s,
},
dst,
src,
@@ -295,7 +374,7 @@ impl<'a> ResData<'a> {
MatchRes::Error(es) => {
self.errs.push(ResErr::Type {
errs: es,
origin,
origin: origin.origin(self),
dst,
src,
});
@@ -303,11 +382,28 @@ impl<'a> ResData<'a> {
}
}
}
pub fn try_res_id<K>(&mut self, x: impl ResID) -> Result<ID<K>, InstrRes> {
x.try_id(&mut self.s, &mut self.types, &mut self.errs)
.map(|id| resolve_refs(self.types, id))
}
pub fn res_id<'b: 'a, K>(
&mut self,
x: impl ResID<K>,
ctx: ResolveCtx<'b>,
) -> Option<ID<K>> {
match self.try_res_id(x) {
Ok(id) => return Some(id),
Err(InstrRes::Unfinished) => self.unfinished.push(ctx),
Err(InstrRes::Finished) => (),
}
None
}
}
pub struct TypeMismatch {
dst: TypeID,
src: TypeID,
impl TypeResData<'_> {
pub fn res_id(&self, x: impl TypeIDed) -> TypeID {
resolve_refs(self.types, x.type_id(self.sources))
}
}
pub enum MatchRes {
@@ -316,6 +412,7 @@ pub enum MatchRes {
Error(Vec<TypeMismatch>),
}
#[derive(Debug, Clone, Copy)]
pub enum InstrRes {
Finished,
Unfinished,
@@ -330,11 +427,218 @@ impl BitOrAssign for InstrRes {
}
}
impl InstrRes {
pub fn unfinished(&self) -> bool {
match self {
Self::Finished => false,
Self::Unfinished => true,
impl FromResidual<Option<Infallible>> for InstrRes {
fn from_residual(_: Option<Infallible>) -> Self {
Self::Unfinished
}
}
trait ResID<K> {
fn try_id(
&self,
s: &mut Sources,
types: &mut Vec<Type>,
errs: &mut Vec<ResErr>,
) -> Result<ID<K>, InstrRes>;
}
impl<T: TypeIDed> ResID<Type> for T {
fn try_id(
&self,
s: &mut Sources,
_: &mut Vec<Type>,
errs: &mut Vec<ResErr>,
kind: KindTy,
) -> Result<TypeID, InstrRes> {
Ok(self.type_id(s))
}
}
impl VarInst {
pub fn resolve(&mut self, s: &mut Sources) {
match &self.status {
VarStatus::Var(id) => self.status = VarStatus::Cooked,
VarStatus::Struct(id, ids) => todo!(),
VarStatus::Unres { path, name, gargs, fields } => todo!(),
VarStatus::Partial { v, fields } => todo!(),
VarStatus::Cooked => todo!(),
}
}
}
impl<K: Kind> ResID<K> for VarInstID {
fn try_id(
&self,
s: &mut Sources,
types: &mut Vec<Type>,
errs: &mut Vec<ResErr>,
) -> Result<ID<K>, InstrRes> {
let kind = K::ty();
let inst = &mut s.insts[self];
let (id, fields) = match &mut inst.status {
VarStatus::Var(id) => {
return Ok(s.vars[id].ty)
},
VarStatus::Unres {
path,
name,
gargs,
fields,
} => {
let mut mid = path.id;
let mut depth = 0;
for mem in &path.path {
let Some(&child) = s.modules[mid].children.get(&mem.name) else {
break;
};
depth += 1;
mid = child;
}
path.path.drain(0..depth);
path.id = mid;
if path.path.len() != 0 {
return Err(InstrRes::Unfinished);
}
let Some(mem) = s.modules[mid].members.get(name) else {
return Err(InstrRes::Unfinished);
};
let vid = match mem.id {
MemberTy::Fn(id) => {
if kind == KindTy::Fn {
return Ok(id.0.into());
}
if !matches!(kind, KindTy::Var | KindTy::Fn) {
errs.push(ResErr::KindMismatch {
origin: inst.origin,
expected: kind,
found: KindTy::Fn,
id: id.0,
});
return Err(InstrRes::Finished);
}
inst_fn_var(
id,
s.fns,
gargs,
inst.origin,
s.vars,
types,
s.generics,
errs,
)
}
MemberTy::Var(id) => {
if !matches!(kind, KindTy::Var | KindTy::Any) {
errs.push(ResErr::KindMismatch {
origin: inst.origin,
expected: kind,
found: KindTy::Var,
id: id.0,
});
return Err(InstrRes::Finished);
}
if !gargs.is_empty() {
errs.push(ResErr::GenericCount {
origin: inst.origin,
expected: 0,
found: gargs.len(),
});
}
id
}
MemberTy::Struct(id) => {
if !matches!(kind, KindTy::Struct | KindTy::Type | KindTy::Any) {
errs.push(ResErr::KindMismatch {
origin: inst.origin,
expected: kind,
found: KindTy::Struct,
id: id.0,
});
return Err(InstrRes::Finished);
}
if fields.len() > 0 {
errs.push(ResErr::UnexpectedField {
origin: inst.origin,
});
return Err(InstrRes::Finished);
}
return Ok(inst_struct_ty(
id, s.structs, gargs, types, s.generics, errs,
));
}
};
if fields.len() > 0 {
inst.status = VarStatus::Partial{v: vid, fields}
}
}
VarStatus::Partial { v, fields } => (*v, fields),
VarStatus::Cooked => return Err(InstrRes::Finished),
};
// I feel like this clone is not necessary but idk how
inst.status = VarStatus::Partial {
v: id,
fields: fields.clone(),
};
// let VarStatus::Partial { v, fields } = inst.status
todo!()
}
}
impl<K> ResID<K> for &VarInstID {
fn try_id(
&self,
s: &mut Sources,
types: &mut Vec<Type>,
errs: &mut Vec<ResErr>,
kind: KindTy,
) -> Result<ID<K>, InstrRes> {
(*self).try_id(s, types, errs, kind)
}
}
pub trait TypeIDed {
fn type_id(&self, s: &Sources) -> TypeID;
}
impl TypeIDed for TypeID {
fn type_id(&self, _: &Sources) -> TypeID {
*self
}
}
impl TypeIDed for VarID {
fn type_id(&self, s: &Sources) -> TypeID {
s.vars[self].ty
}
}
impl TypeIDed for DataID {
fn type_id(&self, s: &Sources) -> TypeID {
s.data[self].ty
}
}
impl<T: TypeIDed> TypeIDed for &T {
fn type_id(&self, s: &Sources) -> TypeID {
(*self).type_id(s)
}
}
impl FromResidual<std::result::Result<std::convert::Infallible, InstrRes>> for InstrRes {
fn from_residual(residual: std::result::Result<std::convert::Infallible, InstrRes>) -> Self {
match residual {
Ok(_) => unreachable!(),
Err(r) => r,
}
}
}
trait HasOrigin {
fn origin(&self, data: &ResData) -> Origin;
}
impl HasOrigin for &VarInstID {
fn origin(&self, data: &ResData) -> Origin {
data.s.insts[*self].origin
}
}

View File

@@ -1,5 +1,9 @@
use super::{GenericID, Len, ModPath, TypeID, UFunc, UProgram, UStruct, UVar, VarID, VarInst};
use crate::ir::ID;
use std::collections::HashMap;
use super::{
push_id, FnID, GenericID, Len, ModPath, Origin, ResErr, StructID, TypeCache, TypeID, UFunc,
UGeneric, UProgram, UStruct, UVar, VarID,
};
#[derive(Debug, Clone, Hash, Eq, PartialEq)]
pub struct FieldRef {
@@ -8,16 +12,24 @@ pub struct FieldRef {
}
#[derive(Clone, Eq, PartialEq, Hash)]
pub struct GenericTy<T> {
pub id: ID<T>,
pub struct StructTy {
pub id: StructID,
pub args: Vec<TypeID>,
}
#[derive(Clone, Eq, PartialEq, Hash)]
pub struct FnTy {
pub id: FnID,
pub args: Vec<TypeID>,
}
#[derive(Clone)]
pub enum Type {
Bits(u32),
Struct(GenericTy<UStruct>),
Fn(GenericTy<UFunc>),
Struct(StructTy),
FnRef(FnTy),
// this can be added for constraints later (F: fn(...) -> ...)
// Fn { args: Vec<TypeID>, ret: TypeID },
Ref(TypeID),
Deref(TypeID),
Slice(TypeID),
@@ -66,36 +78,157 @@ impl Type {
}
}
pub trait TypeIDed {
fn type_id(&self, vars: &[UVar]) -> TypeID;
pub fn inst_fn_var(
id: FnID,
fns: &[UFunc],
gargs: &[TypeID],
origin: Origin,
vars: &mut Vec<UVar>,
types: &mut Vec<Type>,
generics: &[UGeneric],
errs: &mut Vec<ResErr>,
) -> VarID {
let ty = inst_fn_ty(id, fns, gargs, types, generics, errs);
let name = fns[id].name.clone();
push_id(
vars,
UVar {
name,
origin,
ty,
parent: None,
children: Vec::new(),
},
)
}
impl TypeIDed for TypeID {
fn type_id(&self, _: &[UVar]) -> TypeID {
*self
}
pub fn inst_fn_ty(
id: FnID,
fns: &[UFunc],
gargs: &[TypeID],
types: &mut Vec<Type>,
generics: &[UGeneric],
errs: &mut Vec<ResErr>,
) -> TypeID {
let f = &fns[id];
let ty = Type::FnRef(FnTy {
id,
args: inst_generics(&f.gargs, gargs, types, generics, errs),
});
push_id(types, ty)
}
impl TypeIDed for &TypeID {
fn type_id(&self, _: &[UVar]) -> TypeID {
**self
}
pub fn inst_struct_var(
id: StructID,
structs: &[UStruct],
gargs: &[TypeID],
origin: Origin,
vars: &mut Vec<UVar>,
types: &mut Vec<Type>,
generics: &[UGeneric],
errs: &mut Vec<ResErr>,
) -> VarID {
let ty = inst_struct_ty(id, structs, gargs, types, generics, errs);
let name = structs[id].name.clone();
push_id(
vars,
UVar {
name,
origin,
ty,
parent: None,
children: Vec::new(),
},
)
}
impl TypeIDed for VarID {
fn type_id(&self, vars: &[UVar]) -> TypeID {
vars[self].ty
}
pub fn inst_struct_ty(
id: StructID,
structs: &[UStruct],
gargs: &[TypeID],
types: &mut Vec<Type>,
generics: &[UGeneric],
errs: &mut Vec<ResErr>,
) -> TypeID {
let s = &structs[id];
let ty = Type::Struct(StructTy {
id,
args: inst_generics(&s.gargs, gargs, types, generics, errs),
});
push_id(types, ty)
}
impl TypeIDed for VarInst {
fn type_id(&self, vars: &[UVar]) -> TypeID {
self.id.type_id(vars)
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();
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, &ty) in source.iter().zip(args) {
inst_type_ins(
|types, ty| {
let id = gmap[gid];
types[id] = ty;
id
},
ty,
types,
&gmap,
);
}
gargs
}
impl TypeIDed for &VarInst {
fn type_id(&self, vars: &[UVar]) -> TypeID {
self.id.type_id(vars)
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(StructTy {
id: struct_ty.id,
args: struct_ty
.args
.iter()
.map(|id| inst_type(*id, types, gmap))
.collect(),
}),
Type::FnRef(fn_ty) => Type::FnRef(FnTy {
id: fn_ty.id,
args: fn_ty
.args
.iter()
.map(|id| inst_type(*id, types, gmap))
.collect(),
}),
Type::Ref(id) => Type::Ref(inst_type(id, types, gmap)),
Type::Deref(id) => Type::Deref(inst_type(id, types, gmap)),
Type::Slice(id) => Type::Slice(inst_type(id, types, gmap)),
Type::Array(id, len) => Type::Array(inst_type(id, types, gmap), len),
Type::Unit => Type::Unit,
Type::Unres(mod_path) => Type::Unres(mod_path.clone()),
Type::Generic(gid) => return gmap.get(&gid).cloned().unwrap_or_else(|| id),
Type::Infer => Type::Infer,
Type::Error => Type::Error,
};
insert(types, ty)
}

View File

@@ -1,169 +0,0 @@
use super::{Type, UInstrInst, UInstruction, UProgram};
use crate::common::{CompilerMsg, CompilerOutput, FileSpan};
impl UProgram {
pub fn validate(&self, output: &mut CompilerOutput) {
for (id, f) in self.iter_fns() {
self.validate_fn(
&f.instructions,
self.origins.get(id),
&f.ret,
output,
true,
false,
);
}
}
pub fn validate_fn(
&self,
instructions: &[UInstrInst],
origin: FileSpan,
ret: &Type,
output: &mut CompilerOutput,
needs_ret: bool,
breakable: bool,
) {
let mut no_ret = true;
for i in instructions {
match &i.i {
UInstruction::Mv { dst: dest, src } => {
let dest = self.expect(dest.id);
let src = self.expect(src.id);
output.check_assign(self, &src.ty, &dest.ty, i.origin);
}
UInstruction::Ref { dst: dest, src } => {
let dest = self.expect(dest.id);
let src = self.expect(src.id);
output.check_assign(self, &src.ty.clone().rf(), &dest.ty, i.origin);
}
UInstruction::LoadData { dst: dest, src } => {
let dest = self.expect(dest.id);
let src = self.expect(*src);
output.check_assign(self, &src.ty, &dest.ty, i.origin);
}
UInstruction::LoadSlice { dst: dest, src } => {
let dest = self.expect(dest.id);
let src = self.expect(*src);
let Type::Array(srcty, ..) = &src.ty else {
todo!()
};
output.check_assign(self, &Type::Slice(srcty.clone()), &dest.ty, i.origin);
}
UInstruction::LoadFn { dst: dest, src } => todo!(),
UInstruction::Call { dst: dest, f, args } => {
let destty = &self.expect(dest.id).ty;
let f = self.expect(f.id);
let Type::Fn { args: argtys, ret } = &f.ty else {
output.err(CompilerMsg {
msg: format!("Type {} is not callable", self.type_name(&f.ty)),
spans: vec![dest.origin],
});
continue;
};
output.check_assign(self, ret, destty, dest.origin);
if args.len() != argtys.len() {
output.err(CompilerMsg {
msg: "Wrong number of arguments to function".to_string(),
spans: vec![dest.origin],
});
}
for (dst_ty, src) in argtys.iter().zip(args) {
let src_var = self.expect(src.id);
output.check_assign(self, &src_var.ty, dst_ty, src.origin);
}
}
UInstruction::AsmBlock { instructions, args } => {
for arg in args {
// TODO: validate size with enabled targets
// maybe should happen in lowering? but I think it could happen here
// if let Some(size) = self.size_of_var(arg.var.id)
// && size != 64
// {
// output.err(CompilerMsg {
// msg: format!("asm block args must be size 64, is size {}", size),
// spans: vec![arg.var.span],
// });
// }
}
}
UInstruction::Ret { src } => {
let srcty = &self.expect(src.id).ty;
output.check_assign(self, srcty, ret, src.origin);
no_ret = false;
}
UInstruction::Construct { dst: dest, fields } => {
let dest_def = self.expect(dest.id);
let sty = match &dest_def.ty {
Type::Struct(sty) => sty,
_ => {
output.err(CompilerMsg {
msg: format!(
"Type {} cannot be constructed",
self.type_name(&dest_def.ty)
),
spans: vec![dest.origin],
});
continue;
}
};
let def = self.expect(sty.id);
for (name, field) in &def.fields {
if let Some(var) = fields.get(name) {
let mut fty = &field.ty;
if let Type::Generic { id } = fty {
for (g, a) in def.generics.iter().zip(&sty.args) {
if *g == *id {
fty = a;
}
}
}
let ety = &self.expect(var.id).ty;
output.check_assign(self, ety, fty, var.origin);
} else {
output.err(CompilerMsg {
msg: format!("field '{}' missing from struct", name),
spans: vec![dest.origin],
});
}
}
}
UInstruction::If { cond, body } => {
let cond = self.expect(cond.id);
output.check_assign(self, &cond.ty, &Type::Bits(64), i.origin);
self.validate_fn(body, origin, ret, output, false, breakable);
}
UInstruction::Loop { body } => {
self.validate_fn(body, origin, ret, output, false, true);
}
UInstruction::Break => {
if !breakable {
output.err(CompilerMsg {
msg: "Can't break here (outside of loop)".to_string(),
spans: vec![i.origin],
});
}
// TODO
}
UInstruction::Continue => {
if !breakable {
output.err(CompilerMsg {
msg: "Can't continue here (outside of loop)".to_string(),
spans: vec![i.origin],
});
}
// TODO
}
}
}
if needs_ret && no_ret && *ret != Type::Unit {
output.err(CompilerMsg {
msg: format!(
"Function implicitly returns () at the end, must return {}",
self.type_name(ret)
),
spans: vec![origin],
});
}
}
}

View File

@@ -1,14 +1,15 @@
use crate::{
compiler::arch::riscv::Reg,
ir::{
arch::riscv64::RV64Instruction, AsmBlockArg, AsmBlockArgType, Type, UInstruction, VarInst
arch::riscv64::RV64Instruction, AsmBlockArg, AsmBlockArgType, Type, UInstruction, VarInst,
VarInstID,
},
parser::PAsmBlockArg,
};
use super::{FnLowerCtx, FnLowerable, PAsmBlock, PInstruction, PUAsmBlockArg};
type PLAsmBlockArg = PAsmBlockArg<Reg, VarInst>;
type PLAsmBlockArg = PAsmBlockArg<Reg, VarInstID>;
impl FnLowerable for PInstruction {
type Output = RV64Instruction;
@@ -19,7 +20,7 @@ impl FnLowerable for PInstruction {
}
impl FnLowerable for PAsmBlock {
type Output = VarInst;
type Output = VarInstID;
fn lower(&self, ctx: &mut FnLowerCtx) -> Option<Self::Output> {
let mut args = Vec::new();

View File

@@ -1,13 +1,13 @@
use crate::{
ir::{Type, UInstruction, UVar, VarInst},
ir::{Type, UInstruction, UVar, VarInst, VarInstID},
parser::{PConstStatement, PStatementLike},
};
use super::{FnLowerCtx, FnLowerable, Import, PBlock, PStatement};
impl FnLowerable for PBlock {
type Output = VarInst;
fn lower(&self, ctx: &mut FnLowerCtx) -> Option<VarInst> {
type Output = VarInstID;
fn lower(&self, ctx: &mut FnLowerCtx) -> Option<VarInstID> {
let mut last = None;
let mut statements = Vec::new();
let mut fn_nodes = Vec::new();

View File

@@ -1,12 +1,12 @@
use super::{func::FnLowerCtx, FnLowerable, PExpr, PostfixOp};
use crate::{
ir::{Type, UData, UInstruction, VarInst},
ir::{Type, UData, UInstruction, VarInst, VarInstID},
parser::InfixOp,
};
impl FnLowerable for PExpr {
type Output = VarInst;
fn lower(&self, ctx: &mut FnLowerCtx) -> Option<VarInst> {
type Output = VarInstID;
fn lower(&self, ctx: &mut FnLowerCtx) -> Option<VarInstID> {
let mut e = self;
let mut path = Vec::new();
while let PExpr::Member(node, ident) = e {
@@ -20,40 +20,40 @@ impl FnLowerable for PExpr {
Some(match e {
PExpr::Lit(l) => match l {
super::PLiteral::String(s) => {
let dest = ctx.b.temp_var(ctx.origin, Type::Bits(8).slice(ctx.b.p));
let dst = ctx.temp_var(ctx.origin, Type::Bits(8).slice(ctx.p));
let data = s.as_bytes().to_vec();
let src = ctx.b.def_data(UData {
let src = ctx.def_data(UData {
name: format!("string \"{}\"", s.replace("\n", "\\n")),
ty: Type::Bits(8).arr(ctx.b.p, data.len() as u32),
ty: ctx.def_ty(Type::Bits(8).arr(ctx.b.p, data.len() as u32)),
content: data,
});
ctx.push(UInstruction::LoadSlice { dst: dest, src });
dest
ctx.push(UInstruction::LoadSlice { dst, src });
dst
}
super::PLiteral::Char(c) => {
let ty = Type::Bits(8);
let dest = ctx.b.temp_var(ctx.origin, ty.clone());
let src = ctx.b.def_data(UData {
let ty = ctx.def_ty(Type::Bits(8));
let dst = ctx.temp_var(ctx.origin, ty.clone());
let src = ctx.def_data(UData {
name: format!("char '{c}'"),
ty,
content: c.to_string().as_bytes().to_vec(),
});
ctx.push(UInstruction::LoadData { dst: dest, src });
dest
ctx.push(UInstruction::LoadData { dst, src });
dst
}
super::PLiteral::Number(n) => {
// TODO: temp
let ty = Type::Bits(64);
let dest = ctx.b.temp_var(ctx.origin, ty.clone());
let src = ctx.b.def_data(UData {
let ty = ctx.def_ty(Type::Bits(64));
let dst = ctx.temp_var(ctx.origin, ty.clone());
let src = ctx.def_data(UData {
name: format!("num {n:?}"),
ty,
content: n.whole.parse::<i64>().unwrap().to_le_bytes().to_vec(),
});
ctx.push(UInstruction::LoadData { dst: dest, src });
dest
ctx.push(UInstruction::LoadData { dst, src });
dst
}
super::PLiteral::Unit => ctx.b.temp_var(ctx.origin, Type::Unit),
super::PLiteral::Unit => ctx.temp_var(ctx.origin, Type::Unit),
},
PExpr::Ident(i) => ctx.var(i),
PExpr::BinaryOp(op, e1, e2) => match op {
@@ -79,14 +79,17 @@ impl FnLowerable for PExpr {
PostfixOp::Ref => {
let ty = Type::Ref(ctx.b.infer());
let dest = ctx.temp(ty);
ctx.push(UInstruction::Ref { dst: dest, src: res });
ctx.push(UInstruction::Ref {
dst: dest,
src: res,
});
dest
}
PostfixOp::Deref => {
let ty = Type::Deref(ctx.b.infer());
let dest = ctx.temp(ty);
ctx.push(UInstruction::Deref { dst: dest, src: res });
dest
let dst = ctx.temp(ty);
ctx.push(UInstruction::Deref { dst, src: res });
dst
}
PostfixOp::Not => todo!(),
}
@@ -102,7 +105,7 @@ impl FnLowerable for PExpr {
}
let dest = ctx.temp(Type::Infer);
ctx.push(UInstruction::Call {
dst: VarInst { status: , origin: () },
dst: dest,
f: fe,
args: nargs,
});
@@ -110,26 +113,28 @@ impl FnLowerable for PExpr {
}
PExpr::Group(e) => e.lower(ctx)?,
PExpr::Construct(e, map) => {
let dest = ctx.temp(Type::Placeholder);
ctx.push(UInstruction::Construct { dst: dest, fields: () });
dest
let dst = ctx.temp(Type::Infer);
let struc = e.lower(ctx)?;
let fields = map.lower(ctx)?;
ctx.push(UInstruction::Construct { dst, struc, fields });
dst
}
PExpr::If(cond, body) => {
let cond = cond.lower(ctx)?;
ctx.b.push();
ctx.var_stack.push();
let mut body_ctx = ctx.branch();
body.lower(&mut body_ctx);
let body = body_ctx.instructions;
ctx.b.pop();
ctx.var_stack.pop();
ctx.push(UInstruction::If { cond, body });
return None;
}
PExpr::Loop(body) => {
ctx.b.push();
ctx.var_stack.push();
let mut body_ctx = ctx.branch();
body.lower(&mut body_ctx);
let body = body_ctx.instructions;
ctx.b.pop();
ctx.var_stack.pop();
ctx.push(UInstruction::Loop { body });
return None;
}

View File

@@ -1,12 +1,16 @@
use std::collections::HashMap;
use std::{
collections::HashMap,
ops::{Deref, DerefMut},
};
use super::{CompilerMsg, CompilerOutput, FileSpan, FnLowerable, Imports, Node, PFunction};
use crate::{
ir::{
FnID, Origin, Typable, Type, UFunc, UInstrInst, UInstruction, UModuleBuilder, UVar, VarID,
VarInst, VarStatus,
FnID, GenericID, ModPath, Origin, Typable, Type, UFunc, UInstrInst, UInstruction,
UModuleBuilder, VarID, VarInst, VarInstID, VarStatus,
},
parser, util::NameStack,
parser,
util::NameStack,
};
impl Node<PFunction> {
@@ -32,13 +36,9 @@ impl PFunction {
) -> Option<FnID> {
let header = self.header.as_ref()?;
let name = header.name.as_ref()?.0.clone();
let (generic_args, args, ret) = if let Some(header) = self.header.as_ref() {
let (generics, args, ret) = if let Some(header) = self.header.as_ref() {
(
header
.gargs
.iter()
.map(|a| Some(a.lower(b, output)))
.collect(),
header.gargs.iter().flat_map(|a| a.lower(b)).collect(),
header
.args
.iter()
@@ -50,27 +50,32 @@ impl PFunction {
},
)
} else {
(Vec::new(), Vec::new(), b.error)
(Vec::new(), Vec::new(), b.tc.error)
};
let gargs = generics.iter().map(|g| g.1).collect();
let generics = generics.into_iter().collect();
let instructions = {
let mut var_stack = Vec::new();
let mut var_stack = NameStack::new();
let mut ctx = FnLowerCtx {
instructions: Vec::new(),
var_stack: &mut var_stack,
b,
output,
origin: self.body.origin,
generics: &generics,
imports,
};
if let Some(src) = self.body.lower(&mut ctx) {
ctx.instructions.push(UInstrInst {
origin: src.origin,
let res = self.body.lower(&mut ctx);
let mut instructions = ctx.instructions;
if let Some(src) = res {
let origin = b.vars_insts[src].origin;
instructions.push(UInstrInst {
origin,
i: UInstruction::Ret { src },
});
}
ctx.instructions
instructions
};
let gargs = args.iter().map(|a| b.vars[a].ty).collect();
let f = UFunc {
origin,
gargs,
@@ -90,18 +95,32 @@ pub struct FnLowerCtx<'a, 'b> {
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>) -> VarInst {
if let Some(n) = node.as_ref() {
pub fn var(&mut self, node: &Node<parser::PIdent>) -> VarInstID {
let inst = VarInst {
status: if let Some(n) = node.as_ref() {
if let Some(&var) = self.var_stack.search(&n.0) {
return VarInst {
status: VarStatus::Res(var),
VarStatus::Var(var)
} else {
VarStatus::Unres {
path: ModPath {
id: self.b.module,
path: Vec::new(),
},
name: n.0.clone(),
gargs: Vec::new(),
fields: Vec::new(),
}
}
} else {
VarStatus::Cooked
},
origin: node.origin,
}
}
}
};
self.def_var_inst(inst)
}
pub fn err(&mut self, msg: String) {
self.output.err(CompilerMsg::new(msg, self.origin))
@@ -109,7 +128,7 @@ impl<'a, 'b> FnLowerCtx<'a, 'b> {
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: Type) -> VarInst {
pub fn temp<T: Typable>(&mut self, ty: T) -> VarInstID {
self.b.temp_var(self.origin, ty)
}
pub fn push(&mut self, i: UInstruction) {
@@ -118,10 +137,11 @@ impl<'a, 'b> FnLowerCtx<'a, 'b> {
pub fn push_at(&mut self, i: UInstruction, span: FileSpan) {
self.instructions.push(UInstrInst { i, origin: span });
}
pub fn branch(&'a mut self) -> FnLowerCtx<'a, 'b> {
pub fn branch<'c>(&'c mut self) -> FnLowerCtx<'c, 'b> {
FnLowerCtx {
b: self.b,
instructions: Vec::new(),
generics: self.generics,
var_stack: self.var_stack,
output: self.output,
origin: self.origin,
@@ -129,3 +149,17 @@ impl<'a, 'b> FnLowerCtx<'a, 'b> {
}
}
}
impl<'b> Deref for FnLowerCtx<'_, 'b> {
type Target = UModuleBuilder<'b>;
fn deref(&self) -> &Self::Target {
self.b
}
}
impl DerefMut for FnLowerCtx<'_, '_> {
fn deref_mut(&mut self) -> &mut Self::Target {
self.b
}
}

View File

@@ -0,0 +1,22 @@
use std::collections::HashMap;
use crate::{ir::VarInstID, parser::PMap};
use super::{FnLowerCtx, FnLowerable};
impl FnLowerable for PMap {
type Output = HashMap<String, VarInstID>;
fn lower(&self, ctx: &mut FnLowerCtx) -> Option<Self::Output> {
Some(
self.0
.iter()
.flat_map(|n| {
let def = n.as_ref()?;
let name = def.name.as_ref()?.to_string();
let expr = def.val.as_ref()?.lower(ctx)?;
Some((name, expr))
})
.collect(),
)
}
}

View File

@@ -6,6 +6,7 @@ mod expr;
mod func;
mod struc;
mod ty;
mod map;
use super::*;
use crate::ir::{Type, UFunc, UModuleBuilder};

View File

@@ -1,52 +1,18 @@
use crate::{
common::{CompilerOutput, FileSpan},
ir::{StructField, StructID, UInstruction, UModuleBuilder, UProgram, UStruct, VarInst},
parser::{Node, PMap, PStruct, PStructFields},
ir::{StructField, StructID, UModuleBuilder, UProgram, UStruct},
parser::{Node, PStruct, PStructFields},
};
use super::{FnLowerCtx, FnLowerable};
impl FnLowerable for PMap {
type Output = VarInst;
fn lower(&self, ctx: &mut FnLowerCtx) -> Option<VarInst> {
let ty = self.name.lower(ctx.b, ctx.output);
let fields = match &self.fields {
PConstructFields::Named(nodes) => nodes
.iter()
.flat_map(|n| {
let def = n.as_ref()?;
let name = def.name.as_ref()?.to_string();
let expr = def.val.as_ref()?.lower(ctx)?;
Some((name, expr))
})
.collect(),
PConstructFields::Tuple(nodes) => nodes
.iter()
.enumerate()
.flat_map(|(i, n)| {
let expr = n.as_ref()?.lower(ctx)?;
let name = format!("{i}");
Some((name, expr))
})
.collect(),
PConstructFields::None => Default::default(),
};
let id = ctx.temp(ty);
ctx.push(UInstruction::Construct { dst: id, fields });
Some(id)
}
}
impl PStruct {
pub fn lower(
&self,
id: StructID,
p: &mut UModuleBuilder,
b: &mut UModuleBuilder,
output: &mut CompilerOutput,
span: FileSpan,
) -> Option<()> {
p.push();
let generics = self.generics.iter().flat_map(|a| a.lower(p)).collect();
let generics = self.generics.iter().flat_map(|a| a.lower(b)).collect();
let fields = match &self.fields {
PStructFields::Named(nodes) => nodes
.iter()
@@ -54,7 +20,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(p, output);
let ty = tynode.lower(b, output);
Some((name, ty))
})
.collect(),
@@ -62,7 +28,7 @@ impl PStruct {
.iter()
.enumerate()
.flat_map(|(i, n)| {
let ty = n.as_ref()?.lower(p, output, span);
let ty = n.as_ref()?.lower(b, output, span);
Some((format!("{i}"), ty))
})
.collect(),
@@ -72,13 +38,12 @@ impl PStruct {
.map(|(name, ty)| (name, StructField { ty }))
.collect();
let name = self.name.as_ref()?.to_string();
p.def_data(UStruct {
b.def_data(UStruct {
name,
generics,
gargs: generics,
fields,
origin: span,
});
p.pop();
Some(())
}
}

View File

@@ -54,7 +54,7 @@ impl PType {
name: id.0.clone(),
origin,
});
let ty = Type::Unres(ModPath { m: p.module, path });
let ty = Type::Unres(ModPath { id: p.module, path });
return p.def_ty(ty);
}
let ty = match ty {
@@ -65,7 +65,7 @@ impl PType {
origin,
});
path.reverse();
Type::Unres(ModPath { m: p.module, path })
Type::Unres(ModPath { id: p.module, path })
}
PType::Ref(node) => node.lower(p, output).rf(),
PType::Generic(node, nodes) => todo!(),
@@ -75,12 +75,15 @@ impl PType {
}
impl Node<PGenericDef> {
pub fn lower(&self, p: &mut UProgram) -> Option<GenericID> {
pub fn lower(&self, p: &mut UProgram) -> Option<(String, GenericID)> {
let s = self.as_ref()?;
let name = s.name.as_ref()?.to_string();
Some(p.def_generic(UGeneric {
Some((
name.clone(),
p.def_generic(UGeneric {
name,
origin: self.origin,
}))
}),
))
}
}

View File

@@ -1,13 +1,13 @@
use super::{
util::parse_list, Node, PBlock, PIdent, PType, PVarDef, Parsable, ParseResult, ParserCtx,
Symbol,
util::parse_list, Node, PBlock, PGenericDef, PIdent, PType, PVarDef, Parsable, ParseResult,
ParserCtx, Symbol,
};
use std::fmt::Debug;
pub struct PFunctionHeader {
pub name: Node<PIdent>,
pub args: Vec<Node<PVarDef>>,
pub gargs: Vec<Node<PType>>,
pub gargs: Vec<Node<PGenericDef>>,
pub ret: Option<Node<PType>>,
}

View File

@@ -14,7 +14,7 @@ pub struct PStruct {
pub fields: PStructFields,
}
pub struct PMap(Vec<Node<PFieldDef>>);
pub struct PMap(pub Vec<Node<PFieldDef>>);
#[derive(Debug)]
pub enum PStructFields {

View File

@@ -4,7 +4,7 @@ pub struct NameStack<T>(Vec<HashMap<String, T>>);
impl<T> NameStack<T> {
pub fn new() -> Self {
Self(Vec::new())
Self(vec![HashMap::new()])
}
pub fn search(&self, name: &str) -> Option<&T> {
for level in self.0.iter().rev() {
@@ -14,4 +14,10 @@ impl<T> NameStack<T> {
}
None
}
pub fn push(&mut self) {
self.0.push(HashMap::new());
}
pub fn pop(&mut self) {
self.0.pop();
}
}