From 085cb43aaed860068f3c6bfde9b5bbe080e613e2 Mon Sep 17 00:00:00 2001 From: tolk-vm Date: Thu, 14 Nov 2024 17:34:56 +0300 Subject: [PATCH] [FunC] Make Expr::VarApply always impure Expr::_VarApply is now always impure. It means, that for `some_var()`, don't make any considerations about runtime value, it's always called. --- crypto/func/auto-tests/tests/var-apply.fc | 132 ++++++++++++++++++++++ crypto/func/gen-abscode.cpp | 5 +- crypto/func/parse-func.cpp | 2 +- 3 files changed, 137 insertions(+), 2 deletions(-) create mode 100644 crypto/func/auto-tests/tests/var-apply.fc diff --git a/crypto/func/auto-tests/tests/var-apply.fc b/crypto/func/auto-tests/tests/var-apply.fc new file mode 100644 index 00000000..e6eb4f7e --- /dev/null +++ b/crypto/func/auto-tests/tests/var-apply.fc @@ -0,0 +1,132 @@ +tuple empty_tuple() asm "NIL"; +forall X -> (tuple, ()) ~tpush(tuple t, X value) asm "TPUSH"; +builder begin_cell() asm "NEWC"; +cell end_cell(builder b) asm "ENDC"; +slice begin_parse(cell c) asm "CTOS"; + + +_ getBeginCell() { + return begin_cell; +} + +_ getBeginParse() { + return begin_parse; +} + +(int, int) test101() method_id(101) { + var (_, f_end_cell) = (0, end_cell); + builder b = (getBeginCell())().store_int(1, 32); + b~store_int(2, 32); + var s = (getBeginParse())(f_end_cell(b)); + return (s~load_int(32), s~load_int(32)); +} + +() my_throw_always() inline { + throw(1000); +} + +_ get_raiser() inline { + return my_throw_always; +} + +int test102() method_id(102) { + try { + var raiser = get_raiser(); + raiser(); ;; `some_var()` is always impure, the compiler has no considerations about its runtime value + return 0; + } catch (_, code) { + return code; + } +} + +int sum(int a, int b) impure inline { + throw_unless(1000, a + b < 24); + return a + b; +} + +int mul(int a, int b) impure inline { + throw_unless(1001, a * b < 24); + return a * b; +} + +int sum_pure(int a, int b) inline { + throw_unless(1000, a + b < 24); + return a + b; +} + +int mul_pure(int a, int b) inline { + throw_unless(1001, a * b < 24); + return a * b; +} + +int demo_handler(int op, int query_id, int a, int b) { + if (op == 0xF2) { + var func = query_id % 2 == 0 ? sum : mul; + int result = func(a, b); + return 0; ;; result not used, we test that func is nevertheless called + } + if (op == 0xF3) { + var func = query_id % 2 == 0 ? sum_pure : mul_pure; + int result = func(a, b); + return 0; ;; the same for sum_pure, since `some_var()` is always impure + } + if (op == 0xF4) { + var func = query_id % 2 == 0 ? sum : mul; + int result = func(a, b); + return result; + } + return -1; +} + +tuple test103() method_id(103) { + tuple t = empty_tuple(); + try { + t~tpush(demo_handler(0xF2, 122, 100, 200)); + } catch(_, code) { + t~tpush(code); + } + try { + t~tpush(demo_handler(0xF4, 122, 100, 200)); + } catch(_, code) { + t~tpush(code); + } + try { + t~tpush(demo_handler(0xF3, 122, 10, 10)); + } catch(_, code) { + t~tpush(code); + } + try { + t~tpush(demo_handler(0xF3, 123, 10, 10)); + } catch(_, code) { + t~tpush(code); + } + return t; +} + +() always_throw2(int x) impure { + throw (239 + x); +} + +global int -> () global_f; + +int test104() method_id(104) { + try { + global_f = always_throw2; + global_f(1); + return 0; + } catch (_, code) { + return code; + } +} + +() main() { +} + + +{- + method_id | in | out +TESTCASE | 101 | | 1 2 +TESTCASE | 102 | | 1000 +TESTCASE | 103 | | [ 1000 1000 0 1001 ] +TESTCASE | 104 | | 240 +-} diff --git a/crypto/func/gen-abscode.cpp b/crypto/func/gen-abscode.cpp index 9989d10c..7421dabb 100644 --- a/crypto/func/gen-abscode.cpp +++ b/crypto/func/gen-abscode.cpp @@ -380,7 +380,10 @@ std::vector Expr::pre_compile(CodeBlob& code, std::vectorflags = Expr::_IsRvalue | (fun->flags & Expr::_IsImpure); } else { res = new Expr{Expr::_VarApply, {fun, x}}; - res->flags = Expr::_IsRvalue; + res->flags = Expr::_IsRvalue | Expr::_IsImpure; // for `some_var()`, don't make any considerations about runtime value, it's impure } return res; }