Skip to content

Commit 76c9f9e

Browse files
committed
[Tolk] Support defaults for generic type arguments
This is now valid: > struct Container<T = int?> { item: T = null } > Container{} // does Container<int?> Defaults for Ts work both for functions and structs. Their main purpose is to have the `never` default: > struct WithOptField<T = never> { f: T; } I've added a rule that if a struct has the `never` field, it can be missed out from a literal while creation (like it doesn't exist at all). This will be used in stdlib later.
1 parent 578df21 commit 76c9f9e

19 files changed

+434
-97
lines changed

tolk-tester/tests/generics-4.tolk

Lines changed: 181 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,181 @@
1+
fun eqUnusedT<T=null>(v: int) { return v; }
2+
fun eqUnusedU<T, U = never>(v: T) { if (v is int123) { __expect_type(v as U, "never"); } return v; }
3+
4+
fun dup<T1 = int?, T2 = slice?>(x: T1, y: T2): (T1, T2) { return (x, y) }
5+
6+
struct Container1<T=int?> {
7+
item: T;
8+
}
9+
10+
fun getItemOf<T=never>(v: Container1<T>) {
11+
return v.item;
12+
}
13+
14+
struct WithDef1<T = null> {
15+
body: T? = null;
16+
}
17+
18+
fun getBodyOf<TBody = null>(o: WithDef1<TBody>) {
19+
return o.body;
20+
}
21+
22+
struct WithNever<T = never> {
23+
f1: int;
24+
f2: T;
25+
}
26+
27+
fun eqNever<T = never>(o: WithNever<T>) { return o; }
28+
29+
struct MyInit<TInit> {
30+
value: coins;
31+
data: TInit;
32+
}
33+
34+
fun MyInit<TInit>.getValue(self) {
35+
return self.value;
36+
}
37+
38+
struct Parameters<TBody = never, TInit = never> {
39+
bounce: bool;
40+
body: TBody;
41+
init: builder | MyInit<TInit> | null = null;
42+
}
43+
44+
fun createParameters<TBody = null, TInit = null>(bounce: bool, body: TBody, data: TInit): Parameters<TBody, TInit> {
45+
return { bounce, body, init: { value: ton("0"), data } };
46+
}
47+
48+
fun mySend<TBody = never, TInit = never>(p: Parameters<TBody, TInit>): int {
49+
var total = 0;
50+
if (p.bounce) {
51+
total += 1;
52+
}
53+
if (p.body !is never) {
54+
assert(p.body is TBody, 101);
55+
total += 10;
56+
}
57+
if (p.init is MyInit) {
58+
total += 100 + p.init.getValue();
59+
}
60+
return total;
61+
}
62+
63+
fun test1() {
64+
eqUnusedT(100);
65+
eqUnusedU(100);
66+
eqUnusedU(beginCell());
67+
68+
__expect_type(dup(null, null), "(null, null)");
69+
__expect_type(dup(createEmptyTuple(), 6), "(tuple, int)");
70+
}
71+
72+
fun test2() {
73+
var w1 = Container1 { item: 123 };
74+
var w2 = Container1 { item: null };
75+
__expect_type(w1, "Container1<int>");
76+
__expect_type(w2, "Container1<null>");
77+
__expect_type(getItemOf(w1), "int");
78+
__expect_type(getItemOf(w2), "null");
79+
80+
__expect_type(getItemOf({item: null as slice?}), "slice?");
81+
__expect_type(getItemOf({item: null}), "null");
82+
}
83+
84+
@method_id(103)
85+
fun test3() {
86+
__expect_type(WithNever{f1:10}, "WithNever<never>");
87+
__expect_type(WithNever{f1:10,f2:20}, "WithNever<int>");
88+
89+
__expect_type(eqNever({f1:10}), "WithNever<never>");
90+
__expect_type(eqNever({f1:10,f2:20}), "WithNever<int>");
91+
__expect_type(eqNever({f1:10,f2:null}), "WithNever<null>");
92+
93+
var a: WithNever<never> = {f1:10};
94+
return (a, WithNever{f1:20}, eqNever({f1:30}), 777, eqNever({f1:40,f2:40}));
95+
}
96+
97+
@method_id(104)
98+
fun test4() {
99+
__expect_type(getBodyOf({body: 123}), "int?");
100+
__expect_type(getBodyOf({body: null}), "null");
101+
__expect_type(getBodyOf({}), "null");
102+
103+
__expect_type(WithDef1{}, "WithDef1<null>");
104+
__expect_type(WithDef1{}.body, "null");
105+
__expect_type(WithDef1{body: null}.body, "null");
106+
__expect_type(WithDef1{body: 123}.body, "int?");
107+
108+
return (getBodyOf({body: null}), getBodyOf({}), WithDef1{});
109+
}
110+
111+
@method_id(105)
112+
fun test5() {
113+
__expect_type(Parameters { bounce: true }, "Parameters<never, never>");
114+
__expect_type(Parameters { bounce: false, body: 179 }, "Parameters<int, never>");
115+
__expect_type(Parameters { bounce: true, init: beginCell() }, "Parameters<never, never>");
116+
__expect_type(Parameters { bounce: false, body: beginCell(), init: { value: 123, data: 123 } }, "Parameters<builder, int>");
117+
118+
__expect_type(createParameters(true, null, null), "Parameters<null, null>");
119+
__expect_type(createParameters(true, beginCell(), "123"), "Parameters<builder, slice>");
120+
121+
__expect_type(createParameters(true, null, null).body, "null");
122+
__expect_type(createParameters(true, 123, null).body, "int");
123+
__expect_type(createParameters(true, null, null).init, "builder | MyInit<null> | null");
124+
__expect_type(createParameters(true, 123, 456).init, "builder | MyInit<int> | null");
125+
126+
return (createParameters(true, null, null), 777, createParameters(false, 123, 456));
127+
}
128+
129+
@method_id(106)
130+
fun test6() {
131+
var p: Parameters<int, cell> = {
132+
bounce: true,
133+
body: 123,
134+
init: { value: ton("0"), data: beginCell().endCell() }
135+
};
136+
return (p.body is int, p.init is cell, p.init is MyInit, p.init is MyInit && p.init.data.depth() == 0);
137+
}
138+
139+
@method_id(107)
140+
fun test7() {
141+
__expect_type(Parameters{ bounce: false, body: 123, init: { value: ton("0"), data: beginCell() }}, "Parameters<int, builder>");
142+
var v1 = mySend({ bounce: true });
143+
var v2 = mySend({ bounce: false, body: 123, init: beginCell() });
144+
var v3 = mySend({ bounce: false, body: 123, init: { value: ton("0"), data: beginCell() }});
145+
var v4 = mySend({ bounce: true, init: { value: 16, data: null as [int]? }});
146+
return (v1, v2, v3, v4);
147+
}
148+
149+
fun main() {
150+
}
151+
152+
/**
153+
@testcase | 103 | | 10 20 30 777 40 40
154+
@testcase | 104 | | (null) (null) (null)
155+
@testcase | 105 | | -1 (null) 0 (null) 133 777 0 123 0 456 132
156+
@testcase | 106 | | -1 0 -1 -1
157+
@testcase | 107 | | 1 10 110 117
158+
159+
@fif_codegen DECLPROC eqUnusedT<null>
160+
@fif_codegen DECLPROC eqUnusedU<builder,never>
161+
@fif_codegen DECLPROC mySend<never,never>
162+
@fif_codegen DECLPROC mySend<int,never>
163+
@fif_codegen DECLPROC mySend<int,builder>
164+
@fif_codegen DECLPROC mySend<never,[int]?>
165+
166+
@fif_codegen
167+
"""
168+
test6 PROC:<{ //
169+
NEWC // '8
170+
ENDC // p.init.USlot2
171+
-1 PUSHINT // p.init.USlot2 '11=-1
172+
FALSE // p.init.USlot2 '11=-1 '12
173+
TRUE // p.init.USlot2 '11=-1 '12 '14
174+
s0 s3 XCHG // '14 '11=-1 '12 p.init.USlot2
175+
CDEPTH // '14 '11=-1 '12 '19
176+
0 EQINT // '14 '11=-1 '12 '21
177+
0 NEQINT // '14 '11=-1 '12 '18
178+
s1 s3 s0 XCHG3 // '11=-1 '12 '14 '18
179+
}>
180+
"""
181+
*/

