use super::analyze::*;
use super::formal_region::FormalRegion;
use super::region::*;
use super::semantic::TypeCheck;
use crate::ast::search::clear_references;
use crate::ast::*;
use crate::data::*;
impl<'a> AnalyzeContext<'a> {
#[allow(clippy::too_many_arguments)]
pub fn resolve_overloaded_with_target_type(
&self,
scope: &Scope<'_>,
overloaded: OverloadedName,
target_type: Option<&TypeEnt>,
pos: &SrcPos,
designator: &Designator,
reference: &mut Reference,
parameters: &mut ParametersMut<'_>,
diagnostics: &mut dyn DiagnosticHandler,
) -> FatalResult<TypeCheck> {
let mut good = Vec::with_capacity(overloaded.len());
let mut bad = Vec::with_capacity(overloaded.len());
let mut uncertain = false;
for name in overloaded.entities() {
if name.is_function() != target_type.is_some() {
continue;
}
if let Some(operator_len) = parameters.operator_len() {
if name.formals().len() != operator_len {
continue;
}
}
if name.is_generic() {
self.analyze_parameters(scope, parameters, diagnostics)?;
return Ok(TypeCheck::Unknown);
}
let is_correct = if name.signature().match_return_type(target_type) {
self.analyze_parameters_with_formal_region(
pos,
name.formals(),
scope,
parameters,
&mut NullDiagnostics,
)?
} else {
TypeCheck::NotOk
};
parameters.clear_references();
match is_correct {
TypeCheck::Ok => good.push(name),
TypeCheck::NotOk => bad.push(name),
TypeCheck::Unknown => uncertain = true,
}
}
#[allow(clippy::if_same_then_else)]
if good.len() > 1 {
let mut diagnostic =
Diagnostic::error(pos, format!("Ambiguous use of '{}'", designator));
diagnostic.add_subprogram_candidates("Migth be", &mut good);
diagnostics.push(diagnostic);
self.analyze_parameters(scope, parameters, diagnostics)?;
Ok(TypeCheck::Unknown)
} else if uncertain {
self.analyze_parameters(scope, parameters, diagnostics)?;
Ok(TypeCheck::Unknown)
} else if let &[ent] = good.as_slice() {
reference.set_unique_reference(ent.inner());
self.analyze_parameters_with_formal_region(
pos,
ent.formals(),
scope,
parameters,
diagnostics,
)?;
Ok(TypeCheck::Ok)
} else if let &[ent] = bad.as_slice() {
reference.set_unique_reference(ent.inner());
if parameters.is_empty() && ent.formals().is_empty() {
if let Some(target_type) = target_type {
diagnostics.error(
pos,
format!("'{}' does not match {}", designator, target_type.describe()),
)
} else {
let mut diagnostic = Diagnostic::error(
pos,
format!("Could not resolve {}", designator.describe()),
);
diagnostic.add_subprogram_candidates("Does not match", &mut [ent]);
diagnostics.push(diagnostic)
};
} else {
self.analyze_parameters_with_formal_region(
pos,
ent.formals(),
scope,
parameters,
diagnostics,
)?;
}
Ok(TypeCheck::NotOk)
} else {
let mut diagnostic =
Diagnostic::error(pos, format!("Could not resolve {}", designator.describe()));
diagnostic.add_subprogram_candidates("Does not match", &mut bad);
diagnostics.push(diagnostic);
self.analyze_parameters(scope, parameters, diagnostics)?;
Ok(TypeCheck::NotOk)
}
}
fn analyze_parameters_with_formal_region(
&self,
error_pos: &SrcPos, formal_region: &FormalRegion,
scope: &Scope<'_>,
parameters: &mut ParametersMut<'_>,
diagnostics: &mut dyn DiagnosticHandler,
) -> FatalResult<TypeCheck> {
match parameters {
ParametersMut::AssociationList(elems) => self.analyze_assoc_elems_with_formal_region(
error_pos,
formal_region,
scope,
elems,
diagnostics,
),
ParametersMut::Binary(lexpr, rexpr) => {
if let Some((left_formal, right_formal)) = formal_region.binary() {
let check = self.analyze_expression_with_target_type(
scope,
left_formal.type_mark(),
&lexpr.pos,
&mut lexpr.item,
diagnostics,
)?;
if check != TypeCheck::Ok {
self.analyze_expression_pos(
scope,
&rexpr.pos,
&mut rexpr.item,
diagnostics,
)?;
Ok(check)
} else {
Ok(self.analyze_expression_with_target_type(
scope,
right_formal.type_mark(),
&rexpr.pos,
&mut rexpr.item,
diagnostics,
)?)
}
} else {
self.analyze_expression_pos(scope, &lexpr.pos, &mut lexpr.item, diagnostics)?;
self.analyze_expression_pos(scope, &rexpr.pos, &mut rexpr.item, diagnostics)?;
Ok(TypeCheck::NotOk)
}
}
ParametersMut::Unary(expr) => {
if let Some(formal) = formal_region.nth(0) {
self.analyze_expression_with_target_type(
scope,
formal.type_mark(),
&expr.pos,
&mut expr.item,
diagnostics,
)
} else {
self.analyze_expression_pos(scope, &expr.pos, &mut expr.item, diagnostics)?;
Ok(TypeCheck::NotOk)
}
}
}
}
fn analyze_parameters(
&self,
scope: &Scope<'_>,
parameters: &mut ParametersMut<'_>,
diagnostics: &mut dyn DiagnosticHandler,
) -> FatalResult {
match parameters {
ParametersMut::AssociationList(elems) => {
self.analyze_assoc_elems(scope, elems, diagnostics)
}
ParametersMut::Binary(lexpr, rexpr) => {
self.analyze_expression_pos(scope, &lexpr.pos, &mut lexpr.item, diagnostics)?;
self.analyze_expression_pos(scope, &rexpr.pos, &mut rexpr.item, diagnostics)
}
ParametersMut::Unary(expr) => {
self.analyze_expression_pos(scope, &expr.pos, &mut expr.item, diagnostics)
}
}
}
}
pub enum ParametersMut<'a> {
AssociationList(&'a mut [AssociationElement]),
Binary(&'a mut WithPos<Expression>, &'a mut WithPos<Expression>),
Unary(&'a mut WithPos<Expression>),
}
impl ParametersMut<'_> {
fn clear_references(&mut self) {
match self {
ParametersMut::AssociationList(list) => {
for elem in list.iter_mut() {
clear_references(elem);
}
}
ParametersMut::Binary(lexpr, rexpr) => {
clear_references(*lexpr);
clear_references(*rexpr);
}
ParametersMut::Unary(expr) => {
clear_references(*expr);
}
}
}
fn is_empty(&self) -> bool {
match self {
ParametersMut::AssociationList(list) => list.is_empty(),
_ => false,
}
}
fn operator_len(&self) -> Option<usize> {
match self {
ParametersMut::AssociationList(..) => None,
ParametersMut::Unary(..) => Some(1),
ParametersMut::Binary(..) => Some(2),
}
}
}