use crate::*;
use formally::support::*;
use std::{collections::HashMap, iter::zip};
pub trait TypeCheck {
fn type_check(&self) -> Result<Sort>;
}
impl TypeCheck for Term {
fn type_check(&self) -> Result<Sort> {
if let Some(sort) = &*self.0.sort.lock().unwrap() {
return Ok(sort.clone());
}
let sort = self.kind().type_check()?;
*self.0.sort.lock().unwrap() = Some(sort.clone());
Ok(sort)
}
}
impl TypeCheck for TermKind {
fn type_check(&self) -> Result<Sort> {
let sort = match self {
TermKind::Constant(cnst) => cnst.type_check()?,
TermKind::Atom(atom) => atom.type_check()?,
TermKind::Quantified(quant) => quant.type_check()?,
TermKind::Let(let_) => let_.type_check()?,
};
Ok(sort)
}
}
impl TypeCheck for Constant {
fn type_check(&self) -> Result<Sort> {
match self {
Constant::Integer { .. } => Ok(theories::Ints::Int()),
Constant::Rational { .. } => Ok(theories::Reals::Real()),
}
}
}
impl BoundRef {
fn type_check(&self, arguments: &[Term]) -> Result<Sort> {
let domain = self.domain(arguments.len());
if domain.len() != arguments.len() {
error!(
self.function.span(),
"applied {} arguments to a function of {} parameters",
arguments.len(),
domain.len(),
);
return Err(DiagnosticEmitted);
}
#[allow(clippy::mutable_key_type)]
let mut matches = HashMap::new();
for (sort, arg) in zip(domain, arguments) {
let argsort = Sort::of(arg)?;
if !sort.matches_with(&argsort, &mut matches) {
error!(
arg.span(),
"argument of sort `{}` given to parameter of sort `{}`", argsort, sort
);
return Err(DiagnosticEmitted);
}
}
let range = self.function.range().instantiate(&matches)?;
Ok(range)
}
}
impl BoundRef {
pub(crate) fn domain(&self, nargs: usize) -> Vec<Sort> {
let domain = self.function.domain();
let Function::Primitive(prim) = &self.function else {
return domain;
};
match prim.domain() {
[first, second, ..] if prim.associativity().is_some() => {
if *first == *second {
std::iter::repeat_n(first.clone(), nargs).collect()
} else {
domain
}
}
_ => domain,
}
}
}
impl TypeCheck for Atom {
fn type_check(&self) -> Result<Sort> {
match &self.head {
FunctionRef::Bound(bound) => bound.type_check(&self.arguments),
FunctionRef::Unbound(_) => {
internal!(
self.head.span(),
"unresolved symbol `{}` during type checking",
self.head
);
Err(DiagnosticEmitted)
}
}
}
}
impl TypeCheck for Quantified {
fn type_check(&self) -> Result<Sort> {
self.body.type_check()
}
}
impl TypeCheck for Let {
fn type_check(&self) -> Result<Sort> {
self.body.type_check()
}
}
impl TypeCheck for Declared {
fn type_check(&self) -> Result<Sort> {
Ok(self.range.clone())
}
}
impl TypeCheck for Defined {
fn type_check(&self) -> Result<Sort> {
Ok(self.range.clone())
}
}
impl TypeCheck for Sort {
fn type_check(&self) -> Result<Sort> {
match &self.head {
SortHead::Bound(f) => {
for (param, arg) in zip(f.domain().iter(), self.arguments.iter()) {
let argsort = arg.type_check()?;
if *param != argsort {
error!(
None,
"unresolved symbol `{}` during type checking", self.head
);
}
}
Ok(Sort::sort())
}
SortHead::Unbound(_) => {
internal!(
None,
"unresolved symbol `{}` during type checking",
self.head
);
Err(DiagnosticEmitted)
}
}
}
}
impl TypeCheck for SortArgument {
fn type_check(&self) -> Result<Sort> {
match self {
SortArgument::Value(_) => Ok(theories::Ints::Int()),
SortArgument::Sort(s) => s.type_check(),
}
}
}