mirror of
https://github.com/ton-blockchain/ton
synced 2025-03-09 15:40:10 +00:00
[Tolk] Implement logical operators && ||
Unary logical NOT was already implemented earlier. Logical AND OR are expressed via conditional expression: * a && b -> a ? (b != 0) : 0 * a || b -> a ? 1 : (b != 0) They work as expected in any expressions. For instance, having `cond && f()`, f is called only if cond is true. For primitive cases, like `a > 0 && b > 0`, Fift code is not optimal, it could potentially be without IFs. These are moments of future optimizations. For now, it's more than enough.
This commit is contained in:
parent
16824fcfe3
commit
d110022731
4 changed files with 204 additions and 46 deletions
|
@ -1,8 +0,0 @@
|
||||||
fun main() {
|
|
||||||
return 1 && 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
@compilation_should_fail
|
|
||||||
@stderr logical operators are not supported yet
|
|
||||||
*/
|
|
|
@ -57,6 +57,72 @@ 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)];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@method_id(106)
|
||||||
|
fun testAndConstCodegen() {
|
||||||
|
return (
|
||||||
|
[1 && 0, 0 && 1, 0 && 0, 1 && 1],
|
||||||
|
[4 && 3 && 0, 5 && 0 && 7 && 8, (7 && 0) && -19],
|
||||||
|
[4 && 3 && -1, 5 && -100 && 7 && 8, (7 && (1 + 2)) && -19],
|
||||||
|
[true && false, true && true]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@method_id(107)
|
||||||
|
fun testOrConstCodegen() {
|
||||||
|
return (
|
||||||
|
[1 || 0, 0 || 1, 0 || 0, 1 || 1],
|
||||||
|
[0 || 0 || 0, 0 || (0 || 0), ((0 || 0) || 0) || 0],
|
||||||
|
[4 || 3 || -1, 0 || -100 || 0 || 0, (0 || (1 + -1)) || -19],
|
||||||
|
[true || false, false || false]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
global eqCallsCnt: int;
|
||||||
|
|
||||||
|
fun eq(x: int) { return x; }
|
||||||
|
fun eqCnt(x: int) { eqCallsCnt += 1; return x; }
|
||||||
|
fun isGt0(x: int) { return x > 0; }
|
||||||
|
|
||||||
|
fun alwaysThrows(): int { throw 444 ; return 444; }
|
||||||
|
|
||||||
|
@method_id(108)
|
||||||
|
fun testAndSimpleCodegen(a: int, b: int) {
|
||||||
|
return a && b;
|
||||||
|
}
|
||||||
|
|
||||||
|
@method_id(109)
|
||||||
|
fun testOrSimpleCodegen(a: int, b: int) {
|
||||||
|
return a > 0 || b > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@method_id(110)
|
||||||
|
fun testLogicalOps1(x: int) {
|
||||||
|
eqCallsCnt = 0;
|
||||||
|
return (
|
||||||
|
isGt0(x) || !isGt0(x) || alwaysThrows(),
|
||||||
|
x && eqCnt(x) && eqCnt(x - 1) && eqCnt(x - 2),
|
||||||
|
(400 == eq(x)) && alwaysThrows(),
|
||||||
|
(500 == eq(x)) || eqCnt(x) || false,
|
||||||
|
(500 == eq(x)) || eqCnt(x) || true,
|
||||||
|
eqCallsCnt
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@method_id(111)
|
||||||
|
fun testLogicalOps2(first: int) {
|
||||||
|
var s = beginCell().storeInt(1, 32).storeInt(2, 32).storeInt(3, 32).storeInt(4, 32).storeInt(5, 32).endCell().beginParse();
|
||||||
|
var sum = 0;
|
||||||
|
if (first && s.loadUint(32)) {
|
||||||
|
(2 == s.loadUint(32)) && (sum += s.loadUint(32));
|
||||||
|
(3 == s.loadUint(32)) && (sum += s.loadUint(32));
|
||||||
|
(5 == s.preloadUint(32)) && (sum += s.loadUint(32));
|
||||||
|
} else {
|
||||||
|
(10 == s.loadUint(32)) || (20 == s.loadUint(32)) || (3 == s.loadUint(32)) || (4 == s.loadUint(32));
|
||||||
|
sum += s.loadUint(32);
|
||||||
|
}
|
||||||
|
return (s.getRemainingBitsCount(), sum);
|
||||||
|
}
|
||||||
|
|
||||||
fun main() {
|
fun main() {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -80,6 +146,19 @@ fun main() {
|
||||||
@testcase | 104 | 0 | 3 -1 5
|
@testcase | 104 | 0 | 3 -1 5
|
||||||
@testcase | 105 | 0 | [ 0 0 -1 -1 0 ]
|
@testcase | 105 | 0 | [ 0 0 -1 -1 0 ]
|
||||||
@testcase | 105 | null | [ -1 -1 0 -1 0 ]
|
@testcase | 105 | null | [ -1 -1 0 -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
|
||||||
|
@testcase | 108 | 1 0 | 0
|
||||||
|
@testcase | 109 | -5 -4 | 0
|
||||||
|
@testcase | 109 | -5 4 | -1
|
||||||
|
@testcase | 109 | 1 99 | -1
|
||||||
|
@testcase | 110 | 0 | -1 0 0 0 -1 2
|
||||||
|
@testcase | 110 | 1 | -1 0 0 -1 -1 4
|
||||||
|
@testcase | 110 | 2 | -1 0 0 -1 -1 5
|
||||||
|
@testcase | 110 | 500 | -1 -1 0 -1 -1 3
|
||||||
|
@testcase | 111 | 0 | 32 4
|
||||||
|
@testcase | 111 | -1 | 0 8
|
||||||
|
|
||||||
@fif_codegen
|
@fif_codegen
|
||||||
"""
|
"""
|
||||||
|
@ -134,4 +213,83 @@ fun main() {
|
||||||
}>
|
}>
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
@fif_codegen
|
||||||
|
"""
|
||||||
|
testAndConstCodegen PROC:<{
|
||||||
|
//
|
||||||
|
FALSE
|
||||||
|
0 PUSHINT
|
||||||
|
DUP
|
||||||
|
TRUE
|
||||||
|
4 TUPLE
|
||||||
|
FALSE
|
||||||
|
0 PUSHINT
|
||||||
|
DUP
|
||||||
|
TRIPLE
|
||||||
|
TRUE
|
||||||
|
TRUE
|
||||||
|
TRUE
|
||||||
|
TRIPLE
|
||||||
|
FALSE
|
||||||
|
TRUE
|
||||||
|
PAIR
|
||||||
|
}>
|
||||||
|
"""
|
||||||
|
|
||||||
|
@fif_codegen
|
||||||
|
"""
|
||||||
|
testOrConstCodegen PROC:<{
|
||||||
|
//
|
||||||
|
-1 PUSHINT
|
||||||
|
TRUE
|
||||||
|
FALSE
|
||||||
|
s2 PUSH
|
||||||
|
4 TUPLE
|
||||||
|
FALSE
|
||||||
|
FALSE
|
||||||
|
FALSE
|
||||||
|
TRIPLE
|
||||||
|
-1 PUSHINT
|
||||||
|
DUP
|
||||||
|
TRUE
|
||||||
|
TRIPLE
|
||||||
|
-1 PUSHINT
|
||||||
|
FALSE
|
||||||
|
PAIR
|
||||||
|
}>
|
||||||
|
"""
|
||||||
|
|
||||||
|
Currently, && operator is implemented via ?: and is not optimal in primitive cases.
|
||||||
|
For example, `a && b` can be expressed without IFs.
|
||||||
|
These are moments of future optimizations. For now, it's more than enough.
|
||||||
|
@fif_codegen
|
||||||
|
"""
|
||||||
|
testAndSimpleCodegen PROC:<{
|
||||||
|
// a b
|
||||||
|
SWAP // b a
|
||||||
|
IF:<{ // b
|
||||||
|
0 NEQINT // _2
|
||||||
|
}>ELSE<{ // b
|
||||||
|
DROP //
|
||||||
|
0 PUSHINT // _2=0
|
||||||
|
}>
|
||||||
|
}>
|
||||||
|
"""
|
||||||
|
|
||||||
|
@fif_codegen
|
||||||
|
"""
|
||||||
|
testOrSimpleCodegen PROC:<{
|
||||||
|
// a b
|
||||||
|
SWAP // b a
|
||||||
|
0 GTINT // b _3
|
||||||
|
IF:<{ // b
|
||||||
|
DROP //
|
||||||
|
-1 PUSHINT // _4=-1
|
||||||
|
}>ELSE<{ // b
|
||||||
|
0 GTINT // _7
|
||||||
|
0 NEQINT // _4
|
||||||
|
}>
|
||||||
|
}>
|
||||||
|
"""
|
||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -160,6 +160,22 @@ static bool parse_raw_address(const std::string& acc_string, int& workchain, ton
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static Expr* create_expr_apply(SrcLocation loc, SymDef* sym, std::vector<Expr*>&& args) {
|
||||||
|
Expr* apply = new Expr(Expr::_Apply, sym, std::move(args));
|
||||||
|
apply->here = loc;
|
||||||
|
apply->flags = Expr::_IsRvalue;
|
||||||
|
apply->deduce_type();
|
||||||
|
return apply;
|
||||||
|
}
|
||||||
|
|
||||||
|
static Expr* create_expr_int_const(SrcLocation loc, int int_val) {
|
||||||
|
Expr* int_const = new Expr(Expr::_Const, loc);
|
||||||
|
int_const->intval = td::make_refint(int_val);
|
||||||
|
int_const->flags = Expr::_IsRvalue;
|
||||||
|
int_const->e_type = TypeExpr::new_atomic(TypeExpr::_Int);
|
||||||
|
return int_const;
|
||||||
|
}
|
||||||
|
|
||||||
namespace blk_fl {
|
namespace blk_fl {
|
||||||
enum { end = 1, ret = 2, empty = 4 };
|
enum { end = 1, ret = 2, empty = 4 };
|
||||||
typedef int val;
|
typedef int val;
|
||||||
|
@ -238,13 +254,10 @@ static Expr* process_expr(V<ast_binary_operator> v, CodeBlob& code) {
|
||||||
if (x->is_immutable()) {
|
if (x->is_immutable()) {
|
||||||
x->fire_error_modifying_immutable("left side of assignment");
|
x->fire_error_modifying_immutable("left side of assignment");
|
||||||
}
|
}
|
||||||
sym_idx_t name = G.symbols.lookup_add("^_" + operator_name + "_");
|
SymDef* sym = lookup_symbol(calc_sym_idx("^_" + operator_name + "_"));
|
||||||
Expr* y = process_expr(v->get_rhs(), code);
|
Expr* y = process_expr(v->get_rhs(), code);
|
||||||
y->chk_rvalue();
|
y->chk_rvalue();
|
||||||
Expr* z = new Expr{Expr::_Apply, name, {x, y}};
|
Expr* z = create_expr_apply(v->loc, sym, {x, y});
|
||||||
z->here = v->loc;
|
|
||||||
z->flags = Expr::_IsRvalue;
|
|
||||||
z->deduce_type();
|
|
||||||
Expr* res = new Expr{Expr::_Letop, {x->copy(), z}};
|
Expr* res = new Expr{Expr::_Letop, {x->copy(), z}};
|
||||||
res->here = v->loc;
|
res->here = v->loc;
|
||||||
res->flags = x->flags | Expr::_IsRvalue;
|
res->flags = x->flags | Expr::_IsRvalue;
|
||||||
|
@ -276,17 +289,27 @@ static Expr* process_expr(V<ast_binary_operator> v, CodeBlob& code) {
|
||||||
t == tok_mul || t == tok_div || t == tok_mod || t == tok_divC || t == tok_divR) {
|
t == tok_mul || t == tok_div || t == tok_mod || t == tok_divC || t == tok_divR) {
|
||||||
Expr* res = process_expr(v->get_lhs(), code);
|
Expr* res = process_expr(v->get_lhs(), code);
|
||||||
res->chk_rvalue();
|
res->chk_rvalue();
|
||||||
sym_idx_t name = G.symbols.lookup_add("_" + operator_name + "_");
|
SymDef* sym = lookup_symbol(calc_sym_idx("_" + operator_name + "_"));
|
||||||
Expr* x = process_expr(v->get_rhs(), code);
|
Expr* x = process_expr(v->get_rhs(), code);
|
||||||
x->chk_rvalue();
|
x->chk_rvalue();
|
||||||
res = new Expr{Expr::_Apply, name, {res, x}};
|
res = create_expr_apply(v->loc, sym, {res, x});
|
||||||
res->here = v->loc;
|
|
||||||
res->flags = Expr::_IsRvalue;
|
|
||||||
res->deduce_type();
|
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
if (t == tok_logical_and || t == tok_logical_or) {
|
if (t == tok_logical_and || t == tok_logical_or) {
|
||||||
v->error("logical operators are not supported yet");
|
// do the following transformations:
|
||||||
|
// a && b -> a ? (b != 0) : 0
|
||||||
|
// a || b -> a ? 1 : (b != 0)
|
||||||
|
SymDef* sym_neq = lookup_symbol(calc_sym_idx("_!=_"));
|
||||||
|
Expr* lhs = process_expr(v->get_lhs(), code);
|
||||||
|
Expr* rhs = process_expr(v->get_rhs(), code);
|
||||||
|
Expr* e_neq0 = create_expr_apply(v->loc, sym_neq, {rhs, create_expr_int_const(v->loc, 0)});
|
||||||
|
Expr* e_when_true = t == tok_logical_and ? e_neq0 : create_expr_int_const(v->loc, -1);
|
||||||
|
Expr* e_when_false = t == tok_logical_and ? create_expr_int_const(v->loc, 0) : e_neq0;
|
||||||
|
Expr* e_ternary = new Expr(Expr::_CondExpr, {lhs, e_when_true, e_when_false});
|
||||||
|
e_ternary->here = v->loc;
|
||||||
|
e_ternary->flags = Expr::_IsRvalue;
|
||||||
|
e_ternary->deduce_type();
|
||||||
|
return e_ternary;
|
||||||
}
|
}
|
||||||
|
|
||||||
v->error("unsupported binary operator");
|
v->error("unsupported binary operator");
|
||||||
|
@ -294,7 +317,7 @@ static Expr* process_expr(V<ast_binary_operator> v, CodeBlob& code) {
|
||||||
|
|
||||||
static Expr* process_expr(V<ast_unary_operator> v, CodeBlob& code) {
|
static Expr* process_expr(V<ast_unary_operator> v, CodeBlob& code) {
|
||||||
TokenType t = v->tok;
|
TokenType t = v->tok;
|
||||||
sym_idx_t name = G.symbols.lookup_add(static_cast<std::string>(v->operator_name) + "_");
|
SymDef* sym = lookup_symbol(calc_sym_idx(static_cast<std::string>(v->operator_name) + "_"));
|
||||||
Expr* x = process_expr(v->get_rhs(), code);
|
Expr* x = process_expr(v->get_rhs(), code);
|
||||||
x->chk_rvalue();
|
x->chk_rvalue();
|
||||||
|
|
||||||
|
@ -316,11 +339,7 @@ static Expr* process_expr(V<ast_unary_operator> v, CodeBlob& code) {
|
||||||
return x;
|
return x;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto res = new Expr{Expr::_Apply, name, {x}};
|
return create_expr_apply(v->loc, sym, {x});
|
||||||
res->here = v->loc;
|
|
||||||
res->flags = Expr::_IsRvalue;
|
|
||||||
res->deduce_type();
|
|
||||||
return res;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static Expr* process_expr(V<ast_ternary_operator> v, CodeBlob& code) {
|
static Expr* process_expr(V<ast_ternary_operator> v, CodeBlob& code) {
|
||||||
|
@ -683,19 +702,12 @@ static Expr* process_expr(V<ast_string_const> v) {
|
||||||
|
|
||||||
static Expr* process_expr(V<ast_bool_const> v) {
|
static Expr* process_expr(V<ast_bool_const> v) {
|
||||||
SymDef* builtin_sym = lookup_symbol(calc_sym_idx(v->bool_val ? "__true" : "__false"));
|
SymDef* builtin_sym = lookup_symbol(calc_sym_idx(v->bool_val ? "__true" : "__false"));
|
||||||
Expr* res = new Expr{Expr::_Apply, builtin_sym, {}};
|
return create_expr_apply(v->loc, builtin_sym, {});
|
||||||
res->flags = Expr::_IsRvalue;
|
|
||||||
res->deduce_type();
|
|
||||||
return res;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static Expr* process_expr(V<ast_null_keyword> v) {
|
static Expr* process_expr(V<ast_null_keyword> v) {
|
||||||
SymDef* builtin_sym = lookup_symbol(calc_sym_idx("__null"));
|
SymDef* builtin_sym = lookup_symbol(calc_sym_idx("__null"));
|
||||||
Expr* res = new Expr{Expr::_Apply, builtin_sym, {}};
|
return create_expr_apply(v->loc, builtin_sym, {});
|
||||||
res->here = v->loc;
|
|
||||||
res->flags = Expr::_IsRvalue;
|
|
||||||
res->deduce_type();
|
|
||||||
return res;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static Expr* process_expr(V<ast_self_keyword> v, CodeBlob& code) {
|
static Expr* process_expr(V<ast_self_keyword> v, CodeBlob& code) {
|
||||||
|
@ -1116,11 +1128,9 @@ static blk_fl::val process_vertex(V<ast_throw_statement> v, CodeBlob& code) {
|
||||||
args.push_back(process_expr(v->get_thrown_code(), code));
|
args.push_back(process_expr(v->get_thrown_code(), code));
|
||||||
}
|
}
|
||||||
|
|
||||||
Expr* expr = new Expr{Expr::_Apply, builtin_sym, std::move(args)};
|
Expr* apply = create_expr_apply(v->loc, builtin_sym, std::move(args));
|
||||||
expr->here = v->loc;
|
apply->flags |= Expr::_IsImpure;
|
||||||
expr->flags = Expr::_IsRvalue | Expr::_IsImpure;
|
apply->pre_compile(code);
|
||||||
expr->deduce_type();
|
|
||||||
expr->pre_compile(code);
|
|
||||||
return blk_fl::end;
|
return blk_fl::end;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1137,11 +1147,9 @@ static blk_fl::val process_vertex(V<ast_assert_statement> v, CodeBlob& code) {
|
||||||
}
|
}
|
||||||
|
|
||||||
SymDef* builtin_sym = lookup_symbol(calc_sym_idx("__throw_if_unless"));
|
SymDef* builtin_sym = lookup_symbol(calc_sym_idx("__throw_if_unless"));
|
||||||
Expr* expr = new Expr{Expr::_Apply, builtin_sym, std::move(args)};
|
Expr* apply = create_expr_apply(v->loc, builtin_sym, std::move(args));
|
||||||
expr->here = v->loc;
|
apply->flags |= Expr::_IsImpure;
|
||||||
expr->flags = Expr::_IsRvalue | Expr::_IsImpure;
|
apply->pre_compile(code);
|
||||||
expr->deduce_type();
|
|
||||||
expr->pre_compile(code);
|
|
||||||
return blk_fl::end;
|
return blk_fl::end;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -67,8 +67,8 @@ private:
|
||||||
public:
|
public:
|
||||||
|
|
||||||
static constexpr sym_idx_t not_found = 0;
|
static constexpr sym_idx_t not_found = 0;
|
||||||
sym_idx_t lookup(std::string_view str, int mode = 0) {
|
sym_idx_t lookup(std::string_view str) {
|
||||||
return gen_lookup(str, mode);
|
return gen_lookup(str, 0);
|
||||||
}
|
}
|
||||||
sym_idx_t lookup_add(std::string_view str) {
|
sym_idx_t lookup_add(std::string_view str) {
|
||||||
return gen_lookup(str, 1);
|
return gen_lookup(str, 1);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue