the light is getting closer
This commit is contained in:
@@ -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
186
src/ir/upper/error.rs
Normal 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)
|
||||
)
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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() {
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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::*;
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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],
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user