more wrestling
This commit is contained in:
136
src/ir/upper/ident.rs
Normal file
136
src/ir/upper/ident.rs
Normal file
@@ -0,0 +1,136 @@
|
|||||||
|
use std::fmt::Display;
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
/// a generic identifier for all (identifiable) kinds
|
||||||
|
/// eg. a::b::c.d.e
|
||||||
|
/// or a::Result<T,_>
|
||||||
|
pub struct UIdent {
|
||||||
|
pub status: IdentStatus,
|
||||||
|
pub origin: Origin,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum IdentStatus {
|
||||||
|
Res(Res),
|
||||||
|
Unres {
|
||||||
|
base: ResBase,
|
||||||
|
path: Vec<MemberIdent>,
|
||||||
|
},
|
||||||
|
Failed(Option<ResErr>),
|
||||||
|
Cooked,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct MemberIdent {
|
||||||
|
pub ty: MemberTy,
|
||||||
|
pub name: String,
|
||||||
|
pub gargs: Vec<TypeID>,
|
||||||
|
pub origin: Origin,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
pub enum MemberTy {
|
||||||
|
Member,
|
||||||
|
Field,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MemberTy {
|
||||||
|
pub fn sep(&self) -> &'static str {
|
||||||
|
match self {
|
||||||
|
MemberTy::Member => "::",
|
||||||
|
MemberTy::Field => ".",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for MemberTy {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
f.write_str(match self {
|
||||||
|
MemberTy::Member => "member",
|
||||||
|
MemberTy::Field => "field",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub enum Res {
|
||||||
|
Var(VarID),
|
||||||
|
Fn(FnInst),
|
||||||
|
Struct(StructInst),
|
||||||
|
Type(TypeID),
|
||||||
|
Generic(GenericID),
|
||||||
|
Module(ModID),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Res {
|
||||||
|
pub fn kind(&self) -> KindTy {
|
||||||
|
match self {
|
||||||
|
Res::Var(..) => KindTy::Var,
|
||||||
|
Res::Fn(..) => KindTy::Fn,
|
||||||
|
Res::Struct(..) => KindTy::Struct,
|
||||||
|
Res::Type(..) => KindTy::Type,
|
||||||
|
Res::Module(..) => KindTy::Module,
|
||||||
|
Res::Generic(..) => KindTy::Generic,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn display_str(&self, p: &UProgram) -> String {
|
||||||
|
let name = match self {
|
||||||
|
Res::Var(id) => &p.vars[id].name,
|
||||||
|
Res::Fn(fi) => &p.fns[fi.id].name,
|
||||||
|
Res::Struct(si) => &p.structs[si.id].name,
|
||||||
|
Res::Type(id) => &p.type_name(id),
|
||||||
|
Res::Generic(id) => &p.generics[id].name,
|
||||||
|
Res::Module(id) => &p.modules[id].name,
|
||||||
|
};
|
||||||
|
format!("{} '{}'", self.kind(), name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub enum ResBase {
|
||||||
|
Unvalidated(MemRes),
|
||||||
|
Validated(Res),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ResBase {
|
||||||
|
pub fn display_str(&self, p: &UProgram) -> String {
|
||||||
|
match self {
|
||||||
|
ResBase::Unvalidated(uv) => uv.display_str(p),
|
||||||
|
ResBase::Validated(res) => res.display_str(p),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct MemRes {
|
||||||
|
pub mem: Member,
|
||||||
|
pub origin: Origin,
|
||||||
|
pub gargs: Vec<TypeID>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MemRes {
|
||||||
|
pub fn display_str(&self, p: &UProgram) -> String {
|
||||||
|
self.mem.id.display_str(p)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl IdentID {
|
||||||
|
pub fn var(&self, p: &UProgram) -> Option<VarID> {
|
||||||
|
match p.idents[self].status {
|
||||||
|
IdentStatus::Res(Res::Var(id)) => Some(id),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn fun<'a>(&self, p: &'a UProgram) -> Option<&'a FnInst> {
|
||||||
|
match &p.idents[self].status {
|
||||||
|
IdentStatus::Res(Res::Fn(i)) => Some(&i),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn struc<'a>(&self, p: &'a UProgram) -> Option<&'a StructInst> {
|
||||||
|
match &p.idents[self].status {
|
||||||
|
IdentStatus::Res(Res::Struct(i)) => Some(&i),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,8 +1,31 @@
|
|||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
use super::{arch::riscv64::RV64Instruction, DataID, IdentID, Origin, ResStage, Unresolved};
|
use super::{arch::riscv64::RV64Instruction, *};
|
||||||
use crate::compiler::arch::riscv::Reg;
|
use crate::compiler::arch::riscv::Reg;
|
||||||
|
|
||||||
|
pub trait ResStage {
|
||||||
|
type Var;
|
||||||
|
type Func;
|
||||||
|
type Struct;
|
||||||
|
type Type;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Unresolved;
|
||||||
|
impl ResStage for Unresolved {
|
||||||
|
type Var = IdentID;
|
||||||
|
type Func = IdentID;
|
||||||
|
type Struct = IdentID;
|
||||||
|
type Type = IdentID;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Resolved;
|
||||||
|
impl ResStage for Resolved {
|
||||||
|
type Var = VarID;
|
||||||
|
type Func = FnInst;
|
||||||
|
type Struct = StructInst;
|
||||||
|
type Type = TypeID;
|
||||||
|
}
|
||||||
|
|
||||||
pub enum UInstruction<S: ResStage = Unresolved> {
|
pub enum UInstruction<S: ResStage = Unresolved> {
|
||||||
Mv {
|
Mv {
|
||||||
dst: S::Var,
|
dst: S::Var,
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
//! all main IR Upper data structures stored in UProgram
|
//! all main IR Upper data structures stored in UProgram
|
||||||
|
|
||||||
use super::{FnInst, ResErr, StructInst, Type, UInstrInst, UInstruction, UProgram};
|
use super::*;
|
||||||
use crate::{
|
use crate::{
|
||||||
common::FileSpan,
|
common::FileSpan,
|
||||||
ir::{Len, ID},
|
ir::{Len, ID},
|
||||||
@@ -10,29 +10,6 @@ use std::{
|
|||||||
fmt::{Debug, Display},
|
fmt::{Debug, Display},
|
||||||
};
|
};
|
||||||
|
|
||||||
pub trait ResStage {
|
|
||||||
type Var;
|
|
||||||
type Func;
|
|
||||||
type Struct;
|
|
||||||
type Type;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct Unresolved;
|
|
||||||
impl ResStage for Unresolved {
|
|
||||||
type Var = IdentID;
|
|
||||||
type Func = IdentID;
|
|
||||||
type Struct = IdentID;
|
|
||||||
type Type = IdentID;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct Resolved;
|
|
||||||
impl ResStage for Resolved {
|
|
||||||
type Var = VarID;
|
|
||||||
type Func = FnInst;
|
|
||||||
type Struct = StructInst;
|
|
||||||
type Type = TypeID;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub type NamePath = Vec<String>;
|
pub type NamePath = Vec<String>;
|
||||||
|
|
||||||
pub type FnID = ID<UFunc>;
|
pub type FnID = ID<UFunc>;
|
||||||
@@ -79,55 +56,6 @@ pub struct UVar {
|
|||||||
pub children: HashMap<String, VarID>,
|
pub children: HashMap<String, VarID>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// a generic identifier for all (identifiable) kinds
|
|
||||||
/// eg. a::b::c.d.e
|
|
||||||
/// or a::Result<T,_>
|
|
||||||
pub struct UIdent {
|
|
||||||
pub status: IdentStatus,
|
|
||||||
pub origin: Origin,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub enum IdentStatus {
|
|
||||||
Res(Res),
|
|
||||||
Unres {
|
|
||||||
base: ResBase,
|
|
||||||
path: Vec<MemberIdent>,
|
|
||||||
},
|
|
||||||
Failed(Option<ResErr>),
|
|
||||||
Cooked,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct MemberIdent {
|
|
||||||
pub ty: MemberTy,
|
|
||||||
pub name: String,
|
|
||||||
pub gargs: Vec<TypeID>,
|
|
||||||
pub origin: Origin,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Copy)]
|
|
||||||
pub enum MemberTy {
|
|
||||||
Member,
|
|
||||||
Field,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl MemberTy {
|
|
||||||
pub fn sep(&self) -> &'static str {
|
|
||||||
match self {
|
|
||||||
MemberTy::Member => "::",
|
|
||||||
MemberTy::Field => ".",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Display for MemberTy {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
||||||
f.write_str(match self {
|
|
||||||
MemberTy::Member => "member",
|
|
||||||
MemberTy::Field => "field",
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq)]
|
#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq)]
|
||||||
pub struct VarOffset {
|
pub struct VarOffset {
|
||||||
pub id: VarID,
|
pub id: VarID,
|
||||||
@@ -186,7 +114,7 @@ impl MemberID {
|
|||||||
MemberID::Fn(id) => &p.fns[id].name,
|
MemberID::Fn(id) => &p.fns[id].name,
|
||||||
MemberID::Struct(id) => &p.structs[id].name,
|
MemberID::Struct(id) => &p.structs[id].name,
|
||||||
MemberID::Module(id) => &p.modules[id].name,
|
MemberID::Module(id) => &p.modules[id].name,
|
||||||
MemberID::Type(id) => &p.type_name(id),
|
MemberID::Type(def) => &p.type_name(def.ty),
|
||||||
};
|
};
|
||||||
format!("{} '{}'", self.kind(), name)
|
format!("{} '{}'", self.kind(), name)
|
||||||
}
|
}
|
||||||
@@ -218,90 +146,6 @@ impl Display for KindTy {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub enum Res {
|
|
||||||
Var(VarID),
|
|
||||||
Fn(FnInst),
|
|
||||||
Struct(StructInst),
|
|
||||||
Type(TypeID),
|
|
||||||
Generic(GenericID),
|
|
||||||
Module(ModID),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Res {
|
|
||||||
pub fn kind(&self) -> KindTy {
|
|
||||||
match self {
|
|
||||||
Res::Var(..) => KindTy::Var,
|
|
||||||
Res::Fn(..) => KindTy::Fn,
|
|
||||||
Res::Struct(..) => KindTy::Struct,
|
|
||||||
Res::Type(..) => KindTy::Type,
|
|
||||||
Res::Module(..) => KindTy::Module,
|
|
||||||
Res::Generic(..) => KindTy::Generic,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn display_str(&self, p: &UProgram) -> String {
|
|
||||||
let name = match self {
|
|
||||||
Res::Var(id) => &p.vars[id].name,
|
|
||||||
Res::Fn(fi) => &p.fns[fi.id].name,
|
|
||||||
Res::Struct(si) => &p.structs[si.id].name,
|
|
||||||
Res::Type(id) => &p.type_name(id),
|
|
||||||
Res::Generic(id) => &p.generics[id].name,
|
|
||||||
Res::Module(id) => &p.modules[id].name,
|
|
||||||
};
|
|
||||||
format!("{} '{}'", self.kind(), name)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub enum ResBase {
|
|
||||||
Unvalidated(MemRes),
|
|
||||||
Validated(Res),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ResBase {
|
|
||||||
pub fn display_str(&self, p: &UProgram) -> String {
|
|
||||||
match self {
|
|
||||||
ResBase::Unvalidated(uv) => uv.display_str(p),
|
|
||||||
ResBase::Validated(res) => res.display_str(p),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct MemRes {
|
|
||||||
pub mem: Member,
|
|
||||||
pub origin: Origin,
|
|
||||||
pub gargs: Vec<TypeID>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl MemRes {
|
|
||||||
pub fn display_str(&self, p: &UProgram) -> String {
|
|
||||||
self.mem.id.display_str(p)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl IdentID {
|
|
||||||
pub fn var(&self, p: &UProgram) -> Option<VarID> {
|
|
||||||
match p.idents[self].status {
|
|
||||||
IdentStatus::Res(Res::Var(id)) => Some(id),
|
|
||||||
_ => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub fn fun<'a>(&self, p: &'a UProgram) -> Option<&'a FnInst> {
|
|
||||||
match &p.idents[self].status {
|
|
||||||
IdentStatus::Res(Res::Fn(i)) => Some(&i),
|
|
||||||
_ => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub fn struc<'a>(&self, p: &'a UProgram) -> Option<&'a StructInst> {
|
|
||||||
match &p.idents[self].status {
|
|
||||||
IdentStatus::Res(Res::Struct(i)) => Some(&i),
|
|
||||||
_ => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl UFunc {
|
impl UFunc {
|
||||||
pub fn flat_iter(&self) -> impl Iterator<Item = &UInstrInst> {
|
pub fn flat_iter(&self) -> impl Iterator<Item = &UInstrInst> {
|
||||||
InstrIter::new(self.instructions.iter())
|
InstrIter::new(self.instructions.iter())
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ mod program;
|
|||||||
mod ty;
|
mod ty;
|
||||||
mod resolve;
|
mod resolve;
|
||||||
mod error;
|
mod error;
|
||||||
|
mod ident;
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
@@ -13,3 +14,4 @@ pub use program::*;
|
|||||||
pub use ty::*;
|
pub use ty::*;
|
||||||
pub use error::*;
|
pub use error::*;
|
||||||
pub use resolve::*;
|
pub use resolve::*;
|
||||||
|
pub use ident::*;
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ impl UProgram {
|
|||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
let mut types = Vec::new();
|
let mut types = Vec::new();
|
||||||
let tc = TypeCache {
|
let tc = TypeCache {
|
||||||
unit: push_id(&mut types, Type::Unit),
|
unit: push_id(&mut types, Type::Real(RType::Unit)),
|
||||||
error: push_id(&mut types, Type::Error),
|
error: push_id(&mut types, Type::Error),
|
||||||
};
|
};
|
||||||
Self {
|
Self {
|
||||||
@@ -42,7 +42,7 @@ impl UProgram {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn infer(&mut self) -> TypeID {
|
pub fn infer(&mut self) -> TypeID {
|
||||||
self.def_ty(Type::Infer)
|
self.def_ty(RType::Infer.ty())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn def_var(&mut self, v: UVar) -> VarID {
|
pub fn def_var(&mut self, v: UVar) -> VarID {
|
||||||
@@ -79,31 +79,34 @@ impl UProgram {
|
|||||||
|
|
||||||
pub fn type_name(&self, ty: impl Typed) -> String {
|
pub fn type_name(&self, ty: impl Typed) -> String {
|
||||||
match ty.ty(self) {
|
match ty.ty(self) {
|
||||||
Type::Struct(ty) => {
|
Type::Real(rty) => match rty {
|
||||||
format!(
|
RType::Struct(ty) => {
|
||||||
"{}{}",
|
format!(
|
||||||
self.structs[ty.id].name,
|
"{}{}",
|
||||||
self.gparams_str(&ty.gargs)
|
self.structs[ty.id].name,
|
||||||
)
|
self.gparams_str(&ty.gargs)
|
||||||
}
|
)
|
||||||
Type::FnRef(ty) => {
|
}
|
||||||
format!(
|
RType::FnRef(ty) => {
|
||||||
"fn{}({}) -> {}",
|
format!(
|
||||||
&self.gparams_str(&ty.gargs),
|
"fn{}({}) -> {}",
|
||||||
&self.type_list_str(self.fns[ty.id].args.iter().map(|v| self.vars[v].ty)),
|
&self.gparams_str(&ty.gargs),
|
||||||
&self.type_name(self.fns[ty.id].ret)
|
&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) => format!("{}&", self.type_name(t)),
|
}
|
||||||
Type::Deref(t) => format!("{}^", self.type_name(t)),
|
RType::Ref(t) => format!("{}&", self.type_name(t)),
|
||||||
Type::Bits(size) => format!("b{}", size),
|
RType::Deref(t) => format!("{}^", self.type_name(t)),
|
||||||
Type::Array(t, len) => format!("[{}; {len}]", self.type_name(t)),
|
RType::Bits(size) => format!("b{}", size),
|
||||||
Type::Unit => "()".to_string(),
|
RType::Array(t, len) => format!("[{}; {len}]", self.type_name(t)),
|
||||||
Type::Slice(t) => format!("&[{}]", self.type_name(t)),
|
RType::Unit => "()".to_string(),
|
||||||
|
RType::Slice(t) => format!("&[{}]", self.type_name(t)),
|
||||||
|
RType::Infer => "{inferred}".to_string(),
|
||||||
|
},
|
||||||
Type::Error => "{error}".to_string(),
|
Type::Error => "{error}".to_string(),
|
||||||
Type::Infer => "{inferred}".to_string(),
|
|
||||||
Type::Unres(_) => "{unresolved}".to_string(),
|
Type::Unres(_) => "{unresolved}".to_string(),
|
||||||
Type::Generic(id) => self.generics[id].name.clone(),
|
Type::Generic(id) => self.generics[id].name.clone(),
|
||||||
|
Type::Ptr(id) => self.type_name(id),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,773 +0,0 @@
|
|||||||
use super::{
|
|
||||||
inst_fn_var, inst_type, inst_typedef, inst_var, push_id, report_errs, resolve_refs, validate_gargs, ControlFlowOp, DataID, FnInst, IdentID, IdentStatus, KindTy, MemberID, MemberTy, Origin, Res, ResBase, ResErr, StructInst, Type, TypeID, TypeMismatch, UData, UFunc, UGeneric, UIdent, UInstrInst, UInstruction, UModule, UProgram, UStruct, UVar, VarID
|
|
||||||
};
|
|
||||||
use crate::{
|
|
||||||
common::CompilerOutput,
|
|
||||||
ir::{MemRes, Member},
|
|
||||||
};
|
|
||||||
use std::{
|
|
||||||
collections::HashSet,
|
|
||||||
convert::Infallible,
|
|
||||||
ops::{BitOrAssign, FromResidual},
|
|
||||||
};
|
|
||||||
|
|
||||||
// dawg this file is way too long
|
|
||||||
// this is the omega file tho that's super cool
|
|
||||||
|
|
||||||
impl UProgram {
|
|
||||||
pub fn resolve(&mut self, output: &mut CompilerOutput) {
|
|
||||||
let mut unfinished = Vec::new();
|
|
||||||
let mut data = ResData {
|
|
||||||
unfinished: Vec::new(),
|
|
||||||
changed: false,
|
|
||||||
types: &mut self.types,
|
|
||||||
s: Sources {
|
|
||||||
idents: &mut self.idents,
|
|
||||||
vars: &mut self.vars,
|
|
||||||
fns: &self.fns,
|
|
||||||
structs: &self.structs,
|
|
||||||
generics: &self.generics,
|
|
||||||
data: &self.data,
|
|
||||||
modules: &self.modules,
|
|
||||||
},
|
|
||||||
errs: Vec::new(),
|
|
||||||
};
|
|
||||||
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)
|
|
||||||
&& f.instructions
|
|
||||||
.last()
|
|
||||||
.is_none_or(|i| !matches!(i.i, UInstruction::Ret { .. }))
|
|
||||||
{
|
|
||||||
data.errs.push(ResErr::NoReturn { fid });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
while !data.unfinished.is_empty() && data.changed {
|
|
||||||
data.changed = false;
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Copy)]
|
|
||||||
struct ResolveCtx<'a> {
|
|
||||||
ret: TypeID,
|
|
||||||
breakable: bool,
|
|
||||||
i: &'a UInstrInst,
|
|
||||||
}
|
|
||||||
|
|
||||||
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 fi = data.res_id::<UFunc>(f, ctx)?;
|
|
||||||
let f = &data.s.fns[fi.id];
|
|
||||||
for (src, &dest) in args.iter().zip(&f.args) {
|
|
||||||
res |= data.match_types::<UVar, UVar>(dest, src, src);
|
|
||||||
}
|
|
||||||
res |= data.match_types::<UVar, Type>(dst, f.ret, dst);
|
|
||||||
}
|
|
||||||
UInstruction::Mv { dst, src } => {
|
|
||||||
res |= data.match_types::<UVar, UVar>(dst, src, src);
|
|
||||||
}
|
|
||||||
UInstruction::Ref { dst, src } => {
|
|
||||||
let dstid = data.res_var_ty(dst, ctx)?;
|
|
||||||
let Type::Ref(dest_ty) = data.types[dstid] else {
|
|
||||||
compiler_error()
|
|
||||||
};
|
|
||||||
res |= data.match_types::<Type, UVar>(dest_ty, src, src);
|
|
||||||
}
|
|
||||||
UInstruction::Deref { dst, src } => {
|
|
||||||
let srcid = data.res_var_ty(src, ctx)?;
|
|
||||||
let Type::Ref(src_ty) = data.types[srcid] else {
|
|
||||||
let origin = src.origin(data);
|
|
||||||
data.errs.push(ResErr::CannotDeref { origin, ty: srcid });
|
|
||||||
return None;
|
|
||||||
};
|
|
||||||
res |= data.match_types::<UVar, Type>(dst, src_ty, src);
|
|
||||||
}
|
|
||||||
UInstruction::LoadData { dst, src } => {
|
|
||||||
let srcid = src.type_id(&data.s);
|
|
||||||
res |= data.match_types::<UVar, Type>(dst, srcid, dst);
|
|
||||||
}
|
|
||||||
UInstruction::LoadSlice { dst, src } => {
|
|
||||||
let dstid = data.res_var_ty(dst, ctx)?;
|
|
||||||
let srcid = src.type_id(&data.s);
|
|
||||||
let Type::Slice(dstty) = data.types[dstid] else {
|
|
||||||
compiler_error()
|
|
||||||
};
|
|
||||||
let Type::Array(srcty, _) = data.types[srcid] else {
|
|
||||||
compiler_error()
|
|
||||||
};
|
|
||||||
res |= data.match_types(dstty, srcty, dst);
|
|
||||||
}
|
|
||||||
UInstruction::AsmBlock { instructions, args } => {
|
|
||||||
// TODO
|
|
||||||
}
|
|
||||||
UInstruction::Ret { src } => {
|
|
||||||
res |= data.match_types::<Type, UVar>(ctx.ret, src, src);
|
|
||||||
}
|
|
||||||
UInstruction::Construct { dst, struc, fields } => {
|
|
||||||
let si = data.res_id::<UStruct>(dst, ctx)?;
|
|
||||||
let sid = si.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::<Type, UVar>(field.ty, src, src);
|
|
||||||
} else {
|
|
||||||
let origin = dst.origin(data);
|
|
||||||
data.errs.push(ResErr::MissingField {
|
|
||||||
origin,
|
|
||||||
id: sid,
|
|
||||||
name: name.clone(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (name, _) in fields {
|
|
||||||
if !used.contains(name) {
|
|
||||||
let origin = dst.origin(data);
|
|
||||||
data.errs.push(ResErr::UnknownStructField {
|
|
||||||
origin,
|
|
||||||
id: sid,
|
|
||||||
name: name.clone(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
UInstruction::If { cond, body } => {
|
|
||||||
if let Some(id) = data.res_var_ty(cond, ctx) {
|
|
||||||
if !matches!(data.types[id], Type::Bits(64)) {
|
|
||||||
let origin = cond.origin(data);
|
|
||||||
data.errs.push(ResErr::CondType { origin, ty: id });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for i in body {
|
|
||||||
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,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
match res {
|
|
||||||
InstrRes::Finished => (),
|
|
||||||
InstrRes::Unfinished => data.unfinished.push(ctx),
|
|
||||||
}
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
|
|
||||||
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 ResData, dst: impl TypeIDed, src: impl TypeIDed) -> MatchRes {
|
|
||||||
let dstid = data.res_ty(&dst)?;
|
|
||||||
let srcid = data.res_ty(&src)?;
|
|
||||||
if dstid == srcid {
|
|
||||||
return MatchRes::Finished;
|
|
||||||
}
|
|
||||||
let error = || {
|
|
||||||
MatchRes::Error(vec![TypeMismatch {
|
|
||||||
dst: dstid,
|
|
||||||
src: srcid,
|
|
||||||
}])
|
|
||||||
};
|
|
||||||
match (data.types[dstid].clone(), data.types[srcid].clone()) {
|
|
||||||
(Type::Error, _) | (_, Type::Error) => MatchRes::Finished,
|
|
||||||
(Type::Infer, Type::Infer) => MatchRes::Unfinished,
|
|
||||||
(Type::Infer, x) => {
|
|
||||||
data.changed = true;
|
|
||||||
data.types[dstid] = x;
|
|
||||||
dst.finish(&mut data.s, data.types);
|
|
||||||
MatchRes::Finished
|
|
||||||
}
|
|
||||||
(x, Type::Infer) => {
|
|
||||||
data.changed = true;
|
|
||||||
data.types[srcid] = x;
|
|
||||||
src.finish(&mut data.s, data.types);
|
|
||||||
MatchRes::Finished
|
|
||||||
}
|
|
||||||
(Type::Struct(dest), Type::Struct(src)) => {
|
|
||||||
if dest.id != src.id {
|
|
||||||
return error();
|
|
||||||
}
|
|
||||||
match_all(data, dest.gargs.iter().cloned(), src.gargs.iter().cloned())
|
|
||||||
}
|
|
||||||
// (
|
|
||||||
// Type::Fn {
|
|
||||||
// args: dst_args,
|
|
||||||
// ret: dst_ret,
|
|
||||||
// },
|
|
||||||
// Type::Fn {
|
|
||||||
// args: src_args,
|
|
||||||
// ret: src_ret,
|
|
||||||
// },
|
|
||||||
// ) => {
|
|
||||||
// let dst = dst_args.into_iter().chain(once(dst_ret));
|
|
||||||
// let src = src_args.into_iter().chain(once(src_ret));
|
|
||||||
// match_all(data, dst, src)
|
|
||||||
// }
|
|
||||||
(Type::Ref(dest), Type::Ref(src)) => match_types(data, dest, src),
|
|
||||||
(Type::Slice(dest), Type::Slice(src)) => match_types(data, dest, src),
|
|
||||||
(Type::Array(dest, dlen), Type::Array(src, slen)) => {
|
|
||||||
if dlen == slen {
|
|
||||||
match_types(data, dest, src)
|
|
||||||
} else {
|
|
||||||
error()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => error(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn match_all(
|
|
||||||
data: &mut ResData,
|
|
||||||
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> {
|
|
||||||
idents: &'a mut [UIdent],
|
|
||||||
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>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> ResData<'a> {
|
|
||||||
pub fn match_types<Dst: ResKind, Src: ResKind>(
|
|
||||||
&mut self,
|
|
||||||
dst: impl Resolvable<Dst>,
|
|
||||||
src: impl Resolvable<Src>,
|
|
||||||
origin: impl HasOrigin,
|
|
||||||
) -> InstrRes
|
|
||||||
where
|
|
||||||
Dst::Res: TypeIDed,
|
|
||||||
Src::Res: TypeIDed,
|
|
||||||
{
|
|
||||||
let dst = dst
|
|
||||||
.try_res(&mut self.s, self.types, &mut self.errs, &mut self.changed)?
|
|
||||||
.type_id(&self.s);
|
|
||||||
let src = src
|
|
||||||
.try_res(&mut self.s, self.types, &mut self.errs, &mut self.changed)?
|
|
||||||
.type_id(&self.s);
|
|
||||||
let res = match_types(self, dst, src);
|
|
||||||
match res {
|
|
||||||
MatchRes::Unfinished => InstrRes::Unfinished,
|
|
||||||
MatchRes::Finished => InstrRes::Finished,
|
|
||||||
MatchRes::Error(es) => {
|
|
||||||
self.errs.push(ResErr::Type {
|
|
||||||
errs: es,
|
|
||||||
origin: origin.origin(self),
|
|
||||||
dst,
|
|
||||||
src,
|
|
||||||
});
|
|
||||||
InstrRes::Finished
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub fn try_res_id<K: ResKind>(&mut self, x: impl Resolvable<K>) -> Result<K::Res, InstrRes> {
|
|
||||||
x.try_res(
|
|
||||||
&mut self.s,
|
|
||||||
&mut self.types,
|
|
||||||
&mut self.errs,
|
|
||||||
&mut self.changed,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
pub fn res_var_ty<'b: 'a>(
|
|
||||||
&mut self,
|
|
||||||
x: impl Resolvable<UVar>,
|
|
||||||
ctx: ResolveCtx<'b>,
|
|
||||||
) -> Option<TypeID> {
|
|
||||||
self.res_id::<UVar>(x, ctx).map(|i| i.type_id(&self.s))
|
|
||||||
}
|
|
||||||
pub fn res_id<'b: 'a, K: ResKind>(
|
|
||||||
&mut self,
|
|
||||||
x: impl Resolvable<K>,
|
|
||||||
ctx: ResolveCtx<'b>,
|
|
||||||
) -> Option<K::Res> {
|
|
||||||
match self.try_res_id(x) {
|
|
||||||
Ok(id) => return Some(id),
|
|
||||||
Err(InstrRes::Unfinished) => self.unfinished.push(ctx),
|
|
||||||
Err(InstrRes::Finished) => (),
|
|
||||||
}
|
|
||||||
None
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn res_ty(&mut self, x: &impl TypeIDed) -> Result<TypeID, InstrRes> {
|
|
||||||
let id = resolve_refs(self.types, x.type_id(&self.s));
|
|
||||||
Ok(if let Type::Unres(ident) = self.types[id] {
|
|
||||||
match self.try_res_id::<Type>(ident) {
|
|
||||||
Ok(nid) => {
|
|
||||||
// this does NOT feel good lmao
|
|
||||||
self.types[id] = self.types[nid].clone();
|
|
||||||
x.finish(&mut self.s, self.types);
|
|
||||||
nid
|
|
||||||
}
|
|
||||||
Err(res) => return Err(res),
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
id
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub enum MatchRes {
|
|
||||||
Unfinished,
|
|
||||||
Finished,
|
|
||||||
Error(Vec<TypeMismatch>),
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy)]
|
|
||||||
pub enum InstrRes {
|
|
||||||
Finished,
|
|
||||||
Unfinished,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl MemRes {
|
|
||||||
pub fn validate(
|
|
||||||
&self,
|
|
||||||
fns: &[UFunc],
|
|
||||||
structs: &[UStruct],
|
|
||||||
generics: &[UGeneric],
|
|
||||||
types: &mut Vec<Type>,
|
|
||||||
errs: &mut Vec<ResErr>,
|
|
||||||
) -> Result<Res, Option<ResErr>> {
|
|
||||||
let no_gargs = || {
|
|
||||||
if self.gargs.len() > 0 {
|
|
||||||
Err(ResErr::GenericCount {
|
|
||||||
origin: self.origin,
|
|
||||||
expected: 0,
|
|
||||||
found: self.gargs.len(),
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
};
|
|
||||||
Ok(match &self.mem.id {
|
|
||||||
&MemberID::Fn(id) => {
|
|
||||||
validate_gargs(
|
|
||||||
&fns[id].gargs,
|
|
||||||
&self.gargs,
|
|
||||||
generics,
|
|
||||||
types,
|
|
||||||
errs,
|
|
||||||
self.origin,
|
|
||||||
)?;
|
|
||||||
Res::Fn(FnInst {
|
|
||||||
id,
|
|
||||||
gargs: self.gargs.clone(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
&MemberID::Struct(id) => {
|
|
||||||
validate_gargs(
|
|
||||||
&structs[id].gargs,
|
|
||||||
&self.gargs,
|
|
||||||
generics,
|
|
||||||
types,
|
|
||||||
errs,
|
|
||||||
self.origin,
|
|
||||||
)?;
|
|
||||||
Res::Struct(StructInst {
|
|
||||||
id,
|
|
||||||
gargs: self.gargs.clone(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
&MemberID::Var(id) => {
|
|
||||||
no_gargs()?;
|
|
||||||
Res::Var(id)
|
|
||||||
}
|
|
||||||
&MemberID::Module(id) => {
|
|
||||||
no_gargs()?;
|
|
||||||
Res::Module(id)
|
|
||||||
}
|
|
||||||
MemberID::Type(def) => {
|
|
||||||
validate_gargs(&def.gargs, &self.gargs, generics, types, errs, self.origin)?;
|
|
||||||
inst_typedef(def, &self.gargs, types);
|
|
||||||
Res::Type(def.ty)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl IdentID {
|
|
||||||
pub fn resolve(
|
|
||||||
self,
|
|
||||||
s: &mut Sources,
|
|
||||||
types: &mut Vec<Type>,
|
|
||||||
changed: &mut bool,
|
|
||||||
errs: &mut Vec<ResErr>,
|
|
||||||
) -> Result<Res, InstrRes> {
|
|
||||||
let status = &mut s.idents[self].status;
|
|
||||||
// TOOD: there are some clones here that shouldn't be needed
|
|
||||||
Ok(match status {
|
|
||||||
IdentStatus::Res(r) => r.clone(),
|
|
||||||
IdentStatus::Unres { path, base } => {
|
|
||||||
while let Some(mem) = path.pop() {
|
|
||||||
let res = match base {
|
|
||||||
ResBase::Unvalidated(u) => {
|
|
||||||
match u.validate(s.fns, s.structs, s.generics, types, errs) {
|
|
||||||
Ok(res) => res,
|
|
||||||
Err(err) => {
|
|
||||||
*status = IdentStatus::Failed(err);
|
|
||||||
return Err(InstrRes::Finished);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ResBase::Validated(res) => res.clone(),
|
|
||||||
};
|
|
||||||
*base = match (res, mem.ty) {
|
|
||||||
(Res::Module(id), MemberTy::Member) => {
|
|
||||||
let Some(m) = s.modules[id].members.get(&mem.name) else {
|
|
||||||
return Err(InstrRes::Unfinished);
|
|
||||||
};
|
|
||||||
ResBase::Unvalidated(MemRes {
|
|
||||||
mem: m.clone(),
|
|
||||||
origin: mem.origin,
|
|
||||||
gargs: mem.gargs,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
(Res::Var(id), MemberTy::Field) => {
|
|
||||||
// trait resolution here
|
|
||||||
let Some(&child) = s.vars[id].children.get(&mem.name) else {
|
|
||||||
return Err(InstrRes::Unfinished);
|
|
||||||
};
|
|
||||||
ResBase::Unvalidated(MemRes {
|
|
||||||
mem: Member {
|
|
||||||
id: MemberID::Var(child),
|
|
||||||
},
|
|
||||||
origin: mem.origin,
|
|
||||||
gargs: mem.gargs,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
*status = IdentStatus::Failed(Some(ResErr::UnknownMember {
|
|
||||||
origin: mem.origin,
|
|
||||||
ty: mem.ty,
|
|
||||||
name: mem.name.clone(),
|
|
||||||
parent: base.clone(),
|
|
||||||
}));
|
|
||||||
return Err(InstrRes::Finished);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
let res = match base {
|
|
||||||
ResBase::Unvalidated(u) => {
|
|
||||||
match u.validate(s.fns, s.structs, s.generics, types, errs) {
|
|
||||||
Ok(res) => res,
|
|
||||||
Err(err) => {
|
|
||||||
*status = IdentStatus::Failed(err);
|
|
||||||
return Err(InstrRes::Finished);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ResBase::Validated(res) => res.clone(),
|
|
||||||
};
|
|
||||||
*status = IdentStatus::Res(res.clone());
|
|
||||||
*changed = true;
|
|
||||||
res
|
|
||||||
}
|
|
||||||
IdentStatus::Cooked => return Err(InstrRes::Finished),
|
|
||||||
IdentStatus::Failed(_) => return Err(InstrRes::Finished),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl BitOrAssign for InstrRes {
|
|
||||||
fn bitor_assign(&mut self, rhs: Self) {
|
|
||||||
match rhs {
|
|
||||||
InstrRes::Finished => (),
|
|
||||||
InstrRes::Unfinished => *self = InstrRes::Unfinished,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FromResidual<Option<Infallible>> for InstrRes {
|
|
||||||
fn from_residual(_: Option<Infallible>) -> Self {
|
|
||||||
Self::Unfinished
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
trait Resolvable<K: ResKind> {
|
|
||||||
fn try_res(
|
|
||||||
&self,
|
|
||||||
s: &mut Sources,
|
|
||||||
types: &mut Vec<Type>,
|
|
||||||
errs: &mut Vec<ResErr>,
|
|
||||||
changed: &mut bool,
|
|
||||||
) -> Result<K::Res, InstrRes>;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<K: ResKind> Resolvable<K> for IdentID {
|
|
||||||
fn try_res(
|
|
||||||
&self,
|
|
||||||
s: &mut Sources,
|
|
||||||
types: &mut Vec<Type>,
|
|
||||||
errs: &mut Vec<ResErr>,
|
|
||||||
changed: &mut bool,
|
|
||||||
) -> Result<K::Res, InstrRes> {
|
|
||||||
let origin = s.idents[self].origin;
|
|
||||||
let res = self.resolve(s, types, changed, errs)?;
|
|
||||||
match K::from_res(res, types, s, origin) {
|
|
||||||
Ok(res) => Ok(res),
|
|
||||||
Err(res) => {
|
|
||||||
errs.push(ResErr::KindMismatch {
|
|
||||||
origin,
|
|
||||||
expected: K::ty(),
|
|
||||||
found: res,
|
|
||||||
});
|
|
||||||
Err(InstrRes::Finished)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<K: ResKind> Resolvable<K> for &IdentID {
|
|
||||||
fn try_res(
|
|
||||||
&self,
|
|
||||||
s: &mut Sources,
|
|
||||||
types: &mut Vec<Type>,
|
|
||||||
errs: &mut Vec<ResErr>,
|
|
||||||
changed: &mut bool,
|
|
||||||
) -> Result<K::Res, InstrRes> {
|
|
||||||
Resolvable::<K>::try_res(*self, s, types, errs, changed)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Resolvable<UVar> for VarID {
|
|
||||||
fn try_res(
|
|
||||||
&self,
|
|
||||||
s: &mut Sources,
|
|
||||||
types: &mut Vec<Type>,
|
|
||||||
errs: &mut Vec<ResErr>,
|
|
||||||
changed: &mut bool,
|
|
||||||
) -> Result<<UVar as ResKind>::Res, InstrRes> {
|
|
||||||
Ok(*self)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Resolvable<Type> for TypeID {
|
|
||||||
fn try_res(
|
|
||||||
&self,
|
|
||||||
s: &mut Sources,
|
|
||||||
types: &mut Vec<Type>,
|
|
||||||
errs: &mut Vec<ResErr>,
|
|
||||||
changed: &mut bool,
|
|
||||||
) -> Result<<Type as ResKind>::Res, InstrRes> {
|
|
||||||
Ok(*self)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait ResKind {
|
|
||||||
type Res;
|
|
||||||
fn ty() -> KindTy;
|
|
||||||
fn from_res(
|
|
||||||
res: Res,
|
|
||||||
types: &mut Vec<Type>,
|
|
||||||
s: &mut Sources,
|
|
||||||
origin: Origin,
|
|
||||||
) -> Result<Self::Res, Res>;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ResKind for UFunc {
|
|
||||||
type Res = FnInst;
|
|
||||||
fn ty() -> KindTy {
|
|
||||||
KindTy::Fn
|
|
||||||
}
|
|
||||||
fn from_res(res: Res, _: &mut Vec<Type>, _: &mut Sources, _: Origin) -> Result<Self::Res, Res> {
|
|
||||||
match res {
|
|
||||||
Res::Fn(fi) => Ok(fi),
|
|
||||||
_ => Err(res),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ResKind for UVar {
|
|
||||||
type Res = VarID;
|
|
||||||
fn ty() -> KindTy {
|
|
||||||
KindTy::Var
|
|
||||||
}
|
|
||||||
fn from_res(
|
|
||||||
res: Res,
|
|
||||||
types: &mut Vec<Type>,
|
|
||||||
s: &mut Sources,
|
|
||||||
origin: Origin,
|
|
||||||
) -> Result<Self::Res, Res> {
|
|
||||||
Ok(match res {
|
|
||||||
Res::Fn(fty) => inst_fn_var(fty, s.fns, origin, s.vars, types),
|
|
||||||
Res::Var(id) => id,
|
|
||||||
_ => return Err(res),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ResKind for UStruct {
|
|
||||||
type Res = StructInst;
|
|
||||||
fn ty() -> KindTy {
|
|
||||||
KindTy::Struct
|
|
||||||
}
|
|
||||||
fn from_res(res: Res, _: &mut Vec<Type>, _: &mut Sources, _: Origin) -> Result<Self::Res, Res> {
|
|
||||||
match res {
|
|
||||||
Res::Struct(si) => Ok(si),
|
|
||||||
_ => Err(res),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ResKind for Type {
|
|
||||||
type Res = TypeID;
|
|
||||||
fn ty() -> KindTy {
|
|
||||||
KindTy::Type
|
|
||||||
}
|
|
||||||
fn from_res(
|
|
||||||
res: Res,
|
|
||||||
types: &mut Vec<Type>,
|
|
||||||
s: &mut Sources,
|
|
||||||
_: Origin,
|
|
||||||
) -> Result<Self::Res, Res> {
|
|
||||||
Ok(match res {
|
|
||||||
Res::Struct(si) => push_id(types, Type::Struct(si)),
|
|
||||||
Res::Type(id) => id,
|
|
||||||
_ => return Err(res),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait TypeIDed {
|
|
||||||
fn type_id(&self, s: &Sources) -> TypeID;
|
|
||||||
fn finish(&self, s: &mut Sources, types: &mut Vec<Type>) {}
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
|
||||||
}
|
|
||||||
fn finish(&self, s: &mut Sources, types: &mut Vec<Type>) {
|
|
||||||
inst_var(s.vars, s.structs, *self, types);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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<Result<Infallible, InstrRes>> for InstrRes {
|
|
||||||
fn from_residual(residual: Result<Infallible, InstrRes>) -> Self {
|
|
||||||
match residual {
|
|
||||||
Ok(_) => unreachable!(),
|
|
||||||
Err(r) => r,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
trait HasOrigin {
|
|
||||||
fn origin(&self, data: &ResData) -> Origin;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl HasOrigin for &IdentID {
|
|
||||||
fn origin(&self, data: &ResData) -> Origin {
|
|
||||||
data.s.idents[*self].origin
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl FromResidual<Result<Infallible, MatchRes>> for MatchRes {
|
|
||||||
fn from_residual(residual: Result<Infallible, MatchRes>) -> Self {
|
|
||||||
match residual {
|
|
||||||
Ok(_) => unreachable!(),
|
|
||||||
Err(r) => r,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,7 +1,10 @@
|
|||||||
use crate::common::{CompilerMsg, CompilerOutput};
|
use crate::{
|
||||||
|
common::{CompilerMsg, CompilerOutput},
|
||||||
|
ir::RType,
|
||||||
|
};
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
IdentStatus, KindTy, MemRes, MemberTy, Origin, Res, ResBase, StructID, Type, TypeID, UProgram
|
IdentStatus, KindTy, MemberTy, Origin, Res, ResBase, StructID, Type, TypeID, UProgram,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn report_errs(p: &UProgram, output: &mut CompilerOutput, mut errs: Vec<ResErr>) {
|
pub fn report_errs(p: &UProgram, output: &mut CompilerOutput, mut errs: Vec<ResErr>) {
|
||||||
@@ -16,7 +19,11 @@ pub fn report_errs(p: &UProgram, output: &mut CompilerOutput, mut errs: Vec<ResE
|
|||||||
parent: base.clone(),
|
parent: base.clone(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
IdentStatus::Failed(err) => errs.push(err.clone()),
|
IdentStatus::Failed(err) => {
|
||||||
|
if let Some(err) = err {
|
||||||
|
errs.push(err.clone())
|
||||||
|
}
|
||||||
|
}
|
||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -113,11 +120,7 @@ pub fn report_errs(p: &UProgram, output: &mut CompilerOutput, mut errs: Vec<ResE
|
|||||||
}
|
}
|
||||||
for var in &p.vars {
|
for var in &p.vars {
|
||||||
match &p.types[var.ty] {
|
match &p.types[var.ty] {
|
||||||
Type::Error => output.err(CompilerMsg::new(
|
Type::Real(RType::Infer) => output.err(CompilerMsg::new(
|
||||||
format!("Var {:?} is error type!", var.name),
|
|
||||||
var.origin,
|
|
||||||
)),
|
|
||||||
Type::Infer => output.err(CompilerMsg::new(
|
|
||||||
format!("Type of {:?} cannot be inferred", var.name),
|
format!("Type of {:?} cannot be inferred", var.name),
|
||||||
var.origin,
|
var.origin,
|
||||||
)),
|
)),
|
||||||
174
src/ir/upper/resolve/ident.rs
Normal file
174
src/ir/upper/resolve/ident.rs
Normal file
@@ -0,0 +1,174 @@
|
|||||||
|
use super::*;
|
||||||
|
|
||||||
|
impl MemRes {
|
||||||
|
pub fn validate(
|
||||||
|
&self,
|
||||||
|
fns: &[UFunc],
|
||||||
|
structs: &[UStruct],
|
||||||
|
generics: &[UGeneric],
|
||||||
|
types: &mut Vec<Type>,
|
||||||
|
errs: &mut Vec<ResErr>,
|
||||||
|
) -> Result<Res, Option<ResErr>> {
|
||||||
|
let no_gargs = || {
|
||||||
|
if self.gargs.len() > 0 {
|
||||||
|
Err(ResErr::GenericCount {
|
||||||
|
origin: self.origin,
|
||||||
|
expected: 0,
|
||||||
|
found: self.gargs.len(),
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
};
|
||||||
|
Ok(match &self.mem.id {
|
||||||
|
&MemberID::Fn(id) => {
|
||||||
|
validate_gargs(
|
||||||
|
&fns[id].gargs,
|
||||||
|
&self.gargs,
|
||||||
|
generics,
|
||||||
|
types,
|
||||||
|
errs,
|
||||||
|
self.origin,
|
||||||
|
)?;
|
||||||
|
Res::Fn(FnInst {
|
||||||
|
id,
|
||||||
|
gargs: self.gargs.clone(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
&MemberID::Struct(id) => {
|
||||||
|
validate_gargs(
|
||||||
|
&structs[id].gargs,
|
||||||
|
&self.gargs,
|
||||||
|
generics,
|
||||||
|
types,
|
||||||
|
errs,
|
||||||
|
self.origin,
|
||||||
|
)?;
|
||||||
|
Res::Struct(StructInst {
|
||||||
|
id,
|
||||||
|
gargs: self.gargs.clone(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
&MemberID::Var(id) => {
|
||||||
|
no_gargs()?;
|
||||||
|
Res::Var(id)
|
||||||
|
}
|
||||||
|
&MemberID::Module(id) => {
|
||||||
|
no_gargs()?;
|
||||||
|
Res::Module(id)
|
||||||
|
}
|
||||||
|
MemberID::Type(def) => {
|
||||||
|
validate_gargs(&def.gargs, &self.gargs, generics, types, errs, self.origin)?;
|
||||||
|
inst_typedef(def, &self.gargs, types);
|
||||||
|
Res::Type(def.ty)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl IdentID {
|
||||||
|
pub fn resolve(
|
||||||
|
self,
|
||||||
|
s: &mut Sources,
|
||||||
|
types: &mut Vec<Type>,
|
||||||
|
changed: &mut bool,
|
||||||
|
errs: &mut Vec<ResErr>,
|
||||||
|
) -> Result<Res, ResolveRes> {
|
||||||
|
let status = &mut s.idents[self].status;
|
||||||
|
// TOOD: there are some clones here that shouldn't be needed
|
||||||
|
Ok(match status {
|
||||||
|
IdentStatus::Res(r) => r.clone(),
|
||||||
|
IdentStatus::Unres { path, base } => {
|
||||||
|
while let Some(mem) = path.pop() {
|
||||||
|
let res = match base {
|
||||||
|
ResBase::Unvalidated(u) => {
|
||||||
|
match u.validate(s.fns, s.structs, s.generics, types, errs) {
|
||||||
|
Ok(res) => res,
|
||||||
|
Err(err) => {
|
||||||
|
*status = IdentStatus::Failed(err);
|
||||||
|
return Err(ResolveRes::Finished);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ResBase::Validated(res) => res.clone(),
|
||||||
|
};
|
||||||
|
*base = match (res, mem.ty) {
|
||||||
|
(Res::Module(id), MemberTy::Member) => {
|
||||||
|
let Some(m) = s.modules[id].members.get(&mem.name) else {
|
||||||
|
return Err(ResolveRes::Unfinished);
|
||||||
|
};
|
||||||
|
ResBase::Unvalidated(MemRes {
|
||||||
|
mem: m.clone(),
|
||||||
|
origin: mem.origin,
|
||||||
|
gargs: mem.gargs,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
(Res::Var(id), MemberTy::Field) => {
|
||||||
|
// trait resolution here
|
||||||
|
let Some(&child) = s.vars[id].children.get(&mem.name) else {
|
||||||
|
return Err(ResolveRes::Unfinished);
|
||||||
|
};
|
||||||
|
ResBase::Unvalidated(MemRes {
|
||||||
|
mem: Member {
|
||||||
|
id: MemberID::Var(child),
|
||||||
|
},
|
||||||
|
origin: mem.origin,
|
||||||
|
gargs: mem.gargs,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
*status = IdentStatus::Failed(Some(ResErr::UnknownMember {
|
||||||
|
origin: mem.origin,
|
||||||
|
ty: mem.ty,
|
||||||
|
name: mem.name.clone(),
|
||||||
|
parent: base.clone(),
|
||||||
|
}));
|
||||||
|
return Err(ResolveRes::Finished);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
let res = match base {
|
||||||
|
ResBase::Unvalidated(u) => {
|
||||||
|
match u.validate(s.fns, s.structs, s.generics, types, errs) {
|
||||||
|
Ok(res) => res,
|
||||||
|
Err(err) => {
|
||||||
|
*status = IdentStatus::Failed(err);
|
||||||
|
return Err(ResolveRes::Finished);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ResBase::Validated(res) => res.clone(),
|
||||||
|
};
|
||||||
|
*status = IdentStatus::Res(res.clone());
|
||||||
|
*changed = true;
|
||||||
|
res
|
||||||
|
}
|
||||||
|
IdentStatus::Cooked => return Err(ResolveRes::Finished),
|
||||||
|
IdentStatus::Failed(_) => return Err(ResolveRes::Finished),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn validate_gargs(
|
||||||
|
dst: &[GenericID],
|
||||||
|
src: &[TypeID],
|
||||||
|
generics: &[UGeneric],
|
||||||
|
types: &[Type],
|
||||||
|
errs: &mut Vec<ResErr>,
|
||||||
|
origin: Origin,
|
||||||
|
) -> Result<(), Option<ResErr>> {
|
||||||
|
if dst.len() != src.len() {
|
||||||
|
return Err(Some(ResErr::GenericCount {
|
||||||
|
origin,
|
||||||
|
expected: dst.len(),
|
||||||
|
found: src.len(),
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
for (dst, src) in dst.iter().zip(src.iter()) {
|
||||||
|
let g = &generics[dst];
|
||||||
|
let t = &types[src];
|
||||||
|
// TODO: validate trait constraints
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
158
src/ir/upper/resolve/instantiate.rs
Normal file
158
src/ir/upper/resolve/instantiate.rs
Normal file
@@ -0,0 +1,158 @@
|
|||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
pub fn inst_fn_var(
|
||||||
|
fi: FnInst,
|
||||||
|
fns: &[UFunc],
|
||||||
|
origin: Origin,
|
||||||
|
vars: &mut Vec<UVar>,
|
||||||
|
types: &mut Vec<Type>,
|
||||||
|
) -> VarID {
|
||||||
|
let name = fns[fi.id].name.clone();
|
||||||
|
let ty = push_id(types, RType::FnRef(fi).ty());
|
||||||
|
push_id(
|
||||||
|
vars,
|
||||||
|
UVar {
|
||||||
|
name,
|
||||||
|
origin,
|
||||||
|
ty,
|
||||||
|
parent: None,
|
||||||
|
children: HashMap::new(),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn inst_struct_var(
|
||||||
|
si: StructInst,
|
||||||
|
structs: &[UStruct],
|
||||||
|
origin: Origin,
|
||||||
|
vars: &mut Vec<UVar>,
|
||||||
|
types: &mut Vec<Type>,
|
||||||
|
) -> VarID {
|
||||||
|
let name = structs[si.id].name.clone();
|
||||||
|
let ty = push_id(types, RType::Struct(si).ty());
|
||||||
|
let id = push_id(
|
||||||
|
vars,
|
||||||
|
UVar {
|
||||||
|
name,
|
||||||
|
origin,
|
||||||
|
ty,
|
||||||
|
parent: None,
|
||||||
|
children: HashMap::new(),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
inst_var(vars, structs, id, types);
|
||||||
|
id
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn inst_var(vars: &mut Vec<UVar>, structs: &[UStruct], id: VarID, types: &mut Vec<Type>) {
|
||||||
|
match real_type(types, vars[id].ty) {
|
||||||
|
RType::Struct(si) => {
|
||||||
|
let fields = &structs[si.id].fields;
|
||||||
|
let s_gargs = &structs[si.id].gargs;
|
||||||
|
let gmap = inst_gmap(s_gargs, &si.gargs);
|
||||||
|
let children = fields
|
||||||
|
.iter()
|
||||||
|
.map(|(name, f)| {
|
||||||
|
(name.clone(), {
|
||||||
|
let ty = inst_type(f.ty, types, &gmap);
|
||||||
|
let fid = push_id(
|
||||||
|
vars,
|
||||||
|
UVar {
|
||||||
|
name: name.clone(),
|
||||||
|
origin: f.origin,
|
||||||
|
ty,
|
||||||
|
parent: Some(id),
|
||||||
|
children: HashMap::new(),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
inst_var(vars, structs, fid, types);
|
||||||
|
fid
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
vars[id].children = children;
|
||||||
|
}
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// gargs assumed to be valid
|
||||||
|
pub fn inst_typedef(def: &TypeDef, gargs: &[TypeID], types: &mut Vec<Type>) -> TypeID {
|
||||||
|
let gmap = inst_gmap(&def.gargs, &gargs);
|
||||||
|
inst_type(def.ty, types, &gmap)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn inst_gmap(dst: &[GenericID], src: &[TypeID]) -> HashMap<GenericID, TypeID> {
|
||||||
|
let mut gmap = HashMap::new();
|
||||||
|
for (&gid, &tid) in dst.iter().zip(src) {
|
||||||
|
gmap.insert(gid, tid);
|
||||||
|
}
|
||||||
|
gmap
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn inst_type(id: TypeID, types: &mut Vec<Type>, gmap: &HashMap<GenericID, TypeID>) -> TypeID {
|
||||||
|
if gmap.len() == 0 {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
match inst_type_(id, types, gmap) {
|
||||||
|
Some(new) => new,
|
||||||
|
None => id,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn inst_type_(
|
||||||
|
id: TypeID,
|
||||||
|
types: &mut Vec<Type>,
|
||||||
|
gmap: &HashMap<GenericID, TypeID>,
|
||||||
|
) -> Option<TypeID> {
|
||||||
|
let ty = match types[id].clone() {
|
||||||
|
Type::Real(rty) => match rty {
|
||||||
|
RType::Bits(_) => return None,
|
||||||
|
RType::Struct(struct_ty) => RType::Struct(StructInst {
|
||||||
|
id: struct_ty.id,
|
||||||
|
gargs: inst_all(&struct_ty.gargs, types, gmap)?,
|
||||||
|
}),
|
||||||
|
RType::FnRef(fn_ty) => RType::FnRef(FnInst {
|
||||||
|
id: fn_ty.id,
|
||||||
|
gargs: inst_all(&fn_ty.gargs, types, gmap)?,
|
||||||
|
}),
|
||||||
|
RType::Ref(id) => RType::Ref(inst_type_(id, types, gmap)?),
|
||||||
|
RType::Slice(id) => RType::Slice(inst_type_(id, types, gmap)?),
|
||||||
|
RType::Array(id, len) => RType::Array(inst_type_(id, types, gmap)?, len),
|
||||||
|
RType::Unit => return None,
|
||||||
|
RType::Generic(gid) => {
|
||||||
|
return gmap
|
||||||
|
.get(&gid)
|
||||||
|
.map(|id| Some(*id))
|
||||||
|
.unwrap_or_else(|| None)
|
||||||
|
}
|
||||||
|
RType::Infer => RType::Infer,
|
||||||
|
}
|
||||||
|
.ty(),
|
||||||
|
Type::Deref(id) => Type::Deref(inst_type_(id, types, gmap)?),
|
||||||
|
Type::Ptr(id) => Type::Ptr(inst_type_(id, types, gmap)?),
|
||||||
|
Type::Unres(mod_path) => Type::Unres(mod_path.clone()),
|
||||||
|
Type::Error => return None,
|
||||||
|
};
|
||||||
|
Some(push_id(types, ty))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn inst_all(
|
||||||
|
ids: &[TypeID],
|
||||||
|
types: &mut Vec<Type>,
|
||||||
|
gmap: &HashMap<GenericID, TypeID>,
|
||||||
|
) -> Option<Vec<TypeID>> {
|
||||||
|
let mut vec = None;
|
||||||
|
for (i, &id) in ids.iter().enumerate() {
|
||||||
|
if let Some(id) = inst_type_(id, types, gmap) {
|
||||||
|
vec.get_or_insert_with(|| ids.iter().take(i).cloned().collect::<Vec<_>>())
|
||||||
|
.push(id);
|
||||||
|
} else if let Some(vec) = &mut vec {
|
||||||
|
vec.push(id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
vec
|
||||||
|
}
|
||||||
|
|
||||||
138
src/ir/upper/resolve/instr.rs
Normal file
138
src/ir/upper/resolve/instr.rs
Normal file
@@ -0,0 +1,138 @@
|
|||||||
|
use std::collections::HashSet;
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
pub fn resolve_instr<'a>(data: &mut ResData<'a>, ctx: ResolveCtx<'a>) -> Option<()> {
|
||||||
|
let mut res = ResolveRes::Finished;
|
||||||
|
match &ctx.i.i {
|
||||||
|
UInstruction::Call { dst, f, args } => {
|
||||||
|
let fi = data.res_id::<UFunc>(f, ctx)?;
|
||||||
|
let f = &data.s.fns[fi.id];
|
||||||
|
for (src, &dest) in args.iter().zip(&f.args) {
|
||||||
|
res |= data.match_types::<UVar, UVar>(dest, src, src);
|
||||||
|
}
|
||||||
|
res |= data.match_types::<UVar, Type>(dst, f.ret, dst);
|
||||||
|
}
|
||||||
|
UInstruction::Mv { dst, src } => {
|
||||||
|
res |= data.match_types::<UVar, UVar>(dst, src, src);
|
||||||
|
}
|
||||||
|
UInstruction::Ref { dst, src } => {
|
||||||
|
let dstty = data.res_var_ty(dst, ctx)?.0;
|
||||||
|
let &RType::Ref(dest_ty) = dstty else {
|
||||||
|
compiler_error()
|
||||||
|
};
|
||||||
|
res |= data.match_types::<Type, UVar>(dest_ty, src, src);
|
||||||
|
}
|
||||||
|
UInstruction::Deref { dst, src } => {
|
||||||
|
let (srcty, srcid) = data.res_var_ty(src, ctx)?;
|
||||||
|
let &RType::Ref(src_ty) = srcty else {
|
||||||
|
let origin = src.origin(data);
|
||||||
|
data.errs.push(ResErr::CannotDeref { origin, ty: srcid });
|
||||||
|
return None;
|
||||||
|
};
|
||||||
|
res |= data.match_types::<UVar, Type>(dst, src_ty, src);
|
||||||
|
}
|
||||||
|
UInstruction::LoadData { dst, src } => {
|
||||||
|
let srcid = src.type_id(&data.s);
|
||||||
|
res |= data.match_types::<UVar, Type>(dst, srcid, dst);
|
||||||
|
}
|
||||||
|
UInstruction::LoadSlice { dst, src } => {
|
||||||
|
let (dstty, dstid) = data.res_var_ty(dst, ctx)?;
|
||||||
|
let &RType::Slice(dstty) = dstty else {
|
||||||
|
compiler_error()
|
||||||
|
};
|
||||||
|
let srcid = src.type_id(&data.s);
|
||||||
|
let Type::Real(RType::Array(srcty, _)) = data.types[srcid] else {
|
||||||
|
compiler_error()
|
||||||
|
};
|
||||||
|
res |= data.match_types(dstty, srcty, dst);
|
||||||
|
}
|
||||||
|
UInstruction::AsmBlock { instructions, args } => {
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
|
UInstruction::Ret { src } => {
|
||||||
|
res |= data.match_types::<Type, UVar>(ctx.ret, src, src);
|
||||||
|
}
|
||||||
|
UInstruction::Construct { dst, struc, fields } => {
|
||||||
|
let si = data.res_id::<UStruct>(dst, ctx)?;
|
||||||
|
let sid = si.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::<Type, UVar>(field.ty, src, src);
|
||||||
|
} else {
|
||||||
|
let origin = dst.origin(data);
|
||||||
|
data.errs.push(ResErr::MissingField {
|
||||||
|
origin,
|
||||||
|
id: sid,
|
||||||
|
name: name.clone(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (name, _) in fields {
|
||||||
|
if !used.contains(name) {
|
||||||
|
let origin = dst.origin(data);
|
||||||
|
data.errs.push(ResErr::UnknownStructField {
|
||||||
|
origin,
|
||||||
|
id: sid,
|
||||||
|
name: name.clone(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
UInstruction::If { cond, body } => {
|
||||||
|
if let Some(ty) = data.res_var_ty(cond, ctx) {
|
||||||
|
if !matches!(ty.0, RType::Bits(64)) {
|
||||||
|
let id = ty.1;
|
||||||
|
let origin = cond.origin(data);
|
||||||
|
data.errs.push(ResErr::CondType { origin, ty: id });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for i in body {
|
||||||
|
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,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
match res {
|
||||||
|
ResolveRes::Finished => (),
|
||||||
|
ResolveRes::Unfinished => data.unfinished.push(ctx),
|
||||||
|
}
|
||||||
|
return None;
|
||||||
|
}
|
||||||
139
src/ir/upper/resolve/matc.rs
Normal file
139
src/ir/upper/resolve/matc.rs
Normal file
@@ -0,0 +1,139 @@
|
|||||||
|
use super::*;
|
||||||
|
|
||||||
|
pub fn match_types(data: &mut ResData, dst: impl TypeIDed, src: impl TypeIDed) -> MatchRes {
|
||||||
|
let dstid = dst.type_id(&data.s);
|
||||||
|
let srcid = src.type_id(&data.s);
|
||||||
|
let dstty = data.real_ty(&dst)?.clone();
|
||||||
|
let srcty = data.real_ty(&src)?.clone();
|
||||||
|
let error = || {
|
||||||
|
MatchRes::Error(vec![TypeMismatch {
|
||||||
|
dst: dstid,
|
||||||
|
src: srcid,
|
||||||
|
}])
|
||||||
|
};
|
||||||
|
match (dstty, srcty) {
|
||||||
|
// prefer changing dst over src
|
||||||
|
(RType::Infer, _) => {
|
||||||
|
data.changed = true;
|
||||||
|
data.types[dstid] = Type::Ptr(srcid);
|
||||||
|
dst.finish(&mut data.s, data.types);
|
||||||
|
MatchRes::Finished
|
||||||
|
}
|
||||||
|
(_, RType::Infer) => {
|
||||||
|
data.changed = true;
|
||||||
|
data.types[srcid] = Type::Ptr(dstid);
|
||||||
|
src.finish(&mut data.s, data.types);
|
||||||
|
MatchRes::Finished
|
||||||
|
}
|
||||||
|
(RType::Struct(dest), RType::Struct(src)) => {
|
||||||
|
if dest.id != src.id {
|
||||||
|
return error();
|
||||||
|
}
|
||||||
|
match_all(data, dest.gargs.iter().cloned(), src.gargs.iter().cloned())
|
||||||
|
}
|
||||||
|
// (
|
||||||
|
// Type::Fn {
|
||||||
|
// args: dst_args,
|
||||||
|
// ret: dst_ret,
|
||||||
|
// },
|
||||||
|
// Type::Fn {
|
||||||
|
// args: src_args,
|
||||||
|
// ret: src_ret,
|
||||||
|
// },
|
||||||
|
// ) => {
|
||||||
|
// let dst = dst_args.into_iter().chain(once(dst_ret));
|
||||||
|
// let src = src_args.into_iter().chain(once(src_ret));
|
||||||
|
// match_all(data, dst, src)
|
||||||
|
// }
|
||||||
|
(RType::Ref(dest), RType::Ref(src)) => match_types(data, dest, src),
|
||||||
|
(RType::Slice(dest), RType::Slice(src)) => match_types(data, dest, src),
|
||||||
|
(RType::Array(dest, dlen), RType::Array(src, slen)) => {
|
||||||
|
if dlen == slen {
|
||||||
|
match_types(data, dest, src)
|
||||||
|
} else {
|
||||||
|
error()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => error(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn match_all(
|
||||||
|
data: &mut ResData,
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> ResData<'a> {
|
||||||
|
pub fn match_types<Dst: ResKind, Src: ResKind>(
|
||||||
|
&mut self,
|
||||||
|
dst: impl Resolvable<Dst>,
|
||||||
|
src: impl Resolvable<Src>,
|
||||||
|
origin: impl HasOrigin,
|
||||||
|
) -> ResolveRes
|
||||||
|
where
|
||||||
|
Dst::Res: TypeIDed,
|
||||||
|
Src::Res: TypeIDed,
|
||||||
|
{
|
||||||
|
let dst = dst
|
||||||
|
.try_res(&mut self.s, self.types, &mut self.errs, &mut self.changed)?
|
||||||
|
.type_id(&self.s);
|
||||||
|
let src = src
|
||||||
|
.try_res(&mut self.s, self.types, &mut self.errs, &mut self.changed)?
|
||||||
|
.type_id(&self.s);
|
||||||
|
let res = match_types(self, dst, src);
|
||||||
|
match res {
|
||||||
|
MatchRes::Unfinished => ResolveRes::Unfinished,
|
||||||
|
MatchRes::Finished => ResolveRes::Finished,
|
||||||
|
MatchRes::Error(es) => {
|
||||||
|
self.errs.push(ResErr::Type {
|
||||||
|
errs: es,
|
||||||
|
origin: origin.origin(self),
|
||||||
|
dst,
|
||||||
|
src,
|
||||||
|
});
|
||||||
|
ResolveRes::Finished
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn real_ty(&mut self, x: &impl TypeIDed) -> Result<&RType, MatchRes> {
|
||||||
|
real_type(self.types, x.type_id(&self.s)).map_err(|res| match res {
|
||||||
|
ResolveRes::Finished => MatchRes::Finished,
|
||||||
|
ResolveRes::Unfinished => MatchRes::Unfinished,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum MatchRes {
|
||||||
|
Unfinished,
|
||||||
|
Finished,
|
||||||
|
Error(Vec<TypeMismatch>),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromResidual<Result<Infallible, MatchRes>> for MatchRes {
|
||||||
|
fn from_residual(residual: Result<Infallible, MatchRes>) -> Self {
|
||||||
|
match residual {
|
||||||
|
Ok(_) => unreachable!(),
|
||||||
|
Err(r) => r,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
349
src/ir/upper/resolve/mod.rs
Normal file
349
src/ir/upper/resolve/mod.rs
Normal file
@@ -0,0 +1,349 @@
|
|||||||
|
use super::*;
|
||||||
|
use crate::{
|
||||||
|
common::CompilerOutput,
|
||||||
|
ir::{MemRes, Member},
|
||||||
|
};
|
||||||
|
use std::{
|
||||||
|
convert::Infallible,
|
||||||
|
ops::{BitOrAssign, FromResidual},
|
||||||
|
};
|
||||||
|
|
||||||
|
mod error;
|
||||||
|
mod instr;
|
||||||
|
mod matc;
|
||||||
|
mod ident;
|
||||||
|
mod instantiate;
|
||||||
|
|
||||||
|
pub use error::*;
|
||||||
|
use instr::*;
|
||||||
|
use instantiate::*;
|
||||||
|
|
||||||
|
impl UProgram {
|
||||||
|
pub fn resolve(&mut self, output: &mut CompilerOutput) {
|
||||||
|
let mut unfinished = Vec::new();
|
||||||
|
let mut data = ResData {
|
||||||
|
unfinished: Vec::new(),
|
||||||
|
changed: false,
|
||||||
|
types: &mut self.types,
|
||||||
|
s: Sources {
|
||||||
|
idents: &mut self.idents,
|
||||||
|
vars: &mut self.vars,
|
||||||
|
fns: &self.fns,
|
||||||
|
structs: &self.structs,
|
||||||
|
generics: &self.generics,
|
||||||
|
data: &self.data,
|
||||||
|
modules: &self.modules,
|
||||||
|
},
|
||||||
|
errs: Vec::new(),
|
||||||
|
};
|
||||||
|
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 data.types[f.ret] != RType::Unit.ty()
|
||||||
|
&& f.instructions
|
||||||
|
.last()
|
||||||
|
.is_none_or(|i| !matches!(i.i, UInstruction::Ret { .. }))
|
||||||
|
{
|
||||||
|
data.errs.push(ResErr::NoReturn { fid });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
while !data.unfinished.is_empty() && data.changed {
|
||||||
|
data.changed = false;
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
struct ResolveCtx<'a> {
|
||||||
|
ret: TypeID,
|
||||||
|
breakable: bool,
|
||||||
|
i: &'a UInstrInst,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn compiler_error() -> ! {
|
||||||
|
// TODO: this is probably a compiler error / should never happen
|
||||||
|
panic!("how could this happen to me (you)");
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Sources<'a> {
|
||||||
|
idents: &'a mut [UIdent],
|
||||||
|
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>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> ResData<'a> {
|
||||||
|
pub fn try_res_id<K: ResKind>(&mut self, x: impl Resolvable<K>) -> Result<K::Res, ResolveRes> {
|
||||||
|
x.try_res(
|
||||||
|
&mut self.s,
|
||||||
|
&mut self.types,
|
||||||
|
&mut self.errs,
|
||||||
|
&mut self.changed,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
pub fn res_var_ty<'b: 'a>(
|
||||||
|
&mut self,
|
||||||
|
x: impl Resolvable<UVar>,
|
||||||
|
ctx: ResolveCtx<'b>,
|
||||||
|
) -> Option<(&RType, TypeID)> {
|
||||||
|
let id = self.res_id::<UVar>(x, ctx).map(|i| i.type_id(&self.s))?;
|
||||||
|
real_type(self.types, id).ok().map(|ty| (ty, id))
|
||||||
|
}
|
||||||
|
pub fn res_id<'b: 'a, K: ResKind>(
|
||||||
|
&mut self,
|
||||||
|
x: impl Resolvable<K>,
|
||||||
|
ctx: ResolveCtx<'b>,
|
||||||
|
) -> Option<K::Res> {
|
||||||
|
match self.try_res_id(x) {
|
||||||
|
Ok(id) => return Some(id),
|
||||||
|
Err(ResolveRes::Unfinished) => self.unfinished.push(ctx),
|
||||||
|
Err(ResolveRes::Finished) => (),
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
pub enum ResolveRes {
|
||||||
|
Finished,
|
||||||
|
Unfinished,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BitOrAssign for ResolveRes {
|
||||||
|
fn bitor_assign(&mut self, rhs: Self) {
|
||||||
|
match rhs {
|
||||||
|
ResolveRes::Finished => (),
|
||||||
|
ResolveRes::Unfinished => *self = ResolveRes::Unfinished,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromResidual<Option<Infallible>> for ResolveRes {
|
||||||
|
fn from_residual(_: Option<Infallible>) -> Self {
|
||||||
|
Self::Unfinished
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
trait Resolvable<K: ResKind> {
|
||||||
|
fn try_res(
|
||||||
|
&self,
|
||||||
|
s: &mut Sources,
|
||||||
|
types: &mut Vec<Type>,
|
||||||
|
errs: &mut Vec<ResErr>,
|
||||||
|
changed: &mut bool,
|
||||||
|
) -> Result<K::Res, ResolveRes>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<K: ResKind> Resolvable<K> for IdentID {
|
||||||
|
fn try_res(
|
||||||
|
&self,
|
||||||
|
s: &mut Sources,
|
||||||
|
types: &mut Vec<Type>,
|
||||||
|
errs: &mut Vec<ResErr>,
|
||||||
|
changed: &mut bool,
|
||||||
|
) -> Result<K::Res, ResolveRes> {
|
||||||
|
let origin = s.idents[self].origin;
|
||||||
|
let res = self.resolve(s, types, changed, errs)?;
|
||||||
|
match K::from_res(res, types, s, origin) {
|
||||||
|
Ok(res) => Ok(res),
|
||||||
|
Err(res) => {
|
||||||
|
errs.push(ResErr::KindMismatch {
|
||||||
|
origin,
|
||||||
|
expected: K::ty(),
|
||||||
|
found: res,
|
||||||
|
});
|
||||||
|
Err(ResolveRes::Finished)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<K: ResKind> Resolvable<K> for &IdentID {
|
||||||
|
fn try_res(
|
||||||
|
&self,
|
||||||
|
s: &mut Sources,
|
||||||
|
types: &mut Vec<Type>,
|
||||||
|
errs: &mut Vec<ResErr>,
|
||||||
|
changed: &mut bool,
|
||||||
|
) -> Result<K::Res, ResolveRes> {
|
||||||
|
Resolvable::<K>::try_res(*self, s, types, errs, changed)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Resolvable<UVar> for VarID {
|
||||||
|
fn try_res(
|
||||||
|
&self,
|
||||||
|
s: &mut Sources,
|
||||||
|
types: &mut Vec<Type>,
|
||||||
|
errs: &mut Vec<ResErr>,
|
||||||
|
changed: &mut bool,
|
||||||
|
) -> Result<<UVar as ResKind>::Res, ResolveRes> {
|
||||||
|
Ok(*self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Resolvable<Type> for TypeID {
|
||||||
|
fn try_res(
|
||||||
|
&self,
|
||||||
|
s: &mut Sources,
|
||||||
|
types: &mut Vec<Type>,
|
||||||
|
errs: &mut Vec<ResErr>,
|
||||||
|
changed: &mut bool,
|
||||||
|
) -> Result<<Type as ResKind>::Res, ResolveRes> {
|
||||||
|
Ok(*self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait ResKind {
|
||||||
|
type Res;
|
||||||
|
fn ty() -> KindTy;
|
||||||
|
fn from_res(
|
||||||
|
res: Res,
|
||||||
|
types: &mut Vec<Type>,
|
||||||
|
s: &mut Sources,
|
||||||
|
origin: Origin,
|
||||||
|
) -> Result<Self::Res, Res>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ResKind for UFunc {
|
||||||
|
type Res = FnInst;
|
||||||
|
fn ty() -> KindTy {
|
||||||
|
KindTy::Fn
|
||||||
|
}
|
||||||
|
fn from_res(res: Res, _: &mut Vec<Type>, _: &mut Sources, _: Origin) -> Result<Self::Res, Res> {
|
||||||
|
match res {
|
||||||
|
Res::Fn(fi) => Ok(fi),
|
||||||
|
_ => Err(res),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ResKind for UVar {
|
||||||
|
type Res = VarID;
|
||||||
|
fn ty() -> KindTy {
|
||||||
|
KindTy::Var
|
||||||
|
}
|
||||||
|
fn from_res(
|
||||||
|
res: Res,
|
||||||
|
types: &mut Vec<Type>,
|
||||||
|
s: &mut Sources,
|
||||||
|
origin: Origin,
|
||||||
|
) -> Result<Self::Res, Res> {
|
||||||
|
Ok(match res {
|
||||||
|
Res::Fn(fty) => inst_fn_var(fty, s.fns, origin, s.vars, types),
|
||||||
|
Res::Var(id) => id,
|
||||||
|
_ => return Err(res),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ResKind for UStruct {
|
||||||
|
type Res = StructInst;
|
||||||
|
fn ty() -> KindTy {
|
||||||
|
KindTy::Struct
|
||||||
|
}
|
||||||
|
fn from_res(res: Res, _: &mut Vec<Type>, _: &mut Sources, _: Origin) -> Result<Self::Res, Res> {
|
||||||
|
match res {
|
||||||
|
Res::Struct(si) => Ok(si),
|
||||||
|
_ => Err(res),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ResKind for Type {
|
||||||
|
type Res = TypeID;
|
||||||
|
fn ty() -> KindTy {
|
||||||
|
KindTy::Type
|
||||||
|
}
|
||||||
|
fn from_res(
|
||||||
|
res: Res,
|
||||||
|
types: &mut Vec<Type>,
|
||||||
|
s: &mut Sources,
|
||||||
|
_: Origin,
|
||||||
|
) -> Result<Self::Res, Res> {
|
||||||
|
Ok(match res {
|
||||||
|
Res::Struct(si) => push_id(types, RType::Struct(si).ty()),
|
||||||
|
Res::Type(id) => id,
|
||||||
|
_ => return Err(res),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait TypeIDed {
|
||||||
|
fn type_id(&self, s: &Sources) -> TypeID;
|
||||||
|
fn finish(&self, s: &mut Sources, types: &mut Vec<Type>) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
fn finish(&self, s: &mut Sources, types: &mut Vec<Type>) {
|
||||||
|
inst_var(s.vars, s.structs, *self, types);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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<Result<Infallible, ResolveRes>> for ResolveRes {
|
||||||
|
fn from_residual(residual: Result<Infallible, ResolveRes>) -> Self {
|
||||||
|
match residual {
|
||||||
|
Ok(_) => unreachable!(),
|
||||||
|
Err(r) => r,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
trait HasOrigin {
|
||||||
|
fn origin(&self, data: &ResData) -> Origin;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl HasOrigin for &IdentID {
|
||||||
|
fn origin(&self, data: &ResData) -> Origin {
|
||||||
|
data.s.idents[*self].origin
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,9 +1,4 @@
|
|||||||
use std::collections::HashMap;
|
use super::{FnID, GenericID, IdentID, Len, ResolveRes, StructID, TypeID, UProgram, VarID};
|
||||||
|
|
||||||
use super::{
|
|
||||||
push_id, FnID, GenericID, IdentID, Len, Origin, ResErr, StructID, TypeDef, TypeID, UFunc,
|
|
||||||
UGeneric, UProgram, UStruct, UVar, VarID,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Hash, Eq, PartialEq)]
|
#[derive(Debug, Clone, Hash, Eq, PartialEq)]
|
||||||
pub struct FieldRef {
|
pub struct FieldRef {
|
||||||
@@ -25,23 +20,36 @@ pub struct FnInst {
|
|||||||
pub gargs: Vec<TypeID>,
|
pub gargs: Vec<TypeID>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone, PartialEq)]
|
||||||
pub enum Type {
|
pub enum Type {
|
||||||
|
Real(RType),
|
||||||
|
Deref(TypeID),
|
||||||
|
Ptr(TypeID),
|
||||||
|
Unres(IdentID),
|
||||||
|
Error,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// "real" types
|
||||||
|
#[derive(Clone, PartialEq)]
|
||||||
|
pub enum RType {
|
||||||
Bits(u32),
|
Bits(u32),
|
||||||
Struct(StructInst),
|
Struct(StructInst),
|
||||||
FnRef(FnInst),
|
|
||||||
// this can be added for constraints later (F: fn(...) -> ...)
|
// this can be added for constraints later (F: fn(...) -> ...)
|
||||||
// Fn { args: Vec<TypeID>, ret: TypeID },
|
// Fn { args: Vec<TypeID>, ret: TypeID },
|
||||||
|
// "fake" types
|
||||||
|
FnRef(FnInst),
|
||||||
Ref(TypeID),
|
Ref(TypeID),
|
||||||
Deref(TypeID),
|
|
||||||
Slice(TypeID),
|
Slice(TypeID),
|
||||||
Array(TypeID, Len),
|
Array(TypeID, Len),
|
||||||
Unit,
|
Unit,
|
||||||
// "fake" types
|
|
||||||
Unres(IdentID),
|
|
||||||
Generic(GenericID),
|
|
||||||
Infer,
|
Infer,
|
||||||
Error,
|
Generic(GenericID),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RType {
|
||||||
|
pub const fn ty(self) -> Type {
|
||||||
|
Type::Real(self)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Type {
|
impl Type {
|
||||||
@@ -61,16 +69,16 @@ impl Type {
|
|||||||
|
|
||||||
impl TypeID {
|
impl TypeID {
|
||||||
pub fn rf(self) -> Type {
|
pub fn rf(self) -> Type {
|
||||||
Type::Ref(self)
|
RType::Ref(self).ty()
|
||||||
}
|
}
|
||||||
pub fn derf(self) -> Type {
|
pub fn derf(self) -> Type {
|
||||||
Type::Deref(self)
|
Type::Deref(self)
|
||||||
}
|
}
|
||||||
pub fn arr(self, len: Len) -> Type {
|
pub fn arr(self, len: Len) -> Type {
|
||||||
Type::Array(self, len)
|
RType::Array(self, len).ty()
|
||||||
}
|
}
|
||||||
pub fn slice(self) -> Type {
|
pub fn slice(self) -> Type {
|
||||||
Type::Slice(self)
|
RType::Slice(self).ty()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -80,159 +88,15 @@ impl Type {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn inst_fn_var(
|
pub fn real_type(types: &[Type], id: TypeID) -> Result<&RType, ResolveRes> {
|
||||||
fi: FnInst,
|
match &types[id] {
|
||||||
fns: &[UFunc],
|
Type::Real(rtype) => Ok(rtype),
|
||||||
origin: Origin,
|
&Type::Ptr(id) => real_type(types, id),
|
||||||
vars: &mut Vec<UVar>,
|
&Type::Deref(id) => match real_type(types, id)? {
|
||||||
types: &mut Vec<Type>,
|
&RType::Ref(id) => real_type(types, id),
|
||||||
) -> VarID {
|
_ => Err(ResolveRes::Finished),
|
||||||
let name = fns[fi.id].name.clone();
|
|
||||||
let ty = push_id(types, Type::FnRef(fi));
|
|
||||||
push_id(
|
|
||||||
vars,
|
|
||||||
UVar {
|
|
||||||
name,
|
|
||||||
origin,
|
|
||||||
ty,
|
|
||||||
parent: None,
|
|
||||||
children: HashMap::new(),
|
|
||||||
},
|
},
|
||||||
)
|
Type::Unres(_) => Err(ResolveRes::Unfinished),
|
||||||
}
|
Type::Error => Err(ResolveRes::Finished),
|
||||||
|
|
||||||
pub fn inst_struct_var(
|
|
||||||
si: StructInst,
|
|
||||||
structs: &[UStruct],
|
|
||||||
origin: Origin,
|
|
||||||
vars: &mut Vec<UVar>,
|
|
||||||
types: &mut Vec<Type>,
|
|
||||||
) -> VarID {
|
|
||||||
let name = structs[si.id].name.clone();
|
|
||||||
let ty = push_id(types, Type::Struct(si));
|
|
||||||
let id = push_id(
|
|
||||||
vars,
|
|
||||||
UVar {
|
|
||||||
name,
|
|
||||||
origin,
|
|
||||||
ty,
|
|
||||||
parent: None,
|
|
||||||
children: HashMap::new(),
|
|
||||||
},
|
|
||||||
);
|
|
||||||
inst_var(vars, structs, id, types);
|
|
||||||
id
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn inst_var(vars: &mut Vec<UVar>, structs: &[UStruct], id: VarID, types: &mut Vec<Type>) {
|
|
||||||
match &types[resolve_refs(types, vars[id].ty)] {
|
|
||||||
Type::Struct(si) => {
|
|
||||||
let fields = &structs[si.id].fields;
|
|
||||||
let s_gargs = &structs[si.id].gargs;
|
|
||||||
let gmap = inst_gmap(s_gargs, &si.gargs);
|
|
||||||
let children = fields
|
|
||||||
.iter()
|
|
||||||
.map(|(name, f)| {
|
|
||||||
(name.clone(), {
|
|
||||||
let ty = inst_type(f.ty, types, &gmap);
|
|
||||||
let fid = push_id(
|
|
||||||
vars,
|
|
||||||
UVar {
|
|
||||||
name: name.clone(),
|
|
||||||
origin: f.origin,
|
|
||||||
ty,
|
|
||||||
parent: Some(id),
|
|
||||||
children: HashMap::new(),
|
|
||||||
},
|
|
||||||
);
|
|
||||||
inst_var(vars, structs, fid, types);
|
|
||||||
fid
|
|
||||||
})
|
|
||||||
})
|
|
||||||
.collect();
|
|
||||||
vars[id].children = children;
|
|
||||||
}
|
|
||||||
_ => (),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn resolve_refs(types: &[Type], id: TypeID) -> TypeID {
|
|
||||||
if let Type::Deref(rid) = types[id]
|
|
||||||
&& let Type::Ref(nid) = types[rid]
|
|
||||||
{
|
|
||||||
nid
|
|
||||||
} else {
|
|
||||||
id
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn validate_gargs(
|
|
||||||
dst: &[GenericID],
|
|
||||||
src: &[TypeID],
|
|
||||||
generics: &[UGeneric],
|
|
||||||
types: &[Type],
|
|
||||||
errs: &mut Vec<ResErr>,
|
|
||||||
origin: Origin,
|
|
||||||
) -> Result<(), Option<ResErr>> {
|
|
||||||
if dst.len() != src.len() {
|
|
||||||
return Err(Some(ResErr::GenericCount {
|
|
||||||
origin,
|
|
||||||
expected: dst.len(),
|
|
||||||
found: src.len(),
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
for (dst, src) in dst.iter().zip(src.iter()) {
|
|
||||||
let g = &generics[dst];
|
|
||||||
let t = &types[src];
|
|
||||||
// TODO: validate trait constraints
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// gargs assumed to be valid
|
|
||||||
pub fn inst_typedef(def: &TypeDef, gargs: &[TypeID], types: &mut Vec<Type>) -> TypeID {
|
|
||||||
let gmap = inst_gmap(&def.gargs, &gargs);
|
|
||||||
inst_type(def.ty, types, &gmap)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn inst_gmap(dst: &[GenericID], src: &[TypeID]) -> HashMap<GenericID, TypeID> {
|
|
||||||
let mut gmap = HashMap::new();
|
|
||||||
for (&gid, &tid) in dst.iter().zip(src) {
|
|
||||||
gmap.insert(gid, tid);
|
|
||||||
}
|
|
||||||
gmap
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn inst_type(id: TypeID, types: &mut Vec<Type>, gmap: &HashMap<GenericID, TypeID>) -> TypeID {
|
|
||||||
let ty = match types[id].clone() {
|
|
||||||
Type::Bits(_) => return id,
|
|
||||||
Type::Struct(struct_ty) => Type::Struct(StructInst {
|
|
||||||
id: struct_ty.id,
|
|
||||||
gargs: struct_ty
|
|
||||||
.gargs
|
|
||||||
.iter()
|
|
||||||
.map(|id| inst_type(*id, types, gmap))
|
|
||||||
.collect(),
|
|
||||||
}),
|
|
||||||
Type::FnRef(fn_ty) => Type::FnRef(FnInst {
|
|
||||||
id: fn_ty.id,
|
|
||||||
gargs: fn_ty
|
|
||||||
.gargs
|
|
||||||
.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,
|
|
||||||
};
|
|
||||||
push_id(types, ty)
|
|
||||||
}
|
|
||||||
|
|
||||||
// type Test<T, U> = (&T, &U)
|
|
||||||
|
|||||||
Reference in New Issue
Block a user