#![deny(missing_docs)]
use std::collections::{HashMap, HashSet};
use crate::common::errors::*;
use crate::common::name::Name;
use crate::common::score::Result;
use crate::common::source::{Span, Spanned};
use crate::score::{Def, ScoreContext};
use crate::ty::Ty;
#[derive(Debug)]
pub enum OverloadReq<'ctx> {
Enum(TypeReq<'ctx>),
Subprog(SignatureReq<'ctx>),
}
impl<'ctx> OverloadReq<'ctx> {
pub fn matches(&self, ty: &Ty) -> bool {
match *self {
OverloadReq::Enum(ref req) => req.matches(ty),
OverloadReq::Subprog(ref req) => req.matches(ty),
}
}
}
#[derive(Debug)]
pub struct SignatureReq<'ctx> {
pub return_type: TypeReq<'ctx>,
pub positional: Vec<TypeReq<'ctx>>,
pub named: HashMap<Name, TypeReq<'ctx>>,
}
impl<'ctx> SignatureReq<'ctx> {
pub fn matches(&self, ty: &Ty) -> bool {
if let Ty::Subprog(ref ty) = *ty {
if !self.return_type.is_any()
&& !ty
.ret
.as_ref()
.map(|t| self.return_type.matches(t))
.unwrap_or(false)
{
debugln!("return type mismatch: {} vs {:?}", ty, self);
return false;
}
if self.positional.len() > ty.args.len() {
debugln!("positional length mismatch: {} vs {:?}", ty, self);
return false;
}
let mut arg_iter = ty.args.iter();
for req in &self.positional {
let arg = arg_iter.next().unwrap(); if !req.matches(&arg.ty) {
debugln!(
"positional mismatch: {} vs {:?} in {} vs {:?}",
arg.ty,
req,
ty,
self
);
return false;
}
}
let mut unhandled_names: HashSet<_> = self.named.keys().collect();
for arg in arg_iter {
let name = match arg.name {
Some(name) => name,
None => {
debugln!(
"unhandled positional arg: {} in {} vs {:?}",
arg.ty,
ty,
self
);
return false;
}
};
let req = match self.named.get(&name) {
Some(req) => req,
None => {
debugln!("unknown named arg: {} in {} vs {:?}", name, ty, self);
return false;
}
};
if !req.matches(&arg.ty) {
debugln!(
"named mismatch `{}`: {} vs {:?} in {} vs {:?}",
name,
arg.ty,
req,
ty,
self
);
return false;
}
unhandled_names.remove(&name);
}
unhandled_names.is_empty()
} else {
false
}
}
}
#[derive(Debug)]
pub enum TypeReq<'ctx> {
Any,
One(&'ctx Ty),
Many(Vec<&'ctx Ty>),
}
impl<'ctx> TypeReq<'ctx> {
pub fn is_any(&self) -> bool {
match *self {
TypeReq::Any => true,
_ => false,
}
}
pub fn matches(&self, ty: &Ty) -> bool {
match *self {
TypeReq::Any => true,
TypeReq::One(req) => are_types_matching(req, ty),
TypeReq::Many(ref reqs) => reqs.iter().any(|&req| are_types_matching(req, ty)),
}
}
}
impl<'ctx> Default for TypeReq<'ctx> {
fn default() -> TypeReq<'ctx> {
TypeReq::Any
}
}
fn are_types_matching(a: &Ty, b: &Ty) -> bool {
match (a, b) {
(&Ty::Named(_, ia), &Ty::Named(_, ib)) => ia == ib,
(a, b) => a == b,
}
}
pub fn reduce_overloads(
ctx: &ScoreContext,
defs: &[Spanned<Def>],
req: &OverloadReq,
_span: Span,
) -> Result<Vec<Spanned<Def>>> {
debugln!("resolving overloaded {:?} with requirement {:?}", defs, req);
let filtered: Vec<_> = defs
.iter()
.enumerate()
.filter(|&(_, def)| match (def.value, req) {
(Def::Enum(..), &OverloadReq::Enum(..)) => true,
(Def::BuiltinOp(..), &OverloadReq::Subprog(..)) => true,
(Def::Subprog(..), &OverloadReq::Subprog(..)) => true,
_ => false,
})
.collect();
if filtered.is_empty() {
return Ok(vec![]);
}
let types = filtered
.iter()
.map(|&(i, def)| {
Ok((
i,
match def.value {
Def::Enum(id) => ctx.lazy_typeval(id)?,
Def::BuiltinOp(id) => ctx.lazy_typeval(id)?,
Def::Subprog(id) => ctx.lazy_typeval(id)?,
_ => unreachable!(),
},
))
})
.collect::<Vec<Result<_>>>()
.into_iter()
.collect::<Result<Vec<_>>>()?;
let matched = types
.into_iter()
.filter_map(|(i, ty)| if req.matches(ty) { Some(defs[i]) } else { None })
.collect();
Ok(matched)
}
pub fn resolve_overloads(
ctx: &ScoreContext,
defs: &[Spanned<Def>],
req: &OverloadReq,
span: Span,
) -> Result<Spanned<Def>> {
let reduced = reduce_overloads(ctx, defs, req, span)?;
if reduced.is_empty() {
ctx.emit(
DiagBuilder2::error("no overload applies").span(span), );
debugln!("available definitions: {:#?}", defs);
Err(())
} else if reduced.len() > 1 {
ctx.emit(
DiagBuilder2::error(format!("`{}` is ambiguous", span.extract())).span(span), );
debugln!("matching definitions: {:#?}", reduced);
Err(())
} else {
Ok(reduced.into_iter().next().unwrap())
}
}