Skip to content

Commit 620a12d

Browse files
committed
[Tolk] Support intN and bool constants
Constant evaluator has been rewritten to utilize type inferring mechanism. Now, > const a = 1 as int8; is valid, and constants can not only be int/slice, but also intN/uintN and bool.
1 parent 49cc6d0 commit 620a12d

21 files changed

+238
-88
lines changed

tolk-tester/tests/co1.tolk

+30-2
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,16 @@ const str2int = 0xAABBCC;
1616

1717
const nibbles: int = 4;
1818

19+
const strange_zero = (!10 as int);
20+
const strange_minus_1: int = (!0 as int);
21+
22+
const true1 = true;
23+
const true2 = !!true;
24+
const true3 = true1 && true2;
25+
26+
const false1 = !true;
27+
const false2 = false1 || false;
28+
1929
fun iget1(): int { return int1; }
2030
fun iget2(): int { return int2; }
2131
fun iget3(): int { return int1+int2; }
@@ -43,6 +53,21 @@ asm "SDEQ";
4353
fun stslicer(b: builder, s: slice): builder
4454
asm "STSLICER";
4555

56+
@method_id(101)
57+
fun test1() {
58+
return (strange_zero, strange_minus_1);
59+
}
60+
61+
@method_id(102)
62+
fun test2() {
63+
return (true1, true2, true3);
64+
}
65+
66+
@method_id(103)
67+
fun test3() {
68+
return (false1, false2);
69+
}
70+
4671
fun main() {
4772
var i1: int = iget1();
4873
var i2: int = iget2();
@@ -66,7 +91,10 @@ fun main() {
6691
}
6792

6893
/**
69-
@testcase | 0 | | 0
94+
@testcase | 0 | | 0
95+
@testcase | 101 | | 0 -1
96+
@testcase | 102 | | -1 -1 -1
97+
@testcase | 103 | | 0 0
7098

71-
@code_hash 61273295789179921867241079778489100375537711211918844448475493726205774530743
99+
@code_hash 28102194299745406750019953961984060488024870092664444642078578246708959881688
72100
*/

tolk-tester/tests/intN-tests.tolk

+7-2
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@ fun getAnyInt(): int { return 0; }
33
fun getNullableInt32(): int32? { return 32; }
44
fun getNullableVarInt32(): varint32? { return 32; }
55

6+
const someN_u128: uint128 = 128;
7+
const someN_i32: int32 = 20 + (12 as int32) * (1 as uint8);
8+
69

710
fun autoInferInt8(x: int8) {
811
if (random()) { return x; }
@@ -50,7 +53,8 @@ fun test1(x: int8) {
5053

5154
var z = x + y;
5255
__expect_type(z, "int");
53-
return x / y;
56+
__expect_type(someN_u128, "uint128");
57+
return x / y + someN_u128;
5458
}
5559

5660
fun test2(): (uint8, uint8, uint8) {
@@ -60,6 +64,7 @@ fun test2(): (uint8, uint8, uint8) {
6064

6165
fun test3(op: int32, qid: uint64) {
6266
op = qid as int32;
67+
op = someN_i32;
6368

6469
op + qid;
6570
op = op + qid;
@@ -166,7 +171,7 @@ fun main() {
166171

167172
/**
168173
@testcase | 0 | | [ 0 0 0 ]
169-
@testcase | 101 | 0 | 1
174+
@testcase | 101 | 0 | 129
170175
@testcase | 104 | | 64 64 -1 0
171176
@testcase | 105 | | 3 -1 0
172177
@testcase | 114 | 5 | (null) (null) 0

tolk-tester/tests/invalid-cyclic-1.tolk

+1-2
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,5 @@ const TWO = ONE + 1;
33

44
/**
55
@compilation_should_fail
6-
@stderr const ONE
7-
@stderr undefined symbol `TWO`
6+
@stderr const `TWO` appears, directly or indirectly, in its own initializer
87
*/

tolk-tester/tests/invalid-declaration-13.tolk

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,6 @@ const c: slice = 123 + 456;
22

33
/**
44
@compilation_should_fail
5-
@stderr expression type does not match declared type
5+
@stderr can not assign `int` to `slice`
66
@stderr const c
77
*/
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
const ttt = 1;
2+
const asdf = ttt<int>;
3+
4+
/**
5+
@compilation_should_fail
6+
@stderr generic T not expected here
7+
*/
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
const asdf = ttt;
2+
3+
/**
4+
@compilation_should_fail
5+
@stderr undefined symbol `ttt`
6+
*/
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
fun foo() { return 1; }
2+
3+
const asdf = 1 + foo();
4+
5+
/**
6+
@compilation_should_fail
7+
@stderr not a constant expression
8+
@stderr const asdf
9+
*/
+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
const asdf = 1 + "";
2+
3+
/**
4+
@compilation_should_fail
5+
@stderr can not apply operator `+` to `int` and `slice`
6+
*/

tolk/ast-replacer.h

+1
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,7 @@ class ASTReplacerInFunctionBody : public ASTReplacer {
187187

188188

189189
const std::vector<FunctionPtr>& get_all_not_builtin_functions();
190+
const std::vector<GlobalConstPtr>& get_all_declared_constants();
190191

191192
template<class BodyReplacerT>
192193
void replace_ast_of_all_functions() {

tolk/ast-visitor.h

+1
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,7 @@ class ASTVisitorFunctionBody : public ASTVisitor {
180180

181181

182182
const std::vector<FunctionPtr>& get_all_not_builtin_functions();
183+
const std::vector<GlobalConstPtr>& get_all_declared_constants();
183184

184185
template<class BodyVisitorT>
185186
void visit_ast_of_all_functions() {

tolk/compiler-state.cpp

+4
Original file line numberDiff line numberDiff line change
@@ -70,4 +70,8 @@ const std::vector<FunctionPtr>& get_all_not_builtin_functions() {
7070
return G.all_functions;
7171
}
7272

73+
const std::vector<GlobalConstPtr>& get_all_declared_constants() {
74+
return G.all_constants;
75+
}
76+
7377
} // namespace tolk

tolk/constant-evaluator.cpp

+48-46
Original file line numberDiff line numberDiff line change
@@ -147,17 +147,21 @@ static td::RefInt256 parse_vertex_string_const_as_int(V<ast_string_const> v) {
147147
}
148148
}
149149

150+
static ConstantValue parse_vertex_string_const(V<ast_string_const> v) {
151+
return v->is_bitslice()
152+
? ConstantValue(parse_vertex_string_const_as_slice(v))
153+
: ConstantValue(parse_vertex_string_const_as_int(v));
154+
}
155+
150156

151157
struct ConstantEvaluator {
152158
static bool is_overflow(const td::RefInt256& intval) {
153159
return intval.is_null() || !intval->signed_fits_bits(257);
154160
}
155161

156162
static ConstantValue handle_unary_operator(V<ast_unary_operator> v, const ConstantValue& rhs) {
157-
if (!rhs.is_int()) {
158-
v->error("invalid operator, expecting integer");
159-
}
160-
td::RefInt256 intval = std::get<td::RefInt256>(rhs.value);
163+
tolk_assert(rhs.is_int()); // type inferring has passed before, so it's int/bool
164+
td::RefInt256 intval = rhs.as_int();
161165

162166
switch (v->tok) {
163167
case tok_minus:
@@ -167,7 +171,7 @@ struct ConstantEvaluator {
167171
break;
168172
case tok_bitwise_not:
169173
intval = ~intval;
170-
break;
174+
break;
171175
case tok_logical_not:
172176
intval = td::make_refint(intval == 0 ? -1 : 0);
173177
break;
@@ -178,15 +182,13 @@ struct ConstantEvaluator {
178182
if (is_overflow(intval)) {
179183
v->error("integer overflow");
180184
}
181-
return ConstantValue::from_int(std::move(intval));
185+
return ConstantValue(std::move(intval));
182186
}
183187

184188
static ConstantValue handle_binary_operator(V<ast_binary_operator> v, const ConstantValue& lhs, const ConstantValue& rhs) {
185-
if (!lhs.is_int() || !rhs.is_int()) {
186-
v->error("invalid operator, expecting integer");
187-
}
188-
td::RefInt256 lhs_intval = std::get<td::RefInt256>(lhs.value);
189-
td::RefInt256 rhs_intval = std::get<td::RefInt256>(rhs.value);
189+
tolk_assert(lhs.is_int() && rhs.is_int()); // type inferring has passed before, so they are int/bool
190+
td::RefInt256 lhs_intval = lhs.as_int();
191+
td::RefInt256 rhs_intval = rhs.as_int();
190192
td::RefInt256 intval;
191193

192194
switch (v->tok) {
@@ -238,39 +240,41 @@ struct ConstantEvaluator {
238240
case tok_neq:
239241
intval = td::make_refint(lhs_intval != rhs_intval ? -1 : 0);
240242
break;
243+
case tok_logical_and:
244+
intval = td::make_refint(lhs_intval != 0 && rhs_intval != 0 ? -1 : 0);
245+
break;
246+
case tok_logical_or:
247+
intval = td::make_refint(lhs_intval != 0 || rhs_intval != 0 ? -1 : 0);
248+
break;
241249
default:
242250
v->error("unsupported binary operator in constant expression");
243251
}
244252

245253
if (is_overflow(intval)) {
246254
v->error("integer overflow");
247255
}
248-
return ConstantValue::from_int(std::move(intval));
256+
return ConstantValue(std::move(intval));
249257
}
250258

259+
// `const a = 1 + b`, we met `b`
251260
static ConstantValue handle_reference(V<ast_reference> v) {
252-
// todo better handle "appears, directly or indirectly, in its own initializer"
253-
std::string_view name = v->get_name();
254-
const Symbol* sym = lookup_global_symbol(name);
255-
if (!sym) {
256-
v->error("undefined symbol `" + static_cast<std::string>(name) + "`");
257-
}
258-
GlobalConstPtr const_ref = sym->try_as<GlobalConstPtr>();
261+
GlobalConstPtr const_ref = v->sym->try_as<GlobalConstPtr>();
259262
if (!const_ref) {
260-
v->error("symbol `" + static_cast<std::string>(name) + "` is not a constant");
263+
v->error("symbol `" + static_cast<std::string>(v->get_name()) + "` is not a constant");
261264
}
262-
if (v->has_instantiationTs()) { // SOME_CONST<int>
263-
v->error("constant is not a generic");
265+
266+
if (!const_ref->value.initialized()) { // maybe, `b` was already calculated
267+
eval_and_assign_const_init_value(const_ref); // if not, dig recursively into `b`
264268
}
265-
return {const_ref->value};
269+
return const_ref->value;
266270
}
267271

268272
static ConstantValue visit(AnyExprV v) {
269273
if (auto v_int = v->try_as<ast_int_const>()) {
270-
return ConstantValue::from_int(v_int->intval);
274+
return ConstantValue(v_int->intval);
271275
}
272276
if (auto v_bool = v->try_as<ast_bool_const>()) {
273-
return ConstantValue::from_int(v_bool->bool_val ? -1 : 0);
277+
return ConstantValue(v_bool->bool_val ? -1 : 0);
274278
}
275279
if (auto v_unop = v->try_as<ast_unary_operator>()) {
276280
return handle_unary_operator(v_unop, visit(v_unop->get_rhs()));
@@ -284,34 +288,32 @@ struct ConstantEvaluator {
284288
if (auto v_par = v->try_as<ast_parenthesized_expression>()) {
285289
return visit(v_par->get_expr());
286290
}
287-
if (v->try_as<ast_string_const>()) {
288-
return eval_const_init_value(v);
291+
if (auto v_cast_to = v->try_as<ast_cast_as_operator>()) {
292+
return visit(v_cast_to->get_expr());
293+
}
294+
if (auto v_string = v->try_as<ast_string_const>()) {
295+
return parse_vertex_string_const(v_string);
289296
}
290297
v->error("not a constant expression");
291298
}
292299

293-
static ConstantValue eval_const_init_value(AnyExprV init_value) {
294-
// it init_value is incorrect, an exception is thrown
295-
return visit(init_value);
300+
// evaluate `const a = 2 + 3` into 5
301+
// type inferring has already passed, to types are correct, `const a = 1 + ""` can not occur
302+
// recursive initializers `const a = b; const b = a` also 100% don't exist, checked on type inferring
303+
// if init_value is not a constant expression like `const a = foo()`, an exception is thrown
304+
static ConstantValue eval_const_init_value(GlobalConstPtr const_ref) {
305+
return visit(const_ref->init_value);
296306
}
297307
};
298308

299-
ConstantValue eval_const_init_value(AnyExprV init_value) {
300-
// at first, handle most simple cases, not to launch heavy computation algorithm: just a number, just a string
301-
// just `c = 1` or `c = 0xFF`
302-
if (auto v_int = init_value->try_as<ast_int_const>()) {
303-
return {v_int->intval};
304-
}
305-
// just `c = "strval"`, probably with modifier (address, etc.)
306-
if (auto v_string = init_value->try_as<ast_string_const>()) {
307-
if (v_string->is_bitslice()) {
308-
return {parse_vertex_string_const_as_slice(v_string)};
309-
} else {
310-
return {parse_vertex_string_const_as_int(v_string)};
311-
}
312-
}
313-
// something more complex, like `c = anotherC` or `c = 1 << 8`
314-
return ConstantEvaluator::eval_const_init_value(init_value);
309+
ConstantValue eval_string_const_considering_modifier(AnyExprV v_string) {
310+
tolk_assert(v_string->type == ast_string_const);
311+
return parse_vertex_string_const(v_string->as<ast_string_const>());
312+
}
313+
314+
void eval_and_assign_const_init_value(GlobalConstPtr const_ref) {
315+
ConstantValue init_value = ConstantEvaluator::eval_const_init_value(const_ref);
316+
const_ref->mutate()->assign_const_value(std::move(init_value));
315317
}
316318

317319
} // namespace tolk

tolk/constant-evaluator.h

+19-11
Original file line numberDiff line numberDiff line change
@@ -22,24 +22,32 @@
2222

2323
namespace tolk {
2424

25-
struct ConstantValue {
26-
std::variant<td::RefInt256, std::string> value;
25+
class ConstantValue {
26+
std::variant<
27+
td::RefInt256, // is set for int, intN, coins, bool
28+
std::string // is set for slice, bytesN
29+
> value;
30+
31+
public:
32+
ConstantValue() = default; // by default, not initialized
33+
34+
explicit ConstantValue(int value)
35+
: value(td::make_refint(value)) {}
36+
explicit ConstantValue(td::RefInt256 value)
37+
: value(std::move(value)) {}
38+
explicit ConstantValue(std::string value)
39+
: value(std::move(value)) {}
40+
41+
bool initialized() const { return is_slice() || std::get<td::RefInt256>(value).not_null(); }
2742

2843
bool is_int() const { return std::holds_alternative<td::RefInt256>(value); }
2944
bool is_slice() const { return std::holds_alternative<std::string>(value); }
3045

3146
td::RefInt256 as_int() const { return std::get<td::RefInt256>(value); }
3247
const std::string& as_slice() const { return std::get<std::string>(value); }
33-
34-
static ConstantValue from_int(int value) {
35-
return {td::make_refint(value)};
36-
}
37-
38-
static ConstantValue from_int(td::RefInt256 value) {
39-
return {std::move(value)};
40-
}
4148
};
4249

43-
ConstantValue eval_const_init_value(AnyExprV init_value);
50+
ConstantValue eval_string_const_considering_modifier(AnyExprV v_string);
51+
void eval_and_assign_const_init_value(GlobalConstPtr const_ref);
4452

4553
} // namespace tolk

tolk/pipe-ast-to-legacy.cpp

+4-4
Original file line numberDiff line numberDiff line change
@@ -644,13 +644,13 @@ std::vector<var_idx_t> pre_compile_symbol(SrcLocation loc, const Symbol* sym, Co
644644
return local_ir_idx;
645645
}
646646
if (GlobalConstPtr const_ref = sym->try_as<GlobalConstPtr>()) {
647-
if (const_ref->is_int_const()) {
647+
if (const_ref->value.is_int()) {
648648
std::vector<var_idx_t> rvect = code.create_tmp_var(TypeDataInt::create(), loc, "(glob-const)");
649-
code.emplace_back(loc, Op::_IntConst, rvect, const_ref->as_int_const());
649+
code.emplace_back(loc, Op::_IntConst, rvect, const_ref->value.as_int());
650650
return rvect;
651651
} else {
652652
std::vector<var_idx_t> rvect = code.create_tmp_var(TypeDataSlice::create(), loc, "(glob-const)");
653-
code.emplace_back(loc, Op::_SliceConst, rvect, const_ref->as_slice_const());
653+
code.emplace_back(loc, Op::_SliceConst, rvect, const_ref->value.as_slice());
654654
return rvect;
655655
}
656656
}
@@ -1013,7 +1013,7 @@ static std::vector<var_idx_t> process_int_const(V<ast_int_const> v, CodeBlob& co
10131013
}
10141014

10151015
static std::vector<var_idx_t> process_string_const(V<ast_string_const> v, CodeBlob& code, TypePtr target_type) {
1016-
ConstantValue value = eval_const_init_value(v);
1016+
ConstantValue value = eval_string_const_considering_modifier(v);
10171017
std::vector<var_idx_t> rvect = code.create_tmp_var(v->inferred_type, v->loc, "(str-const)");
10181018
if (value.is_int()) {
10191019
code.emplace_back(v->loc, Op::_IntConst, rvect, value.as_int());

0 commit comments

Comments
 (0)