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:
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-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"

View File

@ -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" }

View File

@ -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);

View File

@ -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(..)
); ));
} }
} }