the light is getting closer
This commit is contained in:
28
src/ir/id.rs
28
src/ir/id.rs
@@ -79,6 +79,20 @@ impl<T> IndexMut<&ID<T>> for Vec<T> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Index<&mut ID<T>> for Vec<T> {
|
||||
type Output = T;
|
||||
|
||||
fn index(&self, i: &mut ID<T>) -> &Self::Output {
|
||||
&self[i.0]
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> IndexMut<&mut ID<T>> for Vec<T> {
|
||||
fn index_mut(&mut self, i: &mut ID<T>) -> &mut Self::Output {
|
||||
&mut self[i.0]
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Index<ID<T>> for [T] {
|
||||
type Output = T;
|
||||
|
||||
@@ -106,3 +120,17 @@ impl<T> IndexMut<&ID<T>> for [T] {
|
||||
&mut self[i.0]
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Index<&mut ID<T>> for [T] {
|
||||
type Output = T;
|
||||
|
||||
fn index(&self, i: &mut ID<T>) -> &Self::Output {
|
||||
&self[i.0]
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> IndexMut<&mut ID<T>> for [T] {
|
||||
fn index_mut(&mut self, i: &mut ID<T>) -> &mut Self::Output {
|
||||
&mut self[i.0]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
use crate::ir::{
|
||||
AsmBlockArgType, FnID, Size, GenericTy, SymbolSpace, Type, UFunc, UInstrInst, VarOffset,
|
||||
};
|
||||
|
||||
use crate::ir::{AsmBlockArgType, Size, StructTy, SymbolSpace, Type, UFunc, UInstrInst, VarOffset};
|
||||
StructTy
|
||||
use super::{
|
||||
IRLFunction, LInstruction, Len, Symbol, SymbolSpaceBuilder, UInstruction, UProgram, VarID,
|
||||
};
|
||||
@@ -16,11 +14,15 @@ pub struct LProgram {
|
||||
// NOTE: there are THREE places here where I specify size (8)
|
||||
|
||||
impl LProgram {
|
||||
pub fn create(p: &UProgram, start: FnID) -> Result<Self, String> {
|
||||
pub fn create(p: &UProgram) -> Result<Self, String> {
|
||||
let start = p
|
||||
.names
|
||||
.id::<UFunc>(&[], "crate")
|
||||
.ok_or("no start method found")?;
|
||||
let mut ssbuilder = SymbolSpaceBuilder::with_entries(&[start]);
|
||||
let entry = ssbuilder.func(&start);
|
||||
while let Some((sym, i)) = ssbuilder.pop_fn() {
|
||||
let f = &p.fns[i];
|
||||
let f = p.fns[i.0].as_ref().unwrap();
|
||||
let mut fbuilder = LFunctionBuilder::new(p, &mut ssbuilder);
|
||||
for i in &f.instructions {
|
||||
fbuilder.insert_instr(i);
|
||||
@@ -29,7 +31,7 @@ impl LProgram {
|
||||
fbuilder.instrs.push(LInstruction::Ret { src: None });
|
||||
}
|
||||
let res = fbuilder.finish(f);
|
||||
ssbuilder.write_fn(sym, res, Some(f.name.clone()));
|
||||
ssbuilder.write_fn(sym, res, Some(p.names.path(i).to_string()));
|
||||
}
|
||||
let sym_space = ssbuilder.finish().expect("we failed the mission");
|
||||
Ok(Self { sym_space, entry })
|
||||
@@ -372,9 +374,9 @@ impl LFunctionBuilderData<'_> {
|
||||
Some(VarOffset { id: var, offset })
|
||||
}
|
||||
pub fn addr_size(&self) -> Size {
|
||||
64
|
||||
64StructTy
|
||||
}
|
||||
pub fn struct_inst(&mut self, p: &UProgram, ty: &GenericTy) -> &StructInst {
|
||||
pub fn struct_inst(&mut self, p: &UProgram, ty: &StructTy) -> &StructInst {
|
||||
// normally I'd let Some(..) here and return, but polonius does not exist :grief:
|
||||
if self.struct_insts.get(ty).is_none() {
|
||||
let StructInst { id, args } = ty;
|
||||
|
||||
@@ -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],
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,14 +1,15 @@
|
||||
use crate::{
|
||||
compiler::arch::riscv::Reg,
|
||||
ir::{
|
||||
arch::riscv64::RV64Instruction, AsmBlockArg, AsmBlockArgType, Type, UInstruction, VarInst
|
||||
arch::riscv64::RV64Instruction, AsmBlockArg, AsmBlockArgType, Type, UInstruction, VarInst,
|
||||
VarInstID,
|
||||
},
|
||||
parser::PAsmBlockArg,
|
||||
};
|
||||
|
||||
use super::{FnLowerCtx, FnLowerable, PAsmBlock, PInstruction, PUAsmBlockArg};
|
||||
|
||||
type PLAsmBlockArg = PAsmBlockArg<Reg, VarInst>;
|
||||
type PLAsmBlockArg = PAsmBlockArg<Reg, VarInstID>;
|
||||
|
||||
impl FnLowerable for PInstruction {
|
||||
type Output = RV64Instruction;
|
||||
@@ -19,7 +20,7 @@ impl FnLowerable for PInstruction {
|
||||
}
|
||||
|
||||
impl FnLowerable for PAsmBlock {
|
||||
type Output = VarInst;
|
||||
type Output = VarInstID;
|
||||
|
||||
fn lower(&self, ctx: &mut FnLowerCtx) -> Option<Self::Output> {
|
||||
let mut args = Vec::new();
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
use crate::{
|
||||
ir::{Type, UInstruction, UVar, VarInst},
|
||||
ir::{Type, UInstruction, UVar, VarInst, VarInstID},
|
||||
parser::{PConstStatement, PStatementLike},
|
||||
};
|
||||
|
||||
use super::{FnLowerCtx, FnLowerable, Import, PBlock, PStatement};
|
||||
|
||||
impl FnLowerable for PBlock {
|
||||
type Output = VarInst;
|
||||
fn lower(&self, ctx: &mut FnLowerCtx) -> Option<VarInst> {
|
||||
type Output = VarInstID;
|
||||
fn lower(&self, ctx: &mut FnLowerCtx) -> Option<VarInstID> {
|
||||
let mut last = None;
|
||||
let mut statements = Vec::new();
|
||||
let mut fn_nodes = Vec::new();
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
use super::{func::FnLowerCtx, FnLowerable, PExpr, PostfixOp};
|
||||
use crate::{
|
||||
ir::{Type, UData, UInstruction, VarInst},
|
||||
ir::{Type, UData, UInstruction, VarInst, VarInstID},
|
||||
parser::InfixOp,
|
||||
};
|
||||
|
||||
impl FnLowerable for PExpr {
|
||||
type Output = VarInst;
|
||||
fn lower(&self, ctx: &mut FnLowerCtx) -> Option<VarInst> {
|
||||
type Output = VarInstID;
|
||||
fn lower(&self, ctx: &mut FnLowerCtx) -> Option<VarInstID> {
|
||||
let mut e = self;
|
||||
let mut path = Vec::new();
|
||||
while let PExpr::Member(node, ident) = e {
|
||||
@@ -20,40 +20,40 @@ impl FnLowerable for PExpr {
|
||||
Some(match e {
|
||||
PExpr::Lit(l) => match l {
|
||||
super::PLiteral::String(s) => {
|
||||
let dest = ctx.b.temp_var(ctx.origin, Type::Bits(8).slice(ctx.b.p));
|
||||
let dst = ctx.temp_var(ctx.origin, Type::Bits(8).slice(ctx.p));
|
||||
let data = s.as_bytes().to_vec();
|
||||
let src = ctx.b.def_data(UData {
|
||||
let src = ctx.def_data(UData {
|
||||
name: format!("string \"{}\"", s.replace("\n", "\\n")),
|
||||
ty: Type::Bits(8).arr(ctx.b.p, data.len() as u32),
|
||||
ty: ctx.def_ty(Type::Bits(8).arr(ctx.b.p, data.len() as u32)),
|
||||
content: data,
|
||||
});
|
||||
ctx.push(UInstruction::LoadSlice { dst: dest, src });
|
||||
dest
|
||||
ctx.push(UInstruction::LoadSlice { dst, src });
|
||||
dst
|
||||
}
|
||||
super::PLiteral::Char(c) => {
|
||||
let ty = Type::Bits(8);
|
||||
let dest = ctx.b.temp_var(ctx.origin, ty.clone());
|
||||
let src = ctx.b.def_data(UData {
|
||||
let ty = ctx.def_ty(Type::Bits(8));
|
||||
let dst = ctx.temp_var(ctx.origin, ty.clone());
|
||||
let src = ctx.def_data(UData {
|
||||
name: format!("char '{c}'"),
|
||||
ty,
|
||||
content: c.to_string().as_bytes().to_vec(),
|
||||
});
|
||||
ctx.push(UInstruction::LoadData { dst: dest, src });
|
||||
dest
|
||||
ctx.push(UInstruction::LoadData { dst, src });
|
||||
dst
|
||||
}
|
||||
super::PLiteral::Number(n) => {
|
||||
// TODO: temp
|
||||
let ty = Type::Bits(64);
|
||||
let dest = ctx.b.temp_var(ctx.origin, ty.clone());
|
||||
let src = ctx.b.def_data(UData {
|
||||
let ty = ctx.def_ty(Type::Bits(64));
|
||||
let dst = ctx.temp_var(ctx.origin, ty.clone());
|
||||
let src = ctx.def_data(UData {
|
||||
name: format!("num {n:?}"),
|
||||
ty,
|
||||
content: n.whole.parse::<i64>().unwrap().to_le_bytes().to_vec(),
|
||||
});
|
||||
ctx.push(UInstruction::LoadData { dst: dest, src });
|
||||
dest
|
||||
ctx.push(UInstruction::LoadData { dst, src });
|
||||
dst
|
||||
}
|
||||
super::PLiteral::Unit => ctx.b.temp_var(ctx.origin, Type::Unit),
|
||||
super::PLiteral::Unit => ctx.temp_var(ctx.origin, Type::Unit),
|
||||
},
|
||||
PExpr::Ident(i) => ctx.var(i),
|
||||
PExpr::BinaryOp(op, e1, e2) => match op {
|
||||
@@ -79,14 +79,17 @@ impl FnLowerable for PExpr {
|
||||
PostfixOp::Ref => {
|
||||
let ty = Type::Ref(ctx.b.infer());
|
||||
let dest = ctx.temp(ty);
|
||||
ctx.push(UInstruction::Ref { dst: dest, src: res });
|
||||
ctx.push(UInstruction::Ref {
|
||||
dst: dest,
|
||||
src: res,
|
||||
});
|
||||
dest
|
||||
}
|
||||
PostfixOp::Deref => {
|
||||
let ty = Type::Deref(ctx.b.infer());
|
||||
let dest = ctx.temp(ty);
|
||||
ctx.push(UInstruction::Deref { dst: dest, src: res });
|
||||
dest
|
||||
let dst = ctx.temp(ty);
|
||||
ctx.push(UInstruction::Deref { dst, src: res });
|
||||
dst
|
||||
}
|
||||
PostfixOp::Not => todo!(),
|
||||
}
|
||||
@@ -102,7 +105,7 @@ impl FnLowerable for PExpr {
|
||||
}
|
||||
let dest = ctx.temp(Type::Infer);
|
||||
ctx.push(UInstruction::Call {
|
||||
dst: VarInst { status: , origin: () },
|
||||
dst: dest,
|
||||
f: fe,
|
||||
args: nargs,
|
||||
});
|
||||
@@ -110,26 +113,28 @@ impl FnLowerable for PExpr {
|
||||
}
|
||||
PExpr::Group(e) => e.lower(ctx)?,
|
||||
PExpr::Construct(e, map) => {
|
||||
let dest = ctx.temp(Type::Placeholder);
|
||||
ctx.push(UInstruction::Construct { dst: dest, fields: () });
|
||||
dest
|
||||
let dst = ctx.temp(Type::Infer);
|
||||
let struc = e.lower(ctx)?;
|
||||
let fields = map.lower(ctx)?;
|
||||
ctx.push(UInstruction::Construct { dst, struc, fields });
|
||||
dst
|
||||
}
|
||||
PExpr::If(cond, body) => {
|
||||
let cond = cond.lower(ctx)?;
|
||||
ctx.b.push();
|
||||
ctx.var_stack.push();
|
||||
let mut body_ctx = ctx.branch();
|
||||
body.lower(&mut body_ctx);
|
||||
let body = body_ctx.instructions;
|
||||
ctx.b.pop();
|
||||
ctx.var_stack.pop();
|
||||
ctx.push(UInstruction::If { cond, body });
|
||||
return None;
|
||||
}
|
||||
PExpr::Loop(body) => {
|
||||
ctx.b.push();
|
||||
ctx.var_stack.push();
|
||||
let mut body_ctx = ctx.branch();
|
||||
body.lower(&mut body_ctx);
|
||||
let body = body_ctx.instructions;
|
||||
ctx.b.pop();
|
||||
ctx.var_stack.pop();
|
||||
ctx.push(UInstruction::Loop { body });
|
||||
return None;
|
||||
}
|
||||
|
||||
@@ -1,12 +1,16 @@
|
||||
use std::collections::HashMap;
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
ops::{Deref, DerefMut},
|
||||
};
|
||||
|
||||
use super::{CompilerMsg, CompilerOutput, FileSpan, FnLowerable, Imports, Node, PFunction};
|
||||
use crate::{
|
||||
ir::{
|
||||
FnID, Origin, Typable, Type, UFunc, UInstrInst, UInstruction, UModuleBuilder, UVar, VarID,
|
||||
VarInst, VarStatus,
|
||||
FnID, GenericID, ModPath, Origin, Typable, Type, UFunc, UInstrInst, UInstruction,
|
||||
UModuleBuilder, VarID, VarInst, VarInstID, VarStatus,
|
||||
},
|
||||
parser, util::NameStack,
|
||||
parser,
|
||||
util::NameStack,
|
||||
};
|
||||
|
||||
impl Node<PFunction> {
|
||||
@@ -32,13 +36,9 @@ impl PFunction {
|
||||
) -> Option<FnID> {
|
||||
let header = self.header.as_ref()?;
|
||||
let name = header.name.as_ref()?.0.clone();
|
||||
let (generic_args, args, ret) = if let Some(header) = self.header.as_ref() {
|
||||
let (generics, args, ret) = if let Some(header) = self.header.as_ref() {
|
||||
(
|
||||
header
|
||||
.gargs
|
||||
.iter()
|
||||
.map(|a| Some(a.lower(b, output)))
|
||||
.collect(),
|
||||
header.gargs.iter().flat_map(|a| a.lower(b)).collect(),
|
||||
header
|
||||
.args
|
||||
.iter()
|
||||
@@ -50,27 +50,32 @@ impl PFunction {
|
||||
},
|
||||
)
|
||||
} else {
|
||||
(Vec::new(), Vec::new(), b.error)
|
||||
(Vec::new(), Vec::new(), b.tc.error)
|
||||
};
|
||||
let gargs = generics.iter().map(|g| g.1).collect();
|
||||
let generics = generics.into_iter().collect();
|
||||
let instructions = {
|
||||
let mut var_stack = Vec::new();
|
||||
let mut var_stack = NameStack::new();
|
||||
let mut ctx = FnLowerCtx {
|
||||
instructions: Vec::new(),
|
||||
var_stack: &mut var_stack,
|
||||
b,
|
||||
output,
|
||||
origin: self.body.origin,
|
||||
generics: &generics,
|
||||
imports,
|
||||
};
|
||||
if let Some(src) = self.body.lower(&mut ctx) {
|
||||
ctx.instructions.push(UInstrInst {
|
||||
origin: src.origin,
|
||||
let res = self.body.lower(&mut ctx);
|
||||
let mut instructions = ctx.instructions;
|
||||
if let Some(src) = res {
|
||||
let origin = b.vars_insts[src].origin;
|
||||
instructions.push(UInstrInst {
|
||||
origin,
|
||||
i: UInstruction::Ret { src },
|
||||
});
|
||||
}
|
||||
ctx.instructions
|
||||
instructions
|
||||
};
|
||||
let gargs = args.iter().map(|a| b.vars[a].ty).collect();
|
||||
let f = UFunc {
|
||||
origin,
|
||||
gargs,
|
||||
@@ -90,18 +95,32 @@ pub struct FnLowerCtx<'a, 'b> {
|
||||
pub origin: FileSpan,
|
||||
pub imports: &'a mut Imports,
|
||||
pub var_stack: &'a mut NameStack<VarID>,
|
||||
pub generics: &'a HashMap<String, GenericID>,
|
||||
}
|
||||
|
||||
impl<'a, 'b> FnLowerCtx<'a, 'b> {
|
||||
pub fn var(&mut self, node: &Node<parser::PIdent>) -> VarInst {
|
||||
if let Some(n) = node.as_ref() {
|
||||
if let Some(&var) = self.var_stack.search(&n.0) {
|
||||
return VarInst {
|
||||
status: VarStatus::Res(var),
|
||||
origin: node.origin,
|
||||
pub fn var(&mut self, node: &Node<parser::PIdent>) -> VarInstID {
|
||||
let inst = VarInst {
|
||||
status: if let Some(n) = node.as_ref() {
|
||||
if let Some(&var) = self.var_stack.search(&n.0) {
|
||||
VarStatus::Var(var)
|
||||
} else {
|
||||
VarStatus::Unres {
|
||||
path: ModPath {
|
||||
id: self.b.module,
|
||||
path: Vec::new(),
|
||||
},
|
||||
name: n.0.clone(),
|
||||
gargs: Vec::new(),
|
||||
fields: Vec::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
VarStatus::Cooked
|
||||
},
|
||||
origin: node.origin,
|
||||
};
|
||||
self.def_var_inst(inst)
|
||||
}
|
||||
pub fn err(&mut self, msg: String) {
|
||||
self.output.err(CompilerMsg::new(msg, self.origin))
|
||||
@@ -109,7 +128,7 @@ impl<'a, 'b> FnLowerCtx<'a, 'b> {
|
||||
pub fn err_at(&mut self, span: FileSpan, msg: String) {
|
||||
self.output.err(CompilerMsg::new(msg, span))
|
||||
}
|
||||
pub fn temp<T: Typable>(&mut self, ty: Type) -> VarInst {
|
||||
pub fn temp<T: Typable>(&mut self, ty: T) -> VarInstID {
|
||||
self.b.temp_var(self.origin, ty)
|
||||
}
|
||||
pub fn push(&mut self, i: UInstruction) {
|
||||
@@ -118,10 +137,11 @@ impl<'a, 'b> FnLowerCtx<'a, 'b> {
|
||||
pub fn push_at(&mut self, i: UInstruction, span: FileSpan) {
|
||||
self.instructions.push(UInstrInst { i, origin: span });
|
||||
}
|
||||
pub fn branch(&'a mut self) -> FnLowerCtx<'a, 'b> {
|
||||
pub fn branch<'c>(&'c mut self) -> FnLowerCtx<'c, 'b> {
|
||||
FnLowerCtx {
|
||||
b: self.b,
|
||||
instructions: Vec::new(),
|
||||
generics: self.generics,
|
||||
var_stack: self.var_stack,
|
||||
output: self.output,
|
||||
origin: self.origin,
|
||||
@@ -129,3 +149,17 @@ impl<'a, 'b> FnLowerCtx<'a, 'b> {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'b> Deref for FnLowerCtx<'_, 'b> {
|
||||
type Target = UModuleBuilder<'b>;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
self.b
|
||||
}
|
||||
}
|
||||
|
||||
impl DerefMut for FnLowerCtx<'_, '_> {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
self.b
|
||||
}
|
||||
}
|
||||
|
||||
22
src/parser/v3/lower/map.rs
Normal file
22
src/parser/v3/lower/map.rs
Normal file
@@ -0,0 +1,22 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
use crate::{ir::VarInstID, parser::PMap};
|
||||
|
||||
use super::{FnLowerCtx, FnLowerable};
|
||||
|
||||
impl FnLowerable for PMap {
|
||||
type Output = HashMap<String, VarInstID>;
|
||||
fn lower(&self, ctx: &mut FnLowerCtx) -> Option<Self::Output> {
|
||||
Some(
|
||||
self.0
|
||||
.iter()
|
||||
.flat_map(|n| {
|
||||
let def = n.as_ref()?;
|
||||
let name = def.name.as_ref()?.to_string();
|
||||
let expr = def.val.as_ref()?.lower(ctx)?;
|
||||
Some((name, expr))
|
||||
})
|
||||
.collect(),
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -6,6 +6,7 @@ mod expr;
|
||||
mod func;
|
||||
mod struc;
|
||||
mod ty;
|
||||
mod map;
|
||||
|
||||
use super::*;
|
||||
use crate::ir::{Type, UFunc, UModuleBuilder};
|
||||
|
||||
@@ -1,52 +1,18 @@
|
||||
use crate::{
|
||||
common::{CompilerOutput, FileSpan},
|
||||
ir::{StructField, StructID, UInstruction, UModuleBuilder, UProgram, UStruct, VarInst},
|
||||
parser::{Node, PMap, PStruct, PStructFields},
|
||||
ir::{StructField, StructID, UModuleBuilder, UProgram, UStruct},
|
||||
parser::{Node, PStruct, PStructFields},
|
||||
};
|
||||
|
||||
use super::{FnLowerCtx, FnLowerable};
|
||||
|
||||
impl FnLowerable for PMap {
|
||||
type Output = VarInst;
|
||||
fn lower(&self, ctx: &mut FnLowerCtx) -> Option<VarInst> {
|
||||
let ty = self.name.lower(ctx.b, ctx.output);
|
||||
let fields = match &self.fields {
|
||||
PConstructFields::Named(nodes) => nodes
|
||||
.iter()
|
||||
.flat_map(|n| {
|
||||
let def = n.as_ref()?;
|
||||
let name = def.name.as_ref()?.to_string();
|
||||
let expr = def.val.as_ref()?.lower(ctx)?;
|
||||
Some((name, expr))
|
||||
})
|
||||
.collect(),
|
||||
PConstructFields::Tuple(nodes) => nodes
|
||||
.iter()
|
||||
.enumerate()
|
||||
.flat_map(|(i, n)| {
|
||||
let expr = n.as_ref()?.lower(ctx)?;
|
||||
let name = format!("{i}");
|
||||
Some((name, expr))
|
||||
})
|
||||
.collect(),
|
||||
PConstructFields::None => Default::default(),
|
||||
};
|
||||
let id = ctx.temp(ty);
|
||||
ctx.push(UInstruction::Construct { dst: id, fields });
|
||||
Some(id)
|
||||
}
|
||||
}
|
||||
|
||||
impl PStruct {
|
||||
pub fn lower(
|
||||
&self,
|
||||
id: StructID,
|
||||
p: &mut UModuleBuilder,
|
||||
b: &mut UModuleBuilder,
|
||||
output: &mut CompilerOutput,
|
||||
span: FileSpan,
|
||||
) -> Option<()> {
|
||||
p.push();
|
||||
let generics = self.generics.iter().flat_map(|a| a.lower(p)).collect();
|
||||
let generics = self.generics.iter().flat_map(|a| a.lower(b)).collect();
|
||||
let fields = match &self.fields {
|
||||
PStructFields::Named(nodes) => nodes
|
||||
.iter()
|
||||
@@ -54,7 +20,7 @@ impl PStruct {
|
||||
let def = n.as_ref()?;
|
||||
let name = def.name.as_ref()?.to_string();
|
||||
let tynode = def.ty.as_ref()?;
|
||||
let ty = tynode.lower(p, output);
|
||||
let ty = tynode.lower(b, output);
|
||||
Some((name, ty))
|
||||
})
|
||||
.collect(),
|
||||
@@ -62,7 +28,7 @@ impl PStruct {
|
||||
.iter()
|
||||
.enumerate()
|
||||
.flat_map(|(i, n)| {
|
||||
let ty = n.as_ref()?.lower(p, output, span);
|
||||
let ty = n.as_ref()?.lower(b, output, span);
|
||||
Some((format!("{i}"), ty))
|
||||
})
|
||||
.collect(),
|
||||
@@ -72,13 +38,12 @@ impl PStruct {
|
||||
.map(|(name, ty)| (name, StructField { ty }))
|
||||
.collect();
|
||||
let name = self.name.as_ref()?.to_string();
|
||||
p.def_data(UStruct {
|
||||
b.def_data(UStruct {
|
||||
name,
|
||||
generics,
|
||||
gargs: generics,
|
||||
fields,
|
||||
origin: span,
|
||||
});
|
||||
p.pop();
|
||||
Some(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -54,7 +54,7 @@ impl PType {
|
||||
name: id.0.clone(),
|
||||
origin,
|
||||
});
|
||||
let ty = Type::Unres(ModPath { m: p.module, path });
|
||||
let ty = Type::Unres(ModPath { id: p.module, path });
|
||||
return p.def_ty(ty);
|
||||
}
|
||||
let ty = match ty {
|
||||
@@ -65,7 +65,7 @@ impl PType {
|
||||
origin,
|
||||
});
|
||||
path.reverse();
|
||||
Type::Unres(ModPath { m: p.module, path })
|
||||
Type::Unres(ModPath { id: p.module, path })
|
||||
}
|
||||
PType::Ref(node) => node.lower(p, output).rf(),
|
||||
PType::Generic(node, nodes) => todo!(),
|
||||
@@ -75,12 +75,15 @@ impl PType {
|
||||
}
|
||||
|
||||
impl Node<PGenericDef> {
|
||||
pub fn lower(&self, p: &mut UProgram) -> Option<GenericID> {
|
||||
pub fn lower(&self, p: &mut UProgram) -> Option<(String, GenericID)> {
|
||||
let s = self.as_ref()?;
|
||||
let name = s.name.as_ref()?.to_string();
|
||||
Some(p.def_generic(UGeneric {
|
||||
name,
|
||||
origin: self.origin,
|
||||
}))
|
||||
Some((
|
||||
name.clone(),
|
||||
p.def_generic(UGeneric {
|
||||
name,
|
||||
origin: self.origin,
|
||||
}),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
use super::{
|
||||
util::parse_list, Node, PBlock, PIdent, PType, PVarDef, Parsable, ParseResult, ParserCtx,
|
||||
Symbol,
|
||||
util::parse_list, Node, PBlock, PGenericDef, PIdent, PType, PVarDef, Parsable, ParseResult,
|
||||
ParserCtx, Symbol,
|
||||
};
|
||||
use std::fmt::Debug;
|
||||
|
||||
pub struct PFunctionHeader {
|
||||
pub name: Node<PIdent>,
|
||||
pub args: Vec<Node<PVarDef>>,
|
||||
pub gargs: Vec<Node<PType>>,
|
||||
pub gargs: Vec<Node<PGenericDef>>,
|
||||
pub ret: Option<Node<PType>>,
|
||||
}
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ pub struct PStruct {
|
||||
pub fields: PStructFields,
|
||||
}
|
||||
|
||||
pub struct PMap(Vec<Node<PFieldDef>>);
|
||||
pub struct PMap(pub Vec<Node<PFieldDef>>);
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum PStructFields {
|
||||
|
||||
@@ -4,7 +4,7 @@ pub struct NameStack<T>(Vec<HashMap<String, T>>);
|
||||
|
||||
impl<T> NameStack<T> {
|
||||
pub fn new() -> Self {
|
||||
Self(Vec::new())
|
||||
Self(vec![HashMap::new()])
|
||||
}
|
||||
pub fn search(&self, name: &str) -> Option<&T> {
|
||||
for level in self.0.iter().rev() {
|
||||
@@ -14,4 +14,10 @@ impl<T> NameStack<T> {
|
||||
}
|
||||
None
|
||||
}
|
||||
pub fn push(&mut self) {
|
||||
self.0.push(HashMap::new());
|
||||
}
|
||||
pub fn pop(&mut self) {
|
||||
self.0.pop();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user