started adding error types

* converted to string to avoid breaking interface, would be fixed later
* tests would not check error messages now, as messages are not
  finalized
signature
pca006132 2021-01-11 10:40:32 +08:00
parent 779288d685
commit b6e4e68587
4 changed files with 98 additions and 61 deletions

21
Cargo.lock generated
View File

@ -463,6 +463,7 @@ dependencies = [
"num-bigint",
"num-traits",
"rustpython-parser",
"thiserror",
]
[[package]]
@ -840,6 +841,26 @@ dependencies = [
"winapi",
]
[[package]]
name = "thiserror"
version = "1.0.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "76cc616c6abf8c8928e2fdcc0dbfab37175edd8fb49a4641066ad1364fdab146"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "1.0.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9be73a2caec27583d0046ef3796c3794f868a5bc813db689eed00c7631275cd1"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "thread_local"
version = "1.0.1"

View File

@ -7,6 +7,7 @@ edition = "2018"
[dependencies]
num-bigint = "0.3"
num-traits = "0.2"
thiserror = "1.0"
inkwell = { git = "https://github.com/TheDan64/inkwell", branch = "master", features = ["llvm10-0"] }
rustpython-parser = { git = "https://github.com/RustPython/RustPython", branch = "master" }

View File

@ -705,7 +705,7 @@ mod test {
let ast = parse_expression("a == a == 1").unwrap();
let result = infer_expr(&mut ctx, &ast);
assert_eq!(result, Err("not equal".into()));
assert_eq!(result, Err("different types".into()));
let ast = parse_expression("True > False").unwrap();
let result = infer_expr(&mut ctx, &ast);

View File

@ -1,6 +1,21 @@
use super::context::InferenceContext;
use super::typedef::{TypeEnum::*, *};
use std::collections::HashMap;
use thiserror::Error;
#[derive(Error, Debug)]
enum SubstError {
#[error("different type variables after substitution")]
DifferentSubstVar(VariableId, VariableId),
#[error("cannot substitute unbounded type variable into bounded one")]
UnboundedTypeVar(VariableId, VariableId),
#[error("incompatible bound for type variables")]
IncompatibleBound(VariableId, VariableId),
#[error("only subtype of virtual class can be substituted into virtual class type")]
NotVirtualClassSubtype(Type, ClassId),
#[error("different types")]
DifferentTypes(Type, Type),
}
fn find_subst(
ctx: &InferenceContext,
@ -8,8 +23,7 @@ fn find_subst(
sub: &mut HashMap<VariableId, Type>,
mut a: Type,
mut b: Type,
) -> Result<(), String> {
// TODO: fix error messages later
) -> Result<(), SubstError> {
if let TypeVariable(id) = a.as_ref() {
if let Some((assumption_id, t)) = valuation {
if assumption_id == id {
@ -33,14 +47,14 @@ fn find_subst(
return if id_a == id_b {
Ok(())
} else {
Err("different variables".to_string())
Err(SubstError::DifferentSubstVar(*id_a, *id_b))
};
}
let v_a = ctx.get_variable_def(*id_a);
let v_b = ctx.get_variable_def(*id_b);
if !v_b.bound.is_empty() {
if v_a.bound.is_empty() {
return Err("unbounded a".to_string());
return Err(SubstError::UnboundedTypeVar(*id_a, *id_b));
} else {
let diff: Vec<_> = v_a
.bound
@ -48,7 +62,7 @@ fn find_subst(
.filter(|x| !v_b.bound.contains(x))
.collect();
if !diff.is_empty() {
return Err("different domain".to_string());
return Err(SubstError::IncompatibleBound(*id_a, *id_b));
}
}
}
@ -60,7 +74,7 @@ fn find_subst(
if v_a.bound.len() == 1 && v_a.bound[0].as_ref() == b.as_ref() {
Ok(())
} else {
Err("different domain".to_string())
Err(SubstError::DifferentTypes(a.clone(), b.clone()))
}
}
(_, TypeVariable(id_b)) => {
@ -69,7 +83,7 @@ fn find_subst(
sub.insert(*id_b, a.clone());
Ok(())
} else {
Err("different domain".to_string())
Err(SubstError::DifferentTypes(a.clone(), b.clone()))
}
}
(_, VirtualClassType(id_b)) => {
@ -82,7 +96,7 @@ fn find_subst(
parents = [*id_a].to_vec();
}
_ => {
return Err("cannot substitute non-class type into virtual class".to_string());
return Err(SubstError::NotVirtualClassSubtype(a.clone(), *id_b));
}
};
while !parents.is_empty() {
@ -92,11 +106,11 @@ fn find_subst(
let c = ctx.get_class_def(parents.remove(0));
parents.extend_from_slice(&c.parents);
}
Err("not subtype".to_string())
Err(SubstError::NotVirtualClassSubtype(a.clone(), *id_b))
}
(ParametricType(id_a, param_a), ParametricType(id_b, param_b)) => {
if id_a != id_b || param_a.len() != param_b.len() {
Err("different parametric types".to_string())
Err(SubstError::DifferentTypes(a.clone(), b.clone()))
} else {
for (x, y) in param_a.iter().zip(param_b.iter()) {
find_subst(ctx, valuation, sub, x.clone(), y.clone())?;
@ -108,7 +122,7 @@ fn find_subst(
if a == b {
Ok(())
} else {
Err("not equal".to_string())
Err(SubstError::DifferentTypes(a.clone(), b.clone()))
}
}
}
@ -177,7 +191,7 @@ fn resolve_call_rec(
return Err("incorrect parameter number".to_string());
}
for (a, b) in args.iter().zip(fun.args.iter()) {
find_subst(ctx, valuation, &mut subst, a.clone(), b.clone())?;
find_subst(ctx, valuation, &mut subst, a.clone(), b.clone()).map_err(|v| v.to_string())?;
}
let result = fun.result.as_ref().map(|v| v.subst(&subst));
Ok(result.map(|result| {
@ -204,6 +218,7 @@ mod tests {
super::{context::*, primitives::*},
*,
};
use std::matches;
use std::rc::Rc;
fn get_inference_context(ctx: TopLevelContext) -> InferenceContext {
@ -244,25 +259,25 @@ mod tests {
Ok(Some(ctx.get_primitive(FLOAT_TYPE)))
);
assert_eq!(
assert!(matches!(
resolve_call(&ctx, None, "float", &[ctx.get_primitive(BOOL_TYPE)]),
Err("different domain".to_string())
);
Err(..)
));
assert_eq!(
assert!(matches!(
resolve_call(&ctx, None, "float", &[]),
Err("incorrect parameter number".to_string())
);
Err(..)
));
assert_eq!(
resolve_call(&ctx, None, "float", &[v1]),
Ok(Some(ctx.get_primitive(FLOAT_TYPE)))
);
assert_eq!(
assert!(matches!(
resolve_call(&ctx, None, "float", &[v2]),
Err("different domain".to_string())
);
Err(..)
));
}
#[test]
@ -309,40 +324,40 @@ mod tests {
Ok(Some(int64.clone()))
);
assert_eq!(
assert!(matches!(
resolve_call(&ctx, Some(int32), "__add__", &[int64]),
Err("not equal".to_string())
);
Err(..)
));
// with type variables
assert_eq!(
resolve_call(&ctx, Some(v1.clone()), "__add__", &[v1.clone()]),
Ok(Some(v1.clone()))
);
assert_eq!(
assert!(matches!(
resolve_call(&ctx, Some(v0.clone()), "__add__", &[v2.clone()]),
Err("unbounded type var".to_string())
);
assert_eq!(
Err(..)
));
assert!(matches!(
resolve_call(&ctx, Some(v1.clone()), "__add__", &[v0]),
Err("different domain".to_string())
);
assert_eq!(
Err(..)
));
assert!(matches!(
resolve_call(&ctx, Some(v1.clone()), "__add__", &[v2]),
Err("different domain".to_string())
);
assert_eq!(
Err(..)
));
assert!(matches!(
resolve_call(&ctx, Some(v1.clone()), "__add__", &[v3.clone()]),
Err("different domain".to_string())
);
assert_eq!(
Err(..)
));
assert!(matches!(
resolve_call(&ctx, Some(v3.clone()), "__add__", &[v1]),
Err("no such function".to_string())
);
assert_eq!(
Err(..)
));
assert!(matches!(
resolve_call(&ctx, Some(v3.clone()), "__add__", &[v3]),
Err("no such function".to_string())
);
Err(..)
));
}
#[test]
@ -394,10 +409,10 @@ mod tests {
resolve_call(&ctx, None, "foo", &[v2.clone(), v2.clone(), v3.clone()]),
Ok(Some(v2.clone()))
);
assert_eq!(
assert!(matches!(
resolve_call(&ctx, None, "foo", &[v2.clone(), v3.clone(), v3.clone()]),
Err("different variables".to_string())
);
Err(..)
));
assert_eq!(
resolve_call(
@ -417,15 +432,15 @@ mod tests {
),
Ok(Some(v2.clone()))
);
assert_eq!(
assert!(matches!(
resolve_call(
&ctx,
None,
"foo1",
&[ParametricType(TUPLE_TYPE, vec![v2, v3.clone(), v3]).into()]
),
Err("different variables".to_string())
);
Err(..)
));
}
#[test]
@ -479,15 +494,15 @@ mod tests {
),
Ok(None)
);
assert_eq!(
assert!(matches!(
resolve_call(
&ctx,
Some(ParametricType(LIST_TYPE, vec![v0]).into()),
"append",
&[v1]
),
Err("different variables".to_string())
);
Err(..)
));
}
#[test]
@ -561,10 +576,10 @@ mod tests {
Ok(None)
);
assert_eq!(
assert!(matches!(
resolve_call(&ctx, None, "foo", &[ClassType(bar).into()]),
Err("not subtype".to_string())
);
Err(..)
));
assert_eq!(
resolve_call(&ctx, None, "foo1", &[ClassType(foo1).into()]),
@ -576,10 +591,10 @@ mod tests {
Ok(None)
);
assert_eq!(
assert!(matches!(
resolve_call(&ctx, None, "foo1", &[ClassType(foo).into()]),
Err("not subtype".to_string())
);
Err(..)
));
// virtual class substitution
assert_eq!(
@ -594,9 +609,9 @@ mod tests {
resolve_call(&ctx, None, "foo", &[VirtualClassType(foo2).into()]),
Ok(None)
);
assert_eq!(
assert!(matches!(
resolve_call(&ctx, None, "foo", &[VirtualClassType(bar).into()]),
Err("not subtype".to_string())
);
Err(..)
));
}
}