1
0
Fork 0
mirror of https://github.com/ton-blockchain/ton synced 2025-03-09 15:40:10 +00:00

FunC: enable asserts and fix try/catch stack corruption (#699)

* FunC: enable asserts in Release

* FunC: Fix analyzing infinite loops

* FunC: Allow catch with one tensor argument

* FunC: Fix try/catch stack corruption

---------

Co-authored-by: SpyCheese <mikle98@yandex.ru>
This commit is contained in:
EmelyanenkoK 2023-05-15 15:31:42 +03:00 committed by GitHub
parent 5abfe2337e
commit 583178ccb1
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
12 changed files with 276 additions and 129 deletions

View file

@ -38,6 +38,9 @@ TmpVar::TmpVar(var_idx_t _idx, int _cls, TypeExpr* _type, SymDef* sym, const Src
if (!_type) {
v_type = TypeExpr::new_hole();
}
if (cls == _Named) {
undefined = true;
}
}
void TmpVar::set_location(const SrcLocation& loc) {

View file

@ -42,14 +42,14 @@ int CodeBlob::split_vars(bool strict) {
}
std::vector<TypeExpr*> comp_types;
int k = var.v_type->extract_components(comp_types);
assert(k <= 254 && n <= 0x7fff00);
assert((unsigned)k == comp_types.size());
func_assert(k <= 254 && n <= 0x7fff00);
func_assert((unsigned)k == comp_types.size());
if (k != 1) {
var.coord = ~((n << 8) + k);
for (int i = 0; i < k; i++) {
auto v = create_var(vars[j].cls, comp_types[i], 0, vars[j].where.get());
assert(v == n + i);
assert(vars[v].idx == v);
func_assert(v == n + i);
func_assert(vars[v].idx == v);
vars[v].name = vars[j].name;
vars[v].coord = ((int)j << 8) + i + 1;
}
@ -75,9 +75,9 @@ bool CodeBlob::compute_used_code_vars() {
}
bool CodeBlob::compute_used_code_vars(std::unique_ptr<Op>& ops_ptr, const VarDescrList& var_info, bool edit) const {
assert(ops_ptr);
func_assert(ops_ptr);
if (!ops_ptr->next) {
assert(ops_ptr->cl == Op::_Nop);
func_assert(ops_ptr->cl == Op::_Nop);
return ops_ptr->set_var_info(var_info);
}
return compute_used_code_vars(ops_ptr->next, var_info, edit) | ops_ptr->compute_used_vars(*this, edit);
@ -346,7 +346,7 @@ bool Op::std_compute_used_vars(bool disabled) {
}
bool Op::compute_used_vars(const CodeBlob& code, bool edit) {
assert(next);
func_assert(next);
const VarDescrList& next_var_info = next->var_info;
if (cl == _Nop) {
return set_var_info_except(next_var_info, left);
@ -379,7 +379,7 @@ bool Op::compute_used_vars(const CodeBlob& code, bool edit) {
case _Let: {
// left = right
std::size_t cnt = next_var_info.count_used(left);
assert(left.size() == right.size());
func_assert(left.size() == right.size());
auto l_it = left.cbegin(), r_it = right.cbegin();
VarDescrList new_var_info{next_var_info};
new_var_info -= left;
@ -500,7 +500,7 @@ bool Op::compute_used_vars(const CodeBlob& code, bool edit) {
}
changes = (new_var_info.size() == n);
} while (changes <= edit);
assert(left.size() == 1);
func_assert(left.size() == 1);
bool last = new_var_info.count_used(left) == 0;
new_var_info += left;
if (last) {
@ -655,7 +655,7 @@ bool prune_unreachable(std::unique_ptr<Op>& ops) {
ops = std::move(op.block0);
return false;
}
reach = true;
reach = (op.cl != Op::_Again);
break;
}
case Op::_TryCatch: {
@ -684,7 +684,7 @@ void CodeBlob::prune_unreachable_code() {
void CodeBlob::fwd_analyze() {
VarDescrList values;
assert(ops && ops->cl == Op::_Import);
func_assert(ops && ops->cl == Op::_Import);
for (var_idx_t i : ops->left) {
values += i;
if (vars[i].v_type->is_int()) {
@ -765,7 +765,7 @@ VarDescrList Op::fwd_analyze(VarDescrList values) {
break;
case _Let: {
std::vector<VarDescr> old_val;
assert(left.size() == right.size());
func_assert(left.size() == right.size());
for (std::size_t i = 0; i < right.size(); i++) {
const VarDescr* ov = values[right[i]];
if (!ov && verbosity >= 5) {
@ -780,7 +780,7 @@ VarDescrList Op::fwd_analyze(VarDescrList values) {
}
std::cerr << std::endl;
}
// assert(ov);
// func_assert(ov);
if (ov) {
old_val.push_back(*ov);
} else {

View file

@ -0,0 +1,109 @@
int foo(int x) method_id(11) {
try {
if (x == 7) {
throw(44);
}
return x;
} catch (_, _) {
return 2;
}
}
int foo_inline(int x) inline method_id(12) {
try {
if (x == 7) {
throw(44);
}
return x;
} catch (_, _) {
return 2;
}
}
int foo_inlineref(int x) inline_ref method_id(13) {
try {
if (x == 7) {
throw(44);
}
return x;
} catch (_, _) {
return 2;
}
}
int test(int x, int y, int z) method_id(1) {
y = foo(y);
return x * 100 + y * 10 + z;
}
int test_inline(int x, int y, int z) method_id(2) {
y = foo_inline(y);
return x * 100 + y * 10 + z;
}
int test_inlineref(int x, int y, int z) method_id(3) {
y = foo_inlineref(y);
return x * 100 + y * 10 + z;
}
int foo_inline_big(
int x1, int x2, int x3, int x4, int x5, int x6, int x7, int x8, int x9, int x10,
int x11, int x12, int x13, int x14, int x15, int x16, int x17, int x18, int x19, int x20
) inline method_id(14) {
try {
if (x1 == 7) {
throw(44);
}
return x1 + x2 + x3 + x4 + x5 + x6 + x7 + x8 + x9 + x10 + x11 + x12 + x13 + x14 + x15 + x16 + x17 + x18 + x19 + x20;
} catch (_, _) {
return 1;
}
}
int test_inline_big(int x, int y, int z) method_id(4) {
y = foo_inline_big(
y, y + 1, y + 2, y + 3, y + 4, y + 5, y + 6, y + 7, y + 8, y + 9,
y + 10, y + 11, y + 12, y + 13, y + 14, y + 15, y + 16, y + 17, y + 18, y + 19);
return x * 1000000 + y * 1000 + z;
}
int foo_big(
int x1, int x2, int x3, int x4, int x5, int x6, int x7, int x8, int x9, int x10,
int x11, int x12, int x13, int x14, int x15, int x16, int x17, int x18, int x19, int x20
) method_id(15) {
try {
if (x1 == 7) {
throw(44);
}
return x1 + x2 + x3 + x4 + x5 + x6 + x7 + x8 + x9 + x10 + x11 + x12 + x13 + x14 + x15 + x16 + x17 + x18 + x19 + x20;
} catch (_, _) {
return 1;
}
}
int test_big(int x, int y, int z) method_id(5) {
y = foo_big(
y, y + 1, y + 2, y + 3, y + 4, y + 5, y + 6, y + 7, y + 8, y + 9,
y + 10, y + 11, y + 12, y + 13, y + 14, y + 15, y + 16, y + 17, y + 18, y + 19);
return x * 1000000 + y * 1000 + z;
}
() main() {
}
{-
method_id | in | out
TESTCASE | 1 | 1 2 3 | 123
TESTCASE | 1 | 3 8 9 | 389
TESTCASE | 1 | 3 7 9 | 329
TESTCASE | 2 | 1 2 3 | 123
TESTCASE | 2 | 3 8 9 | 389
TESTCASE | 2 | 3 7 9 | 329
TESTCASE | 3 | 1 2 3 | 123
TESTCASE | 3 | 3 8 9 | 389
TESTCASE | 3 | 3 7 9 | 329
TESTCASE | 4 | 4 8 9 | 4350009
TESTCASE | 4 | 4 7 9 | 4001009
TESTCASE | 5 | 4 8 9 | 4350009
TESTCASE | 5 | 4 7 9 | 4001009
-}

View file

@ -429,7 +429,7 @@ AsmOp push_const(td::RefInt256 x) {
}
AsmOp compile_add(std::vector<VarDescr>& res, std::vector<VarDescr>& args, const SrcLocation& where) {
assert(res.size() == 1 && args.size() == 2);
func_assert(res.size() == 1 && args.size() == 2);
VarDescr &r = res[0], &x = args[0], &y = args[1];
if (x.is_int_const() && y.is_int_const()) {
r.set_const(x.int_const + y.int_const);
@ -471,7 +471,7 @@ AsmOp compile_add(std::vector<VarDescr>& res, std::vector<VarDescr>& args, const
}
AsmOp compile_sub(std::vector<VarDescr>& res, std::vector<VarDescr>& args, const SrcLocation& where) {
assert(res.size() == 1 && args.size() == 2);
func_assert(res.size() == 1 && args.size() == 2);
VarDescr &r = res[0], &x = args[0], &y = args[1];
if (x.is_int_const() && y.is_int_const()) {
r.set_const(x.int_const - y.int_const);
@ -504,7 +504,7 @@ AsmOp compile_sub(std::vector<VarDescr>& res, std::vector<VarDescr>& args, const
}
AsmOp compile_negate(std::vector<VarDescr>& res, std::vector<VarDescr>& args, const SrcLocation& where) {
assert(res.size() == 1 && args.size() == 1);
func_assert(res.size() == 1 && args.size() == 1);
VarDescr &r = res[0], &x = args[0];
if (x.is_int_const()) {
r.set_const(-x.int_const);
@ -519,7 +519,7 @@ AsmOp compile_negate(std::vector<VarDescr>& res, std::vector<VarDescr>& args, co
}
AsmOp compile_and(std::vector<VarDescr>& res, std::vector<VarDescr>& args, const SrcLocation& where) {
assert(res.size() == 1 && args.size() == 2);
func_assert(res.size() == 1 && args.size() == 2);
VarDescr &r = res[0], &x = args[0], &y = args[1];
if (x.is_int_const() && y.is_int_const()) {
r.set_const(x.int_const & y.int_const);
@ -532,7 +532,7 @@ AsmOp compile_and(std::vector<VarDescr>& res, std::vector<VarDescr>& args, const
}
AsmOp compile_or(std::vector<VarDescr>& res, std::vector<VarDescr>& args, const SrcLocation& where) {
assert(res.size() == 1 && args.size() == 2);
func_assert(res.size() == 1 && args.size() == 2);
VarDescr &r = res[0], &x = args[0], &y = args[1];
if (x.is_int_const() && y.is_int_const()) {
r.set_const(x.int_const | y.int_const);
@ -545,7 +545,7 @@ AsmOp compile_or(std::vector<VarDescr>& res, std::vector<VarDescr>& args, const
}
AsmOp compile_xor(std::vector<VarDescr>& res, std::vector<VarDescr>& args, const SrcLocation& where) {
assert(res.size() == 1 && args.size() == 2);
func_assert(res.size() == 1 && args.size() == 2);
VarDescr &r = res[0], &x = args[0], &y = args[1];
if (x.is_int_const() && y.is_int_const()) {
r.set_const(x.int_const ^ y.int_const);
@ -558,7 +558,7 @@ AsmOp compile_xor(std::vector<VarDescr>& res, std::vector<VarDescr>& args, const
}
AsmOp compile_not(std::vector<VarDescr>& res, std::vector<VarDescr>& args, const SrcLocation& where) {
assert(res.size() == 1 && args.size() == 1);
func_assert(res.size() == 1 && args.size() == 1);
VarDescr &r = res[0], &x = args[0];
if (x.is_int_const()) {
r.set_const(~x.int_const);
@ -638,12 +638,12 @@ AsmOp compile_mul_internal(VarDescr& r, VarDescr& x, VarDescr& y, const SrcLocat
}
AsmOp compile_mul(std::vector<VarDescr>& res, std::vector<VarDescr>& args, const SrcLocation& where) {
assert(res.size() == 1 && args.size() == 2);
func_assert(res.size() == 1 && args.size() == 2);
return compile_mul_internal(res[0], args[0], args[1], where);
}
AsmOp compile_lshift(std::vector<VarDescr>& res, std::vector<VarDescr>& args, const SrcLocation& where) {
assert(res.size() == 1 && args.size() == 2);
func_assert(res.size() == 1 && args.size() == 2);
VarDescr &r = res[0], &x = args[0], &y = args[1];
if (y.is_int_const()) {
auto yv = y.int_const->to_long();
@ -686,7 +686,7 @@ AsmOp compile_lshift(std::vector<VarDescr>& res, std::vector<VarDescr>& args, co
AsmOp compile_rshift(std::vector<VarDescr>& res, std::vector<VarDescr>& args, const SrcLocation& where,
int round_mode) {
assert(res.size() == 1 && args.size() == 2);
func_assert(res.size() == 1 && args.size() == 2);
VarDescr &r = res[0], &x = args[0], &y = args[1];
if (y.is_int_const()) {
auto yv = y.int_const->to_long();
@ -755,13 +755,13 @@ AsmOp compile_div_internal(VarDescr& r, VarDescr& x, VarDescr& y, const SrcLocat
}
AsmOp compile_div(std::vector<VarDescr>& res, std::vector<VarDescr>& args, const SrcLocation& where, int round_mode) {
assert(res.size() == 1 && args.size() == 2);
func_assert(res.size() == 1 && args.size() == 2);
return compile_div_internal(res[0], args[0], args[1], where, round_mode);
}
AsmOp compile_mod(std::vector<VarDescr>& res, std::vector<VarDescr>& args, const src::SrcLocation& where,
int round_mode) {
assert(res.size() == 1 && args.size() == 2);
func_assert(res.size() == 1 && args.size() == 2);
VarDescr &r = res[0], &x = args[0], &y = args[1];
if (x.is_int_const() && y.is_int_const()) {
r.set_const(mod(x.int_const, y.int_const, round_mode));
@ -802,7 +802,7 @@ AsmOp compile_mod(std::vector<VarDescr>& res, std::vector<VarDescr>& args, const
AsmOp compile_muldiv(std::vector<VarDescr>& res, std::vector<VarDescr>& args, const SrcLocation& where,
int round_mode) {
assert(res.size() == 1 && args.size() == 3);
func_assert(res.size() == 1 && args.size() == 3);
VarDescr &r = res[0], &x = args[0], &y = args[1], &z = args[2];
if (x.is_int_const() && y.is_int_const() && z.is_int_const()) {
r.set_const(muldiv(x.int_const, y.int_const, z.int_const, round_mode));
@ -923,8 +923,8 @@ int compute_compare(const VarDescr& x, const VarDescr& y, int mode) {
}
AsmOp compile_cmp_int(std::vector<VarDescr>& res, std::vector<VarDescr>& args, int mode) {
assert(mode >= 1 && mode <= 7);
assert(res.size() == 1 && args.size() == 2);
func_assert(mode >= 1 && mode <= 7);
func_assert(res.size() == 1 && args.size() == 2);
VarDescr &r = res[0], &x = args[0], &y = args[1];
if (x.is_int_const() && y.is_int_const()) {
int v = compute_compare(x.int_const, y.int_const, mode);
@ -935,7 +935,7 @@ AsmOp compile_cmp_int(std::vector<VarDescr>& res, std::vector<VarDescr>& args, i
}
int v = compute_compare(x, y, mode);
// std::cerr << "compute_compare(" << x << ", " << y << ", " << mode << ") = " << v << std::endl;
assert(v);
func_assert(v);
if (!(v & (v - 1))) {
r.set_const(v - (v >> 2) - 2);
x.unused();
@ -971,7 +971,7 @@ AsmOp compile_cmp_int(std::vector<VarDescr>& res, std::vector<VarDescr>& args, i
}
AsmOp compile_throw(std::vector<VarDescr>& res, std::vector<VarDescr>& args, const SrcLocation&) {
assert(res.empty() && args.size() == 1);
func_assert(res.empty() && args.size() == 1);
VarDescr& x = args[0];
if (x.is_int_const() && x.int_const->unsigned_fits_bits(11)) {
x.unused();
@ -982,7 +982,7 @@ AsmOp compile_throw(std::vector<VarDescr>& res, std::vector<VarDescr>& args, con
}
AsmOp compile_cond_throw(std::vector<VarDescr>& res, std::vector<VarDescr>& args, bool mode) {
assert(res.empty() && args.size() == 2);
func_assert(res.empty() && args.size() == 2);
VarDescr &x = args[0], &y = args[1];
std::string suff = (mode ? "IF" : "IFNOT");
bool skip_cond = false;
@ -1003,7 +1003,7 @@ AsmOp compile_cond_throw(std::vector<VarDescr>& res, std::vector<VarDescr>& args
}
AsmOp compile_throw_arg(std::vector<VarDescr>& res, std::vector<VarDescr>& args, const SrcLocation&) {
assert(res.empty() && args.size() == 2);
func_assert(res.empty() && args.size() == 2);
VarDescr &x = args[1];
if (x.is_int_const() && x.int_const->unsigned_fits_bits(11)) {
x.unused();
@ -1014,7 +1014,7 @@ AsmOp compile_throw_arg(std::vector<VarDescr>& res, std::vector<VarDescr>& args,
}
AsmOp compile_cond_throw_arg(std::vector<VarDescr>& res, std::vector<VarDescr>& args, bool mode) {
assert(res.empty() && args.size() == 3);
func_assert(res.empty() && args.size() == 3);
VarDescr &x = args[1], &y = args[2];
std::string suff = (mode ? "IF" : "IFNOT");
bool skip_cond = false;
@ -1035,7 +1035,7 @@ AsmOp compile_cond_throw_arg(std::vector<VarDescr>& res, std::vector<VarDescr>&
}
AsmOp compile_bool_const(std::vector<VarDescr>& res, std::vector<VarDescr>& args, bool val) {
assert(res.size() == 1 && args.empty());
func_assert(res.size() == 1 && args.empty());
VarDescr& r = res[0];
r.set_const(val ? -1 : 0);
return AsmOp::Const(val ? "TRUE" : "FALSE");
@ -1046,7 +1046,7 @@ AsmOp compile_bool_const(std::vector<VarDescr>& res, std::vector<VarDescr>& args
// int preload_int(slice s, int len) asm "PLDIX";
// int preload_uint(slice s, int len) asm "PLDUX";
AsmOp compile_fetch_int(std::vector<VarDescr>& res, std::vector<VarDescr>& args, bool fetch, bool sgnd) {
assert(args.size() == 2 && res.size() == 1 + (unsigned)fetch);
func_assert(args.size() == 2 && res.size() == 1 + (unsigned)fetch);
auto &y = args[1], &r = res.back();
r.val = (sgnd ? VarDescr::FiniteInt : VarDescr::FiniteUInt);
int v = -1;
@ -1069,7 +1069,7 @@ AsmOp compile_fetch_int(std::vector<VarDescr>& res, std::vector<VarDescr>& args,
// builder store_uint(builder b, int x, int len) asm(x b len) "STUX";
// builder store_int(builder b, int x, int len) asm(x b len) "STIX";
AsmOp compile_store_int(std::vector<VarDescr>& res, std::vector<VarDescr>& args, bool sgnd) {
assert(args.size() == 3 && res.size() == 1);
func_assert(args.size() == 3 && res.size() == 1);
auto& z = args[2];
if (z.is_int_const() && z.int_const > 0 && z.int_const <= 256) {
z.unused();
@ -1079,7 +1079,7 @@ AsmOp compile_store_int(std::vector<VarDescr>& res, std::vector<VarDescr>& args,
}
AsmOp compile_fetch_slice(std::vector<VarDescr>& res, std::vector<VarDescr>& args, bool fetch) {
assert(args.size() == 2 && res.size() == 1 + (unsigned)fetch);
func_assert(args.size() == 2 && res.size() == 1 + (unsigned)fetch);
auto& y = args[1];
int v = -1;
if (y.is_int_const() && y.int_const > 0 && y.int_const <= 256) {
@ -1094,7 +1094,7 @@ AsmOp compile_fetch_slice(std::vector<VarDescr>& res, std::vector<VarDescr>& arg
// <type> <type>_at(tuple t, int index) asm "INDEXVAR";
AsmOp compile_tuple_at(std::vector<VarDescr>& res, std::vector<VarDescr>& args, const SrcLocation&) {
assert(args.size() == 2 && res.size() == 1);
func_assert(args.size() == 2 && res.size() == 1);
auto& y = args[1];
if (y.is_int_const() && y.int_const >= 0 && y.int_const < 16) {
y.unused();
@ -1105,7 +1105,7 @@ AsmOp compile_tuple_at(std::vector<VarDescr>& res, std::vector<VarDescr>& args,
// int null?(X arg)
AsmOp compile_is_null(std::vector<VarDescr>& res, std::vector<VarDescr>& args, const SrcLocation&) {
assert(args.size() == 1 && res.size() == 1);
func_assert(args.size() == 1 && res.size() == 1);
auto &x = args[0], &r = res[0];
if (x.always_null() || x.always_not_null()) {
x.unused();
@ -1118,7 +1118,7 @@ AsmOp compile_is_null(std::vector<VarDescr>& res, std::vector<VarDescr>& args, c
bool compile_run_method(AsmOpList& code, std::vector<VarDescr>& res, std::vector<VarDescr>& args, int n,
bool has_value) {
assert(args.size() == (unsigned)n + 1 && res.size() == (unsigned)has_value);
func_assert(args.size() == (unsigned)n + 1 && res.size() == (unsigned)has_value);
auto& x = args[0];
if (x.is_int_const() && x.int_const->unsigned_fits_bits(14)) {
x.unused();

View file

@ -165,7 +165,7 @@ void Stack::push_new_const(var_idx_t idx, const_idx_t cidx) {
void Stack::assign_var(var_idx_t new_idx, var_idx_t old_idx) {
int i = find(old_idx);
assert(i >= 0 && "variable not found in stack");
func_assert(i >= 0 && "variable not found in stack");
if (new_idx != old_idx) {
at(i).first = new_idx;
modified();
@ -174,10 +174,10 @@ void Stack::assign_var(var_idx_t new_idx, var_idx_t old_idx) {
void Stack::do_copy_var(var_idx_t new_idx, var_idx_t old_idx) {
int i = find(old_idx);
assert(i >= 0 && "variable not found in stack");
func_assert(i >= 0 && "variable not found in stack");
if (find(old_idx, i + 1) < 0) {
issue_push(i);
assert(at(0).first == old_idx);
func_assert(at(0).first == old_idx);
}
assign_var(new_idx, old_idx);
}
@ -199,21 +199,21 @@ void Stack::enforce_state(const StackLayout& req_stack) {
j = 0;
}
issue_xchg(j, depth() - i - 1);
assert(s[i].first == x);
func_assert(s[i].first == x);
}
while (depth() > k) {
issue_pop(0);
}
assert(depth() == k);
func_assert(depth() == k);
for (int i = 0; i < k; i++) {
assert(s[i].first == req_stack[i]);
func_assert(s[i].first == req_stack[i]);
}
}
void Stack::merge_const(const Stack& req_stack) {
assert(s.size() == req_stack.s.size());
func_assert(s.size() == req_stack.s.size());
for (std::size_t i = 0; i < s.size(); i++) {
assert(s[i].first == req_stack.s[i].first);
func_assert(s[i].first == req_stack.s[i].first);
if (s[i].second != req_stack.s[i].second) {
s[i].second = not_const;
}
@ -251,15 +251,15 @@ void Stack::rearrange_top(const StackLayout& top, std::vector<bool> last) {
if (last[i]) {
// rearrange x to be at s(ss-1)
issue_xchg(--ss, j);
assert(get(ss).first == x);
func_assert(get(ss).first == x);
} else {
// create a new copy of x
issue_push(j);
issue_xchg(0, ss);
assert(get(ss).first == x);
func_assert(get(ss).first == x);
}
}
assert(!ss);
func_assert(!ss);
}
void Stack::rearrange_top(var_idx_t top, bool last) {
@ -269,7 +269,7 @@ void Stack::rearrange_top(var_idx_t top, bool last) {
} else {
issue_push(i);
}
assert(get(0).first == top);
func_assert(get(0).first == top);
}
bool Op::generate_code_step(Stack& stack) {
@ -300,7 +300,7 @@ bool Op::generate_code_step(Stack& stack) {
stack.o << push_const(int_const);
stack.push_new_const(left[0], cidx);
} else {
assert(stack.at(i).second == cidx);
func_assert(stack.at(i).second == cidx);
stack.do_copy_var(left[0], stack[i]);
}
return true;
@ -329,7 +329,7 @@ bool Op::generate_code_step(Stack& stack) {
std::string name = sym::symbols.get_name(fun_ref->sym_idx);
stack.o << AsmOp::Custom(name + " GETGLOB", 0, 1);
if (left.size() != 1) {
assert(left.size() <= 15);
func_assert(left.size() <= 15);
stack.o << AsmOp::UnTuple((int)left.size());
}
for (auto i : left) {
@ -337,7 +337,7 @@ bool Op::generate_code_step(Stack& stack) {
}
return true;
} else {
assert(left.size() == 1);
func_assert(left.size() == 1);
auto p = next->var_info[left[0]];
if (!p || p->is_unused() || disabled()) {
return true;
@ -349,10 +349,10 @@ bool Op::generate_code_step(Stack& stack) {
// TODO: create and compile a true lambda instead of this (so that arg_order and ret_order would work correctly)
std::vector<VarDescr> args0, res;
TypeExpr::remove_indirect(func->sym_type);
assert(func->get_type()->is_map());
func_assert(func->get_type()->is_map());
auto wr = func->get_type()->args.at(0)->get_width();
auto wl = func->get_type()->args.at(1)->get_width();
assert(wl >= 0 && wr >= 0);
func_assert(wl >= 0 && wr >= 0);
for (int i = 0; i < wl; i++) {
res.emplace_back(0);
}
@ -370,7 +370,7 @@ bool Op::generate_code_step(Stack& stack) {
return true;
}
case _Let: {
assert(left.size() == right.size());
func_assert(left.size() == right.size());
int i = 0;
std::vector<bool> active;
active.reserve(left.size());
@ -420,13 +420,13 @@ bool Op::generate_code_step(Stack& stack) {
stack.rearrange_top(right, std::move(last));
stack.opt_show();
int k = (int)stack.depth() - (int)right.size();
assert(k >= 0);
func_assert(k >= 0);
if (cl == _Tuple) {
stack.o << AsmOp::Tuple((int)right.size());
assert(left.size() == 1);
func_assert(left.size() == 1);
} else {
stack.o << AsmOp::UnTuple((int)left.size());
assert(right.size() == 1);
func_assert(right.size() == 1);
}
stack.s.resize(k);
for (int i = 0; i < (int)left.size(); i++) {
@ -442,16 +442,16 @@ bool Op::generate_code_step(Stack& stack) {
SymValFunc* func = (fun_ref ? dynamic_cast<SymValFunc*>(fun_ref->value) : nullptr);
auto arg_order = (func ? func->get_arg_order() : nullptr);
auto ret_order = (func ? func->get_ret_order() : nullptr);
assert(!arg_order || arg_order->size() == right.size());
assert(!ret_order || ret_order->size() == left.size());
func_assert(!arg_order || arg_order->size() == right.size());
func_assert(!ret_order || ret_order->size() == left.size());
std::vector<var_idx_t> right1;
if (args.size()) {
assert(args.size() == right.size());
func_assert(args.size() == right.size());
for (int i = 0; i < (int)right.size(); i++) {
int j = arg_order ? arg_order->at(i) : i;
const VarDescr& arg = args.at(j);
if (!arg.is_unused()) {
assert(var_info[arg.idx] && !var_info[arg.idx]->is_unused());
func_assert(var_info[arg.idx] && !var_info[arg.idx]->is_unused());
right1.push_back(arg.idx);
}
}
@ -469,17 +469,25 @@ bool Op::generate_code_step(Stack& stack) {
stack.rearrange_top(right1, std::move(last));
stack.opt_show();
int k = (int)stack.depth() - (int)right1.size();
assert(k >= 0);
func_assert(k >= 0);
for (int i = 0; i < (int)right1.size(); i++) {
if (stack.s[k + i].first != right1[i]) {
std::cerr << stack.o;
}
assert(stack.s[k + i].first == right1[i]);
func_assert(stack.s[k + i].first == right1[i]);
}
auto exec_callxargs = [&](int args, int ret) {
if (args <= 15 && ret <= 15) {
stack.o << exec_arg2_op("CALLXARGS", args, ret, args + 1, ret);
} else {
func_assert(args <= 254 && ret <= 254);
stack.o << AsmOp::Const(PSTRING() << args << " PUSHINT");
stack.o << AsmOp::Const(PSTRING() << ret << " PUSHINT");
stack.o << AsmOp::Custom("CALLXVARARGS", args + 3, ret);
}
};
if (cl == _CallInd) {
// TODO: replace with exec_arg2_op()
stack.o << exec_arg2_op("CALLXARGS", (int)right.size() - 1, (int)left.size(), (int)right.size(),
(int)left.size());
exec_callxargs((int)right.size() - 1, (int)left.size());
} else {
auto func = dynamic_cast<const SymValAsmFunc*>(fun_ref->value);
if (func) {
@ -493,8 +501,14 @@ bool Op::generate_code_step(Stack& stack) {
auto fv = dynamic_cast<const SymValCodeFunc*>(fun_ref->value);
std::string name = sym::symbols.get_name(fun_ref->sym_idx);
bool is_inline = (fv && (fv->flags & 3));
stack.o << AsmOp::Custom(name + (is_inline ? " INLINECALLDICT" : " CALLDICT"), (int)right.size(),
(int)left.size());
if (is_inline) {
stack.o << AsmOp::Custom(name + " INLINECALLDICT", (int)right.size(), (int)left.size());
} else if (fv && fv->code && fv->code->require_callxargs) {
stack.o << AsmOp::Custom(name + (" PREPAREDICT"), 0, 2);
exec_callxargs((int)right.size() + 1, (int)left.size());
} else {
stack.o << AsmOp::Custom(name + " CALLDICT", (int)right.size(), (int)left.size());
}
}
}
stack.s.resize(k);
@ -505,7 +519,7 @@ bool Op::generate_code_step(Stack& stack) {
return true;
}
case _SetGlob: {
assert(fun_ref && dynamic_cast<const SymValGlobVar*>(fun_ref->value));
func_assert(fun_ref && dynamic_cast<const SymValGlobVar*>(fun_ref->value));
std::vector<bool> last;
for (var_idx_t x : right) {
last.push_back(var_info[x] && var_info[x]->is_last());
@ -513,12 +527,12 @@ bool Op::generate_code_step(Stack& stack) {
stack.rearrange_top(right, std::move(last));
stack.opt_show();
int k = (int)stack.depth() - (int)right.size();
assert(k >= 0);
func_assert(k >= 0);
for (int i = 0; i < (int)right.size(); i++) {
if (stack.s[k + i].first != right[i]) {
std::cerr << stack.o;
}
assert(stack.s[k + i].first == right[i]);
func_assert(stack.s[k + i].first == right[i]);
}
if (right.size() > 1) {
stack.o << AsmOp::Tuple((int)right.size());
@ -539,7 +553,7 @@ bool Op::generate_code_step(Stack& stack) {
}
var_idx_t x = left[0];
stack.rearrange_top(x, var_info[x] && var_info[x]->is_last());
assert(stack[0] == x);
func_assert(stack[0] == x);
stack.opt_show();
stack.s.pop_back();
stack.modified();
@ -651,7 +665,7 @@ bool Op::generate_code_step(Stack& stack) {
var_idx_t x = left[0];
//stack.drop_vars_except(block0->var_info, x);
stack.rearrange_top(x, var_info[x] && var_info[x]->is_last());
assert(stack[0] == x);
func_assert(stack[0] == x);
stack.opt_show();
stack.s.pop_back();
stack.modified();
@ -877,12 +891,13 @@ void Op::generate_code_all(Stack& stack) {
void CodeBlob::generate_code(AsmOpList& out, int mode) {
Stack stack{out, mode};
assert(ops && ops->cl == Op::_Import);
func_assert(ops && ops->cl == Op::_Import);
auto args = (int)ops->left.size();
for (var_idx_t x : ops->left) {
stack.push_new_var(x);
}
ops->generate_code_all(stack);
stack.apply_wrappers();
stack.apply_wrappers(require_callxargs && (mode & Stack::_InlineAny) ? args : -1);
if (!(mode & Stack::_DisableOpt)) {
optimize_code(out);
}

View file

@ -72,7 +72,7 @@ td::Result<std::string> fs_read_callback(ReadCallback::Kind kind, const char* qu
void generate_output_func(SymDef* func_sym, std::ostream &outs, std::ostream &errs) {
SymValCodeFunc* func_val = dynamic_cast<SymValCodeFunc*>(func_sym->value);
assert(func_val);
func_assert(func_val);
std::string name = sym::symbols.get_name(func_sym->sym_idx);
if (verbosity >= 2) {
errs << "\n\n=========================\nfunction " << name << " : " << func_val->get_type() << std::endl;
@ -145,6 +145,9 @@ void generate_output_func(SymDef* func_sym, std::ostream &outs, std::ostream &er
if (fv && (fv->flags & 1) && code.ops->noreturn()) {
mode |= Stack::_InlineFunc;
}
if (fv && (fv->flags & 3)) {
mode |= Stack::_InlineAny;
}
code.generate_code(outs, mode, indent + 1);
outs << std::string(indent * 2, ' ') << "}>\n";
if (verbosity >= 2) {
@ -163,7 +166,7 @@ int generate_output(std::ostream &outs, std::ostream &errs) {
}
for (SymDef* func_sym : glob_func) {
SymValCodeFunc* func_val = dynamic_cast<SymValCodeFunc*>(func_sym->value);
assert(func_val);
func_assert(func_val);
std::string name = sym::symbols.get_name(func_sym->sym_idx);
outs << std::string(indent * 2, ' ');
if (func_val->method_id.is_null()) {
@ -173,7 +176,7 @@ int generate_output(std::ostream &outs, std::ostream &errs) {
}
}
for (SymDef* gvar_sym : glob_vars) {
assert(dynamic_cast<SymValGlobVar*>(gvar_sym->value));
func_assert(dynamic_cast<SymValGlobVar*>(gvar_sym->value));
std::string name = sym::symbols.get_name(gvar_sym->sym_idx);
outs << std::string(indent * 2, ' ') << "DECLGLOBVAR " << name << "\n";
}

View file

@ -33,6 +33,10 @@
#include "parser/symtable.h"
#include "td/utils/Status.h"
#define func_assert(expr) \
(bool(expr) ? void(0) \
: throw src::Fatal(PSTRING() << "Assertion failed at " << __FILE__ << ":" << __LINE__ << ": " << #expr))
namespace funC {
extern int verbosity;
@ -310,6 +314,7 @@ struct TmpVar {
int coord;
std::unique_ptr<SrcLocation> where;
std::vector<std::function<void(const SrcLocation &)>> on_modification;
bool undefined = false;
TmpVar(var_idx_t _idx, int _cls, TypeExpr* _type = 0, SymDef* sym = 0, const SrcLocation* loc = 0);
void show(std::ostream& os, int omit_idx = 0) const;
void dump(std::ostream& os) const;
@ -694,6 +699,7 @@ struct CodeBlob {
std::unique_ptr<Op>* cur_ops;
std::stack<std::unique_ptr<Op>*> cur_ops_stack;
int flags = 0;
bool require_callxargs = false;
CodeBlob(TypeExpr* ret = nullptr) : var_cnt(0), in_var_cnt(0), op_cnt(0), ret_type(ret), cur_ops(&ops) {
}
template <typename... Args>
@ -1177,7 +1183,7 @@ struct AsmOpList {
}
template <typename... Args>
AsmOpList& add(Args&&... args) {
list_.emplace_back(std::forward<Args>(args)...);
append(AsmOp(std::forward<Args>(args)...));
adjust_last();
return *this;
}
@ -1564,8 +1570,8 @@ struct Stack {
AsmOpList& o;
enum {
_StkCmt = 1, _CptStkCmt = 2, _DisableOpt = 4, _DisableOut = 128, _Shown = 256,
_InlineFunc = 512, _NeedRetAlt = 1024,
_ModeSave = _InlineFunc | _NeedRetAlt,
_InlineFunc = 512, _NeedRetAlt = 1024, _InlineAny = 2048,
_ModeSave = _InlineFunc | _NeedRetAlt | _InlineAny,
_Garbage = -0x10000
};
int mode;
@ -1612,7 +1618,7 @@ struct Stack {
if (i > 255) {
throw src::Fatal{"Too deep stack"};
}
assert(i >= 0 && i < depth() && "invalid stack reference");
func_assert(i >= 0 && i < depth() && "invalid stack reference");
}
void modified() {
mode &= ~_Shown;
@ -1643,14 +1649,24 @@ struct Stack {
bool operator==(const Stack& y) const & {
return s == y.s;
}
void apply_wrappers() {
void apply_wrappers(int callxargs_count) {
bool is_inline = mode & _InlineFunc;
if (o.retalt_) {
o.insert(0, "SAMEALTSAVE");
o.insert(0, "c2 SAVE");
if (mode & _InlineFunc) {
}
if (callxargs_count != -1 || (is_inline && o.retalt_)) {
o.indent_all();
o.insert(0, "CONT:<{");
o << "}>";
if (callxargs_count != -1) {
if (callxargs_count <= 15) {
o << AsmOp::Custom(PSTRING() << callxargs_count << " -1 CALLXARGS");
} else {
func_assert(callxargs_count <= 254);
o << AsmOp::Custom(PSTRING() << callxargs_count << " PUSHINT -1 PUSHINT CALLXVARARGS");
}
} else {
o << "EXECUTE";
}
}

View file

@ -92,7 +92,7 @@ bool Expr::deduce_type(const Lexem& lem) {
return true;
}
case _VarApply: {
assert(args.size() == 2);
func_assert(args.size() == 2);
TypeExpr* fun_type = TypeExpr::new_map(args[1]->e_type, TypeExpr::new_hole());
try {
unify(fun_type, args[0]->e_type);
@ -107,7 +107,7 @@ bool Expr::deduce_type(const Lexem& lem) {
return true;
}
case _Letop: {
assert(args.size() == 2);
func_assert(args.size() == 2);
try {
// std::cerr << "in assignment: " << args[0]->e_type << " from " << args[1]->e_type << std::endl;
unify(args[0]->e_type, args[1]->e_type);
@ -122,7 +122,7 @@ bool Expr::deduce_type(const Lexem& lem) {
return true;
}
case _LetFirst: {
assert(args.size() == 2);
func_assert(args.size() == 2);
TypeExpr* rhs_type = TypeExpr::new_tensor({args[0]->e_type, TypeExpr::new_hole()});
try {
// std::cerr << "in implicit assignment of a modifying method: " << rhs_type << " and " << args[1]->e_type << std::endl;
@ -140,7 +140,7 @@ bool Expr::deduce_type(const Lexem& lem) {
return true;
}
case _CondExpr: {
assert(args.size() == 3);
func_assert(args.size() == 3);
auto flag_type = TypeExpr::new_atomic(_Int);
try {
unify(args[0]->e_type, flag_type);
@ -204,7 +204,7 @@ int Expr::predefine_vars() {
}
case _Var:
if (!sym) {
assert(val < 0 && here.defined());
func_assert(val < 0 && here.defined());
if (prohibited_var_names.count(sym::symbols.get_name(~val))) {
throw src::ParseError{
here, PSTRING() << "symbol `" << sym::symbols.get_name(~val) << "` cannot be redefined as a variable"};
@ -274,7 +274,7 @@ std::vector<var_idx_t> pre_compile_tensor(const std::vector<Expr *> args, CodeBl
arg_order.resize(args.size());
std::iota(arg_order.begin(), arg_order.end(), 0);
}
assert(args.size() == arg_order.size());
func_assert(args.size() == arg_order.size());
std::vector<std::vector<var_idx_t>> res_lists(args.size());
struct ModifiedVar {
@ -310,7 +310,7 @@ std::vector<var_idx_t> pre_compile_tensor(const std::vector<Expr *> args, CodeBl
}
for (const auto& list : res_lists) {
for (var_idx_t v : list) {
assert(!code.vars.at(v).on_modification.empty());
func_assert(!code.vars.at(v).on_modification.empty());
code.vars.at(v).on_modification.pop_back();
}
}
@ -339,7 +339,7 @@ std::vector<var_idx_t> Expr::pre_compile(CodeBlob& code, std::vector<std::pair<S
return pre_compile_tensor(args, code, lval_globs, {});
}
case _Apply: {
assert(sym);
func_assert(sym);
auto func = dynamic_cast<SymValFunc*>(sym->value);
std::vector<var_idx_t> res;
if (func && func->arg_order.size() == args.size() && !(code.flags & CodeBlob::_ComputeAsmLtr)) {
@ -426,7 +426,7 @@ std::vector<var_idx_t> Expr::pre_compile(CodeBlob& code, std::vector<std::pair<S
}
case _CondExpr: {
auto cond = args[0]->pre_compile(code);
assert(cond.size() == 1);
func_assert(cond.size() == 1);
auto rvect = new_tmp_vect(code);
Op& if_op = code.emplace_back(here, Op::_If, cond);
code.push_set_cur(if_op.block0);

View file

@ -61,9 +61,9 @@ void Optimizer::apply() {
if (!p_ && !q_) {
return;
}
assert(p_ > 0 && p_ <= l_ && q_ >= 0 && q_ <= n && l_ <= n);
func_assert(p_ > 0 && p_ <= l_ && q_ >= 0 && q_ <= n && l_ <= n);
for (int i = p_; i < l_; i++) {
assert(op_[i]);
func_assert(op_[i]);
op_cons_[i]->car = std::move(op_[i]);
op_cons_[i] = nullptr;
}
@ -71,7 +71,7 @@ void Optimizer::apply() {
code_ = std::move(code_->cdr);
}
for (int j = q_ - 1; j >= 0; j--) {
assert(oq_[j]);
func_assert(oq_[j]);
oq_[j]->indent = indent_;
code_ = AsmOpCons::cons(std::move(oq_[j]), std::move(code_));
}
@ -246,7 +246,7 @@ bool Optimizer::rewrite_const_push_xchgs() {
}
show_left();
auto c_op = std::move(op_[0]);
assert(c_op->is_gconst());
func_assert(c_op->is_gconst());
StackTransform t;
q_ = 0;
int pos = 0;
@ -265,31 +265,31 @@ bool Optimizer::rewrite_const_push_xchgs() {
if (b > pos) {
oq_[q_]->b = b - 1;
}
assert(apply_op(t, *oq_[q_]));
func_assert(apply_op(t, *oq_[q_]));
++q_;
}
} else {
assert(op_[i]->is_push(&a));
assert(a != pos);
func_assert(op_[i]->is_push(&a));
func_assert(a != pos);
oq_[q_] = std::move(op_[i]);
if (a > pos) {
oq_[q_]->a = a - 1;
}
assert(apply_op(t, *oq_[q_]));
func_assert(apply_op(t, *oq_[q_]));
++q_;
++pos;
}
}
assert(!pos);
func_assert(!pos);
t.apply_push_newconst();
assert(t <= tr_[p_ - 1]);
func_assert(t <= tr_[p_ - 1]);
oq_[q_++] = std::move(c_op);
show_right();
return true;
}
bool Optimizer::rewrite(int p, AsmOp&& new_op) {
assert(p > 0 && p <= l_);
func_assert(p > 0 && p <= l_);
p_ = p;
q_ = 1;
show_left();
@ -300,7 +300,7 @@ bool Optimizer::rewrite(int p, AsmOp&& new_op) {
}
bool Optimizer::rewrite(int p, AsmOp&& new_op1, AsmOp&& new_op2) {
assert(p > 1 && p <= l_);
func_assert(p > 1 && p <= l_);
p_ = p;
q_ = 2;
show_left();
@ -313,7 +313,7 @@ bool Optimizer::rewrite(int p, AsmOp&& new_op1, AsmOp&& new_op2) {
}
bool Optimizer::rewrite(int p, AsmOp&& new_op1, AsmOp&& new_op2, AsmOp&& new_op3) {
assert(p > 2 && p <= l_);
func_assert(p > 2 && p <= l_);
p_ = p;
q_ = 3;
show_left();
@ -328,7 +328,7 @@ bool Optimizer::rewrite(int p, AsmOp&& new_op1, AsmOp&& new_op2, AsmOp&& new_op3
}
bool Optimizer::rewrite_nop() {
assert(p_ > 0 && p_ <= l_);
func_assert(p_ > 0 && p_ <= l_);
q_ = 0;
show_left();
show_right();

View file

@ -399,7 +399,7 @@ bool check_global_func(const Lexem& cur, sym_idx_t func_name = 0) {
cur.loc.show_error(std::string{"undefined function `"} + symbols.get_name(func_name) +
"`, defining a global function of unknown type");
def = sym::define_global_symbol(func_name, 0, cur.loc);
assert(def && "cannot define global function");
func_assert(def && "cannot define global function");
++undef_func_cnt;
make_new_glob_func(def, TypeExpr::new_func()); // was: ... ::new_func()
return true;
@ -1111,6 +1111,7 @@ blk_fl::val parse_do_stmt(Lexer& lex, CodeBlob& code) {
}
blk_fl::val parse_try_catch_stmt(Lexer& lex, CodeBlob& code) {
code.require_callxargs = true;
lex.expect(_Try);
Op& try_catch_op = code.emplace_back(lex.cur().loc, Op::_TryCatch);
code.push_set_cur(try_catch_op.block0);
@ -1132,7 +1133,7 @@ blk_fl::val parse_try_catch_stmt(Lexer& lex, CodeBlob& code) {
expr->predefine_vars();
expr->define_new_vars(code);
try_catch_op.left = expr->pre_compile(code);
assert(try_catch_op.left.size() == 2);
func_assert(try_catch_op.left.size() == 2 || try_catch_op.left.size() == 1);
blk_fl::val res1 = parse_block_stmt(lex, code);
sym::close_scope(lex);
code.close_pop_cur(lex.cur().loc);
@ -1295,7 +1296,7 @@ SymValAsmFunc* parse_asm_func_body(Lexer& lex, TypeExpr* func_type, const Formal
}
lex.next();
}
assert(arg_order.size() == (unsigned)tot_width);
func_assert(arg_order.size() == (unsigned)tot_width);
}
if (lex.tp() == _Mapsto) {
lex.expect(_Mapsto);
@ -1487,7 +1488,7 @@ void parse_func_def(Lexer& lex) {
std::cerr << "function " << func_name.str << " : " << func_type << std::endl;
}
SymDef* func_sym = sym::define_global_symbol(func_name.val, 0, loc);
assert(func_sym);
func_assert(func_sym);
SymValFunc* func_sym_val = dynamic_cast<SymValFunc*>(func_sym->value);
if (func_sym->value) {
if (func_sym->value->type != SymVal::_Func || !func_sym_val) {

View file

@ -183,7 +183,7 @@ bool StackTransform::is_permutation() const {
if (!is_valid() || d) {
return false;
}
assert(n <= max_n);
func_assert(n <= max_n);
std::array<int, max_n> X, Y;
for (int i = 0; i < n; i++) {
X[i] = A[i].first;

View file

@ -132,7 +132,7 @@ void TypeExpr::replace_with(TypeExpr* te2) {
}
bool TypeExpr::remove_indirect(TypeExpr*& te, TypeExpr* forbidden) {
assert(te);
func_assert(te);
while (te->constr == te_Indirect) {
te = te->args[0];
}
@ -147,8 +147,8 @@ bool TypeExpr::remove_indirect(TypeExpr*& te, TypeExpr* forbidden) {
}
std::vector<TypeExpr*> TypeExpr::remove_forall(TypeExpr*& te) {
assert(te && te->constr == te_ForAll);
assert(te->args.size() >= 1);
func_assert(te && te->constr == te_ForAll);
func_assert(te->args.size() >= 1);
std::vector<TypeExpr*> new_vars;
for (std::size_t i = 1; i < te->args.size(); i++) {
new_vars.push_back(new_hole(1));
@ -162,8 +162,8 @@ std::vector<TypeExpr*> TypeExpr::remove_forall(TypeExpr*& te) {
}
bool TypeExpr::remove_forall_in(TypeExpr*& te, TypeExpr* te2, const std::vector<TypeExpr*>& new_vars) {
assert(te);
assert(te2 && te2->constr == te_ForAll);
func_assert(te);
func_assert(te2 && te2->constr == te_ForAll);
if (te->constr == te_Var) {
for (std::size_t i = 0; i < new_vars.size(); i++) {
if (te == te2->args[i + 1]) {
@ -277,7 +277,7 @@ std::ostream& TypeExpr::print(std::ostream& os, int lex_level) {
return os << "]";
}
case te_Map: {
assert(args.size() == 2);
func_assert(args.size() == 2);
if (lex_level > 0) {
os << "(";
}
@ -290,7 +290,7 @@ std::ostream& TypeExpr::print(std::ostream& os, int lex_level) {
return os;
}
case te_ForAll: {
assert(args.size() >= 1);
func_assert(args.size() >= 1);
if (lex_level > 0) {
os << '(';
}
@ -343,11 +343,11 @@ void check_update_widths(TypeExpr* te1, TypeExpr* te2) {
check_width_compat(te1, te2);
te1->minw = te2->minw = std::max(te1->minw, te2->minw);
te1->maxw = te2->maxw = std::min(te1->maxw, te2->maxw);
assert(te1->minw <= te1->maxw);
func_assert(te1->minw <= te1->maxw);
}
void unify(TypeExpr*& te1, TypeExpr*& te2) {
assert(te1 && te2);
func_assert(te1 && te2);
// std::cerr << "unify( " << te1 << " , " << te2 << " )\n";
while (te1->constr == TypeExpr::te_Indirect) {
te1 = te1->args[0];
@ -390,7 +390,7 @@ void unify(TypeExpr*& te1, TypeExpr*& te2) {
}
if (te1->constr == TypeExpr::te_Unknown) {
if (te2->constr == TypeExpr::te_Unknown) {
assert(te1->value != te2->value);
func_assert(te1->value != te2->value);
}
if (!TypeExpr::remove_indirect(te2, te1)) {
throw UnifyError{te1, te2, "type unification results in an infinite cyclic type"};