more wrestling
This commit is contained in:
@@ -0,0 +1,214 @@
|
||||
use crate::{
|
||||
common::{CompilerMsg, CompilerOutput},
|
||||
ir::RType,
|
||||
};
|
||||
|
||||
use super::{
|
||||
IdentStatus, KindTy, MemberTy, Origin, Res, ResBase, StructID, Type, TypeID, UProgram,
|
||||
};
|
||||
|
||||
pub fn report_errs(p: &UProgram, output: &mut CompilerOutput, mut errs: Vec<ResErr>) {
|
||||
for ident in &p.idents {
|
||||
match &ident.status {
|
||||
IdentStatus::Unres { path, base } => {
|
||||
let mem = path.last().unwrap();
|
||||
errs.push(ResErr::UnknownMember {
|
||||
ty: mem.ty,
|
||||
name: mem.name.clone(),
|
||||
origin: mem.origin,
|
||||
parent: base.clone(),
|
||||
})
|
||||
}
|
||||
IdentStatus::Failed(err) => {
|
||||
if let Some(err) = err {
|
||||
errs.push(err.clone())
|
||||
}
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
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::UnknownStructField { 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,
|
||||
} => output.err(CompilerMsg::new(
|
||||
format!("Expected {expected}, found {}", found.display_str(p)),
|
||||
origin,
|
||||
)),
|
||||
ResErr::UnknownMember {
|
||||
origin,
|
||||
ty,
|
||||
name,
|
||||
parent,
|
||||
} => output.err(CompilerMsg::new(
|
||||
format!("Unknown {ty} {name} of {}", parent.display_str(p)),
|
||||
origin,
|
||||
)),
|
||||
}
|
||||
}
|
||||
for var in &p.vars {
|
||||
match &p.types[var.ty] {
|
||||
Type::Real(RType::Infer) => output.err(CompilerMsg::new(
|
||||
format!("Type of {:?} cannot be inferred", var.name),
|
||||
var.origin,
|
||||
)),
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub enum ResErr {
|
||||
UnknownMember {
|
||||
origin: Origin,
|
||||
ty: MemberTy,
|
||||
name: String,
|
||||
parent: ResBase,
|
||||
},
|
||||
KindMismatch {
|
||||
origin: Origin,
|
||||
expected: KindTy,
|
||||
found: Res,
|
||||
},
|
||||
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,
|
||||
},
|
||||
UnknownStructField {
|
||||
origin: Origin,
|
||||
id: StructID,
|
||||
name: String,
|
||||
},
|
||||
Type {
|
||||
dst: TypeID,
|
||||
src: TypeID,
|
||||
errs: Vec<TypeMismatch>,
|
||||
origin: Origin,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum ControlFlowOp {
|
||||
Break,
|
||||
Continue,
|
||||
}
|
||||
|
||||
impl ControlFlowOp {
|
||||
pub fn str(&self) -> &'static str {
|
||||
match self {
|
||||
ControlFlowOp::Break => "break",
|
||||
ControlFlowOp::Continue => "continue",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
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)
|
||||
)
|
||||
}
|
||||
@@ -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(())
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user