[Ceres]: Add arbitrary size byte arrays (#456)
* Extend compiler to allow bytes()/bytes as type * Add split_any, to_fixed_size, size, to_any_size, Int.to_bytes and String.to_bytes * Add tests * Use and and not andalso in unify, some things have side-effects * Bump to aebytecode v3.3.0 * Changelog + update documentation * fix wording in documentation
This commit is contained in:
parent
32a98112d3
commit
c3788b2b5a
@ -18,6 +18,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
only contains the old datatypes, that can be used to interface existing
|
||||
contracts. Standard library `AENSCompat` is added to convert between old and
|
||||
new pointers.
|
||||
- Introduce arbitrary sized binary arrays (type `bytes()`); adding `Bytes.split_any`,
|
||||
`Bytes.to_fixed_size`, `Bytes.to_any_size`, `Bytes.size`, `String.to_bytes`,
|
||||
and `Int.to_bytes`; and adjust `Bytes.concat` to allow both fixed and arbitrary
|
||||
sized byte arrays.
|
||||
### Changed
|
||||
### Removed
|
||||
- `Bitwise.aes` standard library is removed - the builtin operations are superior.
|
||||
|
@ -371,7 +371,7 @@ Each bit is true if and only if it was 1 in `a` and 0 in `b`
|
||||
|
||||
### Bytes
|
||||
|
||||
#### to_int
|
||||
#### to\_int
|
||||
```
|
||||
Bytes.to_int(b : bytes(n)) : int
|
||||
```
|
||||
@ -379,7 +379,7 @@ Bytes.to_int(b : bytes(n)) : int
|
||||
Interprets the byte array as a big endian integer
|
||||
|
||||
|
||||
#### to_str
|
||||
#### to\_str
|
||||
```
|
||||
Bytes.to_str(b : bytes(n)) : string
|
||||
```
|
||||
@ -392,7 +392,8 @@ Returns the hexadecimal representation of the byte array
|
||||
Bytes.concat : (a : bytes(m), b : bytes(n)) => bytes(m + n)
|
||||
```
|
||||
|
||||
Concatenates two byte arrays
|
||||
Concatenates two byte arrays. If `m` and `n` are known at compile time, the
|
||||
result can be used as a fixed size byte array, otherwise it has type `bytes()`.
|
||||
|
||||
|
||||
#### split
|
||||
@ -402,6 +403,38 @@ Bytes.split(a : bytes(m + n)) : bytes(m) * bytes(n)
|
||||
|
||||
Splits a byte array at given index
|
||||
|
||||
#### split\_any
|
||||
```
|
||||
Bytes.split_any(a : bytes(), at : int) : option(bytes() * bytes(n))
|
||||
```
|
||||
|
||||
Splits an arbitrary size byte array at index `at`. If `at` is positive split
|
||||
from the beginning of the array, if `at` is negative, split `abs(at)` from the
|
||||
_end_ of the array. If the array is shorter than `abs(at)` then `None` is
|
||||
returned.
|
||||
|
||||
#### to\_fixed\_size
|
||||
```
|
||||
Bytes.to_fixed_size(a : bytes()) : option(bytes(n))
|
||||
```
|
||||
|
||||
Converts an arbitrary size byte array to a fix size byte array. If `a` is
|
||||
`n` bytes, `None` is returned.
|
||||
|
||||
#### to\_any\_size
|
||||
```
|
||||
Bytes.to_any_size(a : bytes(n)) : bytes()
|
||||
```
|
||||
|
||||
Converts a fixed size byte array to an arbitrary size byte array. This is a
|
||||
no-op at run-time, and only used during type checking.
|
||||
|
||||
#### size
|
||||
```
|
||||
Bytes.size(a : bytes()) : int
|
||||
```
|
||||
|
||||
Computes the lenght/size of a byte array.
|
||||
|
||||
### Call
|
||||
|
||||
@ -830,12 +863,20 @@ Verifies a standard 64-byte ECDSA signature (`R || S`).
|
||||
|
||||
### Int
|
||||
|
||||
#### to_str
|
||||
#### to\_str
|
||||
```
|
||||
Int.to_str : int => string
|
||||
Int.to_str(n : int) : string
|
||||
```
|
||||
|
||||
Casts integer to string using decimal representation
|
||||
Casts the integer to a string (in decimal representation).
|
||||
|
||||
#### to\_bytes
|
||||
```
|
||||
Int.to_bytes(n : int, size : int) : bytes()
|
||||
```
|
||||
|
||||
Casts the integer to a byte array with `size` bytes (big endian, truncating if
|
||||
necessary).
|
||||
|
||||
|
||||
### Map
|
||||
@ -2412,6 +2453,13 @@ to_int(s : string) : option(int)
|
||||
Converts a decimal ("123", "-253") or a hexadecimal ("0xa2f", "-0xBBB") string into
|
||||
an integer. If the string doesn't contain a valid number `None` is returned.
|
||||
|
||||
#### to\_bytes
|
||||
```
|
||||
to_bytes(s : string) : bytes()
|
||||
```
|
||||
|
||||
Converts string into byte array.
|
||||
|
||||
#### sha3
|
||||
```
|
||||
sha3(s : string) : hash
|
||||
|
@ -1,5 +1,8 @@
|
||||
include "List.aes"
|
||||
namespace String =
|
||||
// Gives a bytes() representation of the string
|
||||
function to_bytes(s : string) : bytes() = StringInternal.to_bytes(s)
|
||||
|
||||
// Computes the SHA3/Keccak hash of the string
|
||||
function sha3(s : string) : hash = StringInternal.sha3(s)
|
||||
// Computes the SHA256 hash of the string.
|
||||
|
@ -14,7 +14,7 @@
|
||||
]}.
|
||||
|
||||
{relx, [{release, {aesophia, "8.0.0"},
|
||||
[aesophia, aebytecode, getopt]},
|
||||
[aesophia, aebytecode]},
|
||||
|
||||
{dev_mode, true},
|
||||
{include_erts, false},
|
||||
|
@ -282,6 +282,8 @@ decode_type(#{list := [Et]}) ->
|
||||
decode_type(#{map := Ets}) ->
|
||||
Ts = decode_types(Ets),
|
||||
["map",$(,lists:join(",", Ts),$)];
|
||||
decode_type(#{bytes := any}) ->
|
||||
["bytes()"];
|
||||
decode_type(#{bytes := Len}) ->
|
||||
["bytes(", integer_to_list(Len), ")"];
|
||||
decode_type(#{variant := Ets}) ->
|
||||
|
@ -89,8 +89,9 @@
|
||||
|
||||
-type field_constraint() :: #field_constraint{} | #record_create_constraint{} | #is_contract_constraint{}.
|
||||
|
||||
-type byte_constraint() :: {is_bytes, utype()}
|
||||
| {add_bytes, aeso_syntax:ann(), concat | split, utype(), utype(), utype()}.
|
||||
-type byte_constraint() :: {is_bytes, term(), utype()}
|
||||
| {is_fixed_bytes, term(), utype()}
|
||||
| {add_bytes, aeso_syntax:ann(), concat | split | split_any, utype(), utype(), utype()}.
|
||||
|
||||
-type aens_resolve_constraint() :: {aens_resolve_type, utype()}.
|
||||
-type oracle_type_constraint() :: {oracle_type, aeso_syntax:ann(), utype()}.
|
||||
@ -829,6 +830,7 @@ global_env() ->
|
||||
[{"length", Fun1(String, Int)},
|
||||
{"concat", Fun([String, String], String)},
|
||||
{"to_list", Fun1(String, List(Char))},
|
||||
{"to_bytes", Fun1(String, Bytes(any))},
|
||||
{"from_list", Fun1(List(Char), String)},
|
||||
{"to_upper", Fun1(String, String)},
|
||||
{"to_lower", Fun1(String, String)},
|
||||
@ -859,14 +861,19 @@ global_env() ->
|
||||
%% Bytes
|
||||
BytesScope = #scope
|
||||
{ funs = MkDefs(
|
||||
[{"to_int", Fun1(Bytes(any), Int)},
|
||||
{"to_str", Fun1(Bytes(any), String)},
|
||||
{"concat", FunC(bytes_concat, [Bytes(any), Bytes(any)], Bytes(any))},
|
||||
{"split", FunC(bytes_split, [Bytes(any)], Pair(Bytes(any), Bytes(any)))}
|
||||
[{"to_int", Fun1(Bytes('_'), Int)},
|
||||
{"to_str", Fun1(Bytes('_'), String)},
|
||||
{"to_fixed_size", Fun1(Bytes(any), Option(Bytes(fixed)))},
|
||||
{"to_any_size", Fun1(Bytes(fixed), Bytes(any))},
|
||||
{"size", Fun1(Bytes('_'), Int)},
|
||||
{"concat", FunC(bytes_concat, [Bytes('_'), Bytes('_')], Bytes('_'))},
|
||||
{"split", FunC1(bytes_split, Bytes(fixed), Pair(Bytes(fixed), Bytes(fixed)))},
|
||||
{"split_any", Fun([Bytes(any), Int], Option(Pair(Bytes(any), Bytes(any))))}
|
||||
]) },
|
||||
|
||||
%% Conversion
|
||||
IntScope = #scope{ funs = MkDefs([{"to_str", Fun1(Int, String)},
|
||||
{"to_bytes", Fun([Int, Int], Bytes(any))},
|
||||
{"mulmod", Fun([Int, Int, Int], Int)}]) },
|
||||
|
||||
AddressScope = #scope{ funs = MkDefs([{"to_str", Fun1(Address, String)},
|
||||
@ -1790,8 +1797,8 @@ lookup_name(Env = #env{ namespace = NS, current_function = CurFn }, As, Id, Opti
|
||||
Freshen = proplists:get_value(freshen, Options, false),
|
||||
check_stateful(Env, Id, Ty),
|
||||
Ty1 = case Ty of
|
||||
{type_sig, _, _, _, _, _} -> freshen_type_sig(As, Ty);
|
||||
_ when Freshen -> freshen_type(As, Ty);
|
||||
{type_sig, _, _, _, _, _} -> freshen_type_sig(As, Ty, [{fun_name, Id}]);
|
||||
_ when Freshen -> freshen_type(As, Ty, [{fun_name, Id}]);
|
||||
_ -> Ty
|
||||
end,
|
||||
{set_qname(QId, Id), Ty1}
|
||||
@ -2672,7 +2679,8 @@ destroy_and_report_unsolved_constraints(Env) ->
|
||||
(_) -> false
|
||||
end, OtherCs2),
|
||||
{BytesCs, OtherCs4} =
|
||||
lists:partition(fun({is_bytes, _}) -> true;
|
||||
lists:partition(fun({is_bytes, _, _}) -> true;
|
||||
({is_fixed_bytes, _, _}) -> true;
|
||||
({add_bytes, _, _, _, _, _}) -> true;
|
||||
(_) -> false
|
||||
end, OtherCs3),
|
||||
@ -2801,15 +2809,21 @@ solve_constraint(Env, C = #dependent_type_constraint{}) ->
|
||||
check_named_argument_constraint(Env, C);
|
||||
solve_constraint(Env, C = #named_argument_constraint{}) ->
|
||||
check_named_argument_constraint(Env, C);
|
||||
solve_constraint(_Env, {is_bytes, _}) -> ok;
|
||||
solve_constraint(Env, {add_bytes, Ann, _, A0, B0, C0}) ->
|
||||
solve_constraint(_Env, {is_bytes, _, _}) -> ok;
|
||||
solve_constraint(_Env, {is_fixed_bytes, _, _}) -> ok;
|
||||
solve_constraint(Env, {add_bytes, Ann, Action, A0, B0, C0}) ->
|
||||
A = unfold_types_in_type(Env, dereference(A0)),
|
||||
B = unfold_types_in_type(Env, dereference(B0)),
|
||||
C = unfold_types_in_type(Env, dereference(C0)),
|
||||
case {A, B, C} of
|
||||
{{bytes_t, _, M}, {bytes_t, _, N}, _} -> unify(Env, {bytes_t, Ann, M + N}, C, {at, Ann});
|
||||
{{bytes_t, _, M}, _, {bytes_t, _, R}} when R >= M -> unify(Env, {bytes_t, Ann, R - M}, B, {at, Ann});
|
||||
{_, {bytes_t, _, N}, {bytes_t, _, R}} when R >= N -> unify(Env, {bytes_t, Ann, R - N}, A, {at, Ann});
|
||||
{{bytes_t, _, M}, {bytes_t, _, N}, _} when is_integer(M), is_integer(N) ->
|
||||
unify(Env, {bytes_t, Ann, M + N}, C, {at, Ann});
|
||||
{{bytes_t, _, M}, _, {bytes_t, _, R}} when is_integer(M), is_integer(R), R >= M ->
|
||||
unify(Env, {bytes_t, Ann, R - M}, B, {at, Ann});
|
||||
{_, {bytes_t, _, N}, {bytes_t, _, R}} when is_integer(N), is_integer(R), R >= N ->
|
||||
unify(Env, {bytes_t, Ann, R - N}, A, {at, Ann});
|
||||
{{bytes_t, _, _}, {bytes_t, _, _}, _} when Action == concat ->
|
||||
unify(Env, {bytes_t, Ann, any}, C, {at, Ann});
|
||||
_ -> ok
|
||||
end;
|
||||
solve_constraint(_, _) -> ok.
|
||||
@ -2818,18 +2832,29 @@ check_bytes_constraints(Env, Constraints) ->
|
||||
InAddConstraint = [ T || {add_bytes, _, _, A, B, C} <- Constraints,
|
||||
T <- [A, B, C],
|
||||
element(1, T) /= bytes_t ],
|
||||
InSplitConstraint = [ T || {add_bytes, _, split, A, B, C} <- Constraints,
|
||||
T <- [A, B, C],
|
||||
element(1, T) /= bytes_t ],
|
||||
%% Skip is_bytes constraints for types that occur in add_bytes constraints
|
||||
%% (no need to generate error messages for both is_bytes and add_bytes).
|
||||
Skip = fun({is_bytes, T}) -> lists:member(T, InAddConstraint);
|
||||
Skip = fun({is_bytes, _, T}) -> lists:member(T, InAddConstraint);
|
||||
({is_fixed_bytes, _, T}) -> lists:member(T, InSplitConstraint);
|
||||
(_) -> false end,
|
||||
[ check_bytes_constraint(Env, C) || C <- Constraints, not Skip(C) ].
|
||||
|
||||
check_bytes_constraint(Env, {is_bytes, Type}) ->
|
||||
check_bytes_constraint(Env, {is_bytes, Ann, Type}) ->
|
||||
Type1 = unfold_types_in_type(Env, instantiate(Type)),
|
||||
case Type1 of
|
||||
{bytes_t, _, _} -> ok;
|
||||
{bytes_t, _, N} when is_integer(N); N == any -> ok;
|
||||
_ ->
|
||||
type_error({unknown_byte_length, Type})
|
||||
type_error({unknown_byte_type, Ann, Type})
|
||||
end;
|
||||
check_bytes_constraint(Env, {is_fixed_bytes, Ann, Type}) ->
|
||||
Type1 = unfold_types_in_type(Env, instantiate(Type)),
|
||||
case Type1 of
|
||||
{bytes_t, _, N} when is_integer(N) -> ok;
|
||||
_ ->
|
||||
type_error({unknown_byte_length, Ann, Type})
|
||||
end;
|
||||
check_bytes_constraint(Env, {add_bytes, Ann, Fun, A0, B0, C0}) ->
|
||||
A = unfold_types_in_type(Env, instantiate(A0)),
|
||||
@ -3130,9 +3155,9 @@ unify1(Env, T, {uvar, A, R}, Variance, When) ->
|
||||
unify1(Env, {uvar, A, R}, T, Variance, When);
|
||||
unify1(_Env, {tvar, _, X}, {tvar, _, X}, _Variance, _When) -> true; %% Rigid type variables
|
||||
unify1(Env, [A|B], [C|D], [V|Variances], When) ->
|
||||
unify0(Env, A, C, V, When) andalso unify0(Env, B, D, Variances, When);
|
||||
unify0(Env, A, C, V, When) and unify0(Env, B, D, Variances, When);
|
||||
unify1(Env, [A|B], [C|D], Variance, When) ->
|
||||
unify0(Env, A, C, Variance, When) andalso unify0(Env, B, D, Variance, When);
|
||||
unify0(Env, A, C, Variance, When) and unify0(Env, B, D, Variance, When);
|
||||
unify1(_Env, X, X, _Variance, _When) ->
|
||||
true;
|
||||
unify1(_Env, _A, {id, _, "void"}, Variance, _When)
|
||||
@ -3177,8 +3202,8 @@ unify1(_Env, {fun_t, _, _, var_args, _}, {fun_t, _, _, _, _}, _Variance, When) -
|
||||
type_error({unify_varargs, When});
|
||||
unify1(Env, {fun_t, _, Named1, Args1, Result1}, {fun_t, _, Named2, Args2, Result2}, Variance, When)
|
||||
when length(Args1) == length(Args2) ->
|
||||
unify0(Env, Named1, Named2, opposite_variance(Variance), When) andalso
|
||||
unify0(Env, Args1, Args2, opposite_variance(Variance), When) andalso
|
||||
unify0(Env, Named1, Named2, opposite_variance(Variance), When) and
|
||||
unify0(Env, Args1, Args2, opposite_variance(Variance), When) and
|
||||
unify0(Env, Result1, Result2, Variance, When);
|
||||
unify1(Env, {app_t, _, {Tag, _, F}, Args1}, {app_t, _, {Tag, _, F}, Args2}, Variance, When)
|
||||
when length(Args1) == length(Args2), Tag == id orelse Tag == qid ->
|
||||
@ -3292,35 +3317,45 @@ create_freshen_tvars() ->
|
||||
destroy_freshen_tvars() ->
|
||||
ets_delete(freshen_tvars).
|
||||
|
||||
freshen_type(Ann, Type) ->
|
||||
freshen_type(Ann, Type, Ctx) ->
|
||||
create_freshen_tvars(),
|
||||
Type1 = freshen(Ann, Type),
|
||||
Type1 = freshen(Ann, Type, Ctx),
|
||||
destroy_freshen_tvars(),
|
||||
Type1.
|
||||
|
||||
freshen(Type) ->
|
||||
freshen(aeso_syntax:get_ann(Type), Type).
|
||||
freshen(aeso_syntax:get_ann(Type), Type, none).
|
||||
|
||||
freshen(Ann, {tvar, _, Name}) ->
|
||||
freshen(Ann, {tvar, _, Name}, _Ctx) ->
|
||||
NewT = case ets_lookup(freshen_tvars, Name) of
|
||||
[] -> fresh_uvar(Ann);
|
||||
[{Name, T}] -> T
|
||||
end,
|
||||
ets_insert(freshen_tvars, {Name, NewT}),
|
||||
NewT;
|
||||
freshen(Ann, {bytes_t, _, any}) ->
|
||||
freshen(Ann, {bytes_t, _, '_'}, Ctx) ->
|
||||
X = fresh_uvar(Ann),
|
||||
add_constraint({is_bytes, X}),
|
||||
add_constraint({is_bytes, Ctx, X}),
|
||||
X;
|
||||
freshen(Ann, T) when is_tuple(T) ->
|
||||
list_to_tuple(freshen(Ann, tuple_to_list(T)));
|
||||
freshen(Ann, [A | B]) ->
|
||||
[freshen(Ann, A) | freshen(Ann, B)];
|
||||
freshen(_, X) ->
|
||||
freshen(Ann, {bytes_t, _, fixed}, Ctx) ->
|
||||
X = fresh_uvar(Ann),
|
||||
add_constraint({is_fixed_bytes, Ctx, X}),
|
||||
X;
|
||||
freshen(Ann, {fun_t, FAnn, NamedArgs, Args, Result}, Ctx) when is_list(Args) ->
|
||||
{fun_t, FAnn, freshen(Ann, NamedArgs, Ctx),
|
||||
[ freshen(Ann, Arg, [{arg, Ix} | Ctx]) || {Arg, Ix} <- lists:zip(Args, lists:seq(1, length(Args))) ],
|
||||
freshen(Ann, Result, [result | Ctx])};
|
||||
freshen(Ann, {fun_t, FAnn, NamedArgs, Arg, Result}, Ctx) ->
|
||||
{fun_t, FAnn, freshen(Ann, NamedArgs, Ctx), freshen(Ann, Arg, Ctx), freshen(Ann, Result, [result | Ctx])};
|
||||
freshen(Ann, T, Ctx) when is_tuple(T) ->
|
||||
list_to_tuple(freshen(Ann, tuple_to_list(T), Ctx));
|
||||
freshen(Ann, [A | B], Ctx) ->
|
||||
[freshen(Ann, A, Ctx) | freshen(Ann, B, Ctx)];
|
||||
freshen(_, X, _Ctx) ->
|
||||
X.
|
||||
|
||||
freshen_type_sig(Ann, TypeSig = {type_sig, _, Constr, _, _, _}) ->
|
||||
FunT = freshen_type(Ann, typesig_to_fun_t(TypeSig)),
|
||||
freshen_type_sig(Ann, TypeSig = {type_sig, _, Constr, _, _, _}, Ctx) ->
|
||||
FunT = freshen_type(Ann, typesig_to_fun_t(TypeSig), Ctx),
|
||||
apply_typesig_constraint(Ann, Constr, FunT),
|
||||
FunT.
|
||||
|
||||
@ -3855,8 +3890,11 @@ mk_error({bad_top_level_decl, Decl}) ->
|
||||
Msg = io_lib:format("The definition of '~s' must appear inside a ~s.",
|
||||
[pp_expr(Id), What]),
|
||||
mk_t_err(pos(Decl), Msg);
|
||||
mk_error({unknown_byte_length, Type}) ->
|
||||
Msg = io_lib:format("Cannot resolve length of byte array.", []),
|
||||
mk_error({unknown_byte_type, Ctx, Type}) ->
|
||||
Msg = io_lib:format("Cannot resolve type of byte array in\n ~s", [pp_context(Ctx)]),
|
||||
mk_t_err(pos(Type), Msg);
|
||||
mk_error({unknown_byte_length, Ctx, Type}) ->
|
||||
Msg = io_lib:format("Cannot resolve length of byte array in\n ~s", [pp_context(Ctx)]),
|
||||
mk_t_err(pos(Type), Msg);
|
||||
mk_error({unsolved_bytes_constraint, Ann, concat, A, B, C}) ->
|
||||
Msg = io_lib:format("Failed to resolve byte array lengths in call to Bytes.concat with arguments of type\n"
|
||||
@ -3870,6 +3908,12 @@ mk_error({unsolved_bytes_constraint, Ann, split, A, B, C}) ->
|
||||
[ pp_type(" - ", C), pp_loc(C), pp_type(" - ", A), pp_loc(A),
|
||||
pp_type(" - ", B), pp_loc(B)]),
|
||||
mk_t_err(pos(Ann), Msg);
|
||||
mk_error({unsolved_bytes_constraint, Ann, split_any, A, B, C}) ->
|
||||
Msg = io_lib:format("Failed to resolve byte arrays in call to Bytes.split_any with argument of type\n"
|
||||
"~s (at ~s)\nand result types\n~s (at ~s)\n~s (at ~s)",
|
||||
[ pp_type(" - ", C), pp_loc(C), pp_type(" - ", A), pp_loc(A),
|
||||
pp_type(" - ", B), pp_loc(B)]),
|
||||
mk_t_err(pos(Ann), Msg);
|
||||
mk_error({failed_to_get_compiler_version, Err}) ->
|
||||
Msg = io_lib:format("Failed to get compiler version. Error: ~p", [Err]),
|
||||
mk_t_err(pos(0, 0), Msg);
|
||||
@ -4236,6 +4280,18 @@ pp_type(Type) ->
|
||||
pp_type(Label, Type) ->
|
||||
prettypr:format(prettypr:beside(prettypr:text(Label), aeso_pretty:type(Type, [show_generated])), 80, 80).
|
||||
|
||||
|
||||
pp_context([{fun_name, Id}]) -> ["a call to ", pp(Id)];
|
||||
pp_context([result | Ctx]) -> ["the result of ", pp_context(Ctx)];
|
||||
pp_context([{arg, N} | Ctx]) ->
|
||||
Cnt = fun(1) -> "first";
|
||||
(2) -> "second";
|
||||
(3) -> "third";
|
||||
(I) -> io_lib:format("~pth", [I])
|
||||
end,
|
||||
["the ", Cnt(N), " argument of ", pp_context(Ctx)];
|
||||
pp_context(none) -> "unknown context".
|
||||
|
||||
src_file(T) -> aeso_syntax:get_ann(file, T, no_file).
|
||||
include_type(T) -> aeso_syntax:get_ann(include_type, T, none).
|
||||
line_number(T) -> aeso_syntax:get_ann(line, T, 0).
|
||||
@ -4287,7 +4343,7 @@ pp({tuple_t, _, []}) ->
|
||||
"unit";
|
||||
pp({tuple_t, _, Cpts}) ->
|
||||
["(", string:join(lists:map(fun pp/1, Cpts), " * "), ")"];
|
||||
pp({bytes_t, _, any}) -> "bytes(_)";
|
||||
pp({bytes_t, _, any}) -> "bytes()";
|
||||
pp({bytes_t, _, Len}) ->
|
||||
["bytes(", integer_to_list(Len), ")"];
|
||||
pp({app_t, _, T, []}) ->
|
||||
|
@ -291,14 +291,15 @@ builtins() ->
|
||||
{"gt_inv", 1}, {"gt_add", 2}, {"gt_mul", 2}, {"gt_pow", 2}, {"gt_is_one", 1},
|
||||
{"pairing", 2}, {"miller_loop", 2}, {"final_exp", 1},
|
||||
{"int_to_fr", 1}, {"int_to_fp", 1}, {"fr_to_int", 1}, {"fp_to_int", 1}]},
|
||||
{["StringInternal"], [{"length", 1}, {"concat", 2}, {"to_list", 1}, {"from_list", 1},
|
||||
{["StringInternal"], [{"length", 1}, {"concat", 2}, {"to_list", 1}, {"from_list", 1}, {"to_bytes", 1},
|
||||
{"sha3", 1}, {"sha256", 1}, {"blake2b", 1}, {"to_lower", 1}, {"to_upper", 1}]},
|
||||
{["Char"], [{"to_int", 1}, {"from_int", 1}]},
|
||||
{["Auth"], [{"tx_hash", none}, {"tx", none}]},
|
||||
{["Bits"], [{"set", 2}, {"clear", 2}, {"test", 2}, {"sum", 1}, {"intersection", 2},
|
||||
{"union", 2}, {"difference", 2}, {"none", none}, {"all", none}]},
|
||||
{["Bytes"], [{"to_int", 1}, {"to_str", 1}, {"concat", 2}, {"split", 1}]},
|
||||
{["Int"], [{"to_str", 1}, {"mulmod", 2}]},
|
||||
{["Bytes"], [{"to_int", 1}, {"to_str", 1}, {"concat", 2}, {"split", 1}, {"to_fixed_size", 1},
|
||||
{"to_any_size", 1}, {"size", 1}, {"split_any", 2}]},
|
||||
{["Int"], [{"to_str", 1}, {"to_bytes", 2}, {"mulmod", 2}]},
|
||||
{["Address"], [{"to_str", 1}, {"to_bytes", 1}, {"to_contract", 1},
|
||||
{"is_oracle", 1}, {"is_contract", 1}, {"is_payable", 1}]}
|
||||
],
|
||||
@ -611,6 +612,9 @@ expr_to_fcode(Env, Type, {qid, Ann, X}) ->
|
||||
{builtin_u, FAnn, B = bytes_split, Ar} ->
|
||||
{fun_t, _, _, _, {tuple_t, _, [{bytes_t, _, N}, _]}} = Type,
|
||||
{builtin_u, FAnn, B, Ar, [{lit, FAnn, {int, N}}]};
|
||||
{builtin_u, FAnn, B = bytes_to_fixed_size, Ar} ->
|
||||
{fun_t, _, _, _, {app_t, _, {id, _, "option"}, [{bytes_t, _, N}]}} = Type,
|
||||
{builtin_u, FAnn, B, Ar, [{lit, FAnn, {int, N}}]};
|
||||
Other -> Other
|
||||
end;
|
||||
|
||||
@ -1166,13 +1170,13 @@ stmts_to_fcode(Env, [Expr | Stmts]) ->
|
||||
op_builtins() ->
|
||||
[map_from_list, map_to_list, map_delete, map_member, map_size,
|
||||
stringinternal_length, stringinternal_concat, stringinternal_to_list, stringinternal_from_list,
|
||||
stringinternal_sha3, stringinternal_sha256, stringinternal_blake2b,
|
||||
stringinternal_to_bytes, stringinternal_sha3, stringinternal_sha256, stringinternal_blake2b,
|
||||
char_to_int, char_from_int, stringinternal_to_lower, stringinternal_to_upper,
|
||||
bits_set, bits_clear, bits_test, bits_sum, bits_intersection, bits_union,
|
||||
bits_difference, int_to_str, int_mulmod, address_to_str, address_to_bytes, crypto_verify_sig,
|
||||
address_to_contract,
|
||||
crypto_verify_sig_secp256k1, crypto_sha3, crypto_sha256, crypto_blake2b, crypto_poseidon,
|
||||
crypto_ecverify_secp256k1, crypto_ecrecover_secp256k1,
|
||||
bits_set, bits_clear, bits_test, bits_sum, bits_intersection, bits_union, bits_difference,
|
||||
int_to_str, int_to_bytes, int_mulmod,
|
||||
address_to_str, address_to_bytes, address_to_contract,
|
||||
crypto_verify_sig, crypto_verify_sig_secp256k1, crypto_sha3, crypto_sha256, crypto_blake2b,
|
||||
crypto_poseidon, crypto_ecverify_secp256k1, crypto_ecrecover_secp256k1,
|
||||
mcl_bls12_381_g1_neg, mcl_bls12_381_g1_norm, mcl_bls12_381_g1_valid,
|
||||
mcl_bls12_381_g1_is_zero, mcl_bls12_381_g1_add, mcl_bls12_381_g1_mul,
|
||||
mcl_bls12_381_g2_neg, mcl_bls12_381_g2_norm, mcl_bls12_381_g2_valid,
|
||||
|
@ -539,6 +539,14 @@ builtin_to_scode(Env, bytes_concat, [_, _] = Args) ->
|
||||
call_to_scode(Env, aeb_fate_ops:bytes_concat(?a, ?a, ?a), Args);
|
||||
builtin_to_scode(Env, bytes_split, [_, _] = Args) ->
|
||||
call_to_scode(Env, aeb_fate_ops:bytes_split(?a, ?a, ?a), Args);
|
||||
builtin_to_scode(Env, bytes_split_any, [_, _] = Args) ->
|
||||
call_to_scode(Env, aeb_fate_ops:bytes_split_any(?a, ?a, ?a), Args);
|
||||
builtin_to_scode(Env, bytes_to_fixed_size, [_, _] = Args) ->
|
||||
call_to_scode(Env, aeb_fate_ops:bytes_to_fixed_size(?a, ?a, ?a), Args);
|
||||
builtin_to_scode(Env, bytes_to_any_size, [A]) ->
|
||||
[to_scode(Env, A)]; %% no_op!
|
||||
builtin_to_scode(Env, bytes_size, [_] = Args) ->
|
||||
call_to_scode(Env, aeb_fate_ops:bytes_size(?a, ?a), Args);
|
||||
builtin_to_scode(Env, abort, [_] = Args) ->
|
||||
call_to_scode(Env, aeb_fate_ops:abort(?a), Args);
|
||||
builtin_to_scode(Env, exit, [_] = Args) ->
|
||||
@ -683,6 +691,7 @@ op_to_scode(map_member) -> aeb_fate_ops:map_member(?a, ?a, ?a);
|
||||
op_to_scode(map_size) -> aeb_fate_ops:map_size_(?a, ?a);
|
||||
op_to_scode(stringinternal_length) -> aeb_fate_ops:str_length(?a, ?a);
|
||||
op_to_scode(stringinternal_concat) -> aeb_fate_ops:str_join(?a, ?a, ?a);
|
||||
op_to_scode(stringinternal_to_bytes) -> aeb_fate_ops:str_to_bytes(?a, ?a);
|
||||
op_to_scode(stringinternal_to_list) -> aeb_fate_ops:str_to_list(?a, ?a);
|
||||
op_to_scode(stringinternal_from_list) -> aeb_fate_ops:str_from_list(?a, ?a);
|
||||
op_to_scode(stringinternal_to_lower) -> aeb_fate_ops:str_to_lower(?a, ?a);
|
||||
@ -699,6 +708,7 @@ 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_bytes) -> aeb_fate_ops:addr_to_bytes(?a, ?a);
|
||||
op_to_scode(int_to_str) -> aeb_fate_ops:int_to_str(?a, ?a);
|
||||
op_to_scode(int_to_bytes) -> aeb_fate_ops:int_to_bytes(?a, ?a, ?a);
|
||||
op_to_scode(int_mulmod) -> aeb_fate_ops:mulmod(?a, ?a, ?a, ?a);
|
||||
op_to_scode(contract_to_address) -> aeb_fate_ops:contract_to_address(?a, ?a);
|
||||
op_to_scode(address_to_contract) -> aeb_fate_ops:address_to_contract(?a, ?a);
|
||||
@ -1019,9 +1029,11 @@ attributes(I) ->
|
||||
{'APPEND', A, B, C} -> Pure(A, [B, C]);
|
||||
{'STR_JOIN', A, B, C} -> Pure(A, [B, C]);
|
||||
{'INT_TO_STR', A, B} -> Pure(A, B);
|
||||
{'INT_TO_BYTES', A, B, C} -> Pure(A, [B, C]);
|
||||
{'ADDR_TO_STR', A, B} -> Pure(A, B);
|
||||
{'STR_REVERSE', A, B} -> Pure(A, B);
|
||||
{'STR_LENGTH', A, B} -> Pure(A, B);
|
||||
{'STR_TO_BYTES', A, B} -> Pure(A, B);
|
||||
{'INT_TO_ADDR', A, B} -> Pure(A, B);
|
||||
{'VARIANT', A, B, C, D} -> Pure(A, [?a, B, C, D]);
|
||||
{'VARIANT_TEST', A, B, C} -> Pure(A, [B, C]);
|
||||
@ -1055,6 +1067,9 @@ attributes(I) ->
|
||||
{'BYTES_TO_STR', A, B} -> Pure(A, [B]);
|
||||
{'BYTES_CONCAT', A, B, C} -> Pure(A, [B, C]);
|
||||
{'BYTES_SPLIT', A, B, C} -> Pure(A, [B, C]);
|
||||
{'BYTES_SPLIT_ANY', A, B, C} -> Pure(A, [B, C]);
|
||||
{'BYTES_SIZE', A, B} -> Pure(A, B);
|
||||
{'BYTES_TO_FIXED_SIZE', A, B, C} -> Pure(A, [B, C]);
|
||||
{'ORACLE_CHECK', A, B, C, D} -> Pure(A, [B, C, D]);
|
||||
{'ORACLE_CHECK_QUERY', A, B, C, D, E} -> Pure(A, [B, C, D, E]);
|
||||
{'IS_ORACLE', A, B} -> Pure(A, [B]);
|
||||
|
@ -264,10 +264,11 @@ type300() ->
|
||||
type400() ->
|
||||
choice(
|
||||
[?RULE(typeAtom(), optional(type_args()),
|
||||
any_bytes(
|
||||
case _2 of
|
||||
none -> _1;
|
||||
{ok, Args} -> {app_t, get_ann(_1), _1, Args}
|
||||
end),
|
||||
end)),
|
||||
?RULE(id("bytes"), parens(token(int)),
|
||||
{bytes_t, get_ann(_1), element(3, _2)})
|
||||
]).
|
||||
@ -792,3 +793,7 @@ auto_imports(L) when is_list(L) ->
|
||||
auto_imports(T) when is_tuple(T) ->
|
||||
auto_imports(tuple_to_list(T));
|
||||
auto_imports(_) -> [].
|
||||
|
||||
any_bytes({id, Ann, "bytes"}) -> {bytes_t, Ann, any};
|
||||
any_bytes({app_t, _, {id, Ann, "bytes"}, []}) -> {bytes_t, Ann, any};
|
||||
any_bytes(Type) -> Type.
|
||||
|
@ -275,7 +275,9 @@ type({tuple_t, _, Args}) ->
|
||||
tuple_type(Args);
|
||||
type({args_t, _, Args}) ->
|
||||
args_type(Args);
|
||||
type({bytes_t, _, any}) -> text("bytes(_)");
|
||||
type({bytes_t, _, any}) -> text("bytes()");
|
||||
type({bytes_t, _, '_'}) -> text("bytes(_)");
|
||||
type({bytes_t, _, fixed}) -> text("bytes(_)");
|
||||
type({bytes_t, _, Len}) ->
|
||||
text(lists:concat(["bytes(", Len, ")"]));
|
||||
type({if_t, _, Id, Then, Else}) ->
|
||||
|
@ -170,6 +170,7 @@ compilable_contracts() ->
|
||||
"namespace_bug",
|
||||
"bytes_to_x",
|
||||
"bytes_concat",
|
||||
"bytes_misc",
|
||||
"aens",
|
||||
"aens_update",
|
||||
"tuple_match",
|
||||
@ -448,6 +449,10 @@ failing_contracts() ->
|
||||
[<<?Pos(12, 42)
|
||||
"Cannot unify `int` and `string`\n"
|
||||
"when checking the type of the expression `r.foo() : map(int, string)` "
|
||||
"against the expected type `map(string, int)`">>,
|
||||
<<?Pos(12, 42)
|
||||
"Cannot unify `string` and `int`\n"
|
||||
"when checking the type of the expression `r.foo() : map(int, string)` "
|
||||
"against the expected type `map(string, int)`">>])
|
||||
, ?TYPE_ERROR(not_toplevel_include,
|
||||
[<<?Pos(2, 11)
|
||||
@ -605,6 +610,21 @@ failing_contracts() ->
|
||||
[<<?Pos(3, 5)
|
||||
"Unbound variable `Chain.event`\n"
|
||||
"Did you forget to define the event type?">>])
|
||||
, ?TYPE_ERROR(bad_bytes_to_x,
|
||||
[<<?Pos(3, 35)
|
||||
"Cannot resolve length of byte array in\n"
|
||||
" the result of a call to Bytes.to_fixed_size">>,
|
||||
<<?Pos(4, 36)
|
||||
"Cannot unify `bytes()` and `bytes(4)`\nwhen checking the application of\n"
|
||||
" `Bytes.to_fixed_size : (bytes()) => option('a)`\n"
|
||||
"to arguments\n"
|
||||
" `b : bytes(4)`">>,
|
||||
<<?Pos(4, 36)
|
||||
"Cannot resolve length of byte array in\n"
|
||||
" the result of a call to Bytes.to_fixed_size">>,
|
||||
<<?Pos(5, 35)
|
||||
"Cannot resolve length of byte array in\n"
|
||||
" the first argument of a call to Bytes.to_any_size">>])
|
||||
, ?TYPE_ERROR(bad_bytes_concat,
|
||||
[<<?Pos(12, 40)
|
||||
"Failed to resolve byte array lengths in call to Bytes.concat with arguments of type\n"
|
||||
@ -629,7 +649,8 @@ failing_contracts() ->
|
||||
"and result type\n"
|
||||
" - 'c (at line 16, column 39)">>,
|
||||
<<?Pos(19, 25)
|
||||
"Cannot resolve length of byte array.">>])
|
||||
"Cannot resolve type of byte array in\n"
|
||||
" the first argument of a call to Bytes.to_str">>])
|
||||
, ?TYPE_ERROR(bad_bytes_split,
|
||||
[<<?Pos(13, 5)
|
||||
"Failed to resolve byte array lengths in call to Bytes.split with argument of type\n"
|
||||
@ -929,6 +950,9 @@ failing_contracts() ->
|
||||
<<?Pos(67,36)
|
||||
"Cannot unify `Cat` and `Animal` in a contravariant context\n"
|
||||
"when checking the application of\n `DT_INV : ((Cat) => Cat) => dt_inv(Cat)`\nto arguments\n `f_c_to_a : (Cat) => Animal`">>,
|
||||
<<?Pos(67,36)
|
||||
"Cannot unify `Cat` and `Animal` in a invariant context\n"
|
||||
"when checking the type of the expression `DT_INV(f_c_to_a) : dt_inv(Cat)` against the expected type `dt_inv(Animal)`">>,
|
||||
<<?Pos(68,36)
|
||||
"Cannot unify `Cat` and `Animal` in a invariant context\n"
|
||||
"when checking the type of the expression `DT_INV(f_c_to_c) : dt_inv(Cat)` against the expected type `dt_inv(Animal)`">>,
|
||||
@ -977,6 +1001,9 @@ failing_contracts() ->
|
||||
<<?Pos(116,59)
|
||||
"Cannot unify `Cat` and `Animal` in a contravariant context\n"
|
||||
"when checking the type of the expression `DT_A_CONTRA_B_CONTRA(f_c_to_c_to_u) : dt_a_contra_b_contra(Cat, Cat)` against the expected type `dt_a_contra_b_contra(Animal, Animal)`">>,
|
||||
<<?Pos(116,59)
|
||||
"Cannot unify `Cat` and `Animal` in a contravariant context\n"
|
||||
"when checking the type of the expression `DT_A_CONTRA_B_CONTRA(f_c_to_c_to_u) : dt_a_contra_b_contra(Cat, Cat)` against the expected type `dt_a_contra_b_contra(Animal, Animal)`">>,
|
||||
<<?Pos(119,59)
|
||||
"Cannot unify `Cat` and `Animal` in a contravariant context\n"
|
||||
"when checking the type of the expression `DT_A_CONTRA_B_CONTRA(f_c_to_a_to_u) : dt_a_contra_b_contra(Cat, Animal)` against the expected type `dt_a_contra_b_contra(Animal, Cat)`">>,
|
||||
@ -1020,6 +1047,9 @@ failing_contracts() ->
|
||||
<<?Pos(19,13)
|
||||
"Cannot unify `Cat` and `Animal` in a contravariant context\n"
|
||||
"when checking the type of the pattern `o07 : oracle(Animal, Cat)` against the expected type `oracle(Cat, Animal)`">>,
|
||||
<<?Pos(19,13)
|
||||
"Cannot unify `Animal` and `Cat` in a covariant context\n"
|
||||
"when checking the type of the pattern `o07 : oracle(Animal, Cat)` against the expected type `oracle(Cat, Animal)`">>,
|
||||
<<?Pos(20,13)
|
||||
"Cannot unify `Cat` and `Animal` in a contravariant context\n"
|
||||
"when checking the type of the pattern `o08 : oracle(Animal, Cat)` against the expected type `oracle(Cat, Cat)`">>,
|
||||
@ -1044,6 +1074,9 @@ failing_contracts() ->
|
||||
<<?Pos(42,13)
|
||||
"Cannot unify `Animal` and `Cat` in a covariant context\n"
|
||||
"when checking the type of the pattern `q13 : oracle_query(Cat, Cat)` against the expected type `oracle_query(Animal, Animal)`">>,
|
||||
<<?Pos(42,13)
|
||||
"Cannot unify `Animal` and `Cat` in a covariant context\n"
|
||||
"when checking the type of the pattern `q13 : oracle_query(Cat, Cat)` against the expected type `oracle_query(Animal, Animal)`">>,
|
||||
<<?Pos(43,13)
|
||||
"Cannot unify `Animal` and `Cat` in a covariant context\n"
|
||||
"when checking the type of the pattern `q14 : oracle_query(Cat, Cat)` against the expected type `oracle_query(Animal, Cat)`">>,
|
||||
|
5
test/contracts/bad_bytes_to_x.aes
Normal file
5
test/contracts/bad_bytes_to_x.aes
Normal file
@ -0,0 +1,5 @@
|
||||
// include "String.aes"
|
||||
contract BytesToX =
|
||||
entrypoint fail1(b : bytes()) = Bytes.to_fixed_size(b)
|
||||
entrypoint fail2(b : bytes(4)) = Bytes.to_fixed_size(b)
|
||||
entrypoint fail3(b : bytes()) = Bytes.to_any_size(b)
|
27
test/contracts/bytes_misc.aes
Normal file
27
test/contracts/bytes_misc.aes
Normal file
@ -0,0 +1,27 @@
|
||||
include "String.aes"
|
||||
contract BytesMisc =
|
||||
entrypoint sizeFixed(b : bytes(4)) : int = Bytes.size(b)
|
||||
entrypoint sizeAny(b : bytes()) : int = Bytes.size(b)
|
||||
entrypoint int_to_bytes(i : int) : bytes() = Int.to_bytes(i, 16)
|
||||
|
||||
entrypoint test(b3 : bytes(3), b7 : bytes(7), bX : bytes, i : int, s : string) =
|
||||
let bi = Int.to_bytes(i, 8)
|
||||
let bs = String.to_bytes(s)
|
||||
|
||||
let b10 = Bytes.concat(b3, b7)
|
||||
|
||||
let (b4, b6 : bytes(6)) = Bytes.split(b10)
|
||||
|
||||
let Some((b8, b2)) = Bytes.split_any(bX, 8)
|
||||
|
||||
let bX7 = Bytes.concat(bX, b7)
|
||||
|
||||
let Some((b5, bX2)) = Bytes.split_any(bX7, 5)
|
||||
|
||||
let Some((b7b, b0)) = Bytes.split_any(bX, Bytes.size(b7))
|
||||
|
||||
let Some(b5b : bytes(5)) = Bytes.to_fixed_size(b5)
|
||||
|
||||
let (b1 : bytes(1), _) = Bytes.split(b5b)
|
||||
|
||||
[bi, bs, b0, Bytes.to_any_size(b1), b2, Bytes.to_any_size(b4), Bytes.to_any_size(b6), b7b, b8, bX2]
|
@ -6,3 +6,5 @@ contract BytesToX =
|
||||
String.concat(Bytes.to_str(b), Bytes.to_str(#ffff))
|
||||
entrypoint to_str_big(b : bytes(65)) : string =
|
||||
Bytes.to_str(b)
|
||||
entrypoint to_fixed(b : bytes()) : option(bytes(4)) = Bytes.to_fixed_size(b)
|
||||
entrypoint to_any(b : bytes(4)) = Bytes.to_any_size(b)
|
||||
|
Loading…
x
Reference in New Issue
Block a user