diff --git a/nac3type/src/inference.rs b/nac3type/src/inference.rs index c7f89ce58e..f37c383570 100644 --- a/nac3type/src/inference.rs +++ b/nac3type/src/inference.rs @@ -16,15 +16,24 @@ fn find_subst( } } + let mut substituted = false; if let TypeVariable(id) = b.as_ref() { if let Some(c) = sub.get(&id) { b = c.clone(); + substituted = true; } } match (a.as_ref(), b.as_ref()) { (BotType, _) => Ok(()), (TypeVariable(id_a), TypeVariable(id_b)) => { + if substituted { + return if id_a == id_b { + Ok(()) + } else { + Err("different variables".to_string()) + }; + } let v_a = ctx.get_variable(*id_a); let v_b = ctx.get_variable(*id_b); if v_b.bound.len() > 0 { @@ -224,6 +233,17 @@ mod tests { Err("different domain".to_string()) ); + assert_eq!( + resolve_call( + &ctx, + None, + "float", + create_tuple(vec![]), + &mut assumptions + ), + Err("different parametric types".to_string()) + ); + let v1 = ctx.add_variable(VarDef { name: "V1", bound: vec![ @@ -269,6 +289,10 @@ mod tests { let mut ctx = basic_ctx(); let mut assumptions = HashMap::new(); + let v0 = Rc::new(TypeVariable(ctx.add_variable(VarDef { + name: "V0", + bound: vec![], + }))); let v1 = Rc::new(TypeVariable(ctx.add_variable(VarDef { name: "V1", bound: vec![ @@ -340,6 +364,26 @@ mod tests { ), Ok(Some(v1.clone())) ); + assert_eq!( + resolve_call( + &ctx, + Some(v0.clone()), + "__add__", + create_tuple(vec![v2.clone()]), + &mut assumptions + ), + Err("unbounded type var".to_string()) + ); + assert_eq!( + resolve_call( + &ctx, + Some(v1.clone()), + "__add__", + create_tuple(vec![v0.clone()]), + &mut assumptions + ), + Err("different domain".to_string()) + ); assert_eq!( resolve_call( &ctx, @@ -350,6 +394,16 @@ mod tests { ), Err("different domain".to_string()) ); + assert_eq!( + resolve_call( + &ctx, + Some(v1.clone()), + "__add__", + create_tuple(vec![v3.clone()]), + &mut assumptions + ), + Err("different domain".to_string()) + ); assert_eq!( resolve_call( &ctx, @@ -370,7 +424,66 @@ mod tests { ), Err("no such function".to_string()) ); + } + #[test] + fn test_multi_generic() { + let mut ctx = basic_ctx(); + let mut assumptions = HashMap::new(); + let v0 = Rc::new(TypeVariable(ctx.add_variable(VarDef { + name: "V0", + bound: vec![], + }))); + let v1 = Rc::new(TypeVariable(ctx.add_variable(VarDef { + name: "V1", + bound: vec![], + }))); + let v2 = Rc::new(TypeVariable(ctx.add_variable(VarDef { + name: "V2", + bound: vec![], + }))); + let v3 = Rc::new(TypeVariable(ctx.add_variable(VarDef { + name: "V3", + bound: vec![], + }))); + + ctx.add_fn( + "foo", + FnDef { + args: create_tuple(vec![v0.clone(), v0.clone(), v1.clone()]), + result: Some(v0.clone()), + }, + ); + + assert_eq!( + resolve_call( + &ctx, + None, + "foo", + create_tuple(vec![v2.clone(), v2.clone(), v2.clone()]), + &mut assumptions + ), + Ok(Some(v2.clone())) + ); + assert_eq!( + resolve_call( + &ctx, + None, + "foo", + create_tuple(vec![v2.clone(), v2.clone(), v3.clone()]), + &mut assumptions + ), + Ok(Some(v2.clone())) + ); + assert_eq!( + resolve_call( + &ctx, + None, + "foo", + create_tuple(vec![v2.clone(), v3.clone(), v3.clone()]), + &mut assumptions + ), + Err("different variables".to_string()) + ); } } -