PT-166407568 polymorphic functions in FATE #574
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
{erl_opts, [debug_info]}.
|
{erl_opts, [debug_info]}.
|
||||||
|
|
||||||
{deps, [ {aebytecode, {git, "https://github.com/aeternity/aebytecode.git", {ref, "f129887"}}}
|
{deps, [ {aebytecode, {git, "https://github.com/aeternity/aebytecode.git", {ref, "53a055b"}}}
|
||||||
, {getopt, "1.0.1"}
|
, {getopt, "1.0.1"}
|
||||||
, {jsx, {git, "https://github.com/talentdeficit/jsx.git",
|
, {jsx, {git, "https://github.com/talentdeficit/jsx.git",
|
||||||
{tag, "2.8.0"}}}
|
{tag, "2.8.0"}}}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
{"1.1.0",
|
{"1.1.0",
|
||||||
[{<<"aebytecode">>,
|
[{<<"aebytecode">>,
|
||||||
{git,"https://github.com/aeternity/aebytecode.git",
|
{git,"https://github.com/aeternity/aebytecode.git",
|
||||||
{ref,"f1298870e526f4e9330447d3a281af5ef4e06e17"}},
|
{ref,"53a055b90a7201761ab7bc2c55d6ddf5a1f38f27"}},
|
||||||
0},
|
0},
|
||||||
{<<"aeserialization">>,
|
{<<"aeserialization">>,
|
||||||
{git,"https://github.com/aeternity/aeserialization.git",
|
{git,"https://github.com/aeternity/aeserialization.git",
|
||||||
|
@ -31,7 +31,7 @@
|
|||||||
map_get | map_get_d | map_set | map_from_list | map_to_list |
|
map_get | map_get_d | map_set | map_from_list | map_to_list |
|
||||||
map_delete | map_member | map_size | string_length |
|
map_delete | map_member | map_size | string_length |
|
||||||
string_concat | bits_set | bits_clear | bits_test | bits_sum |
|
string_concat | bits_set | bits_clear | bits_test | bits_sum |
|
||||||
bits_intersection | bits_union | bits_difference.
|
bits_intersection | bits_union | bits_difference | contract_address.
|
||||||
|
|
||||||
-type flit() :: {int, integer()}
|
-type flit() :: {int, integer()}
|
||||||
| {string, binary()}
|
| {string, binary()}
|
||||||
@ -95,7 +95,7 @@
|
|||||||
| bits
|
| bits
|
||||||
| {variant, [[ftype()]]}
|
| {variant, [[ftype()]]}
|
||||||
| {function, [ftype()], ftype()}
|
| {function, [ftype()], ftype()}
|
||||||
| any.
|
| any | {tvar, var_name()}.
|
||||||
|
|
||||||
-type fun_def() :: #{ attrs := [attribute()],
|
-type fun_def() :: #{ attrs := [attribute()],
|
||||||
args := [{var_name(), ftype()}],
|
args := [{var_name(), ftype()}],
|
||||||
@ -318,7 +318,7 @@ type_to_fcode(Env, Sub, {record_t, Fields}) ->
|
|||||||
type_to_fcode(_Env, _Sub, {bytes_t, _, _N}) ->
|
type_to_fcode(_Env, _Sub, {bytes_t, _, _N}) ->
|
||||||
string; %% TODO: add bytes type to FATE?
|
string; %% TODO: add bytes type to FATE?
|
||||||
type_to_fcode(_Env, Sub, {tvar, _, X}) ->
|
type_to_fcode(_Env, Sub, {tvar, _, X}) ->
|
||||||
maps:get(X, Sub, any);
|
maps:get(X, Sub, {tvar, X});
|
||||||
type_to_fcode(Env, Sub, {fun_t, _, Named, Args, Res}) ->
|
type_to_fcode(Env, Sub, {fun_t, _, Named, Args, Res}) ->
|
||||||
FNamed = [type_to_fcode(Env, Sub, Arg) || {named_arg_t, _, _, Arg, _} <- Named],
|
FNamed = [type_to_fcode(Env, Sub, Arg) || {named_arg_t, _, _, Arg, _} <- Named],
|
||||||
FArgs = [type_to_fcode(Env, Sub, Arg) || Arg <- Args],
|
FArgs = [type_to_fcode(Env, Sub, Arg) || Arg <- Args],
|
||||||
@ -385,6 +385,8 @@ expr_to_fcode(Env, _Type, {tuple, _, Es}) ->
|
|||||||
%% Records
|
%% Records
|
||||||
expr_to_fcode(Env, Type, {proj, _Ann, Rec = {typed, _, _, RecType}, {id, _, X}}) ->
|
expr_to_fcode(Env, Type, {proj, _Ann, Rec = {typed, _, _, RecType}, {id, _, X}}) ->
|
||||||
case RecType of
|
case RecType of
|
||||||
|
{con, _, _} when X == "address" ->
|
||||||
|
{op, contract_address, [expr_to_fcode(Env, Rec)]};
|
||||||
{con, _, _} ->
|
{con, _, _} ->
|
||||||
{fun_t, _, Named, Args, _} = Type,
|
{fun_t, _, Named, Args, _} = Type,
|
||||||
Arity = length(Named) + length(Args),
|
Arity = length(Named) + length(Args),
|
||||||
@ -876,9 +878,21 @@ optimize_fcode(Code = #{ functions := Funs }) ->
|
|||||||
Code#{ functions := maps:map(fun(Name, Def) -> optimize_fun(Code, Name, Def) end, Funs) }.
|
Code#{ functions := maps:map(fun(Name, Def) -> optimize_fun(Code, Name, Def) end, Funs) }.
|
||||||
|
|
||||||
-spec optimize_fun(fcode(), fun_name(), fun_def()) -> fun_def().
|
-spec optimize_fun(fcode(), fun_name(), fun_def()) -> fun_def().
|
||||||
optimize_fun(_Fcode, _Fun, Def = #{ body := _Body }) ->
|
optimize_fun(Fcode, Fun, Def = #{ body := Body }) ->
|
||||||
%% io:format("Optimizing ~p =\n~s\n", [_Fun, prettypr:format(pp_fexpr(_Body))]),
|
%% io:format("Optimizing ~p =\n~s\n", [_Fun, prettypr:format(pp_fexpr(_Body))]),
|
||||||
Def.
|
Def#{ body := inliner(Fcode, Fun, Body) }.
|
||||||
|
|
||||||
|
-spec inliner(fcode(), fun_name(), fexpr()) -> fexpr().
|
||||||
|
inliner(Fcode, Fun, {def, Fun1, Args} = E) when Fun1 /= Fun ->
|
||||||
|
case should_inline(Fcode, Fun1) of
|
||||||
|
false -> E;
|
||||||
|
true -> inline(Fcode, Fun1, Args)
|
||||||
|
end;
|
||||||
|
inliner(_Fcode, _Fun, E) -> E.
|
||||||
|
|
||||||
|
should_inline(_Fcode, _Fun1) -> false == list_to_atom("true"). %% Dialyzer
|
||||||
|
|
||||||
|
inline(_Fcode, Fun, Args) -> {def, Fun, Args}. %% TODO
|
||||||
|
|
||||||
%% -- Helper functions -------------------------------------------------------
|
%% -- Helper functions -------------------------------------------------------
|
||||||
|
|
||||||
@ -1286,6 +1300,7 @@ pp_call(Fun, Args) ->
|
|||||||
|
|
||||||
pp_ftype(T) when is_atom(T) -> pp_text(T);
|
pp_ftype(T) when is_atom(T) -> pp_text(T);
|
||||||
pp_ftype(any) -> pp_text("_");
|
pp_ftype(any) -> pp_text("_");
|
||||||
|
pp_ftype({tvar, X}) -> pp_text(X);
|
||||||
pp_ftype({tuple, Ts}) ->
|
pp_ftype({tuple, Ts}) ->
|
||||||
pp_parens(pp_par(pp_punctuate(pp_text(","), [pp_ftype(T) || T <- Ts])));
|
pp_parens(pp_par(pp_punctuate(pp_text(","), [pp_ftype(T) || T <- Ts])));
|
||||||
pp_ftype({list, T}) ->
|
pp_ftype({list, T}) ->
|
||||||
|
@ -138,16 +138,44 @@ functions_to_scode(ContractName, Functions, Options) ->
|
|||||||
return := Type}} <- maps:to_list(Functions)]).
|
return := Type}} <- maps:to_list(Functions)]).
|
||||||
|
|
||||||
function_to_scode(ContractName, Functions, _Name, Args, Body, ResType, _Options) ->
|
function_to_scode(ContractName, Functions, _Name, Args, Body, ResType, _Options) ->
|
||||||
ArgTypes = [ type_to_scode(T) || {_, T} <- Args ],
|
{ArgTypes, ResType1} = typesig_to_scode(Args, ResType),
|
||||||
SCode = to_scode(init_env(ContractName, Functions, Args), Body),
|
SCode = to_scode(init_env(ContractName, Functions, Args), Body),
|
||||||
{{ArgTypes, type_to_scode(ResType)}, SCode}.
|
{{ArgTypes, ResType1}, SCode}.
|
||||||
|
|
||||||
|
-define(tvars, '$tvars').
|
||||||
|
|
||||||
|
typesig_to_scode(Args, Res) ->
|
||||||
|
put(?tvars, {0, #{}}),
|
||||||
|
R = {[type_to_scode(T) || {_, T} <- Args], type_to_scode(Res)},
|
||||||
|
erase(?tvars),
|
||||||
|
R.
|
||||||
|
|
||||||
|
type_to_scode(integer) -> integer;
|
||||||
|
type_to_scode(boolean) -> boolean;
|
||||||
|
type_to_scode(string) -> string;
|
||||||
|
type_to_scode(address) -> address;
|
||||||
|
type_to_scode(hash) -> hash;
|
||||||
|
type_to_scode(signature) -> signature;
|
||||||
|
type_to_scode(contract) -> contract;
|
||||||
|
type_to_scode(oracle) -> oracle;
|
||||||
|
type_to_scode(oracle_query) -> oracle_query;
|
||||||
|
type_to_scode(name) -> name;
|
||||||
|
type_to_scode(channel) -> channel;
|
||||||
|
type_to_scode(bits) -> bits;
|
||||||
|
type_to_scode(any) -> any;
|
||||||
type_to_scode({variant, Cons}) -> {variant, lists:map(fun(T) -> type_to_scode({tuple, T}) end, Cons)};
|
type_to_scode({variant, Cons}) -> {variant, lists:map(fun(T) -> type_to_scode({tuple, T}) end, Cons)};
|
||||||
type_to_scode({list, Type}) -> {list, type_to_scode(Type)};
|
type_to_scode({list, Type}) -> {list, type_to_scode(Type)};
|
||||||
type_to_scode({tuple, Types}) -> {tuple, lists:map(fun type_to_scode/1, Types)};
|
type_to_scode({tuple, Types}) -> {tuple, lists:map(fun type_to_scode/1, Types)};
|
||||||
type_to_scode({map, Key, Val}) -> {map, type_to_scode(Key), type_to_scode(Val)};
|
type_to_scode({map, Key, Val}) -> {map, type_to_scode(Key), type_to_scode(Val)};
|
||||||
type_to_scode({function, _Args, _Res}) -> {tuple, [string, any]};
|
type_to_scode({function, _Args, _Res}) -> {tuple, [string, any]};
|
||||||
type_to_scode(T) -> T.
|
type_to_scode({tvar, X}) ->
|
||||||
|
{I, Vars} = get(?tvars),
|
||||||
|
case maps:get(X, Vars, false) of
|
||||||
|
false ->
|
||||||
|
put(?tvars, {I + 1, Vars#{ X => I }}),
|
||||||
|
{tvar, I};
|
||||||
|
J -> {tvar, J}
|
||||||
|
end.
|
||||||
|
|
||||||
add_default_init_function(SFuns, StateType) when StateType /= {tuple, []} ->
|
add_default_init_function(SFuns, StateType) when StateType /= {tuple, []} ->
|
||||||
%% Only add default if the type is unit.
|
%% Only add default if the type is unit.
|
||||||
@ -511,7 +539,8 @@ op_to_scode(bits_intersection) -> aeb_fate_ops:bits_and(?a, ?a, ?a);
|
|||||||
op_to_scode(bits_union) -> aeb_fate_ops:bits_or(?a, ?a, ?a);
|
op_to_scode(bits_union) -> aeb_fate_ops:bits_or(?a, ?a, ?a);
|
||||||
op_to_scode(bits_difference) -> aeb_fate_ops:bits_diff(?a, ?a, ?a);
|
op_to_scode(bits_difference) -> aeb_fate_ops:bits_diff(?a, ?a, ?a);
|
||||||
op_to_scode(address_to_str) -> aeb_fate_ops:addr_to_str(?a, ?a);
|
op_to_scode(address_to_str) -> aeb_fate_ops:addr_to_str(?a, ?a);
|
||||||
op_to_scode(int_to_str) -> aeb_fate_ops:int_to_str(?a, ?a).
|
op_to_scode(int_to_str) -> aeb_fate_ops:int_to_str(?a, ?a);
|
||||||
|
op_to_scode(contract_address) -> ?TODO(fate_contract_to_address_conversion).
|
||||||
|
|
||||||
%% PUSH and STORE ?a are the same, so we use STORE to make optimizations
|
%% PUSH and STORE ?a are the same, so we use STORE to make optimizations
|
||||||
%% easier, and specialize to PUSH (which is cheaper) at the end.
|
%% easier, and specialize to PUSH (which is cheaper) at the end.
|
||||||
@ -1354,9 +1383,9 @@ chase_labels([L | Ls], Map, Live) ->
|
|||||||
%% Replace PUSH, RETURN by RETURNR, drop returns after tail calls.
|
%% Replace PUSH, RETURN by RETURNR, drop returns after tail calls.
|
||||||
tweak_returns(['RETURN', {'PUSH', A} | Code]) -> [{'RETURNR', A} | Code];
|
tweak_returns(['RETURN', {'PUSH', A} | Code]) -> [{'RETURNR', A} | Code];
|
||||||
tweak_returns(['RETURN' | Code = [{'CALL_T', _} | _]]) -> Code;
|
tweak_returns(['RETURN' | Code = [{'CALL_T', _} | _]]) -> Code;
|
||||||
tweak_returns(['RETURN' | Code = [{'CALL_TR', _, _} | _]]) -> Code;
|
tweak_returns(['RETURN' | Code = [{'CALL_TR', _, _, _} | _]]) -> Code;
|
||||||
tweak_returns(['RETURN' | Code = [{'CALL_GT', _} | _]]) -> Code;
|
tweak_returns(['RETURN' | Code = [{'CALL_GT', _} | _]]) -> Code;
|
||||||
tweak_returns(['RETURN' | Code = [{'CALL_GTR', _, _} | _]])-> Code;
|
tweak_returns(['RETURN' | Code = [{'CALL_GTR', _, _, _, _} | _]]) -> Code;
|
||||||
tweak_returns(['RETURN' | Code = [{'ABORT', _} | _]]) -> Code;
|
tweak_returns(['RETURN' | Code = [{'ABORT', _} | _]]) -> Code;
|
||||||
tweak_returns(Code) -> Code.
|
tweak_returns(Code) -> Code.
|
||||||
|
|
||||||
@ -1373,6 +1402,8 @@ split_calls(Ref, [I | Code], Acc, Blocks) when element(1, I) == 'CALL';
|
|||||||
element(1, I) == 'CALL_GR';
|
element(1, I) == 'CALL_GR';
|
||||||
element(1, I) == 'jumpif' ->
|
element(1, I) == 'jumpif' ->
|
||||||
split_calls(make_ref(), Code, [], [{Ref, lists:reverse([I | Acc])} | Blocks]);
|
split_calls(make_ref(), Code, [], [{Ref, lists:reverse([I | Acc])} | Blocks]);
|
||||||
|
split_calls(Ref, [{'ABORT', _} = I | _Code], Acc, Blocks) ->
|
||||||
|
lists:reverse([{Ref, lists:reverse([I | Acc])} | Blocks]);
|
||||||
split_calls(Ref, [I | Code], Acc, Blocks) ->
|
split_calls(Ref, [I | Code], Acc, Blocks) ->
|
||||||
split_calls(Ref, Code, [I | Acc], Blocks).
|
split_calls(Ref, Code, [I | Acc], Blocks).
|
||||||
|
|
||||||
|
@ -24,7 +24,8 @@ simple_compile_test_() ->
|
|||||||
type_info := _} when Backend == aevm ->
|
type_info := _} when Backend == aevm ->
|
||||||
?assertMatch(Code when is_binary(Code), ByteCode);
|
?assertMatch(Code when is_binary(Code), ByteCode);
|
||||||
Code when Backend == fate, is_tuple(Code) ->
|
Code when Backend == fate, is_tuple(Code) ->
|
||||||
?assertMatch(#{}, aeb_fate_code:functions(Code));
|
Code1 = aeb_fate_code:deserialize(aeb_fate_code:serialize(Code)),
|
||||||
|
?assertMatch({X, X}, {Code1, Code});
|
||||||
ErrBin ->
|
ErrBin ->
|
||||||
io:format("\n~s", [ErrBin]),
|
io:format("\n~s", [ErrBin]),
|
||||||
error(ErrBin)
|
error(ErrBin)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user