From b6e4e68587a1dcb2b7517787ef7ec54f04911c08 Mon Sep 17 00:00:00 2001 From: pca006132 Date: Mon, 11 Jan 2021 10:40:32 +0800 Subject: [PATCH] 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 --- Cargo.lock | 21 +++ nac3core/Cargo.toml | 1 + .../src/type_check/expression_inference.rs | 2 +- nac3core/src/type_check/inference_core.rs | 135 ++++++++++-------- 4 files changed, 98 insertions(+), 61 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 95b40fd4..b10960ed 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -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" diff --git a/nac3core/Cargo.toml b/nac3core/Cargo.toml index ae548be5..7e753ca4 100644 --- a/nac3core/Cargo.toml +++ b/nac3core/Cargo.toml @@ -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" } diff --git a/nac3core/src/type_check/expression_inference.rs b/nac3core/src/type_check/expression_inference.rs index 54994360..42b7c470 100644 --- a/nac3core/src/type_check/expression_inference.rs +++ b/nac3core/src/type_check/expression_inference.rs @@ -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); diff --git a/nac3core/src/type_check/inference_core.rs b/nac3core/src/type_check/inference_core.rs index ec8f403b..dc205a5a 100644 --- a/nac3core/src/type_check/inference_core.rs +++ b/nac3core/src/type_check/inference_core.rs @@ -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, 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(..) + )); } }