plum
git clone https://git.pyrossh.dev/plum
A statically typed, imperative programming language inspired by rust, python
30ae945
— Peter John
2026-07-24T11:36:01+05:30
test(plum-checker): migrate test fixtures to bracket generics syntax
plum-checker/tests/checker_tests.rs
CHANGED
|
@@ -213,7 +213,7 @@ isNone(o: Option) -> Bool = o == None
|
|
|
213
213
|
fn variant_construction_checks_arg_count_and_types() {
|
|
214
214
|
let src = "\
|
|
215
215
|
enum Option =
|
|
216
|
-
| Some
|
|
216
|
+
| Some[Int]
|
|
217
217
|
| None
|
|
218
218
|
|
|
219
219
|
makeSome(v: Int) -> Option =
|
|
@@ -243,8 +243,8 @@ bad() -> Option =
|
|
|
243
243
|
fn variant_construction_wrong_arg_count_is_error() {
|
|
244
244
|
let src = "\
|
|
245
245
|
enum Shape =
|
|
246
|
-
| Rect
|
|
246
|
+
| Rect[Float, Float]
|
|
247
|
-
| Circle
|
|
247
|
+
| Circle[Float]
|
|
248
248
|
|
|
249
249
|
bad() -> Shape =
|
|
250
250
|
Rect(1.0)
|
|
@@ -258,8 +258,8 @@ bad() -> Shape =
|
|
|
258
258
|
fn constructor_pattern_binds_fields_to_declared_types() {
|
|
259
259
|
let src = "\
|
|
260
260
|
enum Shape =
|
|
261
|
-
| Rect
|
|
261
|
+
| Rect[Float, Float]
|
|
262
|
-
| Circle
|
|
262
|
+
| Circle[Float]
|
|
263
263
|
|
|
264
264
|
area(s: Shape) -> Float =
|
|
265
265
|
match s
|
|
@@ -277,8 +277,8 @@ area(s: Shape) -> Float =
|
|
|
277
277
|
fn constructor_pattern_wrong_field_count_is_error() {
|
|
278
278
|
let src = "\
|
|
279
279
|
enum Shape =
|
|
280
|
-
| Rect
|
|
280
|
+
| Rect[Float, Float]
|
|
281
|
-
| Circle
|
|
281
|
+
| Circle[Float]
|
|
282
282
|
|
|
283
283
|
bad(s: Shape) -> Float =
|
|
284
284
|
match s
|
|
@@ -319,8 +319,8 @@ enum Animal =
|
|
|
319
319
|
#[test]
|
|
320
320
|
fn generic_class_instantiated_at_two_concrete_types_type_checks() {
|
|
321
321
|
let src = "\
|
|
322
|
-
type Box
|
|
322
|
+
type Box[T] =
|
|
323
|
-
value:
|
|
323
|
+
value: T
|
|
324
324
|
|
|
325
325
|
makeIntBox() -> Box =
|
|
326
326
|
Box(value: 5)
|
|
@@ -336,7 +336,7 @@ makeStrBox() -> Box =
|
|
|
336
336
|
#[test]
|
|
337
337
|
fn generic_function_called_with_different_concrete_types_per_site_type_checks() {
|
|
338
338
|
let src = "\
|
|
339
|
-
wrap(value:
|
|
339
|
+
wrap(value: T) -> Bool =
|
|
340
340
|
True
|
|
341
341
|
|
|
342
342
|
useInt() -> Bool =
|
|
@@ -353,7 +353,7 @@ useStr() -> Bool =
|
|
|
353
353
|
#[test]
|
|
354
354
|
fn generic_function_with_two_independent_type_params_type_checks() {
|
|
355
355
|
let src = "\
|
|
356
|
-
pair(first:
|
|
356
|
+
pair(first: T, second: U) -> Bool =
|
|
357
357
|
True
|
|
358
358
|
|
|
359
359
|
use() -> Bool =
|
|
@@ -372,7 +372,7 @@ fn generic_function_with_internally_inconsistent_body_is_rejected_after_speciali
|
|
|
372
372
|
// behavior — rewriting `wrong$Str`'s `-> Int` to `-> Str` — would make the
|
|
373
373
|
// whole program type-check, masking the real `expected Int, found Str` error.
|
|
374
374
|
let src = "\
|
|
375
|
-
wrong(x:
|
|
375
|
+
wrong(x: T) -> Int =
|
|
376
376
|
\"hello\"
|
|
377
377
|
|
|
378
378
|
useIt() -> Str =
|
|
@@ -392,7 +392,7 @@ fn generic_enum_single_instantiation_type_checks() {
|
|
|
392
392
|
// matched, must resolve end-to-end via check_source.
|
|
393
393
|
let src = "\
|
|
394
394
|
enum Option =
|
|
395
|
-
| Some
|
|
395
|
+
| Some[T]
|
|
396
396
|
| None
|
|
397
397
|
|
|
398
398
|
get() -> Int =
|
|
@@ -435,7 +435,7 @@ fn generic_enum_multiple_instantiations_coexist_and_type_check() {
|
|
|
435
435
|
// behavior this task adds (previously this was a documented, rejected limitation).
|
|
436
436
|
let src = "\
|
|
437
437
|
enum Option =
|
|
438
|
-
| Some
|
|
438
|
+
| Some[T]
|
|
439
439
|
| None
|
|
440
440
|
|
|
441
441
|
useInt() -> Int =
|
|
@@ -473,10 +473,10 @@ useStr() -> Str =
|
|
|
473
473
|
#[test]
|
|
474
474
|
fn generic_method_on_generic_class_type_checks() {
|
|
475
475
|
let src = "\
|
|
476
|
-
type Box
|
|
476
|
+
type Box[T] =
|
|
477
|
-
value:
|
|
477
|
+
value: T
|
|
478
478
|
|
|
479
|
-
getValue<Box>() ->
|
|
479
|
+
getValue<Box>() -> T =
|
|
480
480
|
self.value
|
|
481
481
|
|
|
482
482
|
use() -> Int =
|
|
@@ -499,10 +499,10 @@ fn unbounded_recursive_generic_instantiation_is_a_clear_error() {
|
|
|
499
499
|
// declaration, which is never itself a call site and so never reaches the
|
|
500
500
|
// worklist at all) and must fail with a clear, bounded error rather than hang.
|
|
501
501
|
let src = "\
|
|
502
|
-
type Box
|
|
502
|
+
type Box[T] =
|
|
503
|
-
value:
|
|
503
|
+
value: T
|
|
504
504
|
|
|
505
|
-
recurse(v:
|
|
505
|
+
recurse(v: T) -> Int =
|
|
506
506
|
b = Box(value: v)
|
|
507
507
|
recurse(b)
|
|
508
508
|
|
|
@@ -525,7 +525,7 @@ fn ordinary_function_with_bare_generic_enum_param_type_checks() {
|
|
|
525
525
|
// function taking a bare generic-enum-typed parameter.
|
|
526
526
|
let src = "\
|
|
527
527
|
enum Option =
|
|
528
|
-
| Some
|
|
528
|
+
| Some[T]
|
|
529
529
|
| None
|
|
530
530
|
|
|
531
531
|
unwrapOr(o: Option, default: Int) -> Int =
|
|
@@ -556,10 +556,10 @@ fn ordinary_function_with_bare_generic_class_param_type_checks() {
|
|
|
556
556
|
// now, but the identical root cause: `Box` is dropped from the monomorphized
|
|
557
557
|
// output, so a bare `Box`-typed param would otherwise reference nothing.
|
|
558
558
|
let src = "\
|
|
559
|
-
type Box
|
|
559
|
+
type Box[T] =
|
|
560
|
-
value:
|
|
560
|
+
value: T
|
|
561
561
|
|
|
562
|
-
getBoxValue<Box>() ->
|
|
562
|
+
getBoxValue<Box>() -> T =
|
|
563
563
|
self.value
|
|
564
564
|
|
|
565
565
|
sumBox(b: Box) -> Int =
|
plum-checker/tests/monomorphize_tests.rs
CHANGED
|
@@ -3,12 +3,12 @@ use plum_checker::types::PlumType;
|
|
|
3
3
|
use plum_core::ast;
|
|
4
4
|
|
|
5
5
|
#[test]
|
|
6
|
-
fn
|
|
6
|
+
fn is_generic_param_name_accepts_single_uppercase_letters_only() {
|
|
7
|
-
assert!(is_generic_param_name("
|
|
7
|
+
assert!(is_generic_param_name("T"));
|
|
8
|
-
assert!(is_generic_param_name("
|
|
8
|
+
assert!(is_generic_param_name("W"));
|
|
9
9
|
assert!(!is_generic_param_name("Int"));
|
|
10
|
+
assert!(!is_generic_param_name("TU"));
|
|
10
|
-
assert!(!is_generic_param_name("
|
|
11
|
+
assert!(!is_generic_param_name("a"));
|
|
11
|
-
assert!(!is_generic_param_name("A"));
|
|
12
12
|
assert!(!is_generic_param_name(""));
|
|
13
13
|
}
|
|
14
14
|
|
|
@@ -17,37 +17,37 @@ fn class_generic_params_reads_declared_generics_list() {
|
|
|
17
17
|
let c = ast::Class {
|
|
18
18
|
name: "Box".to_string(),
|
|
19
19
|
implements: vec![],
|
|
20
|
-
generics: vec![ast::GenericParam { name: "
|
|
20
|
+
generics: vec![ast::GenericParam { name: "T".to_string(), bounds: vec![] }],
|
|
21
|
-
fields: vec![ast::Field { name: "value".to_string(), ty: ast::Type { name: "
|
|
21
|
+
fields: vec![ast::Field { name: "value".to_string(), ty: ast::Type { name: "T".to_string(), generics: vec![] } }],
|
|
22
22
|
};
|
|
23
|
-
assert_eq!(class_generic_params(&c), vec!["
|
|
23
|
+
assert_eq!(class_generic_params(&c), vec!["T".to_string()]);
|
|
24
24
|
}
|
|
25
25
|
|
|
26
26
|
#[test]
|
|
27
|
-
fn
|
|
27
|
+
fn fn_generic_params_detects_implicit_uppercase_letter_types_in_order() {
|
|
28
28
|
let f = ast::Fn {
|
|
29
29
|
name: "pair".to_string(),
|
|
30
30
|
type_param: None,
|
|
31
31
|
params: vec![
|
|
32
|
-
ast::Param { name: "first".to_string(), ty: ast::ParamType::Type(ast::Type { name: "
|
|
32
|
+
ast::Param { name: "first".to_string(), ty: ast::ParamType::Type(ast::Type { name: "T".to_string(), generics: vec![] }), default: None },
|
|
33
|
-
ast::Param { name: "second".to_string(), ty: ast::ParamType::Type(ast::Type { name: "
|
|
33
|
+
ast::Param { name: "second".to_string(), ty: ast::ParamType::Type(ast::Type { name: "U".to_string(), generics: vec![] }), default: None },
|
|
34
34
|
],
|
|
35
|
-
returns: Some(ast::
|
|
35
|
+
returns: Some(ast::Type { name: "Bool".to_string(), generics: vec![] }),
|
|
36
36
|
body: ast::FnBody::Block(ast::Block { stmts: vec![] }),
|
|
37
37
|
};
|
|
38
|
-
assert_eq!(fn_generic_params(&f), vec!["
|
|
38
|
+
assert_eq!(fn_generic_params(&f), vec!["T".to_string(), "U".to_string()]);
|
|
39
39
|
}
|
|
40
40
|
|
|
41
41
|
#[test]
|
|
42
|
-
fn
|
|
42
|
+
fn enum_generic_params_detects_implicit_uppercase_letter_variant_fields() {
|
|
43
43
|
let e = ast::Enum {
|
|
44
44
|
name: "Option".to_string(),
|
|
45
45
|
variants: vec![
|
|
46
|
-
ast::EnumVariant { name: "Some".to_string(), fields: vec!["
|
|
46
|
+
ast::EnumVariant { name: "Some".to_string(), fields: vec!["T".to_string()] },
|
|
47
47
|
ast::EnumVariant { name: "None".to_string(), fields: vec![] },
|
|
48
48
|
],
|
|
49
49
|
};
|
|
50
|
-
assert_eq!(enum_generic_params(&e), vec!["
|
|
50
|
+
assert_eq!(enum_generic_params(&e), vec!["T".to_string()]);
|
|
51
51
|
}
|
|
52
52
|
|
|
53
53
|
#[test]
|
|
@@ -62,11 +62,11 @@ fn specialize_class_substitutes_generic_field_types_and_clears_generics_list() {
|
|
|
62
62
|
let c = ast::Class {
|
|
63
63
|
name: "Box".to_string(),
|
|
64
64
|
implements: vec![],
|
|
65
|
-
generics: vec![ast::GenericParam { name: "
|
|
65
|
+
generics: vec![ast::GenericParam { name: "T".to_string(), bounds: vec![] }],
|
|
66
|
-
fields: vec![ast::Field { name: "value".to_string(), ty: ast::Type { name: "
|
|
66
|
+
fields: vec![ast::Field { name: "value".to_string(), ty: ast::Type { name: "T".to_string(), generics: vec![] } }],
|
|
67
67
|
};
|
|
68
68
|
let mut bindings = std::collections::BTreeMap::new();
|
|
69
|
-
bindings.insert("
|
|
69
|
+
bindings.insert("T".to_string(), PlumType::TInt);
|
|
70
70
|
let specialized = specialize_class(&c, &Substitution(bindings), "Box$Int");
|
|
71
71
|
assert_eq!(specialized.name, "Box$Int");
|
|
72
72
|
assert!(specialized.generics.is_empty());
|
|
@@ -78,12 +78,12 @@ fn specialize_fn_substitutes_generic_param_and_return_types() {
|
|
|
78
78
|
let f = ast::Fn {
|
|
79
79
|
name: "wrap".to_string(),
|
|
80
80
|
type_param: None,
|
|
81
|
-
params: vec![ast::Param { name: "value".to_string(), ty: ast::ParamType::Type(ast::Type { name: "
|
|
81
|
+
params: vec![ast::Param { name: "value".to_string(), ty: ast::ParamType::Type(ast::Type { name: "T".to_string(), generics: vec![] }), default: None }],
|
|
82
|
-
returns: Some(ast::
|
|
82
|
+
returns: Some(ast::Type { name: "T".to_string(), generics: vec![] }),
|
|
83
83
|
body: ast::FnBody::Expr(ast::Expr::Var("value".to_string())),
|
|
84
84
|
};
|
|
85
85
|
let mut bindings = std::collections::BTreeMap::new();
|
|
86
|
-
bindings.insert("
|
|
86
|
+
bindings.insert("T".to_string(), PlumType::TInt);
|
|
87
87
|
let specialized = specialize_fn(&f, &Substitution(bindings), "wrap$Int", None);
|
|
88
88
|
assert_eq!(specialized.name, "wrap$Int");
|
|
89
89
|
match &specialized.params[0].ty {
|
|
@@ -99,11 +99,11 @@ fn specialize_fn_sets_new_receiver_for_a_method() {
|
|
|
99
99
|
name: "getValue".to_string(),
|
|
100
100
|
type_param: Some("Box".to_string()),
|
|
101
101
|
params: vec![],
|
|
102
|
-
returns: Some(ast::
|
|
102
|
+
returns: Some(ast::Type { name: "T".to_string(), generics: vec![] }),
|
|
103
103
|
body: ast::FnBody::Expr(ast::Expr::Self_),
|
|
104
104
|
};
|
|
105
105
|
let mut bindings = std::collections::BTreeMap::new();
|
|
106
|
-
bindings.insert("
|
|
106
|
+
bindings.insert("T".to_string(), PlumType::TStr);
|
|
107
107
|
let specialized = specialize_fn(&f, &Substitution(bindings), "getValue", Some("Box$Str".to_string()));
|
|
108
108
|
assert_eq!(specialized.name, "getValue");
|
|
109
109
|
assert_eq!(specialized.type_param, Some("Box$Str".to_string()));
|
|
@@ -115,12 +115,12 @@ fn specialize_enum_substitutes_generic_variant_field_names() {
|
|
|
115
115
|
let e = ast::Enum {
|
|
116
116
|
name: "Option".to_string(),
|
|
117
117
|
variants: vec![
|
|
118
|
-
ast::EnumVariant { name: "Some".to_string(), fields: vec!["
|
|
118
|
+
ast::EnumVariant { name: "Some".to_string(), fields: vec!["T".to_string()] },
|
|
119
119
|
ast::EnumVariant { name: "None".to_string(), fields: vec![] },
|
|
120
120
|
],
|
|
121
121
|
};
|
|
122
122
|
let mut bindings = std::collections::BTreeMap::new();
|
|
123
|
-
bindings.insert("
|
|
123
|
+
bindings.insert("T".to_string(), PlumType::TInt);
|
|
124
124
|
let specialized = specialize_enum(&e, &Substitution(bindings), "Option$Int");
|
|
125
125
|
assert_eq!(specialized.name, "Option$Int");
|
|
126
126
|
assert_eq!(specialized.variants[0].fields, vec!["Int".to_string()]);
|