plum
git clone https://git.pyrossh.dev/plum
A statically typed, imperative programming language inspired by rust, python
plum-checker/tests/monomorphize_tests.rs
| 3d6f280 | 1 | #![allow(non_snake_case)] |
| 1b220d2 | 2 | use plum_checker::monomorphize::*; |
| 1b220d2 | 3 | use plum_checker::types::PlumType; |
| 1b220d2 | 4 | use plum_core::ast; |
| 1b220d2 | 5 | |
| 1b220d2 | 6 | #[test] |
| 3d6f280 | 7 | fn isGenericParamNameAcceptsSingleUppercaseLettersOnly() { |
| 3d6f280 | 8 | assert!(isGenericParamName("T")); |
| 3d6f280 | 9 | assert!(isGenericParamName("W")); |
| 3d6f280 | 10 | assert!(!isGenericParamName("Int")); |
| 3d6f280 | 11 | assert!(!isGenericParamName("TU")); |
| 3d6f280 | 12 | assert!(!isGenericParamName("a")); |
| 3d6f280 | 13 | assert!(!isGenericParamName("")); |
| 1b220d2 | 14 | } |
| 1b220d2 | 15 | |
| 1b220d2 | 16 | #[test] |
| 3d6f280 | 17 | fn classGenericParamsReadsDeclaredGenericsList() { |
| 1b220d2 | 18 | let c = ast::Class { |
| 1b220d2 | 19 | name: "Box".to_string(), |
| 1b220d2 | 20 | implements: vec![], |
| 30ae945 | 21 | generics: vec![ast::GenericParam { name: "T".to_string(), bounds: vec![] }], |
| 30ae945 | 22 | fields: vec![ast::Field { name: "value".to_string(), ty: ast::Type { name: "T".to_string(), generics: vec![] } }], |
| 1b220d2 | 23 | }; |
| 3d6f280 | 24 | assert_eq!(classGenericParams(&c), vec!["T".to_string()]); |
| 1b220d2 | 25 | } |
| 1b220d2 | 26 | |
| 1b220d2 | 27 | #[test] |
| 3d6f280 | 28 | fn fnGenericParamsDetectsImplicitUppercaseLetterTypesInOrder() { |
| 1b220d2 | 29 | let f = ast::Fn { |
| 0000000 | 30 | is_extern: false, |
| 1b220d2 | 31 | name: "pair".to_string(), |
| 1b220d2 | 32 | type_param: None, |
| 1b220d2 | 33 | params: vec![ |
| 30ae945 | 34 | ast::Param { name: "first".to_string(), ty: ast::ParamType::Type(ast::Type { name: "T".to_string(), generics: vec![] }), default: None }, |
| 30ae945 | 35 | ast::Param { name: "second".to_string(), ty: ast::ParamType::Type(ast::Type { name: "U".to_string(), generics: vec![] }), default: None }, |
| 1b220d2 | 36 | ], |
| 30ae945 | 37 | returns: Some(ast::Type { name: "Bool".to_string(), generics: vec![] }), |
| 1b220d2 | 38 | body: ast::FnBody::Block(ast::Block { stmts: vec![] }), |
| 1b220d2 | 39 | }; |
| 3d6f280 | 40 | assert_eq!(fnGenericParams(&f), vec!["T".to_string(), "U".to_string()]); |
| 1b220d2 | 41 | } |
| 1b220d2 | 42 | |
| 1b220d2 | 43 | #[test] |
| 3d6f280 | 44 | fn enumGenericParamsDetectsImplicitUppercaseLetterVariantFields() { |
| 1b220d2 | 45 | let e = ast::Enum { |
| 1b220d2 | 46 | name: "Option".to_string(), |
| 4fda634 | 47 | params: vec![], |
| 1b220d2 | 48 | variants: vec![ |
| 4fda634 | 49 | ast::EnumVariant { name: "Some".to_string(), fields: vec!["T".to_string()], values: vec![] }, |
| 4fda634 | 50 | ast::EnumVariant { name: "None".to_string(), fields: vec![], values: vec![] }, |
| 1b220d2 | 51 | ], |
| 1b220d2 | 52 | }; |
| 3d6f280 | 53 | assert_eq!(enumGenericParams(&e), vec!["T".to_string()]); |
| 1b220d2 | 54 | } |
| 1b220d2 | 55 | |
| 1b220d2 | 56 | #[test] |
| 3d6f280 | 57 | fn mangleJoinsBaseNameAndTypeArgs() { |
| 1b220d2 | 58 | assert_eq!(mangle("Box", &[PlumType::TInt]), "Box$Int"); |
| 1b220d2 | 59 | assert_eq!(mangle("Pair", &[PlumType::TInt, PlumType::TStr]), "Pair$Int$Str"); |
| 1b220d2 | 60 | assert_eq!(mangle("Green", &[]), "Green"); |
| 1b220d2 | 61 | } |
| 1b220d2 | 62 | |
| 1b220d2 | 63 | #[test] |
| 3d6f280 | 64 | fn specializeClassSubstitutesGenericFieldTypesAndClearsGenericsList() { |
| 1b220d2 | 65 | let c = ast::Class { |
| 1b220d2 | 66 | name: "Box".to_string(), |
| 1b220d2 | 67 | implements: vec![], |
| 30ae945 | 68 | generics: vec![ast::GenericParam { name: "T".to_string(), bounds: vec![] }], |
| 30ae945 | 69 | fields: vec![ast::Field { name: "value".to_string(), ty: ast::Type { name: "T".to_string(), generics: vec![] } }], |
| 1b220d2 | 70 | }; |
| 1b220d2 | 71 | let mut bindings = std::collections::BTreeMap::new(); |
| 30ae945 | 72 | bindings.insert("T".to_string(), PlumType::TInt); |
| 3d6f280 | 73 | let specialized = specializeClass(&c, &Substitution(bindings), "Box$Int"); |
| 1b220d2 | 74 | assert_eq!(specialized.name, "Box$Int"); |
| 1b220d2 | 75 | assert!(specialized.generics.is_empty()); |
| 1b220d2 | 76 | assert_eq!(specialized.fields[0].ty.name, "Int"); |
| 1b220d2 | 77 | } |
| 1b220d2 | 78 | |
| 1b220d2 | 79 | #[test] |
| 3d6f280 | 80 | fn specializeFnSubstitutesGenericParamAndReturnTypes() { |
| 1b220d2 | 81 | let f = ast::Fn { |
| 0000000 | 82 | is_extern: false, |
| 1b220d2 | 83 | name: "wrap".to_string(), |
| 1b220d2 | 84 | type_param: None, |
| 30ae945 | 85 | params: vec![ast::Param { name: "value".to_string(), ty: ast::ParamType::Type(ast::Type { name: "T".to_string(), generics: vec![] }), default: None }], |
| 30ae945 | 86 | returns: Some(ast::Type { name: "T".to_string(), generics: vec![] }), |
| 1b220d2 | 87 | body: ast::FnBody::Expr(ast::Expr::Var("value".to_string())), |
| 1b220d2 | 88 | }; |
| 1b220d2 | 89 | let mut bindings = std::collections::BTreeMap::new(); |
| 30ae945 | 90 | bindings.insert("T".to_string(), PlumType::TInt); |
| 3d6f280 | 91 | let specialized = specializeFn(&f, &Substitution(bindings), "wrap$Int", None); |
| 1b220d2 | 92 | assert_eq!(specialized.name, "wrap$Int"); |
| 1b220d2 | 93 | match &specialized.params[0].ty { |
| 1b220d2 | 94 | ast::ParamType::Type(t) => assert_eq!(t.name, "Int"), |
| 1b220d2 | 95 | _ => panic!("expected ParamType::Type"), |
| 1b220d2 | 96 | } |
| 1b220d2 | 97 | assert_eq!(specialized.returns.unwrap().name, "Int"); |
| 1b220d2 | 98 | } |
| 1b220d2 | 99 | |
| 1b220d2 | 100 | #[test] |
| 3d6f280 | 101 | fn specializeFnSetsNewReceiverForAMethod() { |
| 1b220d2 | 102 | let f = ast::Fn { |
| 0000000 | 103 | is_extern: false, |
| 1b220d2 | 104 | name: "getValue".to_string(), |
| 1b220d2 | 105 | type_param: Some("Box".to_string()), |
| 1b220d2 | 106 | params: vec![], |
| 30ae945 | 107 | returns: Some(ast::Type { name: "T".to_string(), generics: vec![] }), |
| 1b220d2 | 108 | body: ast::FnBody::Expr(ast::Expr::Self_), |
| 1b220d2 | 109 | }; |
| 1b220d2 | 110 | let mut bindings = std::collections::BTreeMap::new(); |
| 30ae945 | 111 | bindings.insert("T".to_string(), PlumType::TStr); |
| 3d6f280 | 112 | let specialized = specializeFn(&f, &Substitution(bindings), "getValue", Some("Box$Str".to_string())); |
| 1b220d2 | 113 | assert_eq!(specialized.name, "getValue"); |
| 1b220d2 | 114 | assert_eq!(specialized.type_param, Some("Box$Str".to_string())); |
| 1b220d2 | 115 | assert_eq!(specialized.returns.unwrap().name, "Str"); |
| 1b220d2 | 116 | } |
| 1b220d2 | 117 | |
| 1b220d2 | 118 | #[test] |
| 3d6f280 | 119 | fn specializeEnumSubstitutesGenericVariantFieldNames() { |
| 1b220d2 | 120 | let e = ast::Enum { |
| 1b220d2 | 121 | name: "Option".to_string(), |
| 4fda634 | 122 | params: vec![], |
| 1b220d2 | 123 | variants: vec![ |
| 4fda634 | 124 | ast::EnumVariant { name: "Some".to_string(), fields: vec!["T".to_string()], values: vec![] }, |
| 4fda634 | 125 | ast::EnumVariant { name: "None".to_string(), fields: vec![], values: vec![] }, |
| 1b220d2 | 126 | ], |
| 1b220d2 | 127 | }; |
| 1b220d2 | 128 | let mut bindings = std::collections::BTreeMap::new(); |
| 30ae945 | 129 | bindings.insert("T".to_string(), PlumType::TInt); |
| 3d6f280 | 130 | let specialized = specializeEnum(&e, &Substitution(bindings), "Option$Int"); |
| 1b220d2 | 131 | assert_eq!(specialized.name, "Option$Int"); |
| 1b220d2 | 132 | assert_eq!(specialized.variants[0].fields, vec!["Int".to_string()]); |
| 1b220d2 | 133 | assert!(specialized.variants[1].fields.is_empty()); |
| 1b220d2 | 134 | } |