use std::collections::VecDeque; use std::str::FromStr; use polytype::*; #[test] fn test_context_is_send() { let context: Context<&'static str> = Context::default(); let _: &(dyn Send + Sync) = &context; } #[test] fn test_tp_macro() { assert_eq!(tp!(bool), Type::Constructed("bool", vec![])); assert_eq!( tp!(list(tp!(bool))), Type::Constructed("list", vec![Type::Constructed("bool", vec![])]), ); assert_eq!( tp!(list(tp!(tuple(tp!(bool), tp!(int))))), Type::Constructed( "list", vec![Type::Constructed( "tuple", vec![ Type::Constructed("bool", vec![]), Type::Constructed("int", vec![]), ], )], ), ); assert_eq!( tp!(list( tp!(unusually_large_identifier_requiring_wrap), tp!(unusually_large_identifier_requiring_wrap), )), Type::Constructed( "list", vec![ Type::Constructed("unusually_large_identifier_requiring_wrap", vec![]), Type::Constructed("unusually_large_identifier_requiring_wrap", vec![]), ], ), ); assert_eq!(tp!(0), Type::Variable(0)); assert_eq!( tp!(hashmap(tp!(str), tp!(@arrow[tp!(int), tp!(0), tp!(bool)]))), Type::Constructed( "hashmap", vec![ Type::Constructed("str", vec![]), Type::arrow( Type::Constructed("int", vec![]), Type::arrow(Type::Variable(0), Type::Constructed("bool", vec![])), ), ], ) ); // arrows assert_eq!(tp!(@arrow[Type::Variable(0)]), Type::Variable(0)); let arg = Type::Variable(0); let ret = Type::Variable(1); let t = tp!(@arrow[arg, ret]); assert_eq!(t, tp!(@arrow[Type::Variable(0), Type::Variable(1)])); assert_eq!( tp!(@arrow[Type::Variable(0), Type::Variable(1), Type::Variable(2)]), Type::arrow( Type::Variable(0), Type::arrow(Type::Variable(1), Type::Variable(2)), ) ); assert_eq!( tp!(@arrow[ Type::Variable(0), Type::Variable(1), Type::Variable(2), Type::Variable(3), ]), Type::arrow( Type::Variable(0), Type::arrow( Type::Variable(1), Type::arrow(Type::Variable(2), Type::Variable(3)), ), ) ); } #[test] fn test_ptp_macro() { assert_eq!( ptp!(bool), TypeScheme::Monotype(Type::Constructed("bool", vec![])) ); assert_eq!( ptp!(list(tp!(bool))), TypeScheme::Monotype(Type::Constructed( "list", vec![Type::Constructed("bool", vec![])], )) ); assert_eq!( ptp!(0; 0), TypeScheme::Polytype { variable: 0, body: Box::new(TypeScheme::Monotype(Type::Variable(0))), } ); assert_eq!( ptp!(0; @arrow[tp!(0), tp!(0)]), TypeScheme::Polytype { variable: 0, body: Box::new(TypeScheme::Monotype(Type::Constructed( "→", vec![Type::Variable(0), Type::Variable(0)], ))), } ); } #[test] fn test_arrow_methods() { let t0 = Type::Variable(0); let t1 = Type::Constructed("int", vec![]); let t2 = Type::arrow(t0.clone(), t1.clone()); let ta1 = Type::arrow(t2.clone(), Type::arrow(t1.clone(), t0.clone())); let ta2 = tp!(@arrow[t2.clone(), t1.clone(), t0.clone()]); let ta3 = tp!(@arrow[tp!(@arrow[tp!(0), tp!(int)]), tp!(int), tp!(0)]); assert_eq!(ta3, ta1); assert_eq!(ta3, ta2); let t = tp!(@arrow[tp!(@arrow[tp!(0), tp!(int)]), tp!(int), tp!(0)]); assert_eq!( t.args(), Some(VecDeque::from(vec![ &tp!(@arrow[tp!(0), tp!(int)]), &tp!(int), ])), ); assert_eq!(t.returns(), Some(&tp!(0))); } #[test] fn test_tp_from_vecdeque() { let mut tps = VecDeque::new(); tps.push_back(Type::Variable(0)); let tp: Type = tps.clone().into(); assert_eq!(tp, Type::Variable(0)); tps.push_back(Type::Variable(1)); let tp: Type = tps.clone().into(); assert_eq!(tp, Type::arrow(Type::Variable(0), Type::Variable(1))); tps.push_back(Type::Variable(2)); let tp: Type = tps.clone().into(); assert_eq!( tp, Type::arrow( Type::Variable(0), Type::arrow(Type::Variable(1), Type::Variable(2)) ) ); tps.push_back(Type::Variable(3)); let tp: Type = tps.clone().into(); assert_eq!( tp, Type::arrow( Type::Variable(0), Type::arrow( Type::Variable(1), Type::arrow(Type::Variable(2), Type::Variable(3)) ) ) ); } #[test] fn test_tp_from_vec() { let mut tps = Vec::new(); tps.push(Type::Variable(0)); let tp: Type = tps.clone().into(); assert_eq!(tp, Type::Variable(0)); tps.push(Type::Variable(1)); let tp: Type = tps.clone().into(); assert_eq!(tp, Type::arrow(Type::Variable(0), Type::Variable(1))); tps.push(Type::Variable(2)); let tp: Type = tps.clone().into(); assert_eq!( tp, Type::arrow( Type::Variable(0), Type::arrow(Type::Variable(1), Type::Variable(2)) ) ); tps.push(Type::Variable(3)); let tp: Type = tps.clone().into(); assert_eq!( tp, Type::arrow( Type::Variable(0), Type::arrow( Type::Variable(1), Type::arrow(Type::Variable(2), Type::Variable(3)) ) ) ); } #[test] fn test_unify_one_side_polymorphic() { let mut ctx = Context::default(); ctx.unify( &tp!(list(tp!(@arrow[tp!(int), tp!(bool)]))), &tp!(list(tp!(0))), ) .expect("one side polymorphic"); } #[test] fn test_unify_one_side_polymorphic_fail() { let mut ctx = Context::default(); ctx.unify(&tp!(@arrow[tp!(int), tp!(bool)]), &tp!(list(tp!(0)))) .expect_err("incompatible types"); } #[test] fn test_unify_both_sides_polymorphic() { let mut ctx = Context::default(); ctx.unify( &tp!(list(tp!(@arrow[tp!(int), tp!(0)]))), &tp!(list(tp!(@arrow[tp!(1), tp!(bool)]))), ) .expect("both sides polymorphic"); } #[test] fn test_unify_both_sides_polymorphic_occurs() { let mut ctx = Context::default(); ctx.unify(&tp!(0), &tp!(list(tp!(@arrow[tp!(0), tp!(bool)])))) .expect_err("circular polymorphic types"); } #[test] fn test_unify_nonstring_name() { #[derive(Debug, Clone, PartialEq, Eq)] struct N(u32); impl Name for N { fn arrow() -> Self { N(0) } } let ts = TypeScheme::Polytype { variable: 0, body: Box::new(TypeScheme::Monotype(Type::Constructed( N(3), vec![Type::Variable(0)], ))), }; let mut ctx = Context::default(); let t = ts.instantiate(&mut ctx); ctx.unify( &Type::Constructed( N(3), vec![Type::arrow( Type::Constructed(N(1), vec![]), Type::Constructed(N(2), vec![]), )], ), &t, ) .expect("nonstring one side polymorphic"); let mut ctx = Context::default(); let t = ts.instantiate(&mut ctx); ctx.unify( &Type::arrow( Type::Constructed(N(1), vec![]), Type::Constructed(N(2), vec![]), ), &t, ) .expect_err("nonstring incompatible types"); } #[test] fn test_merge_no_sacreds() { let mut ctx = Context::default(); let a = ctx.new_variable(); let b = ctx.new_variable(); let _ = ctx.new_variable(); ctx.unify(&Type::arrow(a, b), &tp!(@arrow[tp!(int), tp!(bool)])) .unwrap(); let mut ctx2 = Context::default(); let _ = ctx2.new_variable(); let pt = ptp!(0, 1; @arrow[tp!(0), tp!(1)]); let mut t = pt.instantiate(&mut ctx2); ctx2.extend(1, tp!(bool)); let mut last = ctx2.new_variable(); assert_eq!(t.apply(&ctx2).to_string(), "bool → t2"); let ctx_change = ctx.merge(ctx2, vec![]); ctx_change.reify_type(&mut t); assert_eq!(t.to_string(), "t4 → t5"); assert_eq!(t.apply(&ctx).to_string(), "bool → t5"); ctx_change.reify_type(&mut last); assert_eq!(last, tp!(6)); assert_eq!(ctx.new_variable(), tp!(7)); } #[test] fn test_merge_with_sacreds() { let mut ctx = Context::default(); let a = ctx.new_variable(); let b = ctx.new_variable(); let _ = ctx.new_variable(); ctx.unify(&Type::arrow(a, b), &tp!(@arrow[tp!(int), tp!(bool)])) .unwrap(); let mut ctx2 = Context::default(); let _ = ctx2.new_variable(); let pt = ptp!(0, 1; @arrow[tp!(0), tp!(1)]); let mut t = pt.instantiate(&mut ctx2); ctx2.extend(2, tp!(bool)); let mut last = ctx2.new_variable(); assert_eq!(t.apply(&ctx2).to_string(), "t1 → bool"); let ctx_change = ctx.merge(ctx2, vec![0, 1]); ctx_change.reify_type(&mut t); assert_eq!(t.to_string(), "t1 → t5"); assert_eq!(t.apply(&ctx).to_string(), "bool → bool"); ctx_change.reify_type(&mut last); assert_eq!(last, tp!(6)); assert_eq!(ctx.new_variable(), tp!(7)); } #[cfg(feature = "parser")] #[test] fn test_parse() { let t = tp!(int); assert_eq!(&t, &Type::from_str("int").expect("parse 1")); assert_eq!(t, Type::from_str(&t.to_string()).expect("parse 2")); let t = tp!(0); assert_eq!(&t, &Type::from_str("t0").expect("parse 3")); assert_eq!(t, Type::from_str(&t.to_string()).expect("parse 4")); let t = tp!(@arrow[tp!(int), tp!(int)]); assert_eq!(&t, &Type::from_str("int -> int").expect("parse 5")); assert_eq!(t, Type::from_str(&t.to_string()).expect("parse 6")); let t = tp!(list(tp!(@arrow[tp!(int), tp!(2)]))); assert_eq!(&t, &Type::from_str("list(int -> t2)").expect("parse 7")); assert_eq!(t, Type::from_str(&t.to_string()).expect("parse 8")); let t = tp!(hashmap(tp!(str), tp!(@arrow[tp!(int), tp!(0), tp!(bool)]))); assert_eq!( &t, &Type::from_str("hashmap(str, int -> t0 -> bool)").expect("parse 9") ); assert_eq!(t, Type::from_str(&t.to_string()).expect("parse 10")); let t = tp!(@arrow[ tp!(@arrow[tp!(1), tp!(0), tp!(1)]), tp!(1), tp!(list(tp!(0))), tp!(1), ]); assert_eq!( &t, &Type::from_str("(t1 → t0 → t1) → t1 → list(t0) → t1").expect("parse 11") ); assert_eq!(t, Type::from_str(&t.to_string()).expect("parse 12")); }