tolk-tester/tests/invalid-semantics/err-4628.tolk

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,6 @@ fun main() {
1111

1212
/**
1313
@compilation_should_fail
14-
@stderr field `item` missed in initialization of struct `Wrapper<T>`
14+
@stderr can not deduce T for generic struct `Wrapper<T>`
1515
@stderr = Wrapper {
1616
*/
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
struct Wrapper<T = int> {
2+
item: T;
3+
value: int;
4+
}
5+
6+
fun main() {
7+
var c = Wrapper {
8+
value: null
9+
};
10+
}
11+
12+
/**
13+
@compilation_should_fail
14+
@stderr field `item` missed in initialization of struct `Wrapper<int>`
15+
@stderr = Wrapper {
16+
*/
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
struct Data<T> {
2+
data: T;
3+
}
4+
5+
struct Parameters<TBody, TData> {
6+
bounce: bool,
7+
dest: slice | Data<TData>,
8+
body: TBody,
9+
}
10+
11+
fun f<TBody, TData>(params: Parameters<TBody, TData>) {
12+
}
13+
14+
fun main() {
15+
f({
16+
dest: "sss",
17+
body: 123,
18+
})
19+
}
20+
21+
/**
22+
@compilation_should_fail
23+
@stderr can not deduce TData for generic struct `Parameters<TBody, TData>`
24+
@stderr dest: "sss"
25+
*/
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
struct A {
2+
f1: never;
3+
}
4+
5+
fun main() {
6+
var a: A = {}; // f1 is never, can be omitted
7+
a.f1 = 100;
8+
}
9+
10+
/**
11+
@compilation_should_fail
12+
@stderr can not assign `int` to field of type `never`
13+
@stderr a.f1 = 100
14+
*/

tolk-tester/tests/struct-tests.tolk

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -474,6 +474,18 @@ fun test33(): WithDefaults {
474474
return { f2: 5, f3: null };
475475
}
476476

477+
struct WithNever {
478+
f1: int;
479+
f2: never;
480+
f3: int;
481+
}
482+
483+
@method_id(134)
484+
fun test34() {
485+
var o1: WithNever = { f1: 10, f3: 20 }; // f2 is `never`, it can be omitted
486+
__expect_type(o1.f2, "never");
487+
return o1;
488+
}
477489

478490

479491
fun main(x: int8, y: MInt) {
@@ -526,6 +538,7 @@ type PointAlias = Point;
526538
@testcase | 131 | | 5 141 (null) 141 (null) 0 777 0 0 -1 777 0 -1
527539
@testcase | 132 | | 10 20 10 0 0 20 0 0 0 0
528540
@testcase | 133 | | -1 0 5 (null) (null) (null) 0 0 46
541+
@testcase | 134 | | 10 20
529542

530543
@fif_codegen
531544
"""

tolk/ast-from-tokens.cpp

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -279,9 +279,15 @@ static V<ast_genericsT_list> parse_genericsT_list(Lexer& lex) {
279279
lex.expect(tok_lt, "`<`");
280280
while (true) {
281281
lex.check(tok_identifier, "T");
282+
SrcLocation locT = lex.cur_location();
282283
std::string_view nameT = lex.cur_str();
283-
genericsT_items.emplace_back(createV<ast_genericsT_item>(lex.cur_location(), nameT));
284284
lex.next();
285+
AnyTypeV default_type = nullptr;
286+
if (lex.tok() == tok_assign) { // <T = int?>
287+
lex.next();
288+
default_type = parse_type_expression(lex);
289+
}
290+
genericsT_items.emplace_back(createV<ast_genericsT_item>(locT, nameT, default_type));
285291
if (lex.tok() != tok_comma) {
286292
break;
287293
}

tolk/ast-replicator.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -211,7 +211,7 @@ class ASTReplicator final {
211211
return createV<ast_identifier>(v->loc, v->name);
212212
}
213213
static V<ast_genericsT_item> clone(V<ast_genericsT_item> v) {
214-
return createV<ast_genericsT_item>(v->loc, v->nameT);
214+
return createV<ast_genericsT_item>(v->loc, v->nameT, clone(v->default_type_node));
215215
}
216216
static V<ast_genericsT_list> clone(V<ast_genericsT_list> v) {
217217
return createV<ast_genericsT_list>(v->loc, clone(v->get_items()));

tolk/ast.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1127,12 +1127,14 @@ struct Vertex<ast_asm_body> final : ASTStatementVararg {
11271127
template<>
11281128
// ast_genericsT_item is generics T at declaration
11291129
// example: `fun f<T1, T2>` has a list of 2 generic Ts
1130+
// example: `struct Params<TInit=null>` has 1 generic T with default
11301131
struct Vertex<ast_genericsT_item> final : ASTOtherLeaf {
11311132
std::string_view nameT;
1133+
AnyTypeV default_type_node; // exists for `<T = int>`, nullptr otherwise
11321134

1133-
Vertex(SrcLocation loc, std::string_view nameT)
1135+
Vertex(SrcLocation loc, std::string_view nameT, AnyTypeV default_type_node)
11341136
: ASTOtherLeaf(ast_genericsT_item, loc)
1135-
, nameT(nameT) {}
1137+
, nameT(nameT), default_type_node(default_type_node) {}
11361138
};
11371139

11381140
template<>

tolk/builtins.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1119,7 +1119,7 @@ void define_builtins() {
11191119
TypePtr Never = TypeDataNever::create();
11201120

11211121
TypePtr typeT = TypeDataGenericT::create("T");
1122-
const GenericsDeclaration* declGenericT = new GenericsDeclaration(std::vector<std::string_view>{"T"}, 0);
1122+
const GenericsDeclaration* declGenericT = new GenericsDeclaration(std::vector<GenericsDeclaration::ItemT>{{"T", nullptr}}, 0);
11231123

11241124
std::vector ParamsInt1 = {Int};
11251125
std::vector ParamsInt2 = {Int, Int};

0 commit comments

Comments
 (0)