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

@@ -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);
}
for arg in args.iter().skip(1) {
str = str + ", " + &self.type_name(arg);
}
if !args.is_empty() {
str += ">";
}
format!("{}{}", self.structs[ty.id].name, self.gparams_str(&ty.args))
}
Type::Fn { args, ret } => {
str += "fn(";
if let Some(arg) = args.first() {
str += &self.type_name(arg);
}
for arg in args.iter().skip(1) {
str = str + ", " + &self.type_name(arg);
}
str += ") -> ";
str += &self.type_name(ret);
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)
)
}
Type::Ref(t) => {
str += &self.type_name(t);
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}",
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(),
}
}
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 {
str = str + ", " + &self.type_name(arg);
}
str
}
pub fn gparams_str(&self, args: &[TypeID]) -> String {
let mut str = String::new();
if !args.is_empty() {
str += "<";
}
str += &self.type_list_str(args.iter().cloned());
if !args.is_empty() {
str += ">";
}
str
}
}
pub fn push_id<T>(v: &mut Vec<T>, t: T) -> ID<T> {
let id = ID::new(v.len());
v.push(t);
id
}
impl<'a> UModuleBuilder<'a> {
pub fn new(program: &'a mut UProgram, id: ModID, error: TypeID) -> Self {
pub fn new(program: &'a mut UProgram, id: ModID) -> 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());
id
}
pub fn temp_var<T: Typable>(&mut self, origin: Origin, ty: T) -> VarInst {
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,
structs: &self.structs,
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,163 +82,205 @@ 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;
};
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);
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 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 } => {
for i in body {
resolve_instr(
data,
ResolveCtx {
ret: ctx.ret,
breakable: true,
i,
},
);
}
}
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,
});
}
}
UInstruction::Loop { body } => {}
UInstruction::Break => {}
UInstruction::Continue => {}
}
uf
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();
}
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) {
MatchRes::Unfinished => finished = false,
MatchRes::Error(errs) => errors.extend(errs),
MatchRes::Finished => (),
}
}
if finished {
if errors.is_empty() {
MatchRes::Finished
} else {
MatchRes::Error(errors)
}
} else {
MatchRes::Unfinished
}
match_all(data, dest.args.iter().cloned(), src.args.iter().cloned())
}
(
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)) => {
// (
// 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 {
@@ -242,49 +291,79 @@ pub fn match_types<T1: TypeIDed, T2: TypeIDed>(
}
}
struct ResData<'a> {
changed: bool,
types: &'a mut [Type],
vars: &'a [UVar],
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();
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 => (),
}
}
if finished {
if errors.is_empty() {
MatchRes::Finished
} else {
MatchRes::Error(errors)
}
} else {
MatchRes::Unfinished
}
}
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 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],
});
}
}
}