Merge branch 'master' into docs
This commit is contained in:
+60
-9
@@ -796,6 +796,10 @@ coerce_map_to_record(O, N, MemberTypes, Map) ->
|
||||
case zip_record_fields(MemberTypes, Map) of
|
||||
{ok, Zipped} ->
|
||||
case coerce_zipped_bindings(Zipped, to_fate, field) of
|
||||
{ok, [SingleElem]} ->
|
||||
% Singleton records aren't implemented as FATE tuples at
|
||||
% all.
|
||||
{ok, SingleElem};
|
||||
{ok, Converted} ->
|
||||
{ok, {tuple, list_to_tuple(Converted)}};
|
||||
Errors ->
|
||||
@@ -821,10 +825,18 @@ coerce_record_to_map(O, N, MemberTypes, Tuple) ->
|
||||
single_error({record_too_few_terms, O, N, Tuple});
|
||||
{error, too_many_terms} ->
|
||||
single_error({record_too_many_terms, O, N, Tuple});
|
||||
Errors ->
|
||||
Errors
|
||||
{error, Errors} ->
|
||||
correct_record_error_paths(Names, Errors)
|
||||
end.
|
||||
|
||||
correct_record_error_paths(Names, Errors) ->
|
||||
CorrectOne = fun({Error, [{record_element, N} | Path]}) ->
|
||||
FieldName = lists:nth(N + 1, Names),
|
||||
{Error, [{record_element, N, FieldName} | Path]}
|
||||
end,
|
||||
Corrected = lists:map(CorrectOne, Errors),
|
||||
{error, Corrected}.
|
||||
|
||||
zip_record_fields(Fields, Map) ->
|
||||
case lists:mapfoldl(fun zip_record_field/2, {Map, []}, Fields) of
|
||||
{_, {_, Missing = [_|_]}} ->
|
||||
@@ -915,6 +927,11 @@ fate_to_erlang({O, N, {variant, Variants}}, {variant, _, Tag, Tuple}) ->
|
||||
Terms = tuple_to_list(Tuple),
|
||||
{Name, TermTypes} = lists:nth(Tag + 1, Variants),
|
||||
coerce_variant2(O, N, Variants, Name, Tag, TermTypes, Terms, from_fate);
|
||||
fate_to_erlang({O, N, {record, [SingleMemberType]}}, Data) ->
|
||||
% Singleton records aren't implemented as FATE tuples at all.
|
||||
% Pretend they are, so we can get the full error indexing of the
|
||||
% non-singletone case.
|
||||
coerce_record_to_map(O, N, [SingleMemberType], {Data});
|
||||
fate_to_erlang({O, N, {record, MemberTypes}}, {tuple, Tuple}) ->
|
||||
coerce_record_to_map(O, N, MemberTypes, Tuple);
|
||||
fate_to_erlang({O, N, {unknown_type, _}}, Data) ->
|
||||
@@ -927,15 +944,30 @@ fate_to_erlang({O, N, {unknown_type, _}}, Data) ->
|
||||
io:format(Message, [O, N, Data])
|
||||
end,
|
||||
{ok, Data};
|
||||
fate_to_erlang({O, N, _}, Data) ->
|
||||
case N of
|
||||
already_normalized ->
|
||||
io:format("Warning: Unimplemented type ~p.~nUsing term as is:~n~p~n", [O, Data]);
|
||||
_ ->
|
||||
io:format("Warning: Unimplemented type ~p (i.e. ~p).~nUsing term as is:~n~p~n", [O, N, Data])
|
||||
end,
|
||||
fate_to_erlang(Type, Data) ->
|
||||
TypeStr = type_to_iolist(Type),
|
||||
io:format("Warning: Could not coerce term into ~s. Using term as is: ~p~n", [TypeStr, Data]),
|
||||
{ok, Data}.
|
||||
|
||||
type_to_iolist({O, already_normalized, S}) ->
|
||||
% Already normalized. Example output:
|
||||
% type {map, [string, integer]}
|
||||
opaque_type_to_iolist(O, S);
|
||||
type_to_iolist({O, N, S}) ->
|
||||
% Type alias. Print the alias, and then print the normalized version in
|
||||
% parentheses. Example output:
|
||||
% type "my_alias" (i.e. record type {"my_record_type", [integer]})
|
||||
io_lib:format("type ~p (i.e. ~s)", [O, opaque_type_to_iolist(N, S)]).
|
||||
|
||||
opaque_type_to_iolist(N, {record, _}) ->
|
||||
% N is the name of a record definition.
|
||||
io_lib:format("record type ~p", [N]);
|
||||
opaque_type_to_iolist(N, {variant, _}) ->
|
||||
% N is the name of a variant definition.
|
||||
io_lib:format("variant type ~p", [N]);
|
||||
opaque_type_to_iolist(N, _) ->
|
||||
% N is some other constructive type.
|
||||
io_lib:format("type ~p", [N]).
|
||||
|
||||
|
||||
%%% AACI Getters
|
||||
@@ -1120,6 +1152,25 @@ record_substitution_test() ->
|
||||
{ok, {[], Output}} = get_function_signature(AACI, "f"),
|
||||
check_roundtrip(Output, #{"a" => 123, "b" => 456}, {tuple, {123, 456}}).
|
||||
|
||||
singleton_record_substitution_test() ->
|
||||
Contract = "
|
||||
contract C =
|
||||
record single('t) = { it: 't }
|
||||
entrypoint f(): single(int) = { it = 1 }
|
||||
entrypoint g(): single(single(int)) = { it = { it = 2 } }
|
||||
entrypoint h(): single(int * int) = { it = (3, 4) }
|
||||
",
|
||||
{ok, AACI} = aaci_from_string(Contract),
|
||||
{ok, {[], FOutput}} = get_function_signature(AACI, "f"),
|
||||
check_roundtrip(FOutput, #{"it" => 123}, 123),
|
||||
{ok, {[], GOutput}} = get_function_signature(AACI, "g"),
|
||||
check_roundtrip(GOutput, #{"it" => #{"it" => 123}}, 123),
|
||||
{ok, {[], HOutput}} = get_function_signature(AACI, "h"),
|
||||
check_roundtrip(HOutput, #{"it" => {123, 456}}, {tuple, {123, 456}}),
|
||||
% Also check that records have accurate paths, since the implementation for
|
||||
% record error paths is a bit fiddly.
|
||||
{error, [{{tuple_too_many_terms, _, _, _}, [{record_element, 0, "it"}]}]} = fate_to_erlang(HOutput, {tuple, {1, 2, 3}}).
|
||||
|
||||
tuple_substitution_test() ->
|
||||
Contract = "
|
||||
contract C =
|
||||
|
||||
Reference in New Issue
Block a user