coerce hashes

It turns out there are a lot of types that, like option, should only be
valid as an opaque/normalized type, but should be substituted for
something different in the flat representation. If we restructure things
a little then we can implement all of these in one go.
This commit is contained in:
Jarvis Carroll 2025-09-23 19:22:30 +10:00
parent 3822bb69c9
commit a1be8cbc14

View File

@ -1658,6 +1658,12 @@ normalize_opaque_type(T, Types) ->
true -> {ok, true, T, T}
end.
% This function evaluates type aliases in a loop, until eventually a usable
% definition is found.
%
% It also evaluates built-in and standard library types such as options and
% names, to their defined variant representation, as well as evaluating
% certain binary types like hash, fp, and fr, into their byte representations.
% FIXME detect infinite loops
% FIXME detect builtins with the wrong number of arguments
% FIXME should nullary types have an empty list of arguments added before now?
@ -1665,6 +1671,10 @@ normalize_opaque_type({option, [T]}, _Types, IsFirst) ->
% Just like user-made ADTs, 'option' is considered part of the type, and so
% options are considered normalised.
{ok, IsFirst, {option, [T]}, {variant, [{"None", []}, {"Some", [T]}]}};
normalize_opaque_type(hash, _Types, IsFirst) ->
% For coercion purposes, hash is indistinguishable from bytes(32), so we
% treat it like a type alias.
{ok, IsFirst, hash, {bytes, [32]}};
normalize_opaque_type(T, Types, IsFirst) when is_list(T) ->
normalize_opaque_type({T, []}, Types, IsFirst);
normalize_opaque_type({T, TypeArgs}, Types, IsFirst) when is_list(T) ->
@ -1709,7 +1719,10 @@ normalize_opaque_type3(NextT, Types) ->
% Strings indicate names that should be substituted. Atoms indicate built in
% types, which don't need to be expanded, except for option.
% TODO: Stop calling this, so that we can stop redundantly enumerating all the
% built in types.
type_is_expanded({option, _}) -> false;
type_is_expanded(hash) -> false;
type_is_expanded(X) when is_atom(X) -> true;
type_is_expanded({X, _}) when is_atom(X) -> true;
type_is_expanded(_) -> false.
@ -2578,6 +2591,13 @@ coerce_unicode_test() ->
{ok, $ḉ} = coerce(Type, <<""/utf8>>, to_fate),
ok.
coerce_hash_test() ->
{ok, Type} = annotate_type(hash, #{}),
Hash = list_to_binary(lists:seq(1,32)),
try_coerce(Type, Hash, Hash),
ok.
%%% Complex AACI paramter and namespace tests
@ -2674,20 +2694,20 @@ obscure_aaci_test() ->
entrypoint any_bytes(): bytes() = Bytes.to_any_size(#112233)
entrypoint bits(): bits = Bits.all
entrypoint character(): char = 'a'
entrypoint hash(): hash = #00112233445566778899AABBCCDDEEFF00112233445566778899AABBCCDDEEFF
",
{ok, AACI} = aaci_from_string(Contract),
IntAnnotated = {integer, already_normalized, integer},
OptionFlat = {variant, [{"None", []}, {"Some", [IntAnnotated]}]},
OptionAnnotated = {{option, [integer]}, already_normalized, OptionFlat},
{ok, {[], OptionAnnotated}} = aaci_lookup_spec(AACI, "options"),
{ok, {[], {{option, [integer]}, _, _}}} = aaci_lookup_spec(AACI, "options"),
{ok, {[], {_, _, {bytes, [4]}}}} = aaci_lookup_spec(AACI, "fixed_bytes"),
{ok, {[], {_, _, {bytes, [any]}}}} = aaci_lookup_spec(AACI, "any_bytes"),
{ok, {[], {{bytes, [4]}, _, _}}} = aaci_lookup_spec(AACI, "fixed_bytes"),
{ok, {[], {{bytes, [any]}, _, _}}} = aaci_lookup_spec(AACI, "any_bytes"),
{ok, {[], {_, _, bits}}} = aaci_lookup_spec(AACI, "bits"),
{ok, {[], {bits, _, _}}} = aaci_lookup_spec(AACI, "bits"),
{ok, {[], {_, _, char}}} = aaci_lookup_spec(AACI, "character"),
{ok, {[], {char, _, _}}} = aaci_lookup_spec(AACI, "character"),
{ok, {[], {hash, _, _}}} = aaci_lookup_spec(AACI, "hash"),
ok.