use std::fmt;
use thiserror::Error;
use super::ty::*;
use crate::lexer::Span;
pub type TypeResult<T> = Result<T, TypeError>;
#[derive(Debug, Clone)]
pub struct TypeErrorWithSpan {
pub error: TypeError,
pub span: Span,
pub help: Option<String>,
pub notes: Vec<String>,
}
impl TypeErrorWithSpan {
pub fn new(error: TypeError, span: Span) -> Self {
Self {
error,
span,
help: None,
notes: Vec::new(),
}
}
pub fn with_help(mut self, help: impl Into<String>) -> Self {
self.help = Some(help.into());
self
}
pub fn with_note(mut self, note: impl Into<String>) -> Self {
self.notes.push(note.into());
self
}
}
impl fmt::Display for TypeErrorWithSpan {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.error)?;
if let Some(help) = &self.help {
write!(f, "\n help: {}", help)?;
}
for note in &self.notes {
write!(f, "\n note: {}", note)?;
}
Ok(())
}
}
impl std::error::Error for TypeErrorWithSpan {}
#[derive(Debug, Clone, Error)]
pub enum TypeError {
#[error("type mismatch: expected `{expected}`, found `{found}`")]
TypeMismatch { expected: Ty, found: Ty },
#[error("infinite type: `{var}` occurs in `{ty}`")]
InfiniteType { var: TyVarId, ty: Ty },
#[error("mutability mismatch: expected `{expected:?}`, found `{found:?}`")]
MutabilityMismatch {
expected: Mutability,
found: Mutability,
},
#[error("array length mismatch: expected `{expected}`, found `{found}`")]
ArrayLengthMismatch { expected: usize, found: usize },
#[error("expected {expected} arguments, found {found}")]
ArityMismatch { expected: usize, found: usize },
#[error("unsafety mismatch")]
UnsafetyMismatch,
#[error("lifetime mismatch: expected `{expected}`, found `{found}`")]
LifetimeMismatch {
expected: super::ty::Lifetime,
found: super::ty::Lifetime,
},
#[error("ABI mismatch: expected `{expected}`, found `{found}`")]
AbiMismatch {
expected: std::sync::Arc<str>,
found: std::sync::Arc<str>,
},
#[error("undefined variable: `{name}`")]
UndefinedVariable { name: String },
#[error("undefined type: `{name}`")]
UndefinedType { name: String },
#[error("undefined function: `{name}`")]
UndefinedFunction { name: String },
#[error("type `{ty}` has no field `{field}`")]
UndefinedField { ty: Ty, field: String },
#[error("type `{ty}` has no method `{method}`")]
UndefinedMethod { ty: Ty, method: String },
#[error("enum `{enum_name}` has no variant `{variant}`")]
UndefinedVariant { enum_name: String, variant: String },
#[error("type `{ty}` is not callable")]
NotCallable { ty: Ty },
#[error("type `{ty}` cannot be indexed")]
NotIndexable { ty: Ty },
#[error("type `{ty}` cannot be dereferenced")]
NotDereferenceable { ty: Ty },
#[error("cannot borrow `{variable}` as mutable because it is already borrowed")]
AlreadyBorrowed { variable: String },
#[error("cannot borrow `{variable}` as mutable more than once at a time")]
DoubleMutableBorrow { variable: String },
#[error("use of moved value: `{variable}`")]
UseAfterMove { variable: String },
#[error("cannot return reference to local variable `{variable}`")]
ReferenceEscapesScope { variable: String },
#[error("cannot apply binary operator `{op}` to types `{left}` and `{right}`")]
InvalidBinaryOp { op: String, left: Ty, right: Ty },
#[error("cannot apply unary operator `{op}` to type `{ty}`")]
InvalidUnaryOp { op: String, ty: Ty },
#[error("invalid assignment target")]
InvalidAssignTarget,
#[error("cannot assign to immutable variable `{name}`")]
ImmutableAssignment { name: String },
#[error("pattern type mismatch: expected `{expected}`, found `{found}`")]
PatternMismatch { expected: Ty, found: Ty },
#[error("refutable pattern in irrefutable position")]
RefutablePattern,
#[error("non-exhaustive patterns")]
NonExhaustivePatterns,
#[error("non-exhaustive match: missing variants {}", missing_variants.join(", "))]
NonExhaustiveMatch { missing_variants: Vec<String> },
#[error("`break` outside of loop")]
BreakOutsideLoop,
#[error("`continue` outside of loop")]
ContinueOutsideLoop,
#[error("`return` outside of function")]
ReturnOutsideFunction,
#[error("function returns `{found}` but expected `{expected}`")]
ReturnTypeMismatch { expected: Ty, found: Ty },
#[error("the trait bound `{ty}: {trait_id:?}` is not satisfied")]
TraitNotImplemented {
ty: Ty,
trait_id: super::traits::TraitId,
},
#[error("multiple implementations of trait `{trait_name}` for type `{ty}`")]
AmbiguousImpl { trait_name: String, ty: Ty },
#[error("associated type `{name}` not found in trait `{trait_name}`")]
AssocTypeNotFound { name: String, trait_name: String },
#[error("associated type `{assoc_name}` not defined")]
AssociatedTypeNotDefined { assoc_name: String },
#[error("internal error: {0}")]
InternalError(String),
#[error("expected {expected} type arguments, found {found}")]
WrongTypeArgCount { expected: usize, found: usize },
#[error("type `{ty}` does not satisfy bound `{bound}`")]
BoundNotSatisfied { ty: Ty, bound: String },
#[error("duplicate definition: `{name}`")]
DuplicateDefinition { name: String },
#[error("type annotations needed")]
TypeAnnotationNeeded,
#[error("unsafe operation outside of `unsafe` block")]
UnsafeOutsideUnsafe,
#[error("internal type error: {0}")]
Internal(String),
#[error("unknown effect `{name}`")]
UnknownEffect { name: String },
#[error("function `{func_name}` performs effect `{effect_name}` but does not declare it")]
UnhandledEffect {
func_name: String,
effect_name: String,
},
#[error("function performs undeclared effect `{effect_name}`")]
UndeclaredEffect {
func_name: String,
effect_name: String,
declared_effects: Vec<String>,
},
#[error("unknown operation `{operation}` in effect `{effect_name}`")]
UnknownEffectOperation {
effect_name: String,
operation: String,
},
#[error("handler for effect `{effect_name}` is missing operation `{operation}`")]
MissingHandlerClause {
effect_name: String,
operation: String,
},
}
impl TypeError {
pub fn is_fatal(&self) -> bool {
matches!(self, TypeError::Internal(_))
}
pub fn suggestion(&self) -> Option<String> {
match self {
TypeError::TypeMismatch { expected, .. } => {
Some(format!("consider adding a type annotation: `: {}`", expected))
}
TypeError::ImmutableAssignment { name } => {
Some(format!("consider making `{}` mutable: `let mut {}`", name, name))
}
TypeError::TypeAnnotationNeeded => {
Some("consider adding a type annotation".to_string())
}
TypeError::UnsafeOutsideUnsafe => {
Some("consider wrapping in an `unsafe` block".to_string())
}
TypeError::UnknownEffect { name } => {
Some(format!(
"define the effect:\n effect {} {{\n fn operation_name(params) -> ReturnType,\n }}",
name
))
}
TypeError::UnhandledEffect { func_name, effect_name } => {
Some(format!(
"either add `~ {}` to the function signature:\n fn {}() ~ {} {{ ... }}\n\nor handle the effect with a handler:\n handle {{ ... }} with {{\n {}.operation(args) => |resume| {{\n // handle the operation\n resume(())\n }},\n }}",
effect_name, func_name, effect_name, effect_name
))
}
TypeError::UndeclaredEffect { func_name: _, effect_name, declared_effects } => {
let declared = declared_effects.join(", ");
Some(format!(
"add `{}` to the effect annotations: ~ {}, {}",
effect_name, declared, effect_name
))
}
TypeError::UnknownEffectOperation { effect_name, operation } => {
Some(format!(
"effect `{}` does not define operation `{}`; check available operations",
effect_name, operation
))
}
TypeError::MissingHandlerClause { effect_name, operation } => {
Some(format!(
"add a handler clause for `{}`:\n {}.{}(params) => |resume| {{\n // handle the {} operation\n resume(())\n }},",
operation, effect_name, operation, operation
))
}
TypeError::NonExhaustiveMatch { missing_variants } => {
Some(format!(
"add arms for the missing variants: {}, or add a wildcard `_` arm",
missing_variants.join(", ")
))
}
_ => None,
}
}
}