mirror of
https://github.com/ton-blockchain/ton
synced 2025-03-09 15:40:10 +00:00
[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)
This commit is contained in:
parent
3540424aa1
commit
799e2d1265
101 changed files with 5402 additions and 2713 deletions
|
@ -35,7 +35,7 @@ fun test88(x: int) {
|
|||
}
|
||||
|
||||
@method_id(89)
|
||||
fun test89(last: int) {
|
||||
fun test89(last: int): (int, int, int, int) {
|
||||
var t: tuple = createEmptyTuple();
|
||||
t.tuplePush(1);
|
||||
t.tuplePush(2);
|
||||
|
|
|
@ -9,6 +9,7 @@ fun calc_phi(): int {
|
|||
repeat (70) { n*=10; };
|
||||
var p= 1;
|
||||
var `q`=1;
|
||||
_=`q`;
|
||||
do {
|
||||
(p,q)=(q,p+q);
|
||||
} while (q <= n); //;;
|
||||
|
@ -27,7 +28,7 @@ fun calc_sqrt2(): int {
|
|||
return mulDivRound(p, n, q);
|
||||
}
|
||||
|
||||
fun calc_root(m: auto): auto {
|
||||
fun calc_root(m: int) {
|
||||
var base: int=1;
|
||||
repeat(70) { base *= 10; }
|
||||
var (a, b, c) = (1,0,-m);
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
@deprecated
|
||||
fun twice(f: auto, x: auto): auto {
|
||||
fun twice(f: int -> int, x: int) {
|
||||
return f (f (x));
|
||||
}
|
||||
|
||||
|
|
|
@ -138,5 +138,5 @@ fun main() {
|
|||
inc CALLDICT // self newY
|
||||
}>
|
||||
"""
|
||||
@code_hash 33262590582878205026101577472505372101182291690814957175155528952950621243206
|
||||
@code_hash 7627024945492125068389905298530400936797031708759561372406088054030801992712
|
||||
*/
|
||||
|
|
28
tolk-tester/tests/assignment-tests.tolk
Normal file
28
tolk-tester/tests/assignment-tests.tolk
Normal file
|
@ -0,0 +1,28 @@
|
|||
fun extractFromTypedTuple(params: [int]) {
|
||||
var [payload: int] = params;
|
||||
return payload + 10;
|
||||
}
|
||||
|
||||
@method_id(101)
|
||||
fun test101(x: int) {
|
||||
var params = [x];
|
||||
return extractFromTypedTuple(params);
|
||||
}
|
||||
|
||||
fun autoInferIntNull(x: int) {
|
||||
if (x > 10) { return null; }
|
||||
return x;
|
||||
}
|
||||
|
||||
fun main(value: int) {
|
||||
var (x: int, y) = (autoInferIntNull(value), autoInferIntNull(value * 2));
|
||||
if (x == null && y == null) { return null; }
|
||||
return x == null || y == null ? -1 : x + y;
|
||||
}
|
||||
|
||||
/**
|
||||
@testcase | 0 | 3 | 9
|
||||
@testcase | 0 | 6 | -1
|
||||
@testcase | 0 | 11 | (null)
|
||||
@testcase | 101 | 78 | 88
|
||||
*/
|
|
@ -4,7 +4,7 @@ fun check_assoc(a: int, b: int, c: int): int {
|
|||
return op(op(a, b), c) == op(a, op(b, c));
|
||||
}
|
||||
|
||||
fun unnamed_args(_: int, _: slice, _: auto): auto {
|
||||
fun unnamed_args(_: int, _: slice, _: int) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -14,7 +14,7 @@ fun main(x: int, y: int, z: int): int {
|
|||
}
|
||||
|
||||
@method_id(101)
|
||||
fun test101(x: int, z: int): auto {
|
||||
fun test101(x: int, z: int) {
|
||||
return unnamed_args(x, "asdf", z);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
fun check_assoc(op: auto, a: int, b: int, c: int) {
|
||||
fun check_assoc(op: (int, int) -> int, a: int, b: int, c: int) {
|
||||
return op(op(a, b), c) == op(a, op(b, c));
|
||||
}
|
||||
|
||||
|
|
150
tolk-tester/tests/generics-1.tolk
Normal file
150
tolk-tester/tests/generics-1.tolk
Normal file
|
@ -0,0 +1,150 @@
|
|||
fun eq1<X>(value: X): X { return value; }
|
||||
fun eq2<X>(value: X) { return value; }
|
||||
fun eq3<X>(value: X): X { var cp: [X] = [eq1(value)]; var ((([v: X]))) = cp; return v; }
|
||||
fun eq4<X>(value: X) { return eq1<X>(value); }
|
||||
|
||||
@method_id(101)
|
||||
fun test101(x: int) {
|
||||
var (a, b, c) = (x, (x,x), [x,x]);
|
||||
return (eq1(a), eq1(b), eq1(c), eq2(a), eq2(b), eq2(c), eq3(a), eq4(b), eq3(createEmptyTuple()));
|
||||
}
|
||||
|
||||
fun getTwo<X>(): X { return 2 as X; }
|
||||
|
||||
fun takeInt(a: int) { return a; }
|
||||
|
||||
@method_id(102)
|
||||
fun test102(): (int, int, int, [(int, int)]) {
|
||||
var a: int = getTwo();
|
||||
var _: int = getTwo();
|
||||
var b = getTwo() as int;
|
||||
var c: int = 1 ? getTwo() : getTwo();
|
||||
var c redef = getTwo();
|
||||
return (eq1<int>(a), eq2<int>(b), takeInt(getTwo()), [(getTwo(), getTwo())]);
|
||||
}
|
||||
|
||||
@method_id(103)
|
||||
fun test103(first: int): (int, int, int) {
|
||||
var t = createEmptyTuple();
|
||||
var cs = beginCell().storeInt(100, 32).endCell().beginParse();
|
||||
t.tuplePush(first);
|
||||
t.tuplePush(2);
|
||||
t.tuplePush(cs);
|
||||
cs = t.tupleAt(2);
|
||||
cs = t.tupleAt(2) as slice;
|
||||
return (t.tupleAt(0), cs.loadInt(32), t.tupleAt<slice>(2).loadInt(32));
|
||||
}
|
||||
|
||||
fun manyEq<T1, T2, T3>(a: T1, b: T2, c: T3): [T1, T2, T3] {
|
||||
return [a, b, c];
|
||||
}
|
||||
|
||||
@method_id(104)
|
||||
fun test104(f: int) {
|
||||
return (
|
||||
manyEq(1 ? 1 : 1, f ? 0 : null, !f ? getTwo() as int : null),
|
||||
manyEq((f ? null as int : eq2(2), beginCell().storeBool(true).endCell().beginParse().loadBool()), 0, eq4(f))
|
||||
);
|
||||
}
|
||||
|
||||
fun calcSum<X>(x: X, y: X) { return x + y; }
|
||||
|
||||
@method_id(105)
|
||||
fun test105() {
|
||||
if (0) { calcSum(((0)), null); }
|
||||
return (calcSum(1, 2));
|
||||
}
|
||||
|
||||
fun calcYPlus1<Y>(value: Y) { return value + 1; }
|
||||
fun calcLoad32(cs: slice) { return cs.loadInt(32); }
|
||||
fun calcTensorPlus1(tens: (int, int)) { var (f, s) = tens; return (f + 1, s + 1); }
|
||||
fun calcTensorMul2(tens: (int, int)) { var (f, s) = tens; return (f * 2, s * 2); }
|
||||
fun cellToSlice(c: cell) { return c.beginParse(); }
|
||||
fun abstractTransform<X, Y, R>(xToY: (X) -> Y, yToR: (((Y))) -> R, initialX: X): R {
|
||||
var y = xToY(initialX);
|
||||
return yToR(y);
|
||||
}
|
||||
|
||||
@method_id(106)
|
||||
fun test106() {
|
||||
var c = beginCell().storeInt(106, 32).endCell();
|
||||
return [
|
||||
abstractTransform(cellToSlice, calcLoad32, c),
|
||||
abstractTransform(calcYPlus1<int>, calcYPlus1<int>, 0),
|
||||
abstractTransform(calcTensorPlus1, calcTensorMul2, (2, 2))
|
||||
];
|
||||
}
|
||||
|
||||
fun callTupleFirst<X, Y>(t: X): Y { return t.tupleFirst(); }
|
||||
fun callTuplePush<T, V>(mutate self: T, v1: V, v2: V): self { self.tuplePush(v1); tuplePush(mutate self, v2); return self; }
|
||||
fun getTupleLastInt(t: tuple) { return t.tupleLast<int>(); }
|
||||
fun getTupleSize(t: tuple) { return t.tupleSize(); }
|
||||
fun callAnyFn<TObj, TResult>(f: (TObj) -> TResult, arg: TObj) { return f(arg); }
|
||||
fun callAnyFn2<TCallback>(f: TCallback, arg: tuple) { return f(arg); }
|
||||
|
||||
global t107: tuple;
|
||||
|
||||
@method_id(107)
|
||||
fun test107() {
|
||||
t107 = createEmptyTuple();
|
||||
callTuplePush(mutate t107, 1, 2);
|
||||
t107.callTuplePush(3, 4).callTuplePush(5, 6);
|
||||
var first: int = t107.callTupleFirst();
|
||||
return (
|
||||
callAnyFn<tuple, int>(getTupleSize, t107),
|
||||
callAnyFn2(getTupleSize, t107),
|
||||
first,
|
||||
callTupleFirst(t107) as int,
|
||||
callAnyFn(getTupleLastInt, t107),
|
||||
callAnyFn2(getTupleLastInt, t107)
|
||||
);
|
||||
}
|
||||
|
||||
global g108: int;
|
||||
|
||||
fun inc108(by: int) { g108 += by; }
|
||||
fun getInc108() { return inc108; }
|
||||
fun returnResult<RetT>(f: () -> RetT): RetT { return f(); }
|
||||
fun applyAndReturn<ArgT, RetT>(f: () -> (ArgT) -> RetT, arg: ArgT): () -> ArgT -> RetT {
|
||||
f()(arg);
|
||||
return f;
|
||||
}
|
||||
|
||||
@method_id(108)
|
||||
fun test108() {
|
||||
g108 = 0;
|
||||
getInc108()(1);
|
||||
returnResult<(int) -> void>(getInc108)(2);
|
||||
applyAndReturn<int, void>(getInc108, 10)()(10);
|
||||
returnResult(getInc108)(2);
|
||||
applyAndReturn(getInc108, 10)()(10);
|
||||
return g108;
|
||||
}
|
||||
|
||||
fun main(x: int): (int, [[int, int]]) {
|
||||
try { if(x) { throw (1, x); } }
|
||||
catch (excNo, arg) { return (arg as int, [[eq2(arg as int), getTwo()]]); }
|
||||
return (0, [[x, 1]]);
|
||||
}
|
||||
|
||||
/**
|
||||
@testcase | 0 | 1 | 1 [ [ 1 2 ] ]
|
||||
@testcase | 101 | 0 | 0 0 0 [ 0 0 ] 0 0 0 [ 0 0 ] 0 0 0 []
|
||||
@testcase | 102 | | 2 2 2 [ 2 2 ]
|
||||
@testcase | 103 | 0 | 0 100 100
|
||||
@testcase | 104 | 0 | [ 1 (null) 2 ] [ 2 -1 0 0 ]
|
||||
@testcase | 105 | | 3
|
||||
@testcase | 106 | | [ 106 2 6 6 ]
|
||||
@testcase | 107 | | 6 6 1 1 6 6
|
||||
@testcase | 108 | | 45
|
||||
|
||||
@fif_codegen DECLPROC eq1<int>
|
||||
@fif_codegen DECLPROC eq1<tuple>
|
||||
@fif_codegen DECLPROC eq1<(int,int)>
|
||||
@fif_codegen DECLPROC eq1<[int,int]>
|
||||
@fif_codegen DECLPROC getTwo<int>
|
||||
|
||||
@fif_codegen_avoid DECLPROC eq1
|
||||
@fif_codegen_avoid DECLPROC eq2
|
||||
@fif_codegen_avoid DECLPROC eq3
|
||||
*/
|
|
@ -1,9 +1,10 @@
|
|||
fun main() {
|
||||
return true();
|
||||
const asdf = 1;
|
||||
|
||||
fun main(x: int) {
|
||||
return x.asdf();
|
||||
}
|
||||
|
||||
/**
|
||||
@compilation_should_fail
|
||||
The message is weird now, but later I'll rework error messages anyway.
|
||||
@stderr cannot apply expression of type int to an expression of type (): cannot unify type () -> ??2 with int
|
||||
@stderr calling a non-function
|
||||
*/
|
||||
|
|
10
tolk-tester/tests/invalid-call-9.tolk
Normal file
10
tolk-tester/tests/invalid-call-9.tolk
Normal file
|
@ -0,0 +1,10 @@
|
|||
fun getOne() { return 1; }
|
||||
|
||||
fun main() {
|
||||
return getOne<int>();
|
||||
}
|
||||
|
||||
/**
|
||||
@compilation_should_fail
|
||||
@stderr calling a not generic function with generic T
|
||||
*/
|
13
tolk-tester/tests/invalid-declaration-11.tolk
Normal file
13
tolk-tester/tests/invalid-declaration-11.tolk
Normal file
|
@ -0,0 +1,13 @@
|
|||
// this function is declared incorrectly,
|
||||
// since it should return 2 values onto a stack (1 for returned slice, 1 for mutated int)
|
||||
// but contains not 2 numbers in asm ret_order
|
||||
fun loadAddress2(mutate self: int): slice
|
||||
asm( -> 1 0 2) "LDMSGADDR";
|
||||
|
||||
fun main(){}
|
||||
|
||||
/**
|
||||
@compilation_should_fail
|
||||
@stderr ret_order (after ->) expected to contain 2 numbers
|
||||
@stderr asm( -> 1 0 2)
|
||||
*/
|
16
tolk-tester/tests/invalid-declaration-12.tolk
Normal file
16
tolk-tester/tests/invalid-declaration-12.tolk
Normal file
|
@ -0,0 +1,16 @@
|
|||
fun proxy(x: int) {
|
||||
return factorial(x);
|
||||
}
|
||||
|
||||
fun factorial(x: int) {
|
||||
if (x <= 0) {
|
||||
return 1;
|
||||
}
|
||||
return x * proxy(x-1);
|
||||
}
|
||||
|
||||
/**
|
||||
@compilation_should_fail
|
||||
@stderr could not infer return type of `factorial`, because it appears in a recursive call chain
|
||||
@stderr fun factorial
|
||||
*/
|
7
tolk-tester/tests/invalid-declaration-13.tolk
Normal file
7
tolk-tester/tests/invalid-declaration-13.tolk
Normal file
|
@ -0,0 +1,7 @@
|
|||
const c: slice = 123 + 456;
|
||||
|
||||
/**
|
||||
@compilation_should_fail
|
||||
@stderr expression type does not match declared type
|
||||
@stderr const c
|
||||
*/
|
10
tolk-tester/tests/invalid-generics-1.tolk
Normal file
10
tolk-tester/tests/invalid-generics-1.tolk
Normal file
|
@ -0,0 +1,10 @@
|
|||
fun f<X>(v: int, x: X) {}
|
||||
|
||||
fun failCantDeduceWithoutArgument() {
|
||||
return f(1);
|
||||
}
|
||||
|
||||
/**
|
||||
@compilation_should_fail
|
||||
@stderr can not deduce X for generic function `f<X>`
|
||||
*/
|
9
tolk-tester/tests/invalid-generics-10.tolk
Normal file
9
tolk-tester/tests/invalid-generics-10.tolk
Normal file
|
@ -0,0 +1,9 @@
|
|||
fun invalidReferencingGenericMethodWithoutGeneric() {
|
||||
var t = createEmptyTuple();
|
||||
var cb = t.tupleLast;
|
||||
}
|
||||
|
||||
/**
|
||||
@compilation_should_fail
|
||||
@stderr can not use a generic function `tupleLast<T>` as non-call
|
||||
*/
|
11
tolk-tester/tests/invalid-generics-11.tolk
Normal file
11
tolk-tester/tests/invalid-generics-11.tolk
Normal file
|
@ -0,0 +1,11 @@
|
|||
global gVar: int;
|
||||
|
||||
fun main() {
|
||||
var x = gVar<int>;
|
||||
return x;
|
||||
}
|
||||
|
||||
/**
|
||||
@compilation_should_fail
|
||||
@stderr generic T not expected here
|
||||
*/
|
10
tolk-tester/tests/invalid-generics-2.tolk
Normal file
10
tolk-tester/tests/invalid-generics-2.tolk
Normal file
|
@ -0,0 +1,10 @@
|
|||
fun f<T>(v: int, x: T) {}
|
||||
|
||||
fun failCantDeduceWithPlainNull() {
|
||||
return f(0, null);
|
||||
}
|
||||
|
||||
/**
|
||||
@compilation_should_fail
|
||||
@stderr can not deduce T for generic function `f<T>`
|
||||
*/
|
11
tolk-tester/tests/invalid-generics-3.tolk
Normal file
11
tolk-tester/tests/invalid-generics-3.tolk
Normal file
|
@ -0,0 +1,11 @@
|
|||
fun f<T>(x: T, y: T) {}
|
||||
|
||||
fun failIncompatibleTypesForT() {
|
||||
return f(32, "");
|
||||
}
|
||||
|
||||
/**
|
||||
@compilation_should_fail
|
||||
@stderr T is both int and slice for generic function `f<T>`
|
||||
@stderr f(32
|
||||
*/
|
10
tolk-tester/tests/invalid-generics-4.tolk
Normal file
10
tolk-tester/tests/invalid-generics-4.tolk
Normal file
|
@ -0,0 +1,10 @@
|
|||
fun f<T>(x: T): void asm "NOP";
|
||||
|
||||
fun failInstantiatingAsmFunctionWithNon1Slot() {
|
||||
f((1, 2));
|
||||
}
|
||||
|
||||
/**
|
||||
@compilation_should_fail
|
||||
@stderr can not call `f<T>` with T=(int, int), because it occupies 2 stack slots in TVM, not 1
|
||||
*/
|
10
tolk-tester/tests/invalid-generics-5.tolk
Normal file
10
tolk-tester/tests/invalid-generics-5.tolk
Normal file
|
@ -0,0 +1,10 @@
|
|||
fun f<T>(x: T): void asm "NOP";
|
||||
|
||||
fun failUsingGenericFunctionPartially() {
|
||||
var cb = f;
|
||||
}
|
||||
|
||||
/**
|
||||
@compilation_should_fail
|
||||
@stderr can not use a generic function `f<T>` as non-call
|
||||
*/
|
10
tolk-tester/tests/invalid-generics-6.tolk
Normal file
10
tolk-tester/tests/invalid-generics-6.tolk
Normal file
|
@ -0,0 +1,10 @@
|
|||
fun eq<X>(t: X) { return t; }
|
||||
|
||||
fun failUsingGenericFunctionPartially() {
|
||||
var cb = createEmptyTuple().eq().eq().tuplePush;
|
||||
}
|
||||
|
||||
/**
|
||||
@compilation_should_fail
|
||||
@stderr can not use a generic function `tuplePush<T>` as non-call
|
||||
*/
|
18
tolk-tester/tests/invalid-generics-7.tolk
Normal file
18
tolk-tester/tests/invalid-generics-7.tolk
Normal file
|
@ -0,0 +1,18 @@
|
|||
fun failOnInstantiation(a: slice) {
|
||||
var b: slice = foo(a);
|
||||
}
|
||||
|
||||
fun bar<X>(value: X) : X {
|
||||
return 1;
|
||||
}
|
||||
fun foo<X>(value: X) : X {
|
||||
return bar(value);
|
||||
}
|
||||
|
||||
/**
|
||||
@compilation_should_fail
|
||||
@stderr while instantiating generic function `foo<slice>`
|
||||
@stderr while instantiating generic function `bar<slice>`
|
||||
@stderr can not convert type `int` to return type `slice`
|
||||
@stderr return 1
|
||||
*/
|
11
tolk-tester/tests/invalid-generics-8.tolk
Normal file
11
tolk-tester/tests/invalid-generics-8.tolk
Normal file
|
@ -0,0 +1,11 @@
|
|||
fun withT1T2<T1, T2>(a: (T1, T2)) {}
|
||||
|
||||
fun wrongTCountPassed() {
|
||||
withT1T2<int>((5, ""));
|
||||
}
|
||||
|
||||
/**
|
||||
@compilation_should_fail
|
||||
@stderr wrong count of generic T: expected 2, got 1
|
||||
@stderr <int>
|
||||
*/
|
8
tolk-tester/tests/invalid-generics-9.tolk
Normal file
8
tolk-tester/tests/invalid-generics-9.tolk
Normal file
|
@ -0,0 +1,8 @@
|
|||
fun invalidProvidingGenericTsToNotGeneric() {
|
||||
beginCell<builder>();
|
||||
}
|
||||
|
||||
/**
|
||||
@compilation_should_fail
|
||||
@stderr calling a not generic function with generic T
|
||||
*/
|
9
tolk-tester/tests/invalid-mutate-16.tolk
Normal file
9
tolk-tester/tests/invalid-mutate-16.tolk
Normal file
|
@ -0,0 +1,9 @@
|
|||
fun cantCallMutatingFunctionWithAssignmentLValue() {
|
||||
var t: tuple = createEmptyTuple();
|
||||
(t = createEmptyTuple()).tuplePush(1);
|
||||
}
|
||||
|
||||
/**
|
||||
@compilation_should_fail
|
||||
@stderr assignment can not be used as lvalue
|
||||
*/
|
13
tolk-tester/tests/invalid-mutate-17.tolk
Normal file
13
tolk-tester/tests/invalid-mutate-17.tolk
Normal file
|
@ -0,0 +1,13 @@
|
|||
@pure
|
||||
fun tupleMut(mutate self: tuple): int
|
||||
asm "TLEN";
|
||||
|
||||
fun main() {
|
||||
var t = createEmptyTuple();
|
||||
return [[t.tupleMut]];
|
||||
}
|
||||
|
||||
/**
|
||||
@compilation_should_fail
|
||||
@stderr saving `tupleMut` into a variable is impossible, since it has `mutate` parameters
|
||||
*/
|
|
@ -4,5 +4,5 @@ fun load_u32(cs: slice): (slice, int) {
|
|||
|
||||
/**
|
||||
@compilation_should_fail
|
||||
@stderr expected `(`, got `32`
|
||||
@stderr expected `;`, got `32`
|
||||
*/
|
||||
|
|
|
@ -1,16 +0,0 @@
|
|||
global set: int;
|
||||
|
||||
@pure
|
||||
fun someF(): int {
|
||||
var set redef = 0;
|
||||
return set;
|
||||
}
|
||||
|
||||
/**
|
||||
@compilation_should_fail
|
||||
@stderr
|
||||
"""
|
||||
an impure operation in a pure function
|
||||
var set
|
||||
"""
|
||||
*/
|
|
@ -4,6 +4,6 @@ fun cantReturnNothingFromSelf(mutate self: int): self {
|
|||
|
||||
/**
|
||||
@compilation_should_fail
|
||||
@stderr missing return; forgot `return self`?
|
||||
@stderr missing return
|
||||
@stderr }
|
||||
*/
|
||||
|
|
|
@ -4,5 +4,5 @@ fun main(x: int) {
|
|||
|
||||
/**
|
||||
@compilation_should_fail
|
||||
@stderr null is not a function: use `null`, not `null()`
|
||||
@stderr calling a non-function
|
||||
*/
|
||||
|
|
9
tolk-tester/tests/invalid-syntax-6.tolk
Normal file
9
tolk-tester/tests/invalid-syntax-6.tolk
Normal file
|
@ -0,0 +1,9 @@
|
|||
fun main() {
|
||||
var a = 1;
|
||||
(a += 1) += 2;
|
||||
}
|
||||
|
||||
/**
|
||||
@compilation_should_fail
|
||||
@stderr assignment can not be used as lvalue
|
||||
*/
|
9
tolk-tester/tests/invalid-syntax-7.tolk
Normal file
9
tolk-tester/tests/invalid-syntax-7.tolk
Normal file
|
@ -0,0 +1,9 @@
|
|||
fun main() {
|
||||
var x = 1;
|
||||
x += (var y = 2);
|
||||
}
|
||||
|
||||
/**
|
||||
@compilation_should_fail
|
||||
@stderr expected <expression>, got `var`
|
||||
*/
|
|
@ -6,5 +6,5 @@ fun main() {
|
|||
/**
|
||||
@compilation_should_fail
|
||||
@stderr .tolk:2
|
||||
@stderr expected <type>, got `scli`
|
||||
@stderr unknown type name `scli`
|
||||
*/
|
||||
|
|
10
tolk-tester/tests/invalid-typing-12.tolk
Normal file
10
tolk-tester/tests/invalid-typing-12.tolk
Normal file
|
@ -0,0 +1,10 @@
|
|||
fun failAssignNullToTensor() {
|
||||
var ab = (1, 2);
|
||||
ab = null;
|
||||
return ab;
|
||||
}
|
||||
|
||||
/**
|
||||
@compilation_should_fail
|
||||
@stderr can not assign `null` to variable of type `(int, int)`
|
||||
*/
|
|
@ -15,5 +15,5 @@ fun cantMixDifferentThis() {
|
|||
|
||||
/**
|
||||
@compilation_should_fail
|
||||
@stderr cannot apply function appendBuilder : builder -> (builder, ()) to arguments of type int: cannot unify type int with builder
|
||||
@stderr can not call method for `builder` with object of type `int`
|
||||
*/
|
||||
|
|
|
@ -7,8 +7,6 @@ fun cantCallNotChainedMethodsInAChain(x: int) {
|
|||
}
|
||||
|
||||
/**
|
||||
The error is very weird, but nevertheless, the type system prevents of doing such errors.
|
||||
|
||||
@compilation_should_fail
|
||||
@stderr cannot apply function incNotChained : int -> (int, ()) to arguments of type (): cannot unify type () with int
|
||||
@stderr can not call method for `int` with object of type `void`
|
||||
*/
|
||||
|
|
|
@ -7,8 +7,7 @@ fun failWhenReturnANotChainedValue(x: int): int {
|
|||
}
|
||||
|
||||
/**
|
||||
The error is very weird, but nevertheless, the type system prevents of doing such errors.
|
||||
|
||||
@compilation_should_fail
|
||||
@stderr previous function return type int cannot be unified with return statement expression type (): cannot unify type () with int
|
||||
@stderr x.incNotChained()
|
||||
@stderr can not convert type `void` to return type `int`
|
||||
*/
|
||||
|
|
|
@ -4,5 +4,5 @@ fun failWhenTernaryConditionNotInt(cs: slice) {
|
|||
|
||||
/**
|
||||
@compilation_should_fail
|
||||
@stderr condition of ternary ?: operator must be an integer
|
||||
@stderr condition of ternary operator must be an integer
|
||||
*/
|
||||
|
|
9
tolk-tester/tests/invalid-typing-7.tolk
Normal file
9
tolk-tester/tests/invalid-typing-7.tolk
Normal file
|
@ -0,0 +1,9 @@
|
|||
fun failAssignPlainNullToVariable() {
|
||||
var x = null;
|
||||
}
|
||||
|
||||
/**
|
||||
@compilation_should_fail
|
||||
@stderr can not infer type of `x`, it's always null
|
||||
@stderr specify its type with `x: <type>` or use `null as <type>`
|
||||
*/
|
8
tolk-tester/tests/invalid-typing-8.tolk
Normal file
8
tolk-tester/tests/invalid-typing-8.tolk
Normal file
|
@ -0,0 +1,8 @@
|
|||
fun failExplicitCastIncompatible(c: cell) {
|
||||
return c as slice;
|
||||
}
|
||||
|
||||
/**
|
||||
@compilation_should_fail
|
||||
@stderr type `cell` can not be cast to `slice`
|
||||
*/
|
13
tolk-tester/tests/invalid-typing-9.tolk
Normal file
13
tolk-tester/tests/invalid-typing-9.tolk
Normal file
|
@ -0,0 +1,13 @@
|
|||
fun getTupleLastGetter<X>(): tuple -> X {
|
||||
return tupleLast<X>;
|
||||
}
|
||||
|
||||
fun failTypeMismatch() {
|
||||
var t = createEmptyTuple();
|
||||
var c: cell = getTupleLastGetter<int>()(t);
|
||||
}
|
||||
|
||||
/**
|
||||
@compilation_should_fail
|
||||
@stderr can not assign `int` to variable of type `cell`
|
||||
*/
|
|
@ -54,7 +54,8 @@ fun testDict(last: int) {
|
|||
|
||||
@method_id(105)
|
||||
fun testNotNull(x: int) {
|
||||
return [x == null, null == x, !(x == null), null == null, +(null != null)];
|
||||
// return [x == null, null == x, !(x == null), null == null, +(null != null)];
|
||||
return [x == null, null == x, !(x == null)];
|
||||
}
|
||||
|
||||
@method_id(106)
|
||||
|
@ -144,8 +145,8 @@ fun main() {
|
|||
@testcase | 104 | 50 | 3 5 -1
|
||||
@testcase | 104 | 100 | 3 5 5
|
||||
@testcase | 104 | 0 | 3 -1 5
|
||||
@testcase | 105 | 0 | [ 0 0 -1 -1 0 ]
|
||||
@testcase | 105 | null | [ -1 -1 0 -1 0 ]
|
||||
@testcase | 105 | 0 | [ 0 0 -1 ]
|
||||
@testcase | 105 | null | [ -1 -1 0 ]
|
||||
@testcase | 106 | | [ 0 0 0 -1 ] [ 0 0 0 ] [ -1 -1 -1 ] [ 0 -1 ]
|
||||
@testcase | 107 | | [ -1 -1 0 -1 ] [ 0 0 0 ] [ -1 -1 -1 ] [ -1 0 ]
|
||||
@testcase | 108 | 1 2 | -1
|
||||
|
|
|
@ -154,7 +154,7 @@ fun getSumOfNumbersInCell(c: cell): int {
|
|||
|
||||
@method_id(110)
|
||||
fun testStoreChaining() {
|
||||
var b = beginCell().storeUint(1, 32).storeUint(2, 32).storeUint(3, 32);
|
||||
var b = ((beginCell()).storeUint(1, 32)).storeUint(2, 32).storeUint(3, 32);
|
||||
b.storeUint(4, 32);
|
||||
b.myStoreUint(5, 32).storeUint(6, 32);
|
||||
storeUint(mutate b, 7, 32);
|
||||
|
@ -198,7 +198,7 @@ fun testStoreAndMutateBoth() {
|
|||
b.myStoreU32_and_mutate_x(mutate x);
|
||||
|
||||
var cs: slice = b.endCell().beginParse();
|
||||
var (n1,n2,n3,n4,n5) = (cs.loadUint(32),cs.loadUint(32),cs.loadUint(32),cs.loadUint(32),cs.loadUint(32));
|
||||
var (n1,n2,n3,n4,n5) = (cs.loadUint(32),((cs)).loadUint(32),cs.loadUint(32),cs.loadUint(32),cs.loadUint(32));
|
||||
assert(n5 == x) throw 100;
|
||||
|
||||
return [n1,n2,n3,n4,n5];
|
||||
|
|
|
@ -7,12 +7,14 @@ fun test1() {
|
|||
numbers = listPrepend(2, numbers);
|
||||
numbers = listPrepend(3, numbers);
|
||||
numbers = listPrepend(4, numbers);
|
||||
var (h, numbers redef) = listSplit(numbers);
|
||||
var (h: int, numbers redef) = listSplit(numbers);
|
||||
h += listGetHead(numbers);
|
||||
|
||||
_ = null;
|
||||
(_, _) = (null, null);
|
||||
var t = createEmptyTuple();
|
||||
do {
|
||||
var num = numbers.listNext();
|
||||
var num: int = numbers.listNext();
|
||||
t.tuplePush(num);
|
||||
} while (numbers != null);
|
||||
|
||||
|
@ -44,7 +46,7 @@ fun test3(x: int) {
|
|||
}
|
||||
|
||||
fun getUntypedNull() {
|
||||
var untyped = null;
|
||||
var untyped: null = null;
|
||||
if (true) {
|
||||
return untyped;
|
||||
}
|
||||
|
@ -52,8 +54,8 @@ fun getUntypedNull() {
|
|||
}
|
||||
|
||||
@method_id(104)
|
||||
fun test4() {
|
||||
var (_, (_, untyped)) = (3, (createEmptyTuple, null));
|
||||
fun test4(): null {
|
||||
var (_, (_, untyped: null)) = (3, (createEmptyTuple, null));
|
||||
if (true) {
|
||||
return untyped;
|
||||
}
|
||||
|
@ -62,15 +64,10 @@ fun test4() {
|
|||
|
||||
@method_id(105)
|
||||
fun test5() {
|
||||
var n = getUntypedNull();
|
||||
var n: slice = getUntypedNull();
|
||||
return !(null == n) ? n.loadInt(32) : 100;
|
||||
}
|
||||
|
||||
@method_id(106)
|
||||
fun test6(x: int) {
|
||||
return x > null; // this compiles (for now), but fails at runtime
|
||||
}
|
||||
|
||||
@method_id(107)
|
||||
fun test7() {
|
||||
var b = beginCell().storeMaybeRef(null);
|
||||
|
@ -132,15 +129,6 @@ fun main() {
|
|||
}>
|
||||
"""
|
||||
|
||||
@fif_codegen
|
||||
"""
|
||||
test6 PROC:<{
|
||||
// x
|
||||
PUSHNULL // x _1
|
||||
GREATER // _2
|
||||
}>
|
||||
"""
|
||||
|
||||
@fif_codegen
|
||||
"""
|
||||
test7 PROC:<{
|
||||
|
|
|
@ -56,7 +56,7 @@ fun test8(b: int): int {
|
|||
return a;
|
||||
}
|
||||
|
||||
fun `_<p`(a: auto, b: auto): int { return true; }
|
||||
fun `_<p`(a: int, b: int): int { return true; }
|
||||
|
||||
fun main() {
|
||||
// ok to parse
|
||||
|
|
|
@ -32,7 +32,7 @@ fun test1(): int {
|
|||
@method_id(102)
|
||||
fun test2(value: int): int {
|
||||
save_contract_data(value);
|
||||
var (_, restored: auto) = get_contract_data();
|
||||
var (_, restored) = get_contract_data();
|
||||
return restored;
|
||||
}
|
||||
|
||||
|
|
|
@ -10,9 +10,9 @@ fun used_from_noncall2(): int { return int20; }
|
|||
fun used_as_noncall2(): int { return 0 * 0 + used_from_noncall2() + (0 << 0); }
|
||||
|
||||
global unused_gv: int;
|
||||
global used_gv: auto;
|
||||
global used_gv: int;
|
||||
|
||||
fun receiveGetter(): (() -> int) { return used_as_noncall2; }
|
||||
fun receiveGetter(): () -> int { return used_as_noncall2; }
|
||||
|
||||
@pure
|
||||
fun usedButOptimizedOut(x: int): int { return x + 2; }
|
||||
|
|
|
@ -187,7 +187,7 @@ fun myTupleAt<T>(self: tuple, idx: int): T {
|
|||
global tup111: tuple;
|
||||
|
||||
@method_id(111)
|
||||
fun testForallFunctionsWithSelf() {
|
||||
fun testForallFunctionsWithSelf(): (int, int, tuple) {
|
||||
var t = createEmptyTuple();
|
||||
tup111 = createEmptyTuple();
|
||||
t.myTuplePush(10);
|
||||
|
|
|
@ -218,7 +218,7 @@ fun fixed248_log2_const(): int {
|
|||
@pure
|
||||
@inline
|
||||
fun Pi_const_f254(): int {
|
||||
var (c: auto, _) = Pi_xconst_f254();
|
||||
var (c, _) = Pi_xconst_f254();
|
||||
return c;
|
||||
}
|
||||
|
||||
|
@ -1019,7 +1019,8 @@ fun test_nrand(n: int): tuple {
|
|||
repeat (n) {
|
||||
var x: int = fixed248_nrand();
|
||||
var bucket: int = (abs(x) >> 243); // 255 buckets starting from x=0, each 1/32 wide
|
||||
t.tset(bucket, t.tupleAt(bucket) + 1);
|
||||
var at_bucket: int = t.tupleAt(bucket);
|
||||
t.tset(bucket, at_bucket + 1);
|
||||
}
|
||||
return t;
|
||||
}
|
||||
|
|
|
@ -1,6 +1,3 @@
|
|||
fun unsafeGetInt<X>(any: X): int
|
||||
asm "NOP";
|
||||
|
||||
fun foo(x: int): int {
|
||||
try {
|
||||
if (x == 7) {
|
||||
|
@ -28,7 +25,7 @@ fun foo_inlineref(x: int): int {
|
|||
if (x == 7) { throw (44, 2); }
|
||||
return x;
|
||||
} catch (_, arg) {
|
||||
return unsafeGetInt(arg);
|
||||
return arg as int;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -83,7 +80,7 @@ fun foo_big(
|
|||
}
|
||||
return x1 + x2 + x3 + x4 + x5 + x6 + x7 + x8 + x9 + x10 + x11 + x12 + x13 + x14 + x15 + x16 + x17 + x18 + x19 + x20;
|
||||
} catch (code, arg) {
|
||||
return unsafeGetInt(arg);
|
||||
return arg as int;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -92,7 +92,7 @@ fun always_throw2(x: int) {
|
|||
throw 239 + x;
|
||||
}
|
||||
|
||||
global global_f: int -> ();
|
||||
global global_f: int -> void;
|
||||
|
||||
@method_id(104)
|
||||
fun testGlobalVarApply() {
|
||||
|
@ -105,6 +105,30 @@ fun testGlobalVarApply() {
|
|||
}
|
||||
}
|
||||
|
||||
@method_id(105)
|
||||
fun testVarApply2() {
|
||||
var creator = createEmptyTuple;
|
||||
var t = creator();
|
||||
t.tuplePush(1);
|
||||
var sizer = t.tupleSize;
|
||||
return sizer(t);
|
||||
}
|
||||
|
||||
fun getTupleLastGetter<X>(): (tuple) -> X {
|
||||
return tupleLast<X>;
|
||||
}
|
||||
|
||||
@method_id(106)
|
||||
fun testVarApply3() {
|
||||
var t = createEmptyTuple();
|
||||
t.tuplePush(1);
|
||||
t.tuplePush([2]);
|
||||
var getIntAt = t.tupleAt<int>;
|
||||
var getTupleFirstInt = createEmptyTuple().tupleFirst<int>;
|
||||
var getTupleLastTuple = getTupleLastGetter<tuple>();
|
||||
return (getIntAt(t, 0), getTupleFirstInt(t), getTupleLastTuple(t), getTupleLastGetter<tuple>()(t));
|
||||
}
|
||||
|
||||
fun main() {}
|
||||
|
||||
/**
|
||||
|
@ -112,4 +136,6 @@ fun main() {}
|
|||
@testcase | 102 | | 1000
|
||||
@testcase | 103 | | [ 1000 1000 0 1001 ]
|
||||
@testcase | 104 | | 240
|
||||
@testcase | 105 | | 1
|
||||
@testcase | 106 | | 1 1 [ 2 ] [ 2 ]
|
||||
*/
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue