forked from M-Labs/nac3
178 lines
6.2 KiB
Rust
178 lines
6.2 KiB
Rust
|
use std::fmt::Display;
|
||
|
use std::collections::HashMap;
|
||
|
|
||
|
use crate::typecheck::typedef::TypeEnum;
|
||
|
|
||
|
use super::typedef::{Type, Unifier, RecordKey};
|
||
|
use nac3parser::ast::{Location, StrRef};
|
||
|
|
||
|
#[derive(Debug, Clone)]
|
||
|
pub enum TypeErrorKind {
|
||
|
TooManyArguments {
|
||
|
expected: usize,
|
||
|
got: usize,
|
||
|
},
|
||
|
MissingArgs(String),
|
||
|
UnknownArgName(StrRef),
|
||
|
IncorrectArgType {
|
||
|
name: StrRef,
|
||
|
expected: Type,
|
||
|
got: Type,
|
||
|
},
|
||
|
FieldUnificationError {
|
||
|
field: RecordKey,
|
||
|
types: (Type, Type),
|
||
|
loc: (Option<Location>, Option<Location>),
|
||
|
},
|
||
|
IncompatibleRange(Type, Vec<Type>),
|
||
|
IncompatibleTypes(Type, Type),
|
||
|
MutationError(RecordKey, Type),
|
||
|
NoSuchField(RecordKey, Type),
|
||
|
TupleIndexOutOfBounds {
|
||
|
index: i32,
|
||
|
len: i32,
|
||
|
},
|
||
|
RequiresTypeAnn,
|
||
|
PolymorphicFunctionPointer,
|
||
|
}
|
||
|
|
||
|
#[derive(Debug, Clone)]
|
||
|
pub struct TypeError {
|
||
|
pub kind: TypeErrorKind,
|
||
|
pub loc: Option<Location>,
|
||
|
}
|
||
|
|
||
|
impl TypeError {
|
||
|
pub fn new(kind: TypeErrorKind, loc: Option<Location>) -> TypeError {
|
||
|
TypeError { kind, loc }
|
||
|
}
|
||
|
|
||
|
pub fn at(mut self, loc: Option<Location>) -> TypeError {
|
||
|
self.loc = self.loc.or(loc);
|
||
|
self
|
||
|
}
|
||
|
|
||
|
pub fn to_display(self, unifier: &Unifier) -> DisplayTypeError {
|
||
|
DisplayTypeError {
|
||
|
err: self,
|
||
|
unifier
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
pub struct DisplayTypeError<'a> {
|
||
|
pub err: TypeError,
|
||
|
pub unifier: &'a Unifier
|
||
|
}
|
||
|
|
||
|
fn loc_to_str(loc: Option<Location>) -> String {
|
||
|
match loc {
|
||
|
Some(loc) => format!("(in {})", loc),
|
||
|
None => "".to_string(),
|
||
|
}
|
||
|
}
|
||
|
|
||
|
impl<'a> Display for DisplayTypeError<'a> {
|
||
|
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||
|
use TypeErrorKind::*;
|
||
|
let mut notes = Some(HashMap::new());
|
||
|
match &self.err.kind {
|
||
|
TooManyArguments { expected, got } => {
|
||
|
write!(f, "Too many arguments. Expected {} but got {}", expected, got)
|
||
|
}
|
||
|
MissingArgs(args) => {
|
||
|
write!(f, "Missing arguments: {}", args)
|
||
|
}
|
||
|
UnknownArgName(name) => {
|
||
|
write!(f, "Unknown argument name: {}", name)
|
||
|
}
|
||
|
IncorrectArgType {
|
||
|
name,
|
||
|
expected,
|
||
|
got,
|
||
|
} => {
|
||
|
let expected = self.unifier.stringify_with_notes(*expected, &mut notes);
|
||
|
let got = self.unifier.stringify_with_notes(*got, &mut notes);
|
||
|
write!(
|
||
|
f,
|
||
|
"Incorrect argument type for {}. Expected {}, but got {}",
|
||
|
name, expected, got
|
||
|
)
|
||
|
},
|
||
|
FieldUnificationError { field, types, loc } => {
|
||
|
let lhs = self.unifier.stringify_with_notes(types.0, &mut notes);
|
||
|
let rhs = self.unifier.stringify_with_notes(types.1, &mut notes);
|
||
|
write!(
|
||
|
f,
|
||
|
"Unable to unify field {}: Got types {}{} and {}{}",
|
||
|
field, lhs, loc_to_str(loc.0), rhs, loc_to_str(loc.1)
|
||
|
)
|
||
|
}
|
||
|
IncompatibleRange(t, ts) => {
|
||
|
let t = self.unifier.stringify_with_notes(*t, &mut notes);
|
||
|
let ts = ts.iter().map(|t| self.unifier.stringify_with_notes(*t, &mut notes)).collect::<Vec<_>>();
|
||
|
write!(f, "Expected any one of these types: {}, but got {}", ts.join(", "), t)
|
||
|
}
|
||
|
IncompatibleTypes(t1, t2) => {
|
||
|
let type1 = self.unifier.get_ty_immutable(*t1);
|
||
|
let type2 = self.unifier.get_ty_immutable(*t2);
|
||
|
match (&*type1, &*type2) {
|
||
|
(TypeEnum::TCall(calls), _) => {
|
||
|
let loc = self.unifier.calls[calls[0].0].loc;
|
||
|
let result = write!(f, "{} is not callable", self.unifier.stringify_with_notes(*t2, &mut notes));
|
||
|
if let Some(loc) = loc {
|
||
|
result?;
|
||
|
write!(f, " (in {})", loc)?;
|
||
|
return Ok(())
|
||
|
}
|
||
|
result
|
||
|
}
|
||
|
(TypeEnum::TTuple { ty: ty1 }, TypeEnum::TTuple { ty: ty2 }) if ty1.len() != ty2.len() => {
|
||
|
let t1 = self.unifier.stringify_with_notes(*t1, &mut notes);
|
||
|
let t2 = self.unifier.stringify_with_notes(*t2, &mut notes);
|
||
|
write!(f, "Tuple length mismatch: got {} and {}", t1, t2)
|
||
|
}
|
||
|
_ => {
|
||
|
let t1 = self.unifier.stringify_with_notes(*t1, &mut notes);
|
||
|
let t2 = self.unifier.stringify_with_notes(*t2, &mut notes);
|
||
|
write!(f, "Incompatible types: {} and {}", t1, t2)
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
MutationError(name, t) => {
|
||
|
if let TypeEnum::TTuple { .. } = &*self.unifier.get_ty_immutable(*t) {
|
||
|
write!(f, "Cannot assign to an element of a tuple")
|
||
|
} else {
|
||
|
let t = self.unifier.stringify_with_notes(*t, &mut notes);
|
||
|
write!(f, "Cannot assign to field {} of {}, which is immutable", name, t)
|
||
|
}
|
||
|
}
|
||
|
NoSuchField(name, t) => {
|
||
|
let t = self.unifier.stringify_with_notes(*t, &mut notes);
|
||
|
write!(f, "`{}::{}` field does not exist", t, name)
|
||
|
}
|
||
|
TupleIndexOutOfBounds { index, len } => {
|
||
|
write!(f, "Tuple index out of bounds. Got {} but tuple has only {} elements", index, len)
|
||
|
}
|
||
|
RequiresTypeAnn => {
|
||
|
write!(f, "Unable to infer virtual object type: Type annotation required")
|
||
|
}
|
||
|
PolymorphicFunctionPointer => {
|
||
|
write!(f, "Polymorphic function pointers is not supported")
|
||
|
}
|
||
|
}?;
|
||
|
if let Some(loc) = self.err.loc {
|
||
|
write!(f, " at {}", loc)?;
|
||
|
}
|
||
|
let notes = notes.unwrap();
|
||
|
if !notes.is_empty() {
|
||
|
write!(f, "\n\nNotes:")?;
|
||
|
for line in notes.values() {
|
||
|
write!(f, "\n {}", line)?;
|
||
|
}
|
||
|
}
|
||
|
Ok(())
|
||
|
}
|
||
|
}
|
||
|
|