Skip to content

Commit 799e2d1

Browse files
committed
[Tolk] Rewrite the type system from Hindley-Milner to static typing
FunC's (and Tolk's before this PR) type system is based on Hindley-Milner. This is a common approach for functional languages, where types are inferred from usage through unification. As a result, type declarations are not necessary: () f(a,b) { return a+b; } // a and b now int, since `+` (int, int) While this approach works for now, problems arise with the introduction of new types like bool, where `!x` must handle both int and bool. It will also become incompatible with int32 and other strict integers. This will clash with structure methods, struggle with proper generics, and become entirely impractical for union types. This PR completely rewrites the type system targeting the future. 1) type of any expression is inferred and never changed 2) this is available because dependent expressions already inferred 3) forall completely removed, generic functions introduced (they work like template functions actually, instantiated while inferring) 4) instantiation `<...>` syntax, example: `t.tupleAt<int>(0)` 5) `as` keyword, for example `t.tupleAt(0) as int` 6) methods binding is done along with type inferring, not before ("before", as worked previously, was always a wrong approach)
1 parent 3540424 commit 799e2d1

File tree

101 files changed

+5371
-2682
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

101 files changed

+5371
-2682
lines changed

crypto/smartcont/tolk-stdlib/common.tolk

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,17 +17,17 @@ fun createEmptyTuple(): tuple
1717
/// Appends a value to tuple, resulting in `Tuple t' = (x1, ..., xn, value)`.
1818
/// If its size exceeds 255, throws a type check exception.
1919
@pure
20-
fun tuplePush<X>(mutate self: tuple, value: X): void
20+
fun tuplePush<T>(mutate self: tuple, value: T): void
2121
asm "TPUSH";
2222

2323
/// Returns the first element of a non-empty tuple.
2424
@pure
25-
fun tupleFirst<X>(t: tuple): X
25+
fun tupleFirst<T>(t: tuple): T
2626
asm "FIRST";
2727

2828
/// Returns the [`index`]-th element of a tuple.
2929
@pure
30-
fun tupleAt<X>(t: tuple, index: int): X
30+
fun tupleAt<T>(t: tuple, index: int): T
3131
builtin;
3232

3333
/// Returns the size of a tuple (elements count in it).
@@ -37,7 +37,7 @@ fun tupleSize(t: tuple): int
3737

3838
/// Returns the last element of a non-empty tuple.
3939
@pure
40-
fun tupleLast(t: tuple): int
40+
fun tupleLast<T>(t: tuple): T
4141
asm "LAST";
4242

4343

@@ -306,11 +306,11 @@ fun getBuilderDepth(b: builder): int
306306
*/
307307

308308
/// Dump a variable [x] to the debug log.
309-
fun debugPrint<X>(x: X): void
309+
fun debugPrint<T>(x: T): void
310310
builtin;
311311

312312
/// Dump a string [x] to the debug log.
313-
fun debugPrintString<X>(x: X): void
313+
fun debugPrintString<T>(x: T): void
314314
builtin;
315315

316316
/// Dumps the stack (at most the top 255 values) and shows the total stack depth.

crypto/smartcont/tolk-stdlib/gas-payments.tolk

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,3 +61,9 @@ fun calculateOriginalMessageFee(workchain: int, incomingFwdFee: int): int
6161
/// If it has no debt, `0` is returned.
6262
fun getMyStorageDuePayment(): int
6363
asm "DUEPAYMENT";
64+
65+
/// Returns the amount of nanotoncoins charged for storage.
66+
/// (during storage phase preceeding to current computation phase)
67+
@pure
68+
fun getMyStoragePaidPayment(): int
69+
asm "STORAGEFEES";

tolk-tester/tests/a10.tolk

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ fun test88(x: int) {
3535
}
3636

3737
@method_id(89)
38-
fun test89(last: int) {
38+
fun test89(last: int): (int, int, int, int) {
3939
var t: tuple = createEmptyTuple();
4040
t.tuplePush(1);
4141
t.tuplePush(2);

tolk-tester/tests/a6.tolk

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ fun calc_phi(): int {
99
repeat (70) { n*=10; };
1010
var p= 1;
1111
var `q`=1;
12+
_=`q`;
1213
do {
1314
(p,q)=(q,p+q);
1415
} while (q <= n); //;;
@@ -27,7 +28,7 @@ fun calc_sqrt2(): int {
2728
return mulDivRound(p, n, q);
2829
}
2930

30-
fun calc_root(m: auto): auto {
31+
fun calc_root(m: int) {
3132
var base: int=1;
3233
repeat(70) { base *= 10; }
3334
var (a, b, c) = (1,0,-m);

tolk-tester/tests/a6_5.tolk

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
@deprecated
2-
fun twice(f: auto, x: auto): auto {
2+
fun twice(f: int -> int, x: int) {
33
return f (f (x));
44
}
55

tolk-tester/tests/allow_post_modification.tolk

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -138,5 +138,5 @@ fun main() {
138138
inc CALLDICT // self newY
139139
}>
140140
"""
141-
@code_hash 33262590582878205026101577472505372101182291690814957175155528952950621243206
141+
@code_hash 7627024945492125068389905298530400936797031708759561372406088054030801992712
142142
*/
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
fun extractFromTypedTuple(params: [int]) {
2+
var [payload: int] = params;
3+
return payload + 10;
4+
}
5+
6+
@method_id(101)
7+
fun test101(x: int) {
8+
var params = [x];
9+
return extractFromTypedTuple(params);
10+
}
11+
12+
fun autoInferIntNull(x: int) {
13+
if (x > 10) { return null; }
14+
return x;
15+
}
16+
17+
fun main(value: int) {
18+
var (x: int, y) = (autoInferIntNull(value), autoInferIntNull(value * 2));
19+
if (x == null && y == null) { return null; }
20+
return x == null || y == null ? -1 : x + y;
21+
}
22+
23+
/**
24+
@testcase | 0 | 3 | 9
25+
@testcase | 0 | 6 | -1
26+
@testcase | 0 | 11 | (null)
27+
@testcase | 101 | 78 | 88
28+
*/

tolk-tester/tests/c2.tolk

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ fun check_assoc(a: int, b: int, c: int): int {
44
return op(op(a, b), c) == op(a, op(b, c));
55
}
66

7-
fun unnamed_args(_: int, _: slice, _: auto): auto {
7+
fun unnamed_args(_: int, _: slice, _: int) {
88
return true;
99
}
1010

@@ -14,7 +14,7 @@ fun main(x: int, y: int, z: int): int {
1414
}
1515

1616
@method_id(101)
17-
fun test101(x: int, z: int): auto {
17+
fun test101(x: int, z: int) {
1818
return unnamed_args(x, "asdf", z);
1919
}
2020

tolk-tester/tests/c2_1.tolk

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
fun check_assoc(op: auto, a: int, b: int, c: int) {
1+
fun check_assoc(op: (int, int) -> int, a: int, b: int, c: int) {
22
return op(op(a, b), c) == op(a, op(b, c));
33
}
44

tolk-tester/tests/generics-1.tolk

Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
fun eq1<X>(value: X): X { return value; }
2+
fun eq2<X>(value: X) { return value; }
3+
fun eq3<X>(value: X): X { var cp: [X] = [eq1(value)]; var ((([v: X]))) = cp; return v; }
4+
fun eq4<X>(value: X) { return eq1<X>(value); }
5+
6+
@method_id(101)
7+
fun test101(x: int) {
8+
var (a, b, c) = (x, (x,x), [x,x]);
9+
return (eq1(a), eq1(b), eq1(c), eq2(a), eq2(b), eq2(c), eq3(a), eq4(b), eq3(createEmptyTuple()));
10+
}
11+
12+
fun getTwo<X>(): X { return 2 as X; }
13+
14+
fun takeInt(a: int) { return a; }
15+
16+
@method_id(102)
17+
fun test102(): (int, int, int, [(int, int)]) {
18+
var a: int = getTwo();
19+
var _: int = getTwo();
20+
var b = getTwo() as int;
21+
var c: int = 1 ? getTwo() : getTwo();
22+
var c redef = getTwo();
23+
return (eq1<int>(a), eq2<int>(b), takeInt(getTwo()), [(getTwo(), getTwo())]);
24+
}
25+
26+
@method_id(103)
27+
fun test103(first: int): (int, int, int) {
28+
var t = createEmptyTuple();
29+
var cs = beginCell().storeInt(100, 32).endCell().beginParse();
30+
t.tuplePush(first);
31+
t.tuplePush(2);
32+
t.tuplePush(cs);
33+
cs = t.tupleAt(2);
34+
cs = t.tupleAt(2) as slice;
35+
return (t.tupleAt(0), cs.loadInt(32), t.tupleAt<slice>(2).loadInt(32));
36+
}
37+
38+
fun manyEq<T1, T2, T3>(a: T1, b: T2, c: T3): [T1, T2, T3] {
39+
return [a, b, c];
40+
}
41+
42+
@method_id(104)
43+
fun test104(f: int) {
44+
return (
45+
manyEq(1 ? 1 : 1, f ? 0 : null, !f ? getTwo() as int : null),
46+
manyEq((f ? null as int : eq2(2), beginCell().storeBool(true).endCell().beginParse().loadBool()), 0, eq4(f))
47+
);
48+
}
49+
50+
fun calcSum<X>(x: X, y: X) { return x + y; }
51+
52+
@method_id(105)
53+
fun test105() {
54+
if (0) { calcSum(((0)), null); }
55+
return (calcSum(1, 2));
56+
}
57+
58+
fun calcYPlus1<Y>(value: Y) { return value + 1; }
59+
fun calcLoad32(cs: slice) { return cs.loadInt(32); }
60+
fun calcTensorPlus1(tens: (int, int)) { var (f, s) = tens; return (f + 1, s + 1); }
61+
fun calcTensorMul2(tens: (int, int)) { var (f, s) = tens; return (f * 2, s * 2); }
62+
fun cellToSlice(c: cell) { return c.beginParse(); }
63+
fun abstractTransform<X, Y, R>(xToY: (X) -> Y, yToR: (((Y))) -> R, initialX: X): R {
64+
var y = xToY(initialX);
65+
return yToR(y);
66+
}
67+
68+
@method_id(106)
69+
fun test106() {
70+
var c = beginCell().storeInt(106, 32).endCell();
71+
return [
72+
abstractTransform(cellToSlice, calcLoad32, c),
73+
abstractTransform(calcYPlus1<int>, calcYPlus1<int>, 0),
74+
abstractTransform(calcTensorPlus1, calcTensorMul2, (2, 2))
75+
];
76+
}
77+
78+
fun callTupleFirst<X, Y>(t: X): Y { return t.tupleFirst(); }
79+
fun callTuplePush<T, V>(mutate self: T, v1: V, v2: V): self { self.tuplePush(v1); tuplePush(mutate self, v2); return self; }
80+
fun getTupleLastInt(t: tuple) { return t.tupleLast<int>(); }
81+
fun getTupleSize(t: tuple) { return t.tupleSize(); }
82+
fun callAnyFn<TObj, TResult>(f: (TObj) -> TResult, arg: TObj) { return f(arg); }
83+
fun callAnyFn2<TCallback>(f: TCallback, arg: tuple) { return f(arg); }
84+
85+
global t107: tuple;
86+
87+
@method_id(107)
88+
fun test107() {
89+
t107 = createEmptyTuple();
90+
callTuplePush(mutate t107, 1, 2);
91+
t107.callTuplePush(3, 4).callTuplePush(5, 6);
92+
var first: int = t107.callTupleFirst();
93+
return (
94+
callAnyFn<tuple, int>(getTupleSize, t107),
95+
callAnyFn2(getTupleSize, t107),
96+
first,
97+
callTupleFirst(t107) as int,
98+
callAnyFn(getTupleLastInt, t107),
99+
callAnyFn2(getTupleLastInt, t107)
100+
);
101+
}
102+
103+
global g108: int;
104+
105+
fun inc108(by: int) { g108 += by; }
106+
fun getInc108() { return inc108; }
107+
fun returnResult<RetT>(f: () -> RetT): RetT { return f(); }
108+
fun applyAndReturn<ArgT, RetT>(f: () -> (ArgT) -> RetT, arg: ArgT): () -> ArgT -> RetT {
109+
f()(arg);
110+
return f;
111+
}
112+
113+
@method_id(108)
114+
fun test108() {
115+
g108 = 0;
116+
getInc108()(1);
117+
returnResult<(int) -> void>(getInc108)(2);
118+
applyAndReturn<int, void>(getInc108, 10)()(10);
119+
returnResult(getInc108)(2);
120+
applyAndReturn(getInc108, 10)()(10);
121+
return g108;
122+
}
123+
124+
fun main(x: int): (int, [[int, int]]) {
125+
try { if(x) { throw (1, x); } }
126+
catch (excNo, arg) { return (arg as int, [[eq2(arg as int), getTwo()]]); }
127+
return (0, [[x, 1]]);
128+
}
129+
130+
/**
131+
@testcase | 0 | 1 | 1 [ [ 1 2 ] ]
132+
@testcase | 101 | 0 | 0 0 0 [ 0 0 ] 0 0 0 [ 0 0 ] 0 0 0 []
133+
@testcase | 102 | | 2 2 2 [ 2 2 ]
134+
@testcase | 103 | 0 | 0 100 100
135+
@testcase | 104 | 0 | [ 1 (null) 2 ] [ 2 -1 0 0 ]
136+
@testcase | 105 | | 3
137+
@testcase | 106 | | [ 106 2 6 6 ]
138+
@testcase | 107 | | 6 6 1 1 6 6
139+
@testcase | 108 | | 45
140+
141+
@fif_codegen DECLPROC eq1<int>
142+
@fif_codegen DECLPROC eq1<tuple>
143+
@fif_codegen DECLPROC eq1<(int,int)>
144+
@fif_codegen DECLPROC eq1<[int,int]>
145+
@fif_codegen DECLPROC getTwo<int>
146+
147+
@fif_codegen_avoid DECLPROC eq1
148+
@fif_codegen_avoid DECLPROC eq2
149+
@fif_codegen_avoid DECLPROC eq3
150+
*/

0 commit comments

Comments
 (0)