Compare commits

..

7 Commits

Author SHA1 Message Date
Hans Svensson 1538af79ed [Ceres] Add Chain.network_id (#468)
* Add Chain.network_id

* Bump aebytecode version
2023-07-03 08:04:16 +02:00
Hans Svensson c3788b2b5a [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
2023-06-30 16:21:50 +02:00
Hans Svensson 32a98112d3 [Ceres]: Document generic all names delegation signatures (#440) 2023-06-29 15:48:20 +04:00
Hans Svensson acd2fa8184 [Ceres]: document changes to Auth.tx_hash (#439) 2023-06-29 15:48:20 +04:00
Hans Svensson c5394c3068 [Ceres]: Introduce AENSv2 to add raw data pointers (#426)
Remove unused variable in AENSCompat
2023-06-29 15:48:20 +04:00
Hans Svensson a347795475 [Ceres]: Add bitwise ops, Address.to_bytes and Crypto.poseidon 2023-06-29 15:48:20 +04:00
Hans Svensson c6df9e875f Let CERES compiler be v8.0.0 tentatively 2023-06-29 15:48:20 +04:00
9 changed files with 229 additions and 228 deletions
+1 -9
View File
@@ -24,8 +24,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
sized byte arrays. sized byte arrays.
- `Chain.network_id` - a function to get hold of the Chain's network id. - `Chain.network_id` - a function to get hold of the Chain's network id.
### Changed ### Changed
- `Crypto.verify_sig` is changed to have `msg : bytes()`. I.e. the
signed data can be of any length (used to be limited to `bytes(32)`/`hash`).
### Removed ### Removed
- `Bitwise.aes` standard library is removed - the builtin operations are superior. - `Bitwise.aes` standard library is removed - the builtin operations are superior.
@@ -35,11 +33,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Removed ### Removed
### Fixed ### Fixed
## [7.3.0]
### Fixed
- Fixed a bug with polymorphism that allowed functions with the same name but different type to be considered as implementations for their corresponding interface function.
- Fixed a bug in the byte code optimization that incorrectly reordered dependent instructions.
## [7.2.1] ## [7.2.1]
### Fixed ### Fixed
- Fixed bugs with the newly added debugging symbols - Fixed bugs with the newly added debugging symbols
@@ -429,8 +422,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Simplify calldata creation - instead of passing a compiled contract, simply - Simplify calldata creation - instead of passing a compiled contract, simply
pass a (stubbed) contract string. pass a (stubbed) contract string.
[Unreleased]: https://github.com/aeternity/aesophia/compare/v7.3.0...HEAD [Unreleased]: https://github.com/aeternity/aesophia/compare/v7.2.1...HEAD
[7.3.0]: https://github.com/aeternity/aesophia/compare/v7.2.1...v7.3.0
[7.2.1]: https://github.com/aeternity/aesophia/compare/v7.2.0...v7.2.1 [7.2.1]: https://github.com/aeternity/aesophia/compare/v7.2.0...v7.2.1
[7.2.0]: https://github.com/aeternity/aesophia/compare/v7.1.0...v7.2.0 [7.2.0]: https://github.com/aeternity/aesophia/compare/v7.1.0...v7.2.0
[7.1.0]: https://github.com/aeternity/aesophia/compare/v7.0.1...v7.1.0 [7.1.0]: https://github.com/aeternity/aesophia/compare/v7.0.1...v7.1.0
+12 -13
View File
@@ -57,12 +57,6 @@ Address.to_str(a : address) : string
Base58 encoded string Base58 encoded string
#### to_bytes
```
Address.to_bytes(a : address) : bytes(32)
```
The binary representation of the address.
#### is_contract #### is_contract
``` ```
@@ -142,7 +136,7 @@ datatype name = Name(address, Chain.ttl, map(string, AENSv2.pointee))
``` ```
datatype pointee = AccountPt(address) | OraclePt(address) datatype pointee = AccountPt(address) | OraclePt(address)
| ContractPt(address) | ChannelPt(address) | DataPt(bytes()) | ContractPt(address) | ChannelPt(address) | DataPt(string)
``` ```
Note: on-chain there is a maximum length enforced for `DataPt`, it is 1024 bytes. Note: on-chain there is a maximum length enforced for `DataPt`, it is 1024 bytes.
@@ -425,7 +419,7 @@ Bytes.to_fixed_size(a : bytes()) : option(bytes(n))
``` ```
Converts an arbitrary size byte array to a fix size byte array. If `a` is Converts an arbitrary size byte array to a fix size byte array. If `a` is
not `n` bytes, `None` is returned. `n` bytes, `None` is returned.
#### to\_any\_size #### to\_any\_size
``` ```
@@ -570,6 +564,14 @@ Chain.block_height : int"
The height of the current block (i.e. the block in which the current call will be included). The height of the current block (i.e. the block in which the current call will be included).
#### to_bytes
```
Address.to_bytes(a : address) : bytes(32)
```
The binary representation of the address.
##### bytecode_hash ##### bytecode_hash
``` ```
Chain.bytecode_hash : 'c => option(hash) Chain.bytecode_hash : 'c => option(hash)
@@ -832,14 +834,11 @@ Hash any object to blake2b
#### verify_sig #### verify_sig
``` ```
Crypto.verify_sig(msg : bytes(), pubkey : address, sig : signature) : bool Crypto.verify_sig(msg : hash, pubkey : address, sig : signature) : bool
``` ```
Checks if the signature of `msg` was made using private key corresponding to Checks if the signature of `msg` was made using private key corresponding to
the `pubkey`. the `pubkey`
Note: before v8 of the compiler, `msg` had type `hash` (i.e. `bytes(32)`).
#### ecverify_secp256k1 #### ecverify_secp256k1
``` ```
+173 -154
View File
@@ -155,6 +155,7 @@
, in_pattern = false :: boolean() , in_pattern = false :: boolean()
, in_guard = false :: boolean() , in_guard = false :: boolean()
, stateful = false :: boolean() , stateful = false :: boolean()
, unify_throws = true :: boolean()
, current_const = none :: none | aeso_syntax:id() , current_const = none :: none | aeso_syntax:id()
, current_function = none :: none | aeso_syntax:id() , current_function = none :: none | aeso_syntax:id()
, what = top :: top | namespace | contract | contract_interface , what = top :: top | namespace | contract | contract_interface
@@ -756,7 +757,7 @@ global_env() ->
{"OraclePt", Fun1(Address, PointeeV2)}, {"OraclePt", Fun1(Address, PointeeV2)},
{"ContractPt", Fun1(Address, PointeeV2)}, {"ContractPt", Fun1(Address, PointeeV2)},
{"ChannelPt", Fun1(Address, PointeeV2)}, {"ChannelPt", Fun1(Address, PointeeV2)},
{"DataPt", Fun1(Bytes(any), PointeeV2)}, {"DataPt", Fun1(String, PointeeV2)},
%% Name object constructor v2 %% Name object constructor v2
{"Name", Fun([Address, TTL, Map(String, PointeeV2)], AENSNameV2)} {"Name", Fun([Address, TTL, Map(String, PointeeV2)], AENSNameV2)}
]) ])
@@ -775,7 +776,7 @@ global_env() ->
%% Crypto/Curve operations %% Crypto/Curve operations
CryptoScope = #scope CryptoScope = #scope
{ funs = MkDefs( { funs = MkDefs(
[{"verify_sig", Fun([Bytes('_'), Address, SignId], Bool)}, [{"verify_sig", Fun([Hash, Address, SignId], Bool)},
{"verify_sig_secp256k1", Fun([Hash, Bytes(64), SignId], Bool)}, {"verify_sig_secp256k1", Fun([Hash, Bytes(64), SignId], Bool)},
{"ecverify_secp256k1", Fun([Hash, Bytes(20), Bytes(65)], Bool)}, {"ecverify_secp256k1", Fun([Hash, Bytes(20), Bytes(65)], Bool)},
{"ecrecover_secp256k1", Fun([Hash, Bytes(65)], Option(Bytes(20)))}, {"ecrecover_secp256k1", Fun([Hash, Bytes(65)], Option(Bytes(20)))},
@@ -1608,7 +1609,7 @@ check_reserved_entrypoints(Funs) ->
check_fundecl(Env, {fun_decl, Ann, Id = {id, _, Name}, Type = {fun_t, _, _, _, _}}) -> check_fundecl(Env, {fun_decl, Ann, Id = {id, _, Name}, Type = {fun_t, _, _, _, _}}) ->
Type1 = {fun_t, _, Named, Args, Ret} = check_type(Env, Type), Type1 = {fun_t, _, Named, Args, Ret} = check_type(Env, Type),
TypeSig = {type_sig, Ann, none, Named, Args, Ret}, TypeSig = {type_sig, Ann, none, Named, Args, Ret},
register_implementation(Env, Id, TypeSig), register_implementation(Id, TypeSig),
{{Name, TypeSig}, {fun_decl, Ann, Id, Type1}}; {{Name, TypeSig}, {fun_decl, Ann, Id, Type1}};
check_fundecl(Env, {fun_decl, Ann, Id = {id, _, Name}, Type}) -> check_fundecl(Env, {fun_decl, Ann, Id = {id, _, Name}, Type}) ->
type_error({fundecl_must_have_funtype, Ann, Id, Type}), type_error({fundecl_must_have_funtype, Ann, Id, Type}),
@@ -1616,16 +1617,13 @@ check_fundecl(Env, {fun_decl, Ann, Id = {id, _, Name}, Type}) ->
%% Register the function FunId as implemented by deleting it from the functions %% Register the function FunId as implemented by deleting it from the functions
%% to be implemented table if it is included there, or return true otherwise. %% to be implemented table if it is included there, or return true otherwise.
-spec register_implementation(env(), FunId, FunSig) -> true | no_return() when -spec register_implementation(FunId, FunSig) -> true | no_return() when
FunId :: aeso_syntax:id(), FunId :: aeso_syntax:id(),
FunSig :: typesig(). FunSig :: typesig().
register_implementation(Env, Id, Sig) -> register_implementation(Id, Sig) ->
Name = name(Id), Name = name(Id),
case ets_lookup(functions_to_implement, Name) of case ets_lookup(functions_to_implement, Name) of
[{Name, Interface, Decl = {fun_decl, _, DeclId, FunT}}] -> [{Name, Interface, Decl = {fun_decl, _, DeclId, _}}] ->
When = {implement_interface_fun, aeso_syntax:get_ann(Sig), Name, name(Interface)},
unify(Env, typesig_to_fun_t(Sig), FunT, When),
DeclStateful = aeso_syntax:get_ann(stateful, Decl, false), DeclStateful = aeso_syntax:get_ann(stateful, Decl, false),
DeclPayable = aeso_syntax:get_ann(payable, Decl, false), DeclPayable = aeso_syntax:get_ann(payable, Decl, false),
@@ -1653,7 +1651,7 @@ infer_nonrec(Env, LetFun) ->
create_constraints(), create_constraints(),
NewLetFun = {{_, Sig}, _} = infer_letfun(Env, LetFun), NewLetFun = {{_, Sig}, _} = infer_letfun(Env, LetFun),
check_special_funs(Env, NewLetFun), check_special_funs(Env, NewLetFun),
register_implementation(Env, get_letfun_id(LetFun), Sig), register_implementation(get_letfun_id(LetFun), Sig),
solve_then_destroy_and_report_unsolved_constraints(Env), solve_then_destroy_and_report_unsolved_constraints(Env),
Result = {TypeSig, _} = instantiate(NewLetFun), Result = {TypeSig, _} = instantiate(NewLetFun),
print_typesig(TypeSig), print_typesig(TypeSig),
@@ -1683,11 +1681,11 @@ infer_letrec(Env, Defs) ->
Inferred = Inferred =
[ begin [ begin
Res = {{Name, TypeSig}, LetFun} = infer_letfun(ExtendEnv, LF), Res = {{Name, TypeSig}, LetFun} = infer_letfun(ExtendEnv, LF),
register_implementation(Env, get_letfun_id(LetFun), TypeSig), register_implementation(get_letfun_id(LetFun), TypeSig),
Got = proplists:get_value(Name, Funs), Got = proplists:get_value(Name, Funs),
Expect = typesig_to_fun_t(TypeSig), Expect = typesig_to_fun_t(TypeSig),
unify(Env, Got, Expect, {check_typesig, Name, Got, Expect}), unify(Env, Got, Expect, {check_typesig, Name, Got, Expect}),
solve_all_constraints(Env), solve_constraints(Env),
?PRINT_TYPES("Checked ~s : ~s\n", ?PRINT_TYPES("Checked ~s : ~s\n",
[Name, pp(dereference_deep(Got))]), [Name, pp(dereference_deep(Got))]),
Res Res
@@ -2610,124 +2608,61 @@ get_constraints() ->
destroy_constraints() -> destroy_constraints() ->
ets_delete(constraints). ets_delete(constraints).
%% Solve all constraints by iterating until no-progress -spec solve_constraints(env()) -> ok.
solve_constraints(Env) ->
-spec solve_all_constraints(env()) -> ok. %% First look for record fields that appear in only one type definition
solve_all_constraints(Env) -> IsAmbiguous =
Constraints = [C || C <- get_constraints(), not one_shot_field_constraint(Env, C) ], fun(#field_constraint{
solve_constraints_top(Env, Constraints). record_t = RecordType,
field = Field={id, _Attrs, FieldName},
solve_constraints_top(Env, Constraints) ->
UnsolvedCs = solve_constraints(Env, Constraints),
Progress = solve_unknown_record_constraints(Env, UnsolvedCs),
if length(UnsolvedCs) < length(Constraints) orelse Progress == true ->
solve_constraints_top(Env, UnsolvedCs);
true ->
ok
end.
-spec solve_constraints(env(), [constraint()]) -> [constraint()].
solve_constraints(Env, Constraints) ->
[ C1 || C <- Constraints, C1 <- [dereference_deep(C)], not solve_constraint(Env, C1) ].
solve_unknown_record_constraints(Env, Constraints) ->
FieldCs = lists:filter(fun(#field_constraint{record_t = {uvar, _, _}}) -> true; (_) -> false end, Constraints),
FieldCsUVars = lists:usort([UVar || #field_constraint{record_t = UVar = {uvar, _, _}} <- FieldCs]),
FieldConstraint = fun(#field_constraint{ field = F, kind = K, context = Ctx }) -> {K, Ctx, F} end,
FieldsForUVar = fun(UVar) ->
[ FieldConstraint(FC) || FC = #field_constraint{record_t = U} <- FieldCs, U == UVar ]
end,
Solutions = [ solve_for_uvar(Env, UVar, FieldsForUVar(UVar)) || UVar <- FieldCsUVars ],
case lists:member(true, Solutions) of
true -> true;
false -> Solutions
end.
%% -- Simple constraints --
%% Returns true if solved (unified or type error)
solve_constraint(_Env, #field_constraint{record_t = {uvar, _, _}}) ->
false;
solve_constraint(Env, #field_constraint{record_t = RecordType,
field = Field = {id, _As, FieldName},
field_t = FieldType,
context = When}) ->
RecId = record_type_name(RecordType),
Attrs = aeso_syntax:get_ann(RecId),
case lookup_type(Env, RecId) of
{_, {_Ann, {Formals, {What, Fields}}}} when What =:= record_t; What =:= contract_t ->
FieldTypes = [{Name, Type} || {field_t, _, {id, _, Name}, Type} <- Fields],
case proplists:get_value(FieldName, FieldTypes) of
undefined ->
type_error({missing_field, Field, RecId});
FldType ->
solve_field_constraint(Env, FieldType, FldType, RecordType, app_t(Attrs, RecId, Formals), When)
end;
_ ->
type_error({not_a_record_type, instantiate(RecordType), When})
end,
true;
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, _, _}) -> false;
solve_constraint(_Env, {is_fixed_bytes, _, _}) -> false;
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}, _} 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});
_ -> false
end;
solve_constraint(_, _) -> false.
one_shot_field_constraint(Env, #field_constraint{record_t = RecordType,
field = Field = {id, _As, FieldName},
field_t = FieldType, field_t = FieldType,
kind = Kind, kind = Kind,
context = When}) -> context = When }) ->
Arity = fun_arity(dereference_deep(FieldType)), Arity = fun_arity(dereference_deep(FieldType)),
FieldInfos = case Arity of FieldInfos = case Arity of
none -> lookup_record_field(Env, FieldName, Kind); none -> lookup_record_field(Env, FieldName, Kind);
_ -> lookup_record_field_arity(Env, FieldName, Arity, Kind) _ -> lookup_record_field_arity(Env, FieldName, Arity, Kind)
end, end,
case FieldInfos of case FieldInfos of
[] -> [] ->
type_error({undefined_field, Field}), type_error({undefined_field, Field}),
true; false;
[#field_info{field_t = FldType, record_t = RecType}] -> [#field_info{field_t = FldType, record_t = RecType}] ->
solve_field_constraint(Env, FieldType, FldType, RecordType, RecType, When),
true;
_ ->
false
end;
one_shot_field_constraint(_Env, _Constraint) ->
false.
solve_field_constraint(Env, FieldType, FldType, RecordType, RecType, When) ->
create_freshen_tvars(), create_freshen_tvars(),
FreshFldType = freshen(FldType), FreshFldType = freshen(FldType),
FreshRecType = freshen(RecType), FreshRecType = freshen(RecType),
destroy_freshen_tvars(), destroy_freshen_tvars(),
unify(Env, FreshFldType, FieldType, {field_constraint, FreshFldType, FieldType, When}), unify(Env, FreshFldType, FieldType, {field_constraint, FreshFldType, FieldType, When}),
unify(Env, FreshRecType, RecordType, {record_constraint, FreshRecType, RecordType, When}). unify(Env, FreshRecType, RecordType, {record_constraint, FreshRecType, RecordType, When}),
false;
_ ->
%% ambiguity--need cleverer strategy
true
end;
(_) -> true
end,
AmbiguousConstraints = lists:filter(IsAmbiguous, get_constraints()),
% The two passes on AmbiguousConstraints are needed
solve_ambiguous_constraints(Env, AmbiguousConstraints ++ AmbiguousConstraints).
-spec solve_ambiguous_constraints(env(), [constraint()]) -> ok.
solve_ambiguous_constraints(Env, Constraints) ->
Unknown = solve_known_record_types(Env, Constraints),
if Unknown == [] -> ok;
length(Unknown) < length(Constraints) ->
%% progress! Keep trying.
solve_ambiguous_constraints(Env, Unknown);
true ->
case solve_unknown_record_types(Env, Unknown) of
true -> %% Progress!
solve_ambiguous_constraints(Env, Unknown);
_ -> ok %% No progress. Report errors later.
end
end.
solve_then_destroy_and_report_unsolved_constraints(Env) -> solve_then_destroy_and_report_unsolved_constraints(Env) ->
solve_all_constraints(Env), solve_constraints(Env),
destroy_and_report_unsolved_constraints(Env). destroy_and_report_unsolved_constraints(Env).
destroy_and_report_unsolved_constraints(Env) -> destroy_and_report_unsolved_constraints(Env) ->
@@ -2759,10 +2694,21 @@ destroy_and_report_unsolved_constraints(Env) ->
(_) -> false (_) -> false
end, OtherCs5), end, OtherCs5),
check_field_constraints(Env, FieldCs), Unsolved = [ S || S <- [ solve_constraint(Env, dereference_deep(C)) || C <- NamedArgCs ],
S == unsolved ],
[ type_error({unsolved_named_argument_constraint, C}) || C <- Unsolved ],
Unknown = solve_known_record_types(Env, FieldCs),
if Unknown == [] -> ok;
true ->
case solve_unknown_record_types(Env, Unknown) of
true -> ok;
Errors -> [ type_error(Err) || Err <- Errors ]
end
end,
check_record_create_constraints(Env, CreateCs), check_record_create_constraints(Env, CreateCs),
check_is_contract_constraints(Env, ContractCs), check_is_contract_constraints(Env, ContractCs),
check_named_args_constraints(Env, NamedArgCs),
check_bytes_constraints(Env, BytesCs), check_bytes_constraints(Env, BytesCs),
check_aens_resolve_constraints(Env, AensResolveCs), check_aens_resolve_constraints(Env, AensResolveCs),
check_oracle_type_constraints(Env, OracleTypeCs), check_oracle_type_constraints(Env, OracleTypeCs),
@@ -2780,21 +2726,20 @@ get_oracle_type(_Fun, _Args, _Ret) -> false.
%% -- Named argument constraints -- %% -- Named argument constraints --
%% True if solved (unified or type error), false otherwise %% 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. -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, _, _} }) ->
false; unsolved;
check_named_argument_constraint(Env, check_named_argument_constraint(Env,
C = #named_argument_constraint{ args = Args, C = #named_argument_constraint{ args = Args,
name = Id = {id, _, Name}, name = Id = {id, _, Name},
type = Type }) -> type = Type }) ->
case [ T || {named_arg_t, _, {id, _, Name1}, T, _} <- Args, Name1 == Name ] of case [ T || {named_arg_t, _, {id, _, Name1}, T, _} <- Args, Name1 == Name ] of
[] -> [] ->
type_error({bad_named_argument, Args, Id}); type_error({bad_named_argument, Args, Id}),
[T] -> false;
unify(Env, T, Type, {check_named_arg_constraint, C}) [T] -> unify(Env, T, Type, {check_named_arg_constraint, C}), true
end, end;
true;
check_named_argument_constraint(Env, check_named_argument_constraint(Env,
#dependent_type_constraint{ named_args_t = NamedArgsT0, #dependent_type_constraint{ named_args_t = NamedArgsT0,
named_args = NamedArgs, named_args = NamedArgs,
@@ -2811,11 +2756,10 @@ check_named_argument_constraint(Env,
ArgEnv = maps:from_list([ {Name, GetVal(Name, Default)} ArgEnv = maps:from_list([ {Name, GetVal(Name, Default)}
|| {named_arg_t, _, {id, _, Name}, _, Default} <- NamedArgsT ]), || {named_arg_t, _, {id, _, Name}, _, Default} <- NamedArgsT ]),
GenType1 = specialize_dependent_type(ArgEnv, GenType), GenType1 = specialize_dependent_type(ArgEnv, GenType),
unify(Env, GenType1, SpecType, {check_expr, App, GenType1, SpecType}); unify(Env, GenType1, SpecType, {check_expr, App, GenType1, SpecType}),
_ -> true;
unify(Env, GenType, SpecType, {check_expr, App, GenType, SpecType}) _ -> unify(Env, GenType, SpecType, {check_expr, App, GenType, SpecType}), true
end, end.
true.
specialize_dependent_type(Env, Type) -> specialize_dependent_type(Env, Type) ->
case dereference(Type) of case dereference(Type) of
@@ -2831,16 +2775,59 @@ specialize_dependent_type(Env, Type) ->
_ -> Type %% Currently no deep dependent types _ -> Type %% Currently no deep dependent types
end. end.
check_field_constraints(Env, Constraints) -> %% -- Bytes constraints --
UnsolvedFieldCs = solve_constraints(Env, Constraints),
case solve_unknown_record_constraints(Env, UnsolvedFieldCs) of
true -> ok;
Errors -> [ type_error(Err) || Err <- Errors ]
end.
check_named_args_constraints(Env, Constraints) -> solve_constraint(_Env, #field_constraint{record_t = {uvar, _, _}}) ->
UnsolvedNamedArgCs = solve_constraints(Env, Constraints), not_solved;
[ type_error({unsolved_named_argument_constraint, C}) || C <- UnsolvedNamedArgCs ]. solve_constraint(Env, C = #field_constraint{record_t = RecType,
field = FieldName,
field_t = FieldType,
context = When}) ->
RecId = record_type_name(RecType),
Attrs = aeso_syntax:get_ann(RecId),
case lookup_type(Env, RecId) of
{_, {_Ann, {Formals, {What, Fields}}}} when What =:= record_t; What =:= contract_t ->
FieldTypes = [{Name, Type} || {field_t, _, {id, _, Name}, Type} <- Fields],
{id, _, FieldString} = FieldName,
case proplists:get_value(FieldString, FieldTypes) of
undefined ->
type_error({missing_field, FieldName, RecId}),
not_solved;
FldType ->
create_freshen_tvars(),
FreshFldType = freshen(FldType),
FreshRecType = freshen(app_t(Attrs, RecId, Formals)),
destroy_freshen_tvars(),
unify(Env, FreshFldType, FieldType, {field_constraint, FreshFldType, FieldType, When}),
unify(Env, FreshRecType, RecType, {record_constraint, FreshRecType, RecType, When}),
C
end;
_ ->
type_error({not_a_record_type, instantiate(RecType), When}),
not_solved
end;
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, {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}, _} 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.
check_bytes_constraints(Env, Constraints) -> check_bytes_constraints(Env, Constraints) ->
InAddConstraint = [ T || {add_bytes, _, _, A, B, C} <- Constraints, InAddConstraint = [ T || {add_bytes, _, _, A, B, C} <- Constraints,
@@ -2948,6 +2935,30 @@ check_is_contract_constraints(Env, [C | Cs]) ->
end, end,
check_is_contract_constraints(Env, Cs). check_is_contract_constraints(Env, Cs).
-spec solve_unknown_record_types(env(), [field_constraint()]) -> true | [tuple()].
solve_unknown_record_types(Env, Unknown) ->
UVars = lists:usort([UVar || #field_constraint{record_t = UVar = {uvar, _, _}} <- Unknown]),
Solutions = [solve_for_uvar(Env, UVar, [{Kind, When, Field}
|| #field_constraint{record_t = U, field = Field, kind = Kind, context = When} <- Unknown,
U == UVar])
|| UVar <- UVars],
case lists:member(true, Solutions) of
true -> true;
false -> Solutions
end.
%% This will solve all kinds of constraints but will only return the
%% unsolved field constraints
-spec solve_known_record_types(env(), [constraint()]) -> [field_constraint()].
solve_known_record_types(Env, Constraints) ->
DerefConstraints = lists:map(fun(C = #field_constraint{record_t = RecordType}) ->
C#field_constraint{record_t = dereference(RecordType)};
(C) -> dereference_deep(C)
end, Constraints),
SolvedConstraints = lists:map(fun(C) -> solve_constraint(Env, dereference_deep(C)) end, DerefConstraints),
Unsolved = DerefConstraints--SolvedConstraints,
lists:filter(fun(#field_constraint{}) -> true; (_) -> false end, Unsolved).
record_type_name({app_t, _Attrs, RecId, _Args}) when ?is_type_id(RecId) -> record_type_name({app_t, _Attrs, RecId, _Args}) when ?is_type_id(RecId) ->
RecId; RecId;
record_type_name(RecId) when ?is_type_id(RecId) -> record_type_name(RecId) when ?is_type_id(RecId) ->
@@ -3126,12 +3137,16 @@ unify0(Env, A, B, Variance, When) ->
unify1(_Env, {uvar, _, R}, {uvar, _, R}, _Variance, _When) -> unify1(_Env, {uvar, _, R}, {uvar, _, R}, _Variance, _When) ->
true; true;
unify1(_Env, {uvar, _, _}, {fun_t, _, _, var_args, _}, _Variance, When) -> unify1(_Env, {uvar, _, _}, {fun_t, _, _, var_args, _}, _Variance, When) ->
type_error({unify_varargs, When}), type_error({unify_varargs, When});
false; unify1(Env, {uvar, A, R}, T, _Variance, When) ->
unify1(_Env, {uvar, A, R}, T, _Variance, When) ->
case occurs_check(R, T) of case occurs_check(R, T) of
true -> true ->
cannot_unify({uvar, A, R}, T, none, When), if
Env#env.unify_throws ->
cannot_unify({uvar, A, R}, T, none, When);
true ->
ok
end,
false; false;
false -> false ->
ets_insert(type_vars, {R, T}), ets_insert(type_vars, {R, T}),
@@ -3158,13 +3173,18 @@ unify1(Env, A = {con, _, NameA}, B = {con, _, NameB}, Variance, When) ->
case is_subtype(Env, NameA, NameB, Variance) of case is_subtype(Env, NameA, NameB, Variance) of
true -> true; true -> true;
false -> false ->
if
Env#env.unify_throws ->
IsSubtype = is_subtype(Env, NameA, NameB, contravariant) orelse IsSubtype = is_subtype(Env, NameA, NameB, contravariant) orelse
is_subtype(Env, NameA, NameB, covariant), is_subtype(Env, NameA, NameB, covariant),
Cxt = case IsSubtype of Cxt = case IsSubtype of
true -> Variance; true -> Variance;
false -> none false -> none
end, end,
cannot_unify(A, B, Cxt, When), cannot_unify(A, B, Cxt, When);
true ->
ok
end,
false false
end; end;
unify1(_Env, {qid, _, Name}, {qid, _, Name}, _Variance, _When) -> unify1(_Env, {qid, _, Name}, {qid, _, Name}, _Variance, _When) ->
@@ -3178,11 +3198,9 @@ unify1(Env, {if_t, _, {id, _, Id}, Then1, Else1}, {if_t, _, {id, _, Id}, Then2,
unify0(Env, Else1, Else2, Variance, When); unify0(Env, Else1, Else2, Variance, When);
unify1(_Env, {fun_t, _, _, _, _}, {fun_t, _, _, var_args, _}, _Variance, When) -> unify1(_Env, {fun_t, _, _, _, _}, {fun_t, _, _, var_args, _}, _Variance, When) ->
type_error({unify_varargs, When}), type_error({unify_varargs, When});
false;
unify1(_Env, {fun_t, _, _, var_args, _}, {fun_t, _, _, _, _}, _Variance, When) -> unify1(_Env, {fun_t, _, _, var_args, _}, {fun_t, _, _, _, _}, _Variance, When) ->
type_error({unify_varargs, When}), type_error({unify_varargs, When});
false;
unify1(Env, {fun_t, _, Named1, Args1, Result1}, {fun_t, _, Named2, Args2, Result2}, Variance, When) unify1(Env, {fun_t, _, Named1, Args1, Result1}, {fun_t, _, Named2, Args2, Result2}, Variance, When)
when length(Args1) == length(Args2) -> when length(Args1) == length(Args2) ->
unify0(Env, Named1, Named2, opposite_variance(Variance), When) and unify0(Env, Named1, Named2, opposite_variance(Variance), When) and
@@ -3204,7 +3222,7 @@ unify1(Env, {tuple_t, _, As}, {tuple_t, _, Bs}, Variance, When)
when length(As) == length(Bs) -> when length(As) == length(Bs) ->
unify0(Env, As, Bs, Variance, When); unify0(Env, As, Bs, Variance, When);
unify1(Env, {named_arg_t, _, Id1, Type1, _}, {named_arg_t, _, Id2, Type2, _}, Variance, When) -> unify1(Env, {named_arg_t, _, Id1, Type1, _}, {named_arg_t, _, Id2, Type2, _}, Variance, When) ->
unify1(Env, Id1, Id2, Variance, {arg_name, Id1, Id2, When}) andalso unify1(Env, Id1, Id2, Variance, {arg_name, Id1, Id2, When}),
unify1(Env, Type1, Type2, Variance, When); unify1(Env, Type1, Type2, Variance, When);
%% The grammar is a bit inconsistent about whether types without %% The grammar is a bit inconsistent about whether types without
%% arguments are represented as applications to an empty list of %% arguments are represented as applications to an empty list of
@@ -3213,8 +3231,13 @@ unify1(Env, {app_t, _, T, []}, B, Variance, When) ->
unify0(Env, T, B, Variance, When); unify0(Env, T, B, Variance, When);
unify1(Env, A, {app_t, _, T, []}, Variance, When) -> unify1(Env, A, {app_t, _, T, []}, Variance, When) ->
unify0(Env, A, T, Variance, When); unify0(Env, A, T, Variance, When);
unify1(_Env, A, B, _Variance, When) -> unify1(Env, A, B, _Variance, When) ->
cannot_unify(A, B, none, When), if
Env#env.unify_throws ->
cannot_unify(A, B, none, When);
true ->
ok
end,
false. false.
is_subtype(_Env, NameA, NameB, invariant) -> is_subtype(_Env, NameA, NameB, invariant) ->
@@ -4166,8 +4189,8 @@ pp_when({if_branches, Then, ThenType0, Else, ElseType0}) ->
Branches = [ {Then, ThenType} | [ {B, ElseType} || B <- if_branches(Else) ] ], Branches = [ {Then, ThenType} | [ {B, ElseType} || B <- if_branches(Else) ] ],
{pos(element(1, hd(Branches))), {pos(element(1, hd(Branches))),
io_lib:format("when comparing the types of the if-branches\n" io_lib:format("when comparing the types of the if-branches\n"
"~s", [string:join([ io_lib:format("~s (at ~s)", [pp_typed(" - ", B, BType), pp_loc(B)]) "~s", [ [ io_lib:format("~s (at ~s)\n", [pp_typed(" - ", B, BType), pp_loc(B)])
|| {B, BType} <- Branches ], "\n")])}; || {B, BType} <- Branches ] ])};
pp_when({case_pat, Pat, PatType0, ExprType0}) -> pp_when({case_pat, Pat, PatType0, ExprType0}) ->
{PatType, ExprType} = instantiate({PatType0, ExprType0}), {PatType, ExprType} = instantiate({PatType0, ExprType0}),
{pos(Pat), {pos(Pat),
@@ -4214,10 +4237,6 @@ pp_when({var_args, Ann, Fun}) ->
{pos(Ann) {pos(Ann)
, io_lib:format("when resolving arguments of variadic function `~s`", [pp_expr(Fun)]) , io_lib:format("when resolving arguments of variadic function `~s`", [pp_expr(Fun)])
}; };
pp_when({implement_interface_fun, Ann, Entrypoint, Interface}) ->
{ pos(Ann)
, io_lib:format("when implementing the entrypoint `~s` from the interface `~s`", [Entrypoint, Interface])
};
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()}.
+2 -2
View File
@@ -337,8 +337,8 @@ init_type_env() ->
["Chain", "ttl"] => ?type({variant, [[integer], [integer]]}), ["Chain", "ttl"] => ?type({variant, [[integer], [integer]]}),
["AENS", "pointee"] => ?type({variant, [[address], [address], [address], [address]]}), ["AENS", "pointee"] => ?type({variant, [[address], [address], [address], [address]]}),
["AENS", "name"] => ?type({variant, [[address, {variant, [[integer], [integer]]}, {map, string, {variant, [[address], [address], [address], [address]]}}]]}), ["AENS", "name"] => ?type({variant, [[address, {variant, [[integer], [integer]]}, {map, string, {variant, [[address], [address], [address], [address]]}}]]}),
["AENSv2", "pointee"] => ?type({variant, [[address], [address], [address], [address], [{bytes, any}]]}), ["AENSv2", "pointee"] => ?type({variant, [[address], [address], [address], [address], [string]]}),
["AENSv2", "name"] => ?type({variant, [[address, {variant, [[integer], [integer]]}, {map, string, {variant, [[address], [address], [address], [address], [{bytes, any}]]}}]]}), ["AENSv2", "name"] => ?type({variant, [[address, {variant, [[integer], [integer]]}, {map, string, {variant, [[address], [address], [address], [address], [string]]}}]]}),
["Chain", "ga_meta_tx"] => ?type({variant, [[address, integer]]}), ["Chain", "ga_meta_tx"] => ?type({variant, [[address, integer]]}),
["Chain", "paying_for_tx"] => ?type({variant, [[address, integer]]}), ["Chain", "paying_for_tx"] => ?type({variant, [[address, integer]]}),
["Chain", "base_tx"] => ?type(BaseTx), ["Chain", "base_tx"] => ?type(BaseTx),
-5
View File
@@ -1177,15 +1177,10 @@ independent({i, _, I}, {i, _, J}) ->
StackI = lists:member(?a, [WI | RI]), StackI = lists:member(?a, [WI | RI]),
StackJ = lists:member(?a, [WJ | RJ]), StackJ = lists:member(?a, [WJ | RJ]),
ReadStoreI = [] /= [ x || {store, _} <- RI ],
ReadStoreJ = [] /= [ x || {store, _} <- RJ ],
if WI == pc; WJ == pc -> false; %% no jumps if WI == pc; WJ == pc -> false; %% no jumps
not (PureI or PureJ) -> false; %% at least one is pure not (PureI or PureJ) -> false; %% at least one is pure
StackI and StackJ -> false; %% cannot both use the stack StackI and StackJ -> false; %% cannot both use the stack
WI == WJ -> false; %% cannot write to the same register WI == WJ -> false; %% cannot write to the same register
ReadStoreI and not PureJ -> false; %% can't read store/state if other is impure
ReadStoreJ and not PureI -> false; %% can't read store/state if other is impure
true -> true ->
%% and cannot write to each other's inputs %% and cannot write to each other's inputs
not lists:member(WI, RJ) andalso not lists:member(WI, RJ) andalso
+10 -12
View File
@@ -15,7 +15,6 @@ from_fate({id, _, "address"}, ?FATE_ADDRESS(Bin)) -> {account_pubkey, [], Bin};
from_fate({app_t, _, {id, _, "oracle"}, _}, ?FATE_ORACLE(Bin)) -> {oracle_pubkey, [], Bin}; from_fate({app_t, _, {id, _, "oracle"}, _}, ?FATE_ORACLE(Bin)) -> {oracle_pubkey, [], Bin};
from_fate({app_t, _, {id, _, "oracle_query"}, _}, ?FATE_ORACLE_Q(Bin)) -> {oracle_query_id, [], Bin}; from_fate({app_t, _, {id, _, "oracle_query"}, _}, ?FATE_ORACLE_Q(Bin)) -> {oracle_query_id, [], Bin};
from_fate({con, _, _Name}, ?FATE_CONTRACT(Bin)) -> {contract_pubkey, [], Bin}; from_fate({con, _, _Name}, ?FATE_CONTRACT(Bin)) -> {contract_pubkey, [], Bin};
from_fate({bytes_t, _, any}, ?FATE_BYTES(Bin)) -> {bytes, [], Bin};
from_fate({bytes_t, _, N}, ?FATE_BYTES(Bin)) when byte_size(Bin) == N -> {bytes, [], Bin}; from_fate({bytes_t, _, N}, ?FATE_BYTES(Bin)) when byte_size(Bin) == N -> {bytes, [], Bin};
from_fate({id, _, "bits"}, ?FATE_BITS(N)) -> make_bits(N); from_fate({id, _, "bits"}, ?FATE_BITS(N)) -> make_bits(N);
from_fate({id, _, "int"}, N) when is_integer(N) -> from_fate({id, _, "int"}, N) when is_integer(N) ->
@@ -79,7 +78,6 @@ from_fate_builtin(QType, Val) ->
Hsh = {bytes_t, [], 32}, Hsh = {bytes_t, [], 32},
I32 = {bytes_t, [], 32}, I32 = {bytes_t, [], 32},
I48 = {bytes_t, [], 48}, I48 = {bytes_t, [], 48},
Bts = {bytes_t, [], any},
Qid = fun(Name) -> {qid, [], Name} end, Qid = fun(Name) -> {qid, [], Name} end,
Map = fun(KT, VT) -> {app_t, [], {id, [], "map"}, [KT, VT]} end, Map = fun(KT, VT) -> {app_t, [], {id, [], "map"}, [KT, VT]} end,
ChainTxArities = [3, 0, 0, 0, 0, 0, 1, 1, 1, 2, 1, 2, 2, 1, 1, 1, 1, 1, 1, 1, 2, 0], ChainTxArities = [3, 0, 0, 0, 0, 0, 1, 1, 1, 2, 1, 2, 2, 1, 1, 1, 1, 1, 1, 1, 2, 0],
@@ -105,16 +103,16 @@ from_fate_builtin(QType, Val) ->
App(["AENSv2","Name"], [Chk(Adr, Addr), Chk(Qid(["Chain", "ttl"]), TTL), App(["AENSv2","Name"], [Chk(Adr, Addr), Chk(Qid(["Chain", "ttl"]), TTL),
Chk(Map(Str, Qid(["AENSv2", "pointee"])), Ptrs)]); Chk(Map(Str, Qid(["AENSv2", "pointee"])), Ptrs)]);
{["AENSv2", "pointee"], {variant, [1, 1, 1, 1, 1], 0, {Value}}} -> {["AENSv2", "pointee"], {variant, [1, 1, 1, 1, 1], 0, {Val}}} ->
App(["AENSv2","AccountPt"], [Chk(Adr, Value)]); App(["AENSv2","AccountPt"], [Chk(Adr, Val)]);
{["AENSv2", "pointee"], {variant, [1, 1, 1, 1, 1], 1, {Value}}} -> {["AENSv2", "pointee"], {variant, [1, 1, 1, 1, 1], 1, {Val}}} ->
App(["AENSv2","OraclePt"], [Chk(Adr, Value)]); App(["AENSv2","OraclePt"], [Chk(Adr, Val)]);
{["AENSv2", "pointee"], {variant, [1, 1, 1, 1, 1], 2, {Value}}} -> {["AENSv2", "pointee"], {variant, [1, 1, 1, 1, 1], 2, {Val}}} ->
App(["AENSv2","ContractPt"], [Chk(Adr, Value)]); App(["AENSv2","ContractPt"], [Chk(Adr, Val)]);
{["AENSv2", "pointee"], {variant, [1, 1, 1, 1, 1], 3, {Value}}} -> {["AENSv2", "pointee"], {variant, [1, 1, 1, 1, 1], 3, {Val}}} ->
App(["AENSv2","ChannelPt"], [Chk(Adr, Value)]); App(["AENSv2","ChannelPt"], [Chk(Adr, Val)]);
{["AENSv2", "pointee"], {variant, [1, 1, 1, 1, 1], 4, {Value}}} -> {["AENSv2", "pointee"], {variant, [1, 1, 1, 1, 1], 4, {Val}}} ->
App(["AENSv2","DataPt"], [Chk(Bts, Value)]); App(["AENSv2","DataPt"], [Chk(Str, Val)]);
{["Chain", "ga_meta_tx"], {variant, [2], 0, {Addr, X}}} -> {["Chain", "ga_meta_tx"], {variant, [2], 0, {Addr, X}}} ->
App(["Chain","GAMetaTx"], [Chk(Adr, Addr), Chk(Int, X)]); App(["Chain","GAMetaTx"], [Chk(Adr, Addr), Chk(Int, X)]);
+4 -4
View File
@@ -893,10 +893,10 @@ failing_contracts() ->
"Trying to implement or extend an undefined interface `Z`">> "Trying to implement or extend an undefined interface `Z`">>
]) ])
, ?TYPE_ERROR(polymorphism_contract_interface_same_name_different_type, , ?TYPE_ERROR(polymorphism_contract_interface_same_name_different_type,
[<<?Pos(5,5) [<<?Pos(9,5)
"Cannot unify `char` and `int`\n" "Duplicate definitions of `f` at\n"
"when implementing the entrypoint `f` from the interface `I1`">> " - line 8, column 5\n"
]) " - line 9, column 5">>])
, ?TYPE_ERROR(polymorphism_contract_missing_implementation, , ?TYPE_ERROR(polymorphism_contract_missing_implementation,
[<<?Pos(4,20) [<<?Pos(4,20)
"Unimplemented entrypoint `f` from the interface `I1` in the contract `I2`">> "Unimplemented entrypoint `f` from the interface `I1` in the contract `I2`">>
+3 -5
View File
@@ -1,21 +1,19 @@
include "Option.aes" include "Option.aes"
include "String.aes"
include "AENSCompat.aes" include "AENSCompat.aes"
contract interface OldAENSContract = contract interface OldAENSContract =
entrypoint set : (string, string, AENS.pointee) => unit entrypoint set : (string, string, AENS.pointee) => unit
entrypoint lookup : (string, string) => AENS.pointee entrypoint lookup : (string, string) => AENS.pointee
main contract AENSUpdate = main contract AENSUpdate =
stateful entrypoint update_name(owner : address, name : string, b : bytes(2)) = stateful entrypoint update_name(owner : address, name : string) =
let p1 : AENSv2.pointee = AENSv2.AccountPt(Call.caller) let p1 : AENSv2.pointee = AENSv2.AccountPt(Call.caller)
let p2 : AENSv2.pointee = AENSv2.OraclePt(Call.caller) let p2 : AENSv2.pointee = AENSv2.OraclePt(Call.caller)
let p3 : AENSv2.pointee = AENSv2.ContractPt(Call.caller) let p3 : AENSv2.pointee = AENSv2.ContractPt(Call.caller)
let p4 : AENSv2.pointee = AENSv2.ChannelPt(Call.caller) let p4 : AENSv2.pointee = AENSv2.ChannelPt(Call.caller)
let p5 : AENSv2.pointee = AENSv2.DataPt(String.to_bytes("any something will do")) let p5 : AENSv2.pointee = AENSv2.DataPt("any something will do")
let p6 : AENSv2.pointee = AENSv2.DataPt(Int.to_bytes(1345, 4))
AENSv2.update(owner, name, None, None, AENSv2.update(owner, name, None, None,
Some({ ["account_pubkey"] = p1, ["oracle_pubkey"] = p2, Some({ ["account_pubkey"] = p1, ["oracle_pubkey"] = p2,
["contract_pubkey"] = p3, ["misc"] = p4, ["data"] = p5, ["data2"] = p6 })) ["contract_pubkey"] = p3, ["misc"] = p4, ["data"] = p5 }))
stateful entrypoint old_interaction(c : OldAENSContract, owner : address, name : string) = stateful entrypoint old_interaction(c : OldAENSContract, owner : address, name : string) =
let p : AENS.pointee = c.lookup(name, "key1") let p : AENS.pointee = c.lookup(name, "key1")
+1 -1
View File
@@ -36,7 +36,7 @@ contract UnappliedBuiltins =
function map_delete() = Map.delete : (_, m) => _ function map_delete() = Map.delete : (_, m) => _
function map_from_list() = Map.from_list : _ => m function map_from_list() = Map.from_list : _ => m
function map_to_list() = Map.to_list : m => _ function map_to_list() = Map.to_list : m => _
function crypto_verify_sig() = Crypto.verify_sig : (bytes(), _, _) => _ function crypto_verify_sig() = Crypto.verify_sig
function crypto_verify_sig_secp256k1() = Crypto.verify_sig_secp256k1 function crypto_verify_sig_secp256k1() = Crypto.verify_sig_secp256k1
function crypto_ecverify_secp256k1() = Crypto.ecverify_secp256k1 function crypto_ecverify_secp256k1() = Crypto.ecverify_secp256k1
function crypto_ecrecover_secp256k1() = Crypto.ecrecover_secp256k1 function crypto_ecrecover_secp256k1() = Crypto.ecrecover_secp256k1