forked from M-Labs/nac3
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
This commit is contained in:
parent
779288d685
commit
b6e4e68587
|
@ -463,6 +463,7 @@ dependencies = [
|
||||||
"num-bigint",
|
"num-bigint",
|
||||||
"num-traits",
|
"num-traits",
|
||||||
"rustpython-parser",
|
"rustpython-parser",
|
||||||
|
"thiserror",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -840,6 +841,26 @@ dependencies = [
|
||||||
"winapi",
|
"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]]
|
[[package]]
|
||||||
name = "thread_local"
|
name = "thread_local"
|
||||||
version = "1.0.1"
|
version = "1.0.1"
|
||||||
|
|
|
@ -7,6 +7,7 @@ edition = "2018"
|
||||||
[dependencies]
|
[dependencies]
|
||||||
num-bigint = "0.3"
|
num-bigint = "0.3"
|
||||||
num-traits = "0.2"
|
num-traits = "0.2"
|
||||||
|
thiserror = "1.0"
|
||||||
inkwell = { git = "https://github.com/TheDan64/inkwell", branch = "master", features = ["llvm10-0"] }
|
inkwell = { git = "https://github.com/TheDan64/inkwell", branch = "master", features = ["llvm10-0"] }
|
||||||
rustpython-parser = { git = "https://github.com/RustPython/RustPython", branch = "master" }
|
rustpython-parser = { git = "https://github.com/RustPython/RustPython", branch = "master" }
|
||||||
|
|
||||||
|
|
|
@ -705,7 +705,7 @@ mod test {
|
||||||
|
|
||||||
let ast = parse_expression("a == a == 1").unwrap();
|
let ast = parse_expression("a == a == 1").unwrap();
|
||||||
let result = infer_expr(&mut ctx, &ast);
|
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 ast = parse_expression("True > False").unwrap();
|
||||||
let result = infer_expr(&mut ctx, &ast);
|
let result = infer_expr(&mut ctx, &ast);
|
||||||
|
|
|
@ -1,6 +1,21 @@
|
||||||
use super::context::InferenceContext;
|
use super::context::InferenceContext;
|
||||||
use super::typedef::{TypeEnum::*, *};
|
use super::typedef::{TypeEnum::*, *};
|
||||||
use std::collections::HashMap;
|
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(
|
fn find_subst(
|
||||||
ctx: &InferenceContext,
|
ctx: &InferenceContext,
|
||||||
|
@ -8,8 +23,7 @@ fn find_subst(
|
||||||
sub: &mut HashMap<VariableId, Type>,
|
sub: &mut HashMap<VariableId, Type>,
|
||||||
mut a: Type,
|
mut a: Type,
|
||||||
mut b: Type,
|
mut b: Type,
|
||||||
) -> Result<(), String> {
|
) -> Result<(), SubstError> {
|
||||||
// TODO: fix error messages later
|
|
||||||
if let TypeVariable(id) = a.as_ref() {
|
if let TypeVariable(id) = a.as_ref() {
|
||||||
if let Some((assumption_id, t)) = valuation {
|
if let Some((assumption_id, t)) = valuation {
|
||||||
if assumption_id == id {
|
if assumption_id == id {
|
||||||
|
@ -33,14 +47,14 @@ fn find_subst(
|
||||||
return if id_a == id_b {
|
return if id_a == id_b {
|
||||||
Ok(())
|
Ok(())
|
||||||
} else {
|
} else {
|
||||||
Err("different variables".to_string())
|
Err(SubstError::DifferentSubstVar(*id_a, *id_b))
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
let v_a = ctx.get_variable_def(*id_a);
|
let v_a = ctx.get_variable_def(*id_a);
|
||||||
let v_b = ctx.get_variable_def(*id_b);
|
let v_b = ctx.get_variable_def(*id_b);
|
||||||
if !v_b.bound.is_empty() {
|
if !v_b.bound.is_empty() {
|
||||||
if v_a.bound.is_empty() {
|
if v_a.bound.is_empty() {
|
||||||
return Err("unbounded a".to_string());
|
return Err(SubstError::UnboundedTypeVar(*id_a, *id_b));
|
||||||
} else {
|
} else {
|
||||||
let diff: Vec<_> = v_a
|
let diff: Vec<_> = v_a
|
||||||
.bound
|
.bound
|
||||||
|
@ -48,7 +62,7 @@ fn find_subst(
|
||||||
.filter(|x| !v_b.bound.contains(x))
|
.filter(|x| !v_b.bound.contains(x))
|
||||||
.collect();
|
.collect();
|
||||||
if !diff.is_empty() {
|
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() {
|
if v_a.bound.len() == 1 && v_a.bound[0].as_ref() == b.as_ref() {
|
||||||
Ok(())
|
Ok(())
|
||||||
} else {
|
} else {
|
||||||
Err("different domain".to_string())
|
Err(SubstError::DifferentTypes(a.clone(), b.clone()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
(_, TypeVariable(id_b)) => {
|
(_, TypeVariable(id_b)) => {
|
||||||
|
@ -69,7 +83,7 @@ fn find_subst(
|
||||||
sub.insert(*id_b, a.clone());
|
sub.insert(*id_b, a.clone());
|
||||||
Ok(())
|
Ok(())
|
||||||
} else {
|
} else {
|
||||||
Err("different domain".to_string())
|
Err(SubstError::DifferentTypes(a.clone(), b.clone()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
(_, VirtualClassType(id_b)) => {
|
(_, VirtualClassType(id_b)) => {
|
||||||
|
@ -82,7 +96,7 @@ fn find_subst(
|
||||||
parents = [*id_a].to_vec();
|
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() {
|
while !parents.is_empty() {
|
||||||
|
@ -92,11 +106,11 @@ fn find_subst(
|
||||||
let c = ctx.get_class_def(parents.remove(0));
|
let c = ctx.get_class_def(parents.remove(0));
|
||||||
parents.extend_from_slice(&c.parents);
|
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)) => {
|
(ParametricType(id_a, param_a), ParametricType(id_b, param_b)) => {
|
||||||
if id_a != id_b || param_a.len() != param_b.len() {
|
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 {
|
} else {
|
||||||
for (x, y) in param_a.iter().zip(param_b.iter()) {
|
for (x, y) in param_a.iter().zip(param_b.iter()) {
|
||||||
find_subst(ctx, valuation, sub, x.clone(), y.clone())?;
|
find_subst(ctx, valuation, sub, x.clone(), y.clone())?;
|
||||||
|
@ -108,7 +122,7 @@ fn find_subst(
|
||||||
if a == b {
|
if a == b {
|
||||||
Ok(())
|
Ok(())
|
||||||
} else {
|
} 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());
|
return Err("incorrect parameter number".to_string());
|
||||||
}
|
}
|
||||||
for (a, b) in args.iter().zip(fun.args.iter()) {
|
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));
|
let result = fun.result.as_ref().map(|v| v.subst(&subst));
|
||||||
Ok(result.map(|result| {
|
Ok(result.map(|result| {
|
||||||
|
@ -204,6 +218,7 @@ mod tests {
|
||||||
super::{context::*, primitives::*},
|
super::{context::*, primitives::*},
|
||||||
*,
|
*,
|
||||||
};
|
};
|
||||||
|
use std::matches;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
fn get_inference_context(ctx: TopLevelContext) -> InferenceContext {
|
fn get_inference_context(ctx: TopLevelContext) -> InferenceContext {
|
||||||
|
@ -244,25 +259,25 @@ mod tests {
|
||||||
Ok(Some(ctx.get_primitive(FLOAT_TYPE)))
|
Ok(Some(ctx.get_primitive(FLOAT_TYPE)))
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(
|
assert!(matches!(
|
||||||
resolve_call(&ctx, None, "float", &[ctx.get_primitive(BOOL_TYPE)]),
|
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", &[]),
|
resolve_call(&ctx, None, "float", &[]),
|
||||||
Err("incorrect parameter number".to_string())
|
Err(..)
|
||||||
);
|
));
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
resolve_call(&ctx, None, "float", &[v1]),
|
resolve_call(&ctx, None, "float", &[v1]),
|
||||||
Ok(Some(ctx.get_primitive(FLOAT_TYPE)))
|
Ok(Some(ctx.get_primitive(FLOAT_TYPE)))
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(
|
assert!(matches!(
|
||||||
resolve_call(&ctx, None, "float", &[v2]),
|
resolve_call(&ctx, None, "float", &[v2]),
|
||||||
Err("different domain".to_string())
|
Err(..)
|
||||||
);
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -309,40 +324,40 @@ mod tests {
|
||||||
Ok(Some(int64.clone()))
|
Ok(Some(int64.clone()))
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(
|
assert!(matches!(
|
||||||
resolve_call(&ctx, Some(int32), "__add__", &[int64]),
|
resolve_call(&ctx, Some(int32), "__add__", &[int64]),
|
||||||
Err("not equal".to_string())
|
Err(..)
|
||||||
);
|
));
|
||||||
|
|
||||||
// with type variables
|
// with type variables
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
resolve_call(&ctx, Some(v1.clone()), "__add__", &[v1.clone()]),
|
resolve_call(&ctx, Some(v1.clone()), "__add__", &[v1.clone()]),
|
||||||
Ok(Some(v1.clone()))
|
Ok(Some(v1.clone()))
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert!(matches!(
|
||||||
resolve_call(&ctx, Some(v0.clone()), "__add__", &[v2.clone()]),
|
resolve_call(&ctx, Some(v0.clone()), "__add__", &[v2.clone()]),
|
||||||
Err("unbounded type var".to_string())
|
Err(..)
|
||||||
);
|
));
|
||||||
assert_eq!(
|
assert!(matches!(
|
||||||
resolve_call(&ctx, Some(v1.clone()), "__add__", &[v0]),
|
resolve_call(&ctx, Some(v1.clone()), "__add__", &[v0]),
|
||||||
Err("different domain".to_string())
|
Err(..)
|
||||||
);
|
));
|
||||||
assert_eq!(
|
assert!(matches!(
|
||||||
resolve_call(&ctx, Some(v1.clone()), "__add__", &[v2]),
|
resolve_call(&ctx, Some(v1.clone()), "__add__", &[v2]),
|
||||||
Err("different domain".to_string())
|
Err(..)
|
||||||
);
|
));
|
||||||
assert_eq!(
|
assert!(matches!(
|
||||||
resolve_call(&ctx, Some(v1.clone()), "__add__", &[v3.clone()]),
|
resolve_call(&ctx, Some(v1.clone()), "__add__", &[v3.clone()]),
|
||||||
Err("different domain".to_string())
|
Err(..)
|
||||||
);
|
));
|
||||||
assert_eq!(
|
assert!(matches!(
|
||||||
resolve_call(&ctx, Some(v3.clone()), "__add__", &[v1]),
|
resolve_call(&ctx, Some(v3.clone()), "__add__", &[v1]),
|
||||||
Err("no such function".to_string())
|
Err(..)
|
||||||
);
|
));
|
||||||
assert_eq!(
|
assert!(matches!(
|
||||||
resolve_call(&ctx, Some(v3.clone()), "__add__", &[v3]),
|
resolve_call(&ctx, Some(v3.clone()), "__add__", &[v3]),
|
||||||
Err("no such function".to_string())
|
Err(..)
|
||||||
);
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -394,10 +409,10 @@ mod tests {
|
||||||
resolve_call(&ctx, None, "foo", &[v2.clone(), v2.clone(), v3.clone()]),
|
resolve_call(&ctx, None, "foo", &[v2.clone(), v2.clone(), v3.clone()]),
|
||||||
Ok(Some(v2.clone()))
|
Ok(Some(v2.clone()))
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert!(matches!(
|
||||||
resolve_call(&ctx, None, "foo", &[v2.clone(), v3.clone(), v3.clone()]),
|
resolve_call(&ctx, None, "foo", &[v2.clone(), v3.clone(), v3.clone()]),
|
||||||
Err("different variables".to_string())
|
Err(..)
|
||||||
);
|
));
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
resolve_call(
|
resolve_call(
|
||||||
|
@ -417,15 +432,15 @@ mod tests {
|
||||||
),
|
),
|
||||||
Ok(Some(v2.clone()))
|
Ok(Some(v2.clone()))
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert!(matches!(
|
||||||
resolve_call(
|
resolve_call(
|
||||||
&ctx,
|
&ctx,
|
||||||
None,
|
None,
|
||||||
"foo1",
|
"foo1",
|
||||||
&[ParametricType(TUPLE_TYPE, vec![v2, v3.clone(), v3]).into()]
|
&[ParametricType(TUPLE_TYPE, vec![v2, v3.clone(), v3]).into()]
|
||||||
),
|
),
|
||||||
Err("different variables".to_string())
|
Err(..)
|
||||||
);
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -479,15 +494,15 @@ mod tests {
|
||||||
),
|
),
|
||||||
Ok(None)
|
Ok(None)
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert!(matches!(
|
||||||
resolve_call(
|
resolve_call(
|
||||||
&ctx,
|
&ctx,
|
||||||
Some(ParametricType(LIST_TYPE, vec![v0]).into()),
|
Some(ParametricType(LIST_TYPE, vec![v0]).into()),
|
||||||
"append",
|
"append",
|
||||||
&[v1]
|
&[v1]
|
||||||
),
|
),
|
||||||
Err("different variables".to_string())
|
Err(..)
|
||||||
);
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -561,10 +576,10 @@ mod tests {
|
||||||
Ok(None)
|
Ok(None)
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(
|
assert!(matches!(
|
||||||
resolve_call(&ctx, None, "foo", &[ClassType(bar).into()]),
|
resolve_call(&ctx, None, "foo", &[ClassType(bar).into()]),
|
||||||
Err("not subtype".to_string())
|
Err(..)
|
||||||
);
|
));
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
resolve_call(&ctx, None, "foo1", &[ClassType(foo1).into()]),
|
resolve_call(&ctx, None, "foo1", &[ClassType(foo1).into()]),
|
||||||
|
@ -576,10 +591,10 @@ mod tests {
|
||||||
Ok(None)
|
Ok(None)
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(
|
assert!(matches!(
|
||||||
resolve_call(&ctx, None, "foo1", &[ClassType(foo).into()]),
|
resolve_call(&ctx, None, "foo1", &[ClassType(foo).into()]),
|
||||||
Err("not subtype".to_string())
|
Err(..)
|
||||||
);
|
));
|
||||||
|
|
||||||
// virtual class substitution
|
// virtual class substitution
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
|
@ -594,9 +609,9 @@ mod tests {
|
||||||
resolve_call(&ctx, None, "foo", &[VirtualClassType(foo2).into()]),
|
resolve_call(&ctx, None, "foo", &[VirtualClassType(foo2).into()]),
|
||||||
Ok(None)
|
Ok(None)
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert!(matches!(
|
||||||
resolve_call(&ctx, None, "foo", &[VirtualClassType(bar).into()]),
|
resolve_call(&ctx, None, "foo", &[VirtualClassType(bar).into()]),
|
||||||
Err("not subtype".to_string())
|
Err(..)
|
||||||
);
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue