CREATE sometimes compiles and sometimes not

This commit is contained in:
radrow 2021-04-26 14:04:34 +02:00
parent 6a46bb74ab
commit c47169a4a4
5 changed files with 113 additions and 61 deletions

View File

@ -267,15 +267,16 @@ contract_call_type({fun_t, Ann, [], Args, Ret}) ->
bind_contract({Contract, Ann, Id, Contents}, Env)
when ?IS_CONTRACT_HEAD(Contract) ->
Key = name(Id),
io:format("BIND CONTRACT ~p\n", [Id]),
Sys = [{origin, system}],
Fields =
[ {field_t, AnnF, Entrypoint, contract_call_type(Type)}
|| {fun_decl, AnnF, Entrypoint, Type} <- Contents ] ++
[ {field_t, AnnF, Entrypoint,
contract_call_type(
{fun_t, AnnF, [], [ArgT || ArgT <- if is_list(Args) -> Args; true -> [Args] end], RetT})
{fun_t, AnnF, [], [ArgT || {typed, _, _, ArgT} <- if is_list(Args) -> Args; true -> [Args] end], RetT})
}
|| {letfun, AnnF, Entrypoint, _Named, Args, {typed, _, _, RetT}} <- Contents
|| {letfun, AnnF, Entrypoint, Args, _Type, {typed, _, _, RetT}} <- Contents
] ++
%% Predefined fields
[ {field_t, Sys, {id, Sys, "address"}, {id, Sys, "address"}} ],
@ -1595,6 +1596,7 @@ infer_var_args_fun(Env, {typed, Ann, Fun, FunType0}, NamedArgs, ArgTypes) ->
{fun_t, _, NamedArgsT, var_args, RetT} = FunType0,
GasCapMock = {named_arg_t, Ann, {id, Ann, "gas"}, {id, Ann, "int"}, {int, Ann, 0}},
ProtectedMock = {named_arg_t, Ann, {id, Ann, "protected"}, {id, Ann, "bool"}, {bool, Ann, false}},
check_contract_construction(Env, RetT, Fun, [GasCapMock, ProtectedMock|NamedArgsT], ArgTypes, RetT),
{fun_t, Ann, NamedArgsT, ArgTypes, RetT};
{qid, _, ["Chain", "clone"]} ->
@ -1618,7 +1620,6 @@ check_contract_construction(Env, ContractT, Fun, NamedArgsT, ArgTypes, RetT) ->
InitT = fresh_uvar(Ann),
unify(Env, InitT, {fun_t, Ann, NamedArgsT, ArgTypes, fresh_uvar(Ann)}, {checking_init_args, Ann, ContractT, ArgTypes}),
unify(Env, RetT, ContractT, {return_contract, Fun, ContractT}),
io:format("DEREF: ~p\n", [dereference_deep(InitT)]),
constrain([ #field_constraint{
record_t = unfold_types_in_type(Env, ContractT),
field = {id, Ann, "init"},

View File

@ -55,6 +55,7 @@
| {oracle_pubkey, binary()}
| {oracle_query_id, binary()}
| {bool, false | true}
| {contract_code, string()} %% for CREATE, by name
| {typerep, ftype()}.
-type fexpr() :: {lit, flit()}
@ -167,15 +168,24 @@
%% and produces Fate intermediate code.
-spec ast_to_fcode(aeso_syntax:ast(), [option()]) -> fcode().
ast_to_fcode(Code, Options) ->
Verbose = lists:member(pp_fcode, Options),
init_fresh_names(),
FCode1 = to_fcode(init_env(Options), Code),
{Env1, FCode1} = to_fcode(init_env(Options), Code),
FCode2 = optimize(FCode1, Options),
Env2 = Env1#{ child_con_env :=
maps:map(
fun (_, FC) -> optimize(FC, Options) end,
maps:get(child_con_env, Env1)
)},
clear_fresh_names(),
{Env2, FCode2}.
optimize(FCode1, Options) ->
Verbose = lists:member(pp_fcode, Options),
[io:format("-- Before lambda lifting --\n~s\n\n", [format_fcode(FCode1)]) || Verbose],
FCode2 = optimize_fcode(FCode1),
[ io:format("-- After optimization --\n~s\n\n", [format_fcode(FCode2)]) || Verbose, FCode2 /= FCode1 ],
FCode3 = lambda_lift(FCode2),
[ io:format("-- After lambda lifting --\n~s\n\n", [format_fcode(FCode3)]) || Verbose, FCode3 /= FCode2 ],
clear_fresh_names(),
FCode3.
%% -- Environment ------------------------------------------------------------
@ -337,7 +347,7 @@ to_fcode(Env, [{Contract, Attrs, Con = {con, _, Name}, Decls}|Rest])
functions => add_init_function(Env1, Con, StateType,
add_event_function(Env1, EventType, Funs)) },
case Contract of
contract_main -> Rest = [], ConFcode;
contract_main -> Rest = [], {Env1, ConFcode};
contract_child ->
Env2 = add_child_con(Env1, Name, ConFcode),
to_fcode(Env2, Rest)
@ -696,7 +706,7 @@ expr_to_fcode(Env, _Type, {app, _Ann, {Op, _}, [A]}) when is_atom(Op) ->
end;
%% Function calls
expr_to_fcode(Env, _Type, {app, _, Fun = {typed, _, FunE, {fun_t, _, NamedArgsT, ArgsT, _}}, Args}) ->
expr_to_fcode(Env, Type, {app, _, Fun = {typed, _, FunE, {fun_t, _, NamedArgsT, ArgsT, _}}, Args}) ->
Args1 = get_named_args(NamedArgsT, Args),
FArgs = [expr_to_fcode(Env, Arg) || Arg <- Args1],
case expr_to_fcode(Env, Fun) of
@ -708,6 +718,14 @@ expr_to_fcode(Env, _Type, {app, _, Fun = {typed, _, FunE, {fun_t, _, NamedArgsT,
FInitArgsT = {typerep, {tuple, [type_to_fcode(Env, T) || T <- ArgsT]}},
builtin_to_fcode(state_layout(Env), chain_clone, [{lit, FInitArgsT}|FArgs])
end;
{builtin_u, chain_create, _Ar} ->
case {ArgsT, Type} of
{var_args, _} -> fcode_error({var_args_not_set, FunE});
{_, {con, _, Contract}} ->
FInitArgsT = {typerep, {tuple, [type_to_fcode(Env, T) || T <- ArgsT]}},
builtin_to_fcode(state_layout(Env), chain_clone, [{lit, {contract_code, Contract}}, {lit, FInitArgsT}|FArgs]);
{_, _} -> fcode_error({not_a_contract_type, Type})
end;
{builtin_u, B, _Ar} -> builtin_to_fcode(state_layout(Env), B, FArgs);
{def_u, F, _Ar} -> {def, F, FArgs};
{remote_u, ArgsT, RetT, Ct, RFun} -> {remote, ArgsT, RetT, Ct, RFun, FArgs};

View File

@ -138,8 +138,9 @@ from_string1(aevm, ContractString, Options) ->
{ok, maybe_generate_aci(Res, FoldedTypedAst, Options)};
from_string1(fate, ContractString, Options) ->
#{ fcode := FCode
, fcode_env := #{child_con_env := ChildContracts}
, folded_typed_ast := FoldedTypedAst } = string_to_code(ContractString, Options),
FateCode = aeso_fcode_to_fate:compile(FCode, Options),
FateCode = aeso_fcode_to_fate:compile(ChildContracts, FCode, Options),
pp_assembler(fate, FateCode, Options),
ByteCode = aeb_fate_code:serialize(FateCode, []),
{ok, Version} = version(),
@ -179,8 +180,9 @@ string_to_code(ContractString, Options) ->
, type_env => TypeEnv
, ast => Ast };
fate ->
Fcode = aeso_ast_to_fcode:ast_to_fcode(UnfoldedTypedAst, Options),
{Env, Fcode} = aeso_ast_to_fcode:ast_to_fcode(UnfoldedTypedAst, Options),
#{ fcode => Fcode
, fcode_env => Env
, unfolded_typed_ast => UnfoldedTypedAst
, folded_typed_ast => FoldedTypedAst
, type_env => TypeEnv

View File

@ -9,7 +9,7 @@
%%%-------------------------------------------------------------------
-module(aeso_fcode_to_fate).
-export([compile/2, term_to_fate/1]).
-export([compile/2, compile/3, term_to_fate/1, term_to_fate/2]).
-ifdef(TEST).
-export([optimize_fun/4, to_basic_blocks/1]).
@ -45,7 +45,7 @@
-define(s(N), {store, N}).
-define(void, {var, 9999}).
-record(env, { contract, vars = [], locals = [], current_function, tailpos = true }).
-record(env, { contract, vars = [], locals = [], current_function, tailpos = true, child_contracts = #{}, options = []}).
%% -- Debugging --------------------------------------------------------------
@ -70,9 +70,11 @@ code_error(Err) ->
%% @doc Main entry point.
compile(FCode, Options) ->
compile(#{}, FCode, Options).
compile(ChildContracts, FCode, Options) ->
#{ contract_name := ContractName,
functions := Functions } = FCode,
SFuns = functions_to_scode(ContractName, Functions, Options),
SFuns = functions_to_scode(ChildContracts, ContractName, Functions, Options),
SFuns1 = optimize_scode(SFuns, Options),
FateCode = to_basic_blocks(SFuns1),
?debug(compile, Options, "~s\n", [aeb_fate_asm:pp(FateCode)]),
@ -85,19 +87,20 @@ make_function_name(event) -> <<"Chain.event">>;
make_function_name({entrypoint, Name}) -> Name;
make_function_name({local_fun, Xs}) -> list_to_binary("." ++ string:join(Xs, ".")).
functions_to_scode(ContractName, Functions, Options) ->
functions_to_scode(ChildContracts, ContractName, Functions, Options) ->
FunNames = maps:keys(Functions),
maps:from_list(
[ {make_function_name(Name), function_to_scode(ContractName, FunNames, Name, Attrs, Args, Body, Type, Options)}
[ {make_function_name(Name), function_to_scode(ChildContracts, ContractName, FunNames, Name, Attrs, Args, Body, Type, Options)}
|| {Name, #{args := Args,
body := Body,
attrs := Attrs,
return := Type}} <- maps:to_list(Functions)]).
function_to_scode(ContractName, Functions, Name, Attrs0, Args, Body, ResType, _Options) ->
function_to_scode(ChildContracts, ContractName, Functions, Name, Attrs0, Args, Body, ResType, Options) ->
{ArgTypes, ResType1} = typesig_to_scode(Args, ResType),
Attrs = Attrs0 -- [stateful], %% Only track private and payable from here.
SCode = to_scode(init_env(ContractName, Functions, Name, Args), Body),
Env = init_env(ChildContracts, ContractName, Functions, Name, Args, Options),
SCode = to_scode(Env, Body),
{Attrs, {ArgTypes, ResType1}, SCode}.
-define(tvars, '$tvars').
@ -142,11 +145,13 @@ types_to_scode(Ts) -> lists:map(fun type_to_scode/1, Ts).
%% -- Environment functions --
init_env(ContractName, FunNames, Name, Args) ->
init_env(ChildContracts, ContractName, FunNames, Name, Args, Options) ->
#env{ vars = [ {X, {arg, I}} || {I, {X, _}} <- with_ixs(Args) ],
contract = ContractName,
child_contracts = ChildContracts,
locals = FunNames,
current_function = Name,
options = Options,
tailpos = true }.
next_var(#env{ vars = Vars }) ->
@ -169,7 +174,7 @@ lookup_var(#env{vars = Vars}, X) ->
%% -- The compiler --
lit_to_fate(L) ->
lit_to_fate(Env, L) ->
case L of
{int, N} -> aeb_fate_data:make_integer(N);
{string, S} -> aeb_fate_data:make_string(S);
@ -179,63 +184,79 @@ lit_to_fate(L) ->
{contract_pubkey, K} -> aeb_fate_data:make_contract(K);
{oracle_pubkey, K} -> aeb_fate_data:make_oracle(K);
{oracle_query_id, H} -> aeb_fate_data:make_oracle_query(H);
{contract_code, C} ->
FCode = maps:get(C, Env#env.child_contracts),
SCode = compile(Env#env.child_contracts, FCode, Env#env.options),
ByteCode = aeb_fate_code:serialize(SCode, []),
{ok, Version} = aeso_compiler:version(),
Code = #{byte_code => ByteCode,
compiler_version => Version,
contract_source => "child_contract_src_placeholder",
type_info => [],
fate_code => SCode,
abi_version => aeb_fate_abi:abi_version(),
payable => maps:get(payable, FCode)
},
Serialized = aeser_contract_code:serialize(Code),
aeb_fate_data:make_contract_bytearray(Serialized);
{typerep, T} -> aeb_fate_data:make_typerep(type_to_scode(T))
end.
term_to_fate(E) -> term_to_fate(#{}, E).
term_to_fate(E) -> term_to_fate(#env{}, #{}, E).
term_to_fate(GlobEnv, E) -> term_to_fate(GlobEnv, #{}, E).
term_to_fate(_Env, {lit, L}) ->
lit_to_fate(L);
term_to_fate(GlobEnv, _Env, {lit, L}) ->
lit_to_fate(GlobEnv, L);
%% negative literals are parsed as 0 - N
term_to_fate(_Env, {op, '-', [{lit, {int, 0}}, {lit, {int, N}}]}) ->
term_to_fate(_GlobEnv, _Env, {op, '-', [{lit, {int, 0}}, {lit, {int, N}}]}) ->
aeb_fate_data:make_integer(-N);
term_to_fate(_Env, nil) ->
term_to_fate(_GlobEnv, _Env, nil) ->
aeb_fate_data:make_list([]);
term_to_fate(Env, {op, '::', [Hd, Tl]}) ->
term_to_fate(GlobEnv, Env, {op, '::', [Hd, Tl]}) ->
%% The Tl will translate into a list, because FATE lists are just lists
[term_to_fate(Env, Hd) | term_to_fate(Env, Tl)];
term_to_fate(Env, {tuple, As}) ->
aeb_fate_data:make_tuple(list_to_tuple([ term_to_fate(Env, A) || A<-As]));
term_to_fate(Env, {con, Ar, I, As}) ->
FateAs = [ term_to_fate(Env, A) || A <- As ],
[term_to_fate(GlobEnv, Env, Hd) | term_to_fate(GlobEnv, Env, Tl)];
term_to_fate(GlobEnv, Env, {tuple, As}) ->
aeb_fate_data:make_tuple(list_to_tuple([ term_to_fate(GlobEnv, Env, A) || A<-As]));
term_to_fate(GlobEnv, Env, {con, Ar, I, As}) ->
FateAs = [ term_to_fate(GlobEnv, Env, A) || A <- As ],
aeb_fate_data:make_variant(Ar, I, list_to_tuple(FateAs));
term_to_fate(_Env, {builtin, bits_all, []}) ->
term_to_fate(_GlobEnv, _Env, {builtin, bits_all, []}) ->
aeb_fate_data:make_bits(-1);
term_to_fate(_Env, {builtin, bits_none, []}) ->
term_to_fate(_GlobEnv, _Env, {builtin, bits_none, []}) ->
aeb_fate_data:make_bits(0);
term_to_fate(_Env, {op, bits_set, [B, I]}) ->
{bits, N} = term_to_fate(B),
J = term_to_fate(I),
term_to_fate(GlobEnv, _Env, {op, bits_set, [B, I]}) ->
{bits, N} = term_to_fate(GlobEnv, B),
J = term_to_fate(GlobEnv, I),
{bits, N bor (1 bsl J)};
term_to_fate(_Env, {op, bits_clear, [B, I]}) ->
{bits, N} = term_to_fate(B),
J = term_to_fate(I),
term_to_fate(GlobEnv, _Env, {op, bits_clear, [B, I]}) ->
{bits, N} = term_to_fate(GlobEnv, B),
J = term_to_fate(GlobEnv, I),
{bits, N band bnot (1 bsl J)};
term_to_fate(Env, {'let', X, E, Body}) ->
Env1 = Env#{ X => term_to_fate(Env, E) },
term_to_fate(Env1, Body);
term_to_fate(Env, {var, X}) ->
term_to_fate(GlobEnv, Env, {'let', X, E, Body}) ->
Env1 = Env#{ X => term_to_fate(GlobEnv, Env, E) },
term_to_fate(GlobEnv, Env1, Body);
term_to_fate(_GlobEnv, Env, {var, X}) ->
case maps:get(X, Env, undefined) of
undefined -> throw(not_a_fate_value);
V -> V
end;
term_to_fate(_Env, {builtin, map_empty, []}) ->
term_to_fate(_GlobEnv, _Env, {builtin, map_empty, []}) ->
aeb_fate_data:make_map(#{});
term_to_fate(Env, {op, map_set, [M, K, V]}) ->
Map = term_to_fate(Env, M),
Map#{term_to_fate(Env, K) => term_to_fate(Env, V)};
term_to_fate(_Env, _) ->
term_to_fate(GlobEnv, Env, {op, map_set, [M, K, V]}) ->
Map = term_to_fate(GlobEnv, Env, M),
Map#{term_to_fate(GlobEnv, Env, K) => term_to_fate(GlobEnv, Env, V)};
term_to_fate(_GlobEnv, _Env, _) ->
throw(not_a_fate_value).
to_scode(Env, T) ->
try term_to_fate(T) of
try term_to_fate(Env, T) of
V -> [push(?i(V))]
catch throw:not_a_fate_value ->
to_scode1(Env, T)
end.
to_scode1(_Env, {lit, L}) ->
[push(?i(lit_to_fate(L)))];
to_scode1(Env, {lit, L}) ->
[push(?i(lit_to_fate(Env, L)))];
to_scode1(_Env, nil) ->
[aeb_fate_ops:nil(?a)];
@ -564,10 +585,9 @@ builtin_to_scode(Env, chain_clone,
[Contract, TypeRep, Value, GasCap, Prot | InitArgs]
);
builtin_to_scode(Env, chain_create,
[_GasCap = {con,[0,1],0,[]}, Prot, Value, Contract | InitArgs]) ->
TypeRep = xd,
call_to_scode(Env, aeb_fate_ops:clone(?a, ?a, ?a, ?a),
[Contract, TypeRep, Value, Prot | InitArgs]
[ Code, TypeRep | InitArgs]) ->
call_to_scode(Env, aeb_fate_ops:create(?a, ?a, ?a),
[Code, TypeRep | InitArgs]
).

View File

@ -1,9 +1,20 @@
// If you need a quick compiler test — this file is for your playground
contract Chuj =
type state = bool
// entrypoint init : (int, bool) => void
entrypoint init(x : int, y : bool) = if(x < 0) abort("xD") else true
contract IntegerAdder =
entrypoint init() = unit
entrypoint addIntegers(x, y) = x + y
contract IntegerAdderHolder =
type state = IntegerAdder
entrypoint init() = Chain.create() : IntegerAdder
entrypoint get() = state
namespace IntegerAdderFactory =
function new() =
let i = Chain.create() : IntegerAdderHolder
i.get()
main contract EnterpriseContract =
entrypoint calculateSomething(x) =
let adder = IntegerAdderFactory.new()
adder.addIntegers(x, 2137)
main contract Test =
stateful entrypoint kek() =
Chain.create(value=3, 123, 555) : Chuj