Contract factories and bytecode introspection #796
@ -41,6 +41,7 @@
|
|||||||
element(1, T) =:= qcon).
|
element(1, T) =:= qcon).
|
||||||
|
|
||||||
-type why_record() :: aeso_syntax:field(aeso_syntax:expr())
|
-type why_record() :: aeso_syntax:field(aeso_syntax:expr())
|
||||||
|
| {var_args, aeso_syntax:ann(), aeso_syntax:expr()}
|
||||||
| {proj, aeso_syntax:ann(), aeso_syntax:expr(), aeso_syntax:id()}.
|
| {proj, aeso_syntax:ann(), aeso_syntax:expr(), aeso_syntax:id()}.
|
||||||
|
|
||||||
-type pos() :: aeso_errors:pos().
|
-type pos() :: aeso_errors:pos().
|
||||||
@ -479,7 +480,6 @@ global_env() ->
|
|||||||
{"gas_limit", Int},
|
{"gas_limit", Int},
|
||||||
{"bytecode_hash",FunC1(bytecode_hash, A, Option(Hash))},
|
{"bytecode_hash",FunC1(bytecode_hash, A, Option(Hash))},
|
||||||
{"create", FunN([ {named_arg_t, Ann, {id, Ann, "value"}, Int, {typed, Ann, {int, Ann, 0}, Int}}
|
{"create", FunN([ {named_arg_t, Ann, {id, Ann, "value"}, Int, {typed, Ann, {int, Ann, 0}, Int}}
|
||||||
, {named_arg_t, Ann, {id, Ann, "code"}, A, undefined}
|
|
||||||
], var_args, A)},
|
], var_args, A)},
|
||||||
{"clone", FunN([ {named_arg_t, Ann, {id, Ann, "gas"}, Int,
|
{"clone", FunN([ {named_arg_t, Ann, {id, Ann, "gas"}, Int,
|
||||||
{qid, Ann, ["Call","gas_left"]}}
|
{qid, Ann, ["Call","gas_left"]}}
|
||||||
@ -1593,9 +1593,10 @@ infer_var_args_fun(Env, {typed, Ann, Fun, FunType0}, NamedArgs, ArgTypes) ->
|
|||||||
case Fun of
|
case Fun of
|
||||||
{qid, _, ["Chain", "create"]} ->
|
{qid, _, ["Chain", "create"]} ->
|
||||||
{fun_t, _, NamedArgsT, var_args, RetT} = FunType0,
|
{fun_t, _, NamedArgsT, var_args, RetT} = FunType0,
|
||||||
check_contract_construction(Env, RetT, Fun, NamedArgsT, ArgTypes, RetT),
|
GasCapMock = {named_arg_t, Ann, {id, Ann, "gas"}, {id, Ann, "int"}, {int, Ann, 0}},
|
||||||
{fun_t, Ann, NamedArgsT, ArgTypes,
|
ProtectedMock = {named_arg_t, Ann, {id, Ann, "protected"}, {id, Ann, "bool"}, {bool, Ann, false}},
|
||||||
{if_t, Ann, {id, Ann, "protected"}, {app_t, Ann, {id, Ann, "option"}, [RetT]}, RetT}};
|
check_contract_construction(Env, RetT, Fun, [GasCapMock, ProtectedMock|NamedArgsT], ArgTypes, RetT),
|
||||||
|
{fun_t, Ann, NamedArgsT, ArgTypes, RetT};
|
||||||
{qid, _, ["Chain", "clone"]} ->
|
{qid, _, ["Chain", "clone"]} ->
|
||||||
{fun_t, _, NamedArgsT, var_args, RetT} = FunType0,
|
{fun_t, _, NamedArgsT, var_args, RetT} = FunType0,
|
||||||
ContractT =
|
ContractT =
|
||||||
@ -1617,14 +1618,15 @@ check_contract_construction(Env, ContractT, Fun, NamedArgsT, ArgTypes, RetT) ->
|
|||||||
InitT = fresh_uvar(Ann),
|
InitT = fresh_uvar(Ann),
|
||||||
unify(Env, InitT, {fun_t, Ann, NamedArgsT, ArgTypes, fresh_uvar(Ann)}, {checking_init_args, Ann, ContractT, ArgTypes}),
|
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}),
|
unify(Env, RetT, ContractT, {return_contract, Fun, ContractT}),
|
||||||
|
io:format("DEREF: ~p\n", [dereference_deep(InitT)]),
|
||||||
constrain([ #field_constraint{
|
constrain([ #field_constraint{
|
||||||
record_t = unfold_types_in_type(Env, ContractT),
|
record_t = unfold_types_in_type(Env, ContractT),
|
||||||
field = {id, Ann, "init"},
|
field = {id, Ann, "init"},
|
||||||
field_t = InitT,
|
field_t = InitT,
|
||||||
kind = project,
|
kind = project,
|
||||||
context = {var_args, Fun} }
|
context = {var_args, Ann, Fun} }
|
||||||
, #is_contract_constraint{ contract_t = ContractT,
|
, #is_contract_constraint{ contract_t = ContractT,
|
||||||
context = {var_args, Fun} }
|
context = {var_args, Ann, Fun} }
|
||||||
]).
|
]).
|
||||||
|
|
||||||
split_args(Args0) ->
|
split_args(Args0) ->
|
||||||
@ -1909,7 +1911,7 @@ solve_named_argument_constraints(Env, Constraints0) ->
|
|||||||
[ C || C <- dereference_deep(Constraints0),
|
[ C || C <- dereference_deep(Constraints0),
|
||||||
unsolved == check_named_argument_constraint(Env, C) ].
|
unsolved == check_named_argument_constraint(Env, C) ].
|
||||||
|
|
||||||
%% If false, a type error have been emitted, so it's safe to drop the constraint.
|
%% If false, a type error has been emitted, so it's safe to drop the constraint.
|
||||||
-spec check_named_argument_constraint(env(), named_argument_constraint()) -> true | false | unsolved.
|
-spec check_named_argument_constraint(env(), named_argument_constraint()) -> true | false | unsolved.
|
||||||
check_named_argument_constraint(_Env, #named_argument_constraint{ args = {uvar, _, _} }) ->
|
check_named_argument_constraint(_Env, #named_argument_constraint{ args = {uvar, _, _} }) ->
|
||||||
unsolved;
|
unsolved;
|
||||||
@ -2410,8 +2412,14 @@ unify1(_Env, {fun_t, _, _, _, _}, {fun_t, _, _, var_args, _}, When) ->
|
|||||||
unify1(_Env, {fun_t, _, _, var_args, _}, {fun_t, _, _, _, _}, When) ->
|
unify1(_Env, {fun_t, _, _, var_args, _}, {fun_t, _, _, _, _}, When) ->
|
||||||
error({unify_varargs, When});
|
error({unify_varargs, When});
|
||||||
unify1(Env, {fun_t, _, Named1, Args1, Result1}, {fun_t, _, Named2, Args2, Result2}, When)
|
unify1(Env, {fun_t, _, Named1, Args1, Result1}, {fun_t, _, Named2, Args2, Result2}, When)
|
||||||
when length(Args1) == length(Args2) ->
|
when length(Args1) == length(Args2) ->
|
||||||
unify(Env, Named1, Named2, When) andalso
|
Named1_ = if is_list(Named1) -> lists:keysort(3, Named1);
|
||||||
|
true -> Named1
|
||||||
|
end,
|
||||||
|
Named2_ = if is_list(Named2) -> lists:keysort(3, Named2);
|
||||||
|
true -> Named2
|
||||||
|
end,
|
||||||
|
unify(Env, Named1_, Named2_, When) andalso
|
||||||
unify(Env, Args1, Args2, When) andalso unify(Env, Result1, Result2, When);
|
unify(Env, Args1, Args2, When) andalso unify(Env, Result1, Result2, When);
|
||||||
unify1(Env, {app_t, _, {Tag, _, F}, Args1}, {app_t, _, {Tag, _, F}, Args2}, When)
|
unify1(Env, {app_t, _, {Tag, _, F}, Args1}, {app_t, _, {Tag, _, F}, Args2}, When)
|
||||||
when length(Args1) == length(Args2), Tag == id orelse Tag == qid ->
|
when length(Args1) == length(Args2), Tag == id orelse Tag == qid ->
|
||||||
@ -2528,10 +2536,6 @@ apply_typesig_constraint(Ann, bytes_concat, {fun_t, _, [], [A, B], C}) ->
|
|||||||
add_bytes_constraint({add_bytes, Ann, concat, A, B, C});
|
add_bytes_constraint({add_bytes, Ann, concat, A, B, C});
|
||||||
apply_typesig_constraint(Ann, bytes_split, {fun_t, _, [], [C], {tuple_t, _, [A, B]}}) ->
|
apply_typesig_constraint(Ann, bytes_split, {fun_t, _, [], [C], {tuple_t, _, [A, B]}}) ->
|
||||||
add_bytes_constraint({add_bytes, Ann, split, A, B, C});
|
add_bytes_constraint({add_bytes, Ann, split, A, B, C});
|
||||||
apply_typesig_constraint(Ann, clone, {fun_t, _, Named, var_args, _}) ->
|
|
||||||
[RefT] = [RefT || {named_arg_t, _, {id, _, "ref"}, RefT, _} <- Named],
|
|
||||||
constrain([#is_contract_constraint{ contract_t = RefT,
|
|
||||||
context = {clone, Ann} }]);
|
|
||||||
apply_typesig_constraint(Ann, bytecode_hash, {fun_t, _, _, [Con], _}) ->
|
apply_typesig_constraint(Ann, bytecode_hash, {fun_t, _, _, [Con], _}) ->
|
||||||
constrain([#is_contract_constraint{ contract_t = Con,
|
constrain([#is_contract_constraint{ contract_t = Con,
|
||||||
context = {bytecode_hash, Ann} }]).
|
context = {bytecode_hash, Ann} }]).
|
||||||
@ -2664,10 +2668,9 @@ mk_error({not_a_contract_type, Type, Cxt}) ->
|
|||||||
end,
|
end,
|
||||||
{Pos, Cxt1} =
|
{Pos, Cxt1} =
|
||||||
case Cxt of
|
case Cxt of
|
||||||
{clone, Ann} ->
|
{var_args, Ann, Fun} ->
|
||||||
{pos(Ann), "when calling Chain.clone"};
|
{pos(Ann),
|
||||||
{bytecode_hash, Ann} ->
|
io_lib:format("when calling variadic function\n~s\n", [pp_expr(" ", Fun)])};
|
||||||
{pos(Ann), "when calling Chain.bytecode_hash"};
|
|
||||||
{contract_literal, Lit} ->
|
{contract_literal, Lit} ->
|
||||||
{pos(Lit),
|
{pos(Lit),
|
||||||
io_lib:format("when checking that the contract literal\n~s\n"
|
io_lib:format("when checking that the contract literal\n~s\n"
|
||||||
@ -2959,6 +2962,12 @@ pp_when({field_constraint, FieldType0, InferredType0, Fld}) ->
|
|||||||
InferredType = instantiate(InferredType0),
|
InferredType = instantiate(InferredType0),
|
||||||
{pos(Fld),
|
{pos(Fld),
|
||||||
case Fld of
|
case Fld of
|
||||||
|
{var_args, _Ann, _Fun} ->
|
||||||
|
io_lib:format("when checking contract construction of type\n~s (at ~s)\nagainst the expected type\n~s\n",
|
||||||
|
[pp_type(" ", FieldType),
|
||||||
|
pp_loc(Fld),
|
||||||
|
pp_type(" ", InferredType)
|
||||||
|
]);
|
||||||
{field, _Ann, LV, Id, E} ->
|
{field, _Ann, LV, Id, E} ->
|
||||||
io_lib:format("when checking the assignment of the field\n~s (at ~s)\nto the old value ~s and the new value\n~s\n",
|
io_lib:format("when checking the assignment of the field\n~s (at ~s)\nto the old value ~s and the new value\n~s\n",
|
||||||
[pp_typed(" ", {lvalue, [], LV}, FieldType),
|
[pp_typed(" ", {lvalue, [], LV}, FieldType),
|
||||||
@ -2981,6 +2990,13 @@ pp_when({record_constraint, RecType0, InferredType0, Fld}) ->
|
|||||||
InferredType = instantiate(InferredType0),
|
InferredType = instantiate(InferredType0),
|
||||||
{Pos, WhyRec} = pp_why_record(Fld),
|
{Pos, WhyRec} = pp_why_record(Fld),
|
||||||
case Fld of
|
case Fld of
|
||||||
|
{var_args, _Ann, _Fun} ->
|
||||||
|
{Pos,
|
||||||
|
io_lib:format("when checking that contract construction of type\n~s\n~s\n"
|
||||||
|
"matches the expected type\n~s\n",
|
||||||
|
[pp_type(" ", RecType), WhyRec, pp_type(" ", InferredType)]
|
||||||
|
)
|
||||||
|
};
|
||||||
{field, _Ann, _LV, _Id, _E} ->
|
{field, _Ann, _LV, _Id, _E} ->
|
||||||
{Pos,
|
{Pos,
|
||||||
io_lib:format("when checking that the record type\n~s\n~s\n"
|
io_lib:format("when checking that the record type\n~s\n~s\n"
|
||||||
@ -3051,17 +3067,21 @@ pp_when({arg_name, Id1, Id2, When}) ->
|
|||||||
{Pos
|
{Pos
|
||||||
, io_lib:format("when unifying names of named arguments: ~s and ~s\n~s", [pp_expr(Id1), pp_expr(Id2), Ctx])
|
, io_lib:format("when unifying names of named arguments: ~s and ~s\n~s", [pp_expr(Id1), pp_expr(Id2), Ctx])
|
||||||
};
|
};
|
||||||
|
pp_when({var_args, Ann, Fun}) ->
|
||||||
|
{pos(Ann)
|
||||||
|
, io_lib:format("when resolving arguments of variadic function\n~s\n", [pp_expr(" ", Fun)])
|
||||||
|
};
|
||||||
pp_when(unknown) -> {pos(0,0), ""}.
|
pp_when(unknown) -> {pos(0,0), ""}.
|
||||||
|
|
||||||
-spec pp_why_record(why_record()) -> {pos(), iolist()}.
|
-spec pp_why_record(why_record()) -> {pos(), iolist()}.
|
||||||
pp_why_record(Fld = {field, _Ann, LV, _Id, _E}) ->
|
pp_why_record({var_args, Ann, Fun}) ->
|
||||||
{pos(Fld),
|
{pos(Ann),
|
||||||
io_lib:format("arising from an assignment of the field ~s (at ~s)",
|
io_lib:format("arising from resolution of variadic function ~s (at ~s)",
|
||||||
[pp_expr("", {lvalue, [], LV}), pp_loc(Fld)])};
|
[pp_expr("", Fun), pp_loc(Fun)])};
|
||||||
pp_why_record(Fld = {field, _Ann, LV, _E}) ->
|
pp_why_record(Fld = {field, _Ann, LV, _E}) ->
|
||||||
{pos(Fld),
|
{pos(Fld),
|
||||||
io_lib:format("arising from an assignment of the field ~s (at ~s)",
|
io_lib:format("arising from an assignment of the field ~s (at ~s)",
|
||||||
[pp_expr("", {lvalue, [], LV}), pp_loc(Fld)])};
|
[pp_expr("", {lvalue, [], LV}), pp_loc(Fld)])};
|
||||||
pp_why_record({proj, _Ann, Rec, FldName}) ->
|
pp_why_record({proj, _Ann, Rec, FldName}) ->
|
||||||
{pos(Rec),
|
{pos(Rec),
|
||||||
io_lib:format("arising from the projection of the field ~s (at ~s)",
|
io_lib:format("arising from the projection of the field ~s (at ~s)",
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
// If you need a quick compiler test — this file is for your playground
|
// If you need a quick compiler test — this file is for your playground
|
||||||
contract interface Remote =
|
contract Chuj =
|
||||||
entrypoint init : int => void
|
type state = bool
|
||||||
entrypoint f : int => int
|
// entrypoint init : (int, bool) => void
|
||||||
|
entrypoint init(x : int, y : bool) = if(x < 0) abort("xD") else true
|
||||||
|
|
||||||
contract Test =
|
main contract Test =
|
||||||
entrypoint kek(r : Remote) =
|
stateful entrypoint kek() =
|
||||||
Chain.clone(ref=r, 123)
|
Chain.create(value=3, 123, 555) : Chuj
|
||||||
Chain.bytecode_hash(r)
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user