Handle singleton records in erlang_to_fate
I realized this case needed special handling in hz_sophia, but didn't get around to covering it properly in the older hz_aaci analogues. While I was at it, I went and improved the error paths for record elements.
This commit is contained in:
+38
-2
@@ -796,6 +796,10 @@ coerce_map_to_record(O, N, MemberTypes, Map) ->
|
|||||||
case zip_record_fields(MemberTypes, Map) of
|
case zip_record_fields(MemberTypes, Map) of
|
||||||
{ok, Zipped} ->
|
{ok, Zipped} ->
|
||||||
case coerce_zipped_bindings(Zipped, to_fate, field) of
|
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, Converted} ->
|
||||||
{ok, {tuple, list_to_tuple(Converted)}};
|
{ok, {tuple, list_to_tuple(Converted)}};
|
||||||
Errors ->
|
Errors ->
|
||||||
@@ -821,10 +825,18 @@ coerce_record_to_map(O, N, MemberTypes, Tuple) ->
|
|||||||
single_error({record_too_few_terms, O, N, Tuple});
|
single_error({record_too_few_terms, O, N, Tuple});
|
||||||
{error, too_many_terms} ->
|
{error, too_many_terms} ->
|
||||||
single_error({record_too_many_terms, O, N, Tuple});
|
single_error({record_too_many_terms, O, N, Tuple});
|
||||||
Errors ->
|
{error, Errors} ->
|
||||||
Errors
|
correct_record_error_paths(Names, Errors)
|
||||||
end.
|
end.
|
||||||
|
|
||||||
|
correct_record_error_paths(Names, Errors) ->
|
||||||
|
CorrectOne = fun({Error, [{record_element, N} | Path]}) ->
|
||||||
|
FieldName = lists:nth(N + 1, Names),
|
||||||
|
{Error, [{record_element, FieldName} | Path]}
|
||||||
|
end,
|
||||||
|
Corrected = lists:map(CorrectOne, Errors),
|
||||||
|
{error, Corrected}.
|
||||||
|
|
||||||
zip_record_fields(Fields, Map) ->
|
zip_record_fields(Fields, Map) ->
|
||||||
case lists:mapfoldl(fun zip_record_field/2, {Map, []}, Fields) of
|
case lists:mapfoldl(fun zip_record_field/2, {Map, []}, Fields) of
|
||||||
{_, {_, Missing = [_|_]}} ->
|
{_, {_, Missing = [_|_]}} ->
|
||||||
@@ -915,6 +927,11 @@ fate_to_erlang({O, N, {variant, Variants}}, {variant, _, Tag, Tuple}) ->
|
|||||||
Terms = tuple_to_list(Tuple),
|
Terms = tuple_to_list(Tuple),
|
||||||
{Name, TermTypes} = lists:nth(Tag + 1, Variants),
|
{Name, TermTypes} = lists:nth(Tag + 1, Variants),
|
||||||
coerce_variant2(O, N, Variants, Name, Tag, TermTypes, Terms, from_fate);
|
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}) ->
|
fate_to_erlang({O, N, {record, MemberTypes}}, {tuple, Tuple}) ->
|
||||||
coerce_record_to_map(O, N, MemberTypes, Tuple);
|
coerce_record_to_map(O, N, MemberTypes, Tuple);
|
||||||
fate_to_erlang({O, N, {unknown_type, _}}, Data) ->
|
fate_to_erlang({O, N, {unknown_type, _}}, Data) ->
|
||||||
@@ -1120,6 +1137,25 @@ record_substitution_test() ->
|
|||||||
{ok, {[], Output}} = get_function_signature(AACI, "f"),
|
{ok, {[], Output}} = get_function_signature(AACI, "f"),
|
||||||
check_roundtrip(Output, #{"a" => 123, "b" => 456}, {tuple, {123, 456}}).
|
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, "it"}]}]} = fate_to_erlang(HOutput, {tuple, {1, 2, 3}}).
|
||||||
|
|
||||||
tuple_substitution_test() ->
|
tuple_substitution_test() ->
|
||||||
Contract = "
|
Contract = "
|
||||||
contract C =
|
contract C =
|
||||||
|
|||||||
Reference in New Issue
Block a user