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

[FunC] Add builtin keyword to be used in stdlib later on

This commit is contained in:
Aleksandr Kirsanov 2024-05-07 13:10:56 +03:00
parent acf0043342
commit cdef8302b0
No known key found for this signature in database
GPG key ID: B758BBAA01FFB3D3
6 changed files with 99 additions and 36 deletions

View file

@ -0,0 +1,33 @@
(int, int) moddiv(int x, int y) pure builtin;
() throw_if(int excno, int cond) builtin;
() throwIf(int excNo, int cond) { return throw_if(excNo, cond); }
forall X -> int null?(X x) pure builtin;
_ test1() method_id(101) {
try {
var (rem, _) = moddiv(1, 1);
throwIf(101, rem != 100500);
return 0;
} catch(_, excNo) {
return excNo;
}
}
(int, int, int) main() {
int x = 112;
int y = 3;
var wh = x~moddiv(y);
throwIf(103, wh != 37);
var cc = nil;
return (wh, x, null?(cc));
}
{-
TESTCASE | 0 | | 37 1 -1
TESTCASE | 101 | | 101
@fif_codegen_avoid DECLPROC moddiv
@fif_codegen_avoid DECLPROC throwIf
-}

View file

@ -0,0 +1,10 @@
(int, int) moddiv2(int x, int y) builtin;
{-
@compilation_should_fail
@stderr
"""
`builtin` used for non-builtin function
(int, int) moddiv2
"""
-}

View file

@ -31,7 +31,7 @@ int glob_func_cnt, undef_func_cnt, glob_var_cnt, const_cnt;
std::vector<SymDef*> glob_func, glob_vars;
std::set<std::string> prohibited_var_names;
SymDef* predefine_builtin_func(std::string name, TypeExpr* func_type) {
SymDef* define_builtin_func_impl(const std::string& name, SymValAsmFunc* func_val) {
if (name.back() == '_') {
prohibited_var_names.insert(name);
}
@ -44,39 +44,40 @@ SymDef* predefine_builtin_func(std::string name, TypeExpr* func_type) {
std::cerr << "fatal: global function `" << name << "` already defined" << std::endl;
std::exit(1);
}
return def;
}
template <typename T>
SymDef* define_builtin_func(std::string name, TypeExpr* func_type, const T& func, bool impure = false) {
SymDef* def = predefine_builtin_func(name, func_type);
def->value = new SymValAsmFunc{func_type, func, !impure};
func_val->flags |= SymValFunc::flagBuiltinFunction;
def->value = func_val;
#ifdef FUNC_DEBUG
dynamic_cast<SymValAsmFunc*>(def->value)->name = name;
#endif
return def;
}
template <typename T>
SymDef* define_builtin_func(std::string name, TypeExpr* func_type, const T& func, std::initializer_list<int> arg_order,
SymDef* define_builtin_func(const std::string& name, TypeExpr* func_type, const simple_compile_func_t& func, bool impure = false) {
return define_builtin_func_impl(name, new SymValAsmFunc{func_type, func, !impure});
}
SymDef* define_builtin_func(const std::string& name, TypeExpr* func_type, const compile_func_t& func, bool impure = false) {
return define_builtin_func_impl(name, new SymValAsmFunc{func_type, func, !impure});
}
SymDef* define_builtin_func(const std::string& name, TypeExpr* func_type, const AsmOp& macro, bool impure = false) {
return define_builtin_func_impl(name, new SymValAsmFunc{func_type, make_simple_compile(macro), !impure});
}
SymDef* define_builtin_func(const std::string& name, TypeExpr* func_type, const simple_compile_func_t& func, std::initializer_list<int> arg_order,
std::initializer_list<int> ret_order = {}, bool impure = false) {
SymDef* def = predefine_builtin_func(name, func_type);
def->value = new SymValAsmFunc{func_type, func, arg_order, ret_order, !impure};
#ifdef FUNC_DEBUG
dynamic_cast<SymValAsmFunc*>(def->value)->name = name;
#endif
return def;
return define_builtin_func_impl(name, new SymValAsmFunc{func_type, func, arg_order, ret_order, !impure});
}
SymDef* define_builtin_func(std::string name, TypeExpr* func_type, const AsmOp& macro,
SymDef* define_builtin_func(const std::string& name, TypeExpr* func_type, const compile_func_t& func, std::initializer_list<int> arg_order,
std::initializer_list<int> ret_order = {}, bool impure = false) {
return define_builtin_func_impl(name, new SymValAsmFunc{func_type, func, arg_order, ret_order, !impure});
}
SymDef* define_builtin_func(const std::string& name, TypeExpr* func_type, const AsmOp& macro,
std::initializer_list<int> arg_order, std::initializer_list<int> ret_order = {},
bool impure = false) {
SymDef* def = predefine_builtin_func(name, func_type);
def->value = new SymValAsmFunc{func_type, make_simple_compile(macro), arg_order, ret_order, !impure};
#ifdef FUNC_DEBUG
dynamic_cast<SymValAsmFunc*>(def->value)->name = name;
#endif
return def;
return define_builtin_func_impl(name, new SymValAsmFunc{func_type, make_simple_compile(macro), arg_order, ret_order, !impure});
}
SymDef* force_autoapply(SymDef* def) {
@ -1152,9 +1153,9 @@ void define_builtins() {
auto Int3 = TypeExpr::new_tensor({Int, Int, Int});
auto TupleInt = TypeExpr::new_tensor({Tuple, Int});
auto SliceInt = TypeExpr::new_tensor({Slice, Int});
auto X = TypeExpr::new_var();
auto Y = TypeExpr::new_var();
auto Z = TypeExpr::new_var();
auto X = TypeExpr::new_var(0);
auto Y = TypeExpr::new_var(1);
auto Z = TypeExpr::new_var(2);
auto XY = TypeExpr::new_tensor({X, Y});
auto arith_bin_op = TypeExpr::new_map(Int2, Int);
auto arith_un_op = TypeExpr::new_map(Int, Int);

View file

@ -113,6 +113,7 @@ enum Keyword {
_Extern,
_Inline,
_InlineRef,
_Builtin,
_AutoApply,
_MethodId,
_Operator,
@ -767,6 +768,7 @@ struct SymValFunc : SymVal {
flagWrapsAnotherF = 4, // (T) thisF(...args) { return anotherF(...args); } (calls to thisF will be replaced)
flagUsedAsNonCall = 8, // used not only as `f()`, but as a 1-st class function (assigned to var, pushed to tuple, etc.)
flagMarkedAsPure = 16, // declared as `pure`, can't call impure and access globals, unused invocations are optimized out
flagBuiltinFunction = 32, // was created via `define_builtin_func()`, not from source code
};
td::RefInt256 method_id; // todo why int256? it's small
@ -802,6 +804,9 @@ struct SymValFunc : SymVal {
bool is_marked_as_pure() const {
return flags & flagMarkedAsPure;
}
bool is_builtin() const {
return flags & flagBuiltinFunction;
}
};
struct SymValCodeFunc : SymValFunc {
@ -1700,8 +1705,8 @@ inline simple_compile_func_t make_simple_compile(AsmOp op) {
return [op](std::vector<VarDescr>& out, std::vector<VarDescr>& in, const SrcLocation&) -> AsmOp { return op; };
}
inline compile_func_t make_ext_compile(std::vector<AsmOp> ops) {
return [ops = std::move(ops)](AsmOpList & dest, std::vector<VarDescr> & out, std::vector<VarDescr> & in)->bool {
inline compile_func_t make_ext_compile(std::vector<AsmOp>&& ops) {
return [ops = std::move(ops)](AsmOpList& dest, std::vector<VarDescr>& out, std::vector<VarDescr>& in)->bool {
return dest.append(ops);
};
}
@ -1716,10 +1721,7 @@ struct SymValAsmFunc : SymValFunc {
compile_func_t ext_compile;
td::uint64 crc;
~SymValAsmFunc() override = default;
SymValAsmFunc(TypeExpr* ft, const AsmOp& _macro, bool marked_as_pure)
: SymValFunc(-1, ft, marked_as_pure), simple_compile(make_simple_compile(_macro)) {
}
SymValAsmFunc(TypeExpr* ft, std::vector<AsmOp> _macro, bool marked_as_pure)
SymValAsmFunc(TypeExpr* ft, std::vector<AsmOp>&& _macro, bool marked_as_pure)
: SymValFunc(-1, ft, marked_as_pure), ext_compile(make_ext_compile(std::move(_macro))) {
}
SymValAsmFunc(TypeExpr* ft, simple_compile_func_t _compile, bool marked_as_pure)

View file

@ -123,6 +123,7 @@ void define_keywords() {
.add_keyword("pure", Kw::_Pure)
.add_keyword("inline", Kw::_Inline)
.add_keyword("inline_ref", Kw::_InlineRef)
.add_keyword("builtin", Kw::_Builtin)
.add_keyword("auto_apply", Kw::_AutoApply)
.add_keyword("method_id", Kw::_MethodId)
.add_keyword("operator", Kw::_Operator)

View file

@ -1358,7 +1358,7 @@ SymValAsmFunc* parse_asm_func_body(Lexer& lex, TypeExpr* func_type, const Formal
for (const int& x : ret_order) {
crc_s += std::string((const char*) (&x), (const char*) (&x + 1));
}
auto res = new SymValAsmFunc{func_type, asm_ops, marked_as_pure};
auto res = new SymValAsmFunc{func_type, std::move(asm_ops), marked_as_pure};
res->arg_order = std::move(arg_order);
res->ret_order = std::move(ret_order);
res->crc = td::crc64(crc_s);
@ -1564,11 +1564,27 @@ void parse_func_def(Lexer& lex) {
method_id = td::make_refint((crc & 0xffff) | 0x10000);
}
}
if (lex.tp() != ';' && lex.tp() != '{' && lex.tp() != _Asm) {
lex.expect('{', "function body block expected");
}
TypeExpr* func_type = TypeExpr::new_map(extract_total_arg_type(arg_list), ret_type);
func_type = compute_type_closure(func_type, type_vars);
if (lex.tp() == _Builtin) {
const SymDef* builtin_func = sym::lookup_symbol(func_name.str);
const SymValFunc* func_val = builtin_func ? dynamic_cast<SymValFunc*>(builtin_func->value) : nullptr;
if (!func_val || !func_val->is_builtin()) {
lex.cur().error("`builtin` used for non-builtin function");
}
#ifdef FUNC_DEBUG
// in release, we don't need this check, since `builtin` is used only in stdlib.fc, which is our responsibility
if (!func_val->sym_type->equals_to(func_type) || func_val->is_marked_as_pure() != marked_as_pure) {
lex.cur().error("declaration for `builtin` function doesn't match an actual one");
}
#endif
lex.next();
lex.expect(';');
return;
}
if (lex.tp() != ';' && lex.tp() != '{' && lex.tp() != _Asm) {
lex.expect('{', "function body block");
}
if (verbosity >= 1) {
std::cerr << "function " << func_name.str << " : " << func_type << std::endl;
}