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:
parent
acf0043342
commit
cdef8302b0
6 changed files with 99 additions and 36 deletions
33
crypto/func/auto-tests/tests/builtin-keyword.fc
Normal file
33
crypto/func/auto-tests/tests/builtin-keyword.fc
Normal 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
|
||||
-}
|
10
crypto/func/auto-tests/tests/invalid-builtin-1.fc
Normal file
10
crypto/func/auto-tests/tests/invalid-builtin-1.fc
Normal 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
|
||||
"""
|
||||
-}
|
|
@ -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);
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue