From 212534976a825994630959bf5f281305c39ca270 Mon Sep 17 00:00:00 2001 From: Gaith Hallak Date: Wed, 17 Nov 2021 16:03:56 +0200 Subject: [PATCH 01/53] Add polymorphism to syntax tree and parser --- src/aeso_parser.erl | 24 ++++++++++++++++++------ src/aeso_syntax.erl | 6 +++--- 2 files changed, 21 insertions(+), 9 deletions(-) diff --git a/src/aeso_parser.erl b/src/aeso_parser.erl index 7e5fa62..7877367 100644 --- a/src/aeso_parser.erl +++ b/src/aeso_parser.erl @@ -96,17 +96,29 @@ decl() -> choice( %% Contract declaration [ ?RULE(token(main), keyword(contract), - con(), tok('='), maybe_block(decl()), {contract_main, _2, _3, _5}) + con(), tok('='), maybe_block(decl()), {contract_main, _2, _3, [], _5}) + , ?RULE(token(main), keyword(contract), + con(), tok(':'), comma_sep(con()), tok('='), maybe_block(decl()), {contract_main, _2, _3, _5, _7}) , ?RULE(keyword(contract), - con(), tok('='), maybe_block(decl()), {contract_child, _1, _2, _4}) + con(), tok('='), maybe_block(decl()), {contract_child, _1, _2, [], _4}) + , ?RULE(keyword(contract), + con(), tok(':'), comma_sep(con()), tok('='), maybe_block(decl()), {contract_child, _1, _2, _4, _6}) , ?RULE(keyword(contract), token(interface), - con(), tok('='), maybe_block(decl()), {contract_interface, _1, _3, _5}) + con(), tok('='), maybe_block(decl()), {contract_interface, _1, _3, [], _5}) + , ?RULE(keyword(contract), token(interface), + con(), tok(':'), comma_sep(con()), tok('='), maybe_block(decl()), {contract_interface, _1, _3, _5, _7}) , ?RULE(token(payable), token(main), keyword(contract), - con(), tok('='), maybe_block(decl()), add_modifiers([_1], {contract_main, _3, _4, _6})) + con(), tok('='), maybe_block(decl()), add_modifiers([_1], {contract_main, _3, _4, [], _6})) + , ?RULE(token(payable), token(main), keyword(contract), + con(), tok(':'), comma_sep(con()), tok('='), maybe_block(decl()), add_modifiers([_1], {contract_main, _3, _4, _6, _8})) , ?RULE(token(payable), keyword(contract), - con(), tok('='), maybe_block(decl()), add_modifiers([_1], {contract_child, _2, _3, _5})) + con(), tok('='), maybe_block(decl()), add_modifiers([_1], {contract_child, _2, _3, [], _5})) + , ?RULE(token(payable), keyword(contract), + con(), tok(':'), comma_sep(con()), tok('='), maybe_block(decl()), add_modifiers([_1], {contract_child, _2, _3, _5, _7})) , ?RULE(token(payable), keyword(contract), token(interface), - con(), tok('='), maybe_block(decl()), add_modifiers([_1], {contract_interface, _2, _4, _6})) + con(), tok('='), maybe_block(decl()), add_modifiers([_1], {contract_interface, _2, _4, [], _6})) + , ?RULE(token(payable), keyword(contract), token(interface), + con(), tok(':'), comma_sep(con()), tok('='), maybe_block(decl()), add_modifiers([_1], {contract_interface, _2, _4, _6, _8})) , ?RULE(keyword(namespace), con(), tok('='), maybe_block(decl()), {namespace, _1, _2, _4}) diff --git a/src/aeso_syntax.erl b/src/aeso_syntax.erl index 1e01153..fbf4a6b 100644 --- a/src/aeso_syntax.erl +++ b/src/aeso_syntax.erl @@ -38,9 +38,9 @@ -type namespace_alias() :: none | con(). -type namespace_parts() :: none | {for, [id()]} | {hiding, [id()]}. --type decl() :: {contract_main, ann(), con(), [decl()]} - | {contract_child, ann(), con(), [decl()]} - | {contract_interface, ann(), con(), [decl()]} +-type decl() :: {contract_main, ann(), con(), [con()], [decl()]} + | {contract_child, ann(), con(), [con()], [decl()]} + | {contract_interface, ann(), con(), [con()], [decl()]} | {namespace, ann(), con(), [decl()]} | {pragma, ann(), pragma()} | {type_decl, ann(), id(), [tvar()]} % Only for error msgs -- 2.30.2 From 4c25489963c49d62c068846b4fb4d81dc7d95328 Mon Sep 17 00:00:00 2001 From: Gaith Hallak Date: Tue, 23 Nov 2021 21:39:29 +0200 Subject: [PATCH 02/53] Add polymorphism to infer types --- src/aeso_ast_infer_types.erl | 90 ++++++++++++++++++++++++++++++++---- 1 file changed, 81 insertions(+), 9 deletions(-) diff --git a/src/aeso_ast_infer_types.erl b/src/aeso_ast_infer_types.erl index 8af28ae..a084634 100644 --- a/src/aeso_ast_infer_types.erl +++ b/src/aeso_ast_infer_types.erl @@ -280,7 +280,7 @@ contract_call_type({fun_t, Ann, [], Args, Ret}) -> Args, {if_t, Ann, Id("protected"), {app_t, Ann, {id, Ann, "option"}, [Ret]}, Ret}}. -spec bind_contract(aeso_syntax:decl(), env()) -> env(). -bind_contract({Contract, Ann, Id, Contents}, Env) +bind_contract({Contract, Ann, Id, _Impls, Contents}, Env) when ?IS_CONTRACT_HEAD(Contract) -> Key = name(Id), Sys = [{origin, system}], @@ -832,7 +832,7 @@ infer(Contracts, Options) -> -spec infer1(env(), [aeso_syntax:decl()], [aeso_syntax:decl()], list(option())) -> {env(), [aeso_syntax:decl()]}. infer1(Env, [], Acc, _Options) -> {Env, lists:reverse(Acc)}; -infer1(Env, [{Contract, Ann, ConName, Code} | Rest], Acc, Options) +infer1(Env, [{Contract, Ann, ConName, Impls, Code} | Rest], Acc, Options) when ?IS_CONTRACT_HEAD(Contract) -> %% do type inference on each contract independently. check_scope_name_clash(Env, contract, ConName), @@ -846,7 +846,23 @@ infer1(Env, [{Contract, Ann, ConName, Code} | Rest], Acc, Options) contract_interface -> ok end, {Env1, Code1} = infer_contract_top(push_scope(contract, ConName, Env), What, Code, Options), - Contract1 = {Contract, Ann, ConName, Code1}, + Contract1 = {Contract, Ann, ConName, Impls, Code1}, + AllInterfaces = [{name(IName), I} || I = {contract_interface, _, IName, _, _} <- Acc], + ImplsNames = lists:map(fun name/1, Impls), + create_type_errors(), + lists:foreach(fun(Impl) -> case proplists:get_value(name(Impl), AllInterfaces) of + undefined -> type_error({referencing_undefined_interface, Impl}); + _ -> ok + end + end, Impls), + case What of + contract -> + ImplementedInterfaces = [proplists:get_value(Name, AllInterfaces) || Name <- ImplsNames], + check_implemented_interfaces(ImplementedInterfaces, ConName, [ Fun || Fun = {letfun, _, _, _, _, _} <- Code1 ], [], AllInterfaces); + contract_interface -> + ok + end, + destroy_and_report_type_errors(Env), Env2 = pop_scope(Env1), Env3 = bind_contract(Contract1, Env2), infer1(Env3, Rest, [Contract1 | Acc], Options); @@ -862,17 +878,67 @@ infer1(Env, [{pragma, _, _} | Rest], Acc, Options) -> %% Pragmas are checked in check_modifiers infer1(Env, Rest, Acc, Options). +check_implemented_interfaces([], _, _, _, _) -> + ok; +check_implemented_interfaces([{contract_interface, _, IName, Extensions, Decls} | Interfaces], ConId, Impls, Acc, AllInterfaces) -> + case lists:member(name(IName), Acc) of + true -> + check_implemented_interfaces(Interfaces, ConId, Impls, Acc, AllInterfaces); + false -> + Unmatched = match_impls(Decls, ConId, name(IName), Impls), + NewInterfaces = [proplists:get_value(name(I), AllInterfaces) || I <- Extensions], + check_implemented_interfaces(Interfaces ++ NewInterfaces, ConId, Unmatched, [name(IName) | Acc], AllInterfaces) + end. + +match_impls([], _, _, Impls) -> + Impls; +match_impls([{fun_decl, _, {id, _, FunName}, {fun_t, _, _, ArgsTypes, RetDecl}} | Decls], ConId, IName, Impls) -> + Match = fun({letfun, _, {id, _, FName}, Args, RetFun, _}) when FName == FunName -> + length(ArgsTypes) == length(Args) andalso + compare_types(RetDecl, RetFun) andalso + lists:all(fun({T1, {typed, _, _, T2}}) -> compare_types(T1, T2) end, + lists:zip(ArgsTypes, Args)); + (_) -> false + end, + UnmatchedImpls = case lists:search(Match, Impls) of + {value, V} -> + lists:delete(V, Impls); + false -> + type_error({unimplemented_interface_function, ConId, IName, FunName}), + Impls + end, + match_impls(Decls, ConId, IName, UnmatchedImpls). + + +-spec compare_types(T, T) -> boolean() when T :: utype() | [utype()]. +compare_types(Types1 = [_ | _], Types2 = [_ | _]) -> + length(Types1) == length(Types2) andalso + lists:all(fun({T1, T2}) -> compare_types(T1, T2) end, lists:zip(Types1, Types2)); +compare_types({fun_t, _, _, Types1, RetType1}, {fun_t, _, _, Types2, RetType2}) -> + % TODO: what about named_args_t and var_args? + compare_types(RetType1, RetType2) andalso compare_types(Types1, Types2); +compare_types({app_t, _, Type1, ArgsTypes1}, {app_t, _, Type2, ArgsTypes2}) -> + compare_types(Type1, Type2) andalso compare_types(ArgsTypes1, ArgsTypes2); +compare_types({tuple_t, _, Types1}, {tuple_t, _, Types2}) -> + compare_types(Types1, Types2); +compare_types(T1 = {Id, _, Type}, {Id, _, Type}) when ?is_type_id(T1) -> + true; +compare_types({if_t, _, {id, _, Id}, Ta1, Tb1}, {if_t, _, {id, _, Id}, Ta2, Tb2}) -> + compare_types(Ta1, Ta2) andalso compare_types(Tb1, Tb2); +compare_types(_, _) -> + false. + %% Asserts that the main contract is somehow defined. identify_main_contract(Contracts, Options) -> - Children = [C || C = {contract_child, _, _, _} <- Contracts], - Mains = [C || C = {contract_main, _, _, _} <- Contracts], + Children = [C || C = {contract_child, _, _, _, _} <- Contracts], + Mains = [C || C = {contract_main, _, _, _, _} <- Contracts], case Mains of [] -> case Children of [] -> type_error( {main_contract_undefined, [{file, File} || {src_file, File} <- Options]}); - [{contract_child, Ann, Con, Body}] -> - (Contracts -- Children) ++ [{contract_main, Ann, Con, Body}]; + [{contract_child, Ann, Con, Impls, Body}] -> + (Contracts -- Children) ++ [{contract_main, Ann, Con, Impls, Body}]; [H|_] -> type_error({ambiguous_main_contract, aeso_syntax:get_ann(H)}) end; @@ -1097,7 +1163,7 @@ check_modifiers(Env, Contracts) -> check_modifiers_(Env, Contracts), destroy_and_report_type_errors(Env). -check_modifiers_(Env, [{Contract, _, Con, Decls} | Rest]) +check_modifiers_(Env, [{Contract, _, Con, _Impls, Decls} | Rest]) when ?IS_CONTRACT_HEAD(Contract) -> IsInterface = Contract =:= contract_interface, check_modifiers1(contract, Decls), @@ -3098,7 +3164,7 @@ mk_error({namespace, _Pos, {con, Pos, Name}, _Def}) -> Msg = io_lib:format("Nested namespaces are not allowed. Namespace `~s` is not defined at top level.", [Name]), mk_t_err(pos(Pos), Msg); -mk_error({Contract, _Pos, {con, Pos, Name}, _Def}) when ?IS_CONTRACT_HEAD(Contract) -> +mk_error({Contract, _Pos, {con, Pos, Name}, _Impls, _Def}) when ?IS_CONTRACT_HEAD(Contract) -> Msg = io_lib:format("Nested contracts are not allowed. Contract `~s` is not defined at top level.", [Name]), mk_t_err(pos(Pos), Msg); @@ -3285,6 +3351,12 @@ mk_error({unknown_warning, Warning}) -> mk_error({empty_record_definition, Ann, Name}) -> Msg = io_lib:format("Empty record definitions are not allowed. Cannot define the record `~s`", [Name]), mk_t_err(pos(Ann), Msg); +mk_error({unimplemented_interface_function, ConId, InterfaceName, FunName}) -> + Msg = io_lib:format("Unimplemented function ~s from the interface ~s in the contract ~s", [FunName, InterfaceName, pp(ConId)]), + mk_t_err(pos(ConId), Msg); +mk_error({referencing_undefined_interface, InterfaceId}) -> + Msg = io_lib:format("Trying to implement or extend an undefined interface ~s", [pp(InterfaceId)]), + mk_t_err(pos(InterfaceId), Msg); mk_error(Err) -> Msg = io_lib:format("Unknown error: ~p", [Err]), mk_t_err(pos(0, 0), Msg). -- 2.30.2 From 6003aacdea109be031cfe3ecf6260a4b2f1bdd9b Mon Sep 17 00:00:00 2001 From: Gaith Hallak Date: Tue, 23 Nov 2021 23:27:35 +0200 Subject: [PATCH 03/53] Fix pretty printing --- src/aeso_pretty.erl | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/aeso_pretty.erl b/src/aeso_pretty.erl index 0ee05f2..d639ce9 100644 --- a/src/aeso_pretty.erl +++ b/src/aeso_pretty.erl @@ -151,12 +151,16 @@ decl(D, Options) -> with_options(Options, fun() -> decl(D) end). -spec decl(aeso_syntax:decl()) -> doc(). -decl({Con, Attrs, C, Ds}) when ?IS_CONTRACT_HEAD(Con) -> +decl({Con, Attrs, C, Is, Ds}) when ?IS_CONTRACT_HEAD(Con) -> Mod = fun({Mod, true}) when Mod == payable -> text(atom_to_list(Mod)); (_) -> empty() end, + ImplsList = case Is of + [] -> [empty()]; + _ -> [text(":"), par(punctuate(text(","), lists:map(fun name/1, Is)), 0)] + end, block(follow( hsep(lists:map(Mod, Attrs) ++ [contract_head(Con)]) - , hsep(name(C), text("="))), decls(Ds)); + , hsep([name(C)] ++ ImplsList ++ [text("=")])), decls(Ds)); decl({namespace, _, C, Ds}) -> block(follow(text("namespace"), hsep(name(C), text("="))), decls(Ds)); decl({pragma, _, Pragma}) -> pragma(Pragma); -- 2.30.2 From d8db908485e6f0f3aec1d9a80cebac1deb81c4db Mon Sep 17 00:00:00 2001 From: Gaith Hallak Date: Tue, 23 Nov 2021 23:28:02 +0200 Subject: [PATCH 04/53] Add new tests and fix old tests --- src/aeso_aci.erl | 8 ++++--- src/aeso_ast_to_fcode.erl | 2 +- src/aeso_compiler.erl | 6 ++--- test/aeso_calldata_tests.erl | 2 +- test/aeso_compiler_tests.erl | 22 +++++++++++++++++++ test/aeso_parser_tests.erl | 2 +- .../contract_interface_polymorphism.aes | 9 ++++++++ ...tract_interface_polymorphism_recursive.aes | 13 +++++++++++ ...polymorphism_same_decl_multi_interface.aes | 8 +++++++ test/contracts/contract_polymorphism.aes | 5 +++++ ...ct_polymorphism_missing_implementation.aes | 8 +++++++ ...polymorphism_same_decl_multi_interface.aes | 8 +++++++ ...tract_polymorphism_undefined_interface.aes | 5 +++++ 13 files changed, 89 insertions(+), 9 deletions(-) create mode 100644 test/contracts/contract_interface_polymorphism.aes create mode 100644 test/contracts/contract_interface_polymorphism_recursive.aes create mode 100644 test/contracts/contract_interface_polymorphism_same_decl_multi_interface.aes create mode 100644 test/contracts/contract_polymorphism.aes create mode 100644 test/contracts/contract_polymorphism_missing_implementation.aes create mode 100644 test/contracts/contract_polymorphism_same_decl_multi_interface.aes create mode 100644 test/contracts/contract_polymorphism_undefined_interface.aes diff --git a/src/aeso_aci.erl b/src/aeso_aci.erl index ae696a4..ed8a44b 100644 --- a/src/aeso_aci.erl +++ b/src/aeso_aci.erl @@ -83,7 +83,7 @@ from_typed_ast(Type, TypedAst) -> string -> do_render_aci_json(JArray) end. -encode_contract(Contract = {Head, _, {con, _, Name}, _}) when ?IS_CONTRACT_HEAD(Head) -> +encode_contract(Contract = {Head, _, {con, _, Name}, _, _}) when ?IS_CONTRACT_HEAD(Head) -> C0 = #{name => encode_name(Name)}, Tdefs0 = [ encode_typedef(T) || T <- sort_decls(contract_types(Contract)) ], @@ -341,10 +341,12 @@ stateful(false) -> "". %% #contract{Ann, Con, [Declarations]}. -contract_funcs({C, _, _, Decls}) when ?IS_CONTRACT_HEAD(C); C == namespace -> +contract_funcs({C, _, _, _, Decls}) when ?IS_CONTRACT_HEAD(C) -> [ D || D <- Decls, is_fun(D)]. -contract_types({C, _, _, Decls}) when ?IS_CONTRACT_HEAD(C); C == namespace -> +contract_types({namespace, _, _, Decls}) -> + [ D || D <- Decls, is_type(D) ]; +contract_types({C, _, _, _, Decls}) when ?IS_CONTRACT_HEAD(C) -> [ D || D <- Decls, is_type(D) ]. is_fun({letfun, _, _, _, _, _}) -> true; diff --git a/src/aeso_ast_to_fcode.erl b/src/aeso_ast_to_fcode.erl index 25802e0..d0328c9 100644 --- a/src/aeso_ast_to_fcode.erl +++ b/src/aeso_ast_to_fcode.erl @@ -326,7 +326,7 @@ get_option(Opt, Env, Default) -> %% -- Compilation ------------------------------------------------------------ -spec to_fcode(env(), aeso_syntax:ast()) -> {env(), fcode()}. -to_fcode(Env, [{Contract, Attrs, Con = {con, _, Name}, Decls}|Rest]) +to_fcode(Env, [{Contract, Attrs, Con = {con, _, Name}, _Impls, Decls}|Rest]) when ?IS_CONTRACT_HEAD(Contract) -> case Contract =:= contract_interface of false -> diff --git a/src/aeso_compiler.erl b/src/aeso_compiler.erl index 5580a7b..2982fd6 100644 --- a/src/aeso_compiler.erl +++ b/src/aeso_compiler.erl @@ -238,8 +238,8 @@ insert_init_function(Code, Options) -> last_contract_indent(Decls) -> case lists:last(Decls) of - {_, _, _, [Decl | _]} -> aeso_syntax:get_ann(col, Decl, 1) - 1; - _ -> 0 + {_, _, _, _, [Decl | _]} -> aeso_syntax:get_ann(col, Decl, 1) - 1; + _ -> 0 end. -spec to_sophia_value(string(), string(), ok | error | revert, binary()) -> @@ -338,7 +338,7 @@ decode_calldata(ContractString, FunName, Calldata, Options0) -> end. -dialyzer({nowarn_function, get_decode_type/2}). -get_decode_type(FunName, [{Contract, Ann, _, Defs}]) when ?IS_CONTRACT_HEAD(Contract) -> +get_decode_type(FunName, [{Contract, Ann, _, _, Defs}]) when ?IS_CONTRACT_HEAD(Contract) -> GetType = fun({letfun, _, {id, _, Name}, Args, Ret, _}) when Name == FunName -> [{Args, Ret}]; ({fun_decl, _, {id, _, Name}, {fun_t, _, _, Args, Ret}}) when Name == FunName -> [{Args, Ret}]; (_) -> [] end, diff --git a/test/aeso_calldata_tests.erl b/test/aeso_calldata_tests.erl index c897180..2fb7a36 100644 --- a/test/aeso_calldata_tests.erl +++ b/test/aeso_calldata_tests.erl @@ -39,7 +39,7 @@ calldata_aci_test_() -> end} || {ContractName, Fun, Args} <- compilable_contracts()]. parse_args(Fun, Args) -> - [{contract_main, _, _, [{letfun, _, _, _, _, [{guarded, _, [], {app, _, _, AST}}]}]}] = + [{contract_main, _, _, _, [{letfun, _, _, _, _, [{guarded, _, [], {app, _, _, AST}}]}]}] = aeso_parser:string("main contract Temp = function foo() = " ++ Fun ++ "(" ++ string:join(Args, ", ") ++ ")"), strip_ann(AST). diff --git a/test/aeso_compiler_tests.erl b/test/aeso_compiler_tests.erl index fd1c9a1..189d6ff 100644 --- a/test/aeso_compiler_tests.erl +++ b/test/aeso_compiler_tests.erl @@ -202,6 +202,8 @@ compilable_contracts() -> "assign_patterns", "patterns_guards", "pipe_operator", + "contract_polymorphism", + "contract_interface_polymorphism", "test" % Custom general-purpose test file. Keep it last on the list. ]. @@ -825,6 +827,26 @@ failing_contracts() -> <> ]) + , ?TYPE_ERROR(contract_interface_polymorphism_recursive, + [<> + ]) + , ?TYPE_ERROR(contract_interface_polymorphism_same_decl_multi_interface, + [<> + ]) + , ?TYPE_ERROR(contract_polymorphism_missing_implementation, + [<> + ]) + , ?TYPE_ERROR(contract_polymorphism_same_decl_multi_interface, + [<> + ]) + , ?TYPE_ERROR(contract_polymorphism_undefined_interface, + [<> + ]) ]. -define(Path(File), "code_errors/" ??File). diff --git a/test/aeso_parser_tests.erl b/test/aeso_parser_tests.erl index 6c577c8..e3b7598 100644 --- a/test/aeso_parser_tests.erl +++ b/test/aeso_parser_tests.erl @@ -15,7 +15,7 @@ simple_contracts_test_() -> Text = "main contract Identity =\n" " function id(x) = x\n", ?assertMatch( - [{contract_main, _, {con, _, "Identity"}, + [{contract_main, _, {con, _, "Identity"}, _, [{letfun, _, {id, _, "id"}, [{id, _, "x"}], {id, _, "_"}, [{guarded, _, [], {id, _, "x"}}]}]}], parse_string(Text)), ok diff --git a/test/contracts/contract_interface_polymorphism.aes b/test/contracts/contract_interface_polymorphism.aes new file mode 100644 index 0000000..6eb9ab7 --- /dev/null +++ b/test/contracts/contract_interface_polymorphism.aes @@ -0,0 +1,9 @@ +contract interface II = + entrypoint f : () => int + +contract interface I : II = + entrypoint g : () => int + +contract C : I = + entrypoint f() = 1 + entrypoint g() = 2 diff --git a/test/contracts/contract_interface_polymorphism_recursive.aes b/test/contracts/contract_interface_polymorphism_recursive.aes new file mode 100644 index 0000000..8e373d7 --- /dev/null +++ b/test/contracts/contract_interface_polymorphism_recursive.aes @@ -0,0 +1,13 @@ +contract interface X : Z = + entrypoint x : () => int + +contract interface Y : X = + entrypoint y : () => int + +contract interface Z : Y = + entrypoint z : () => int + +contract C : Z = + entrypoint x() = 1 + entrypoint y() = 1 + entrypoint z() = 1 diff --git a/test/contracts/contract_interface_polymorphism_same_decl_multi_interface.aes b/test/contracts/contract_interface_polymorphism_same_decl_multi_interface.aes new file mode 100644 index 0000000..9ad40a2 --- /dev/null +++ b/test/contracts/contract_interface_polymorphism_same_decl_multi_interface.aes @@ -0,0 +1,8 @@ +contract interface I = + entrypoint f : () => int + +contract interface II : I = + entrypoint f : () => int + +contract C : II = + entrypoint f() = 1 diff --git a/test/contracts/contract_polymorphism.aes b/test/contracts/contract_polymorphism.aes new file mode 100644 index 0000000..2982cd1 --- /dev/null +++ b/test/contracts/contract_polymorphism.aes @@ -0,0 +1,5 @@ +contract interface Strokable = + entrypoint stroke : () => string + +contract Cat : Strokable = + entrypoint stroke() = "Cat stroke" diff --git a/test/contracts/contract_polymorphism_missing_implementation.aes b/test/contracts/contract_polymorphism_missing_implementation.aes new file mode 100644 index 0000000..4e6639f --- /dev/null +++ b/test/contracts/contract_polymorphism_missing_implementation.aes @@ -0,0 +1,8 @@ +contract interface I1 = + entrypoint f : () => int + +contract interface I2 : I1 = + entrypoint g : () => int + +contract C : I2 = + entrypoint g() = 1 diff --git a/test/contracts/contract_polymorphism_same_decl_multi_interface.aes b/test/contracts/contract_polymorphism_same_decl_multi_interface.aes new file mode 100644 index 0000000..6df635d --- /dev/null +++ b/test/contracts/contract_polymorphism_same_decl_multi_interface.aes @@ -0,0 +1,8 @@ +contract interface I = + entrypoint f : () => int + +contract interface J = + entrypoint f : () => int + +contract C : I, J = + entrypoint f() = 1 diff --git a/test/contracts/contract_polymorphism_undefined_interface.aes b/test/contracts/contract_polymorphism_undefined_interface.aes new file mode 100644 index 0000000..0fc47ac --- /dev/null +++ b/test/contracts/contract_polymorphism_undefined_interface.aes @@ -0,0 +1,5 @@ +contract interface I : H = + entrypoint f : () => unit + +contract C = + entrypoint g() = () -- 2.30.2 From 03413ab0d4fbd38e6a735bdcc3ccee8017191430 Mon Sep 17 00:00:00 2001 From: Gaith Hallak Date: Fri, 26 Nov 2021 18:50:36 +0200 Subject: [PATCH 05/53] Fix the comparison between unit and empty tuple --- src/aeso_ast_infer_types.erl | 6 +++++- test/contracts/contract_interface_polymorphism.aes | 8 ++++---- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/src/aeso_ast_infer_types.erl b/src/aeso_ast_infer_types.erl index a084634..bd9b24a 100644 --- a/src/aeso_ast_infer_types.erl +++ b/src/aeso_ast_infer_types.erl @@ -921,6 +921,10 @@ compare_types({app_t, _, Type1, ArgsTypes1}, {app_t, _, Type2, ArgsTypes2}) -> compare_types(Type1, Type2) andalso compare_types(ArgsTypes1, ArgsTypes2); compare_types({tuple_t, _, Types1}, {tuple_t, _, Types2}) -> compare_types(Types1, Types2); +compare_types({tuple_t, _, []}, {id, _, "unit"}) -> + true; +compare_types({id, _, "unit"}, {tuple_t, _, []}) -> + true; compare_types(T1 = {Id, _, Type}, {Id, _, Type}) when ?is_type_id(T1) -> true; compare_types({if_t, _, {id, _, Id}, Ta1, Tb1}, {if_t, _, {id, _, Id}, Ta2, Tb2}) -> @@ -1447,7 +1451,7 @@ infer_letfun(Env = #env{ namespace = Namespace }, LetFun = {letfun, Ann, Fun, _, {{Name, Sig}, Clause} = infer_letfun1(Env, LetFun), {{Name, Sig}, desugar_clauses(Ann, Fun, Sig, [Clause])}. -infer_letfun1(Env0 = #env{ namespace = NS }, {letfun, Attrib, Fun = {id, NameAttrib, Name}, Args, What, GuardedBodies}) -> +infer_letfun1(Env0 = #env{ namespace = NS }, {letfun, Attrib, Fun = {id, NameAttrib, Name}, Args, What, GuardedBodies}) -> Env = Env0#env{ stateful = aeso_syntax:get_ann(stateful, Attrib, false), current_function = Fun }, {NewEnv, {typed, _, {tuple, _, TypedArgs}, {tuple_t, _, ArgTypes}}} = infer_pattern(Env, {tuple, [{origin, system} | NameAttrib], Args}), diff --git a/test/contracts/contract_interface_polymorphism.aes b/test/contracts/contract_interface_polymorphism.aes index 6eb9ab7..6d51bea 100644 --- a/test/contracts/contract_interface_polymorphism.aes +++ b/test/contracts/contract_interface_polymorphism.aes @@ -1,9 +1,9 @@ contract interface II = - entrypoint f : () => int + entrypoint f : () => unit contract interface I : II = - entrypoint g : () => int + entrypoint g : () => unit contract C : I = - entrypoint f() = 1 - entrypoint g() = 2 + entrypoint f() = () + entrypoint g() = () -- 2.30.2 From df676ff6e9d59841f2208492c2ca7c714f31924c Mon Sep 17 00:00:00 2001 From: Gaith Hallak Date: Fri, 26 Nov 2021 19:03:41 +0200 Subject: [PATCH 06/53] Report undefined interface errors before checking implemented interfaces --- src/aeso_ast_infer_types.erl | 2 ++ test/aeso_compiler_tests.erl | 4 ++++ ...contract_interface_polymorphism_undefined_interface.aes | 5 +++++ .../contract_polymorphism_undefined_interface.aes | 7 ++----- 4 files changed, 13 insertions(+), 5 deletions(-) create mode 100644 test/contracts/contract_interface_polymorphism_undefined_interface.aes diff --git a/src/aeso_ast_infer_types.erl b/src/aeso_ast_infer_types.erl index bd9b24a..f034021 100644 --- a/src/aeso_ast_infer_types.erl +++ b/src/aeso_ast_infer_types.erl @@ -855,6 +855,8 @@ infer1(Env, [{Contract, Ann, ConName, Impls, Code} | Rest], Acc, Options) _ -> ok end end, Impls), + destroy_and_report_type_errors(Env), + create_type_errors(), case What of contract -> ImplementedInterfaces = [proplists:get_value(Name, AllInterfaces) || Name <- ImplsNames], diff --git a/test/aeso_compiler_tests.erl b/test/aeso_compiler_tests.erl index 189d6ff..96eac37 100644 --- a/test/aeso_compiler_tests.erl +++ b/test/aeso_compiler_tests.erl @@ -844,6 +844,10 @@ failing_contracts() -> "Unimplemented function f from the interface J in the contract C">> ]) , ?TYPE_ERROR(contract_polymorphism_undefined_interface, + [<> + ]) + , ?TYPE_ERROR(contract_interface_polymorphism_undefined_interface, [<> ]) diff --git a/test/contracts/contract_interface_polymorphism_undefined_interface.aes b/test/contracts/contract_interface_polymorphism_undefined_interface.aes new file mode 100644 index 0000000..0fc47ac --- /dev/null +++ b/test/contracts/contract_interface_polymorphism_undefined_interface.aes @@ -0,0 +1,5 @@ +contract interface I : H = + entrypoint f : () => unit + +contract C = + entrypoint g() = () diff --git a/test/contracts/contract_polymorphism_undefined_interface.aes b/test/contracts/contract_polymorphism_undefined_interface.aes index 0fc47ac..ba42562 100644 --- a/test/contracts/contract_polymorphism_undefined_interface.aes +++ b/test/contracts/contract_polymorphism_undefined_interface.aes @@ -1,5 +1,2 @@ -contract interface I : H = - entrypoint f : () => unit - -contract C = - entrypoint g() = () +contract C : I = + entrypoint f() = () -- 2.30.2 From 918ff94a3760d6dc2419af499c754379a8311d30 Mon Sep 17 00:00:00 2001 From: Gaith Hallak Date: Sat, 18 Dec 2021 19:01:51 +0200 Subject: [PATCH 07/53] Add test for implementing multiple interfaces --- test/aeso_compiler_tests.erl | 1 + test/contracts/contract_polymorphism_multi_interface.aes | 9 +++++++++ 2 files changed, 10 insertions(+) create mode 100644 test/contracts/contract_polymorphism_multi_interface.aes diff --git a/test/aeso_compiler_tests.erl b/test/aeso_compiler_tests.erl index 96eac37..eb890b2 100644 --- a/test/aeso_compiler_tests.erl +++ b/test/aeso_compiler_tests.erl @@ -203,6 +203,7 @@ compilable_contracts() -> "patterns_guards", "pipe_operator", "contract_polymorphism", + "contract_polymorphism_multi_interface", "contract_interface_polymorphism", "test" % Custom general-purpose test file. Keep it last on the list. ]. diff --git a/test/contracts/contract_polymorphism_multi_interface.aes b/test/contracts/contract_polymorphism_multi_interface.aes new file mode 100644 index 0000000..42349b1 --- /dev/null +++ b/test/contracts/contract_polymorphism_multi_interface.aes @@ -0,0 +1,9 @@ +contract interface I = + entrypoint f : () => int + +contract interface J = + entrypoint g : () => char + +contract C : I, J = + entrypoint f() = 1 + entrypoint g() = 'c' -- 2.30.2 From adb3455f25f042f742176b564b0b35e4515719a5 Mon Sep 17 00:00:00 2001 From: Gaith Hallak Date: Sat, 18 Dec 2021 19:10:45 +0200 Subject: [PATCH 08/53] Add test for implementing two interfaces with entrypoints of same names and different types --- test/aeso_compiler_tests.erl | 6 ++++++ ...morphism_same_name_different_type_multi_interface.aes | 9 +++++++++ 2 files changed, 15 insertions(+) create mode 100644 test/contracts/contract_polymorphism_same_name_different_type_multi_interface.aes diff --git a/test/aeso_compiler_tests.erl b/test/aeso_compiler_tests.erl index eb890b2..192da86 100644 --- a/test/aeso_compiler_tests.erl +++ b/test/aeso_compiler_tests.erl @@ -848,6 +848,12 @@ failing_contracts() -> [<> ]) + , ?TYPE_ERROR(contract_polymorphism_same_name_different_type_multi_interface, + [<> + ]) , ?TYPE_ERROR(contract_interface_polymorphism_undefined_interface, [<> diff --git a/test/contracts/contract_polymorphism_same_name_different_type_multi_interface.aes b/test/contracts/contract_polymorphism_same_name_different_type_multi_interface.aes new file mode 100644 index 0000000..2d6098d --- /dev/null +++ b/test/contracts/contract_polymorphism_same_name_different_type_multi_interface.aes @@ -0,0 +1,9 @@ +contract interface I = + entrypoint f : () => int + +contract interface J = + entrypoint f : () => char + +contract C : I, J = + entrypoint f() = 1 + entrypoint f() = 'c' -- 2.30.2 From 88666a36d2a6b81d6d99757f8b702f804f3eb16c Mon Sep 17 00:00:00 2001 From: Gaith Hallak Date: Sat, 18 Dec 2021 19:20:45 +0200 Subject: [PATCH 09/53] Add tests for interfaces implementing interfaces --- test/aeso_compiler_tests.erl | 10 ++++++++++ ...interface_polymorphism_same_name_different_type.aes | 9 +++++++++ ...ract_interface_polymorphism_same_name_same_type.aes | 8 ++++++++ 3 files changed, 27 insertions(+) create mode 100644 test/contracts/contract_interface_polymorphism_same_name_different_type.aes create mode 100644 test/contracts/contract_interface_polymorphism_same_name_same_type.aes diff --git a/test/aeso_compiler_tests.erl b/test/aeso_compiler_tests.erl index 192da86..7e8f2f0 100644 --- a/test/aeso_compiler_tests.erl +++ b/test/aeso_compiler_tests.erl @@ -836,6 +836,16 @@ failing_contracts() -> [<> ]) + , ?TYPE_ERROR(contract_interface_polymorphism_same_name_same_type, + [<> + ]) + , ?TYPE_ERROR(contract_interface_polymorphism_same_name_different_type, + [<> + ]) , ?TYPE_ERROR(contract_polymorphism_missing_implementation, [<> diff --git a/test/contracts/contract_interface_polymorphism_same_name_different_type.aes b/test/contracts/contract_interface_polymorphism_same_name_different_type.aes new file mode 100644 index 0000000..d324c11 --- /dev/null +++ b/test/contracts/contract_interface_polymorphism_same_name_different_type.aes @@ -0,0 +1,9 @@ +contract interface I1 = + entrypoint f : () => int + +contract interface I2 : I1 = + entrypoint f : () => char + +contract C : I2 = + entrypoint f() = 1 + entrypoint f() = 'c' diff --git a/test/contracts/contract_interface_polymorphism_same_name_same_type.aes b/test/contracts/contract_interface_polymorphism_same_name_same_type.aes new file mode 100644 index 0000000..20ab99f --- /dev/null +++ b/test/contracts/contract_interface_polymorphism_same_name_same_type.aes @@ -0,0 +1,8 @@ +contract interface I1 = + entrypoint f : () => int + +contract interface I2 : I1 = + entrypoint f : () => int + +contract C : I2 = + entrypoint f() = 1 -- 2.30.2 From b1b7f6cf6d6af0808d3de42868347ea68c1c52da Mon Sep 17 00:00:00 2001 From: Gaith Hallak Date: Thu, 23 Dec 2021 18:00:33 +0200 Subject: [PATCH 10/53] Draft: Add variance switching --- src/aeso_ast_infer_types.erl | 25 +++++++++++++++++++++++-- src/aeso_pretty.erl | 2 ++ 2 files changed, 25 insertions(+), 2 deletions(-) diff --git a/src/aeso_ast_infer_types.erl b/src/aeso_ast_infer_types.erl index f034021..519952f 100644 --- a/src/aeso_ast_infer_types.erl +++ b/src/aeso_ast_infer_types.erl @@ -27,6 +27,7 @@ | aeso_syntax:con() | aeso_syntax:qcon() %% contracts | aeso_syntax:tvar() | {if_t, aeso_syntax:ann(), aeso_syntax:id(), utype(), utype()} %% Can branch on named argument (protected) + | {sub_t, aeso_syntax:ann(), utype()} | uvar(). -type uvar() :: {uvar, aeso_syntax:ann(), reference()}. @@ -911,7 +912,6 @@ match_impls([{fun_decl, _, {id, _, FunName}, {fun_t, _, _, ArgsTypes, RetDecl}} end, match_impls(Decls, ConId, IName, UnmatchedImpls). - -spec compare_types(T, T) -> boolean() when T :: utype() | [utype()]. compare_types(Types1 = [_ | _], Types2 = [_ | _]) -> length(Types1) == length(Types2) andalso @@ -1714,7 +1714,7 @@ infer_expr(Env, {app, Ann, Fun, Args0} = App) -> ResultType = fresh_uvar(Ann), when_warning(warn_unused_functions, fun() -> register_function_call(Namespace ++ qname(CurrentFun), Name) end), - unify(Env, FunType, {fun_t, [], NamedArgsVar, ArgTypes, GeneralResultType}, When), + unify(Env, {sub_t, aeso_syntax:get_ann(FunType), FunType}, {fun_t, [], NamedArgsVar, ArgTypes, GeneralResultType}, When), when_warning(warn_negative_spend, fun() -> warn_potential_negative_spend(Ann, NewFun1, NewArgs) end), add_constraint( #dependent_type_constraint{ named_args_t = NamedArgsVar, @@ -2452,6 +2452,8 @@ solve_known_record_types(Env, Constraints) -> Unsolved = DerefConstraints--SolvedConstraints, lists:filter(fun(#field_constraint{}) -> true; (_) -> false end, Unsolved). +record_type_name({sub_t, _, T}) -> + record_type_name(T); record_type_name({app_t, _Attrs, RecId, _Args}) when ?is_type_id(RecId) -> RecId; record_type_name(RecId) when ?is_type_id(RecId) -> @@ -2588,6 +2590,8 @@ unfold_types_in_type(Env, T, Options) when is_tuple(T) -> list_to_tuple(unfold_types_in_type(Env, tuple_to_list(T), Options)); unfold_types_in_type(Env, [H|T], Options) -> [unfold_types_in_type(Env, H, Options)|unfold_types_in_type(Env, T, Options)]; +unfold_types_in_type(Env, {sub_t, Ann, T}, Options) -> + {sub_t, Ann, unfold_types_in_type(Env, T, Options)}; unfold_types_in_type(_Env, X, _Options) -> X. @@ -2639,6 +2643,20 @@ unify1(_Env, {uvar, A, R}, T, When) -> unify1(Env, T, {uvar, A, R}, When) -> unify1(Env, {uvar, A, R}, T, When); unify1(_Env, {tvar, _, X}, {tvar, _, X}, _When) -> true; %% Rigid type variables +unify1(Env, {sub_t, _, T1}, {sub_t, _, T2}, When) -> + % TODO: should be an error + unify(Env, T1, T2, When); +unify1(Env, {sub_t, _, T1}, T2, When) -> + case {T1, T2} of + {{con, _, "Cat"}, {con, _, "Animal"}} -> true; + {{fun_t, A1, NAs1, As1, Tf1}, {fun_t, A2, NAs2, As2, Tf2}} -> + T1Sub = {fun_t, A1, NAs1, As1, {sub_t, aeso_syntax:get_ann(Tf1), Tf1}}, + T2Sub = {fun_t, A2, NAs2, [{sub_t, aeso_syntax:get_ann(Arg), Arg} || Arg <- As2], Tf2}, + unify(Env, T1Sub, T2Sub, When); + _ -> unify(Env, T1, T2, When) + end; +unify1(Env, T1, T2 = {sub_t, _, _}, When) -> + unify1(Env, T2, T1, When); unify1(Env, [A|B], [C|D], When) -> unify(Env, A, C, When) andalso unify(Env, B, D, When); unify1(_Env, X, X, _When) -> @@ -2685,6 +2703,8 @@ unify1(_Env, A, B, When) -> cannot_unify(A, B, When), false. +dereference({sub_t, Ann, T}) -> + {sub_t, Ann, dereference(T)}; dereference(T = {uvar, _, R}) -> case ets_lookup(type_vars, R) of [] -> @@ -2707,6 +2727,7 @@ occurs_check(R, T) -> occurs_check1(R, dereference(T)). occurs_check1(R, {uvar, _, R1}) -> R == R1; +occurs_check1(R, {sub_t, _, T}) -> occurs_check1(R, T); occurs_check1(_, {id, _, _}) -> false; occurs_check1(_, {con, _, _}) -> false; occurs_check1(_, {qid, _, _}) -> false; diff --git a/src/aeso_pretty.erl b/src/aeso_pretty.erl index d639ce9..85ebd09 100644 --- a/src/aeso_pretty.erl +++ b/src/aeso_pretty.erl @@ -278,6 +278,8 @@ type({bytes_t, _, Len}) -> text(lists:concat(["bytes(", Len, ")"])); type({if_t, _, Id, Then, Else}) -> beside(text("if"), args_type([Id, Then, Else])); +type({sub_t, _, T}) -> + text("sub_t"); type({named_arg_t, _, Name, Type, _Default}) -> %% Drop the default value %% follow(hsep(typed(name(Name), Type), text("=")), expr(Default)); -- 2.30.2 From a068822278e77a8f288296be23b07361d54c2a3f Mon Sep 17 00:00:00 2001 From: Gaith Hallak Date: Mon, 27 Dec 2021 11:54:47 +0200 Subject: [PATCH 11/53] Revert "Draft: Add variance switching" This reverts commit 92dc6ac169cfbff447ed59de04994f564876b3fb. --- src/aeso_ast_infer_types.erl | 25 ++----------------------- src/aeso_pretty.erl | 2 -- 2 files changed, 2 insertions(+), 25 deletions(-) diff --git a/src/aeso_ast_infer_types.erl b/src/aeso_ast_infer_types.erl index 519952f..f034021 100644 --- a/src/aeso_ast_infer_types.erl +++ b/src/aeso_ast_infer_types.erl @@ -27,7 +27,6 @@ | aeso_syntax:con() | aeso_syntax:qcon() %% contracts | aeso_syntax:tvar() | {if_t, aeso_syntax:ann(), aeso_syntax:id(), utype(), utype()} %% Can branch on named argument (protected) - | {sub_t, aeso_syntax:ann(), utype()} | uvar(). -type uvar() :: {uvar, aeso_syntax:ann(), reference()}. @@ -912,6 +911,7 @@ match_impls([{fun_decl, _, {id, _, FunName}, {fun_t, _, _, ArgsTypes, RetDecl}} end, match_impls(Decls, ConId, IName, UnmatchedImpls). + -spec compare_types(T, T) -> boolean() when T :: utype() | [utype()]. compare_types(Types1 = [_ | _], Types2 = [_ | _]) -> length(Types1) == length(Types2) andalso @@ -1714,7 +1714,7 @@ infer_expr(Env, {app, Ann, Fun, Args0} = App) -> ResultType = fresh_uvar(Ann), when_warning(warn_unused_functions, fun() -> register_function_call(Namespace ++ qname(CurrentFun), Name) end), - unify(Env, {sub_t, aeso_syntax:get_ann(FunType), FunType}, {fun_t, [], NamedArgsVar, ArgTypes, GeneralResultType}, When), + unify(Env, FunType, {fun_t, [], NamedArgsVar, ArgTypes, GeneralResultType}, When), when_warning(warn_negative_spend, fun() -> warn_potential_negative_spend(Ann, NewFun1, NewArgs) end), add_constraint( #dependent_type_constraint{ named_args_t = NamedArgsVar, @@ -2452,8 +2452,6 @@ solve_known_record_types(Env, Constraints) -> Unsolved = DerefConstraints--SolvedConstraints, lists:filter(fun(#field_constraint{}) -> true; (_) -> false end, Unsolved). -record_type_name({sub_t, _, T}) -> - record_type_name(T); record_type_name({app_t, _Attrs, RecId, _Args}) when ?is_type_id(RecId) -> RecId; record_type_name(RecId) when ?is_type_id(RecId) -> @@ -2590,8 +2588,6 @@ unfold_types_in_type(Env, T, Options) when is_tuple(T) -> list_to_tuple(unfold_types_in_type(Env, tuple_to_list(T), Options)); unfold_types_in_type(Env, [H|T], Options) -> [unfold_types_in_type(Env, H, Options)|unfold_types_in_type(Env, T, Options)]; -unfold_types_in_type(Env, {sub_t, Ann, T}, Options) -> - {sub_t, Ann, unfold_types_in_type(Env, T, Options)}; unfold_types_in_type(_Env, X, _Options) -> X. @@ -2643,20 +2639,6 @@ unify1(_Env, {uvar, A, R}, T, When) -> unify1(Env, T, {uvar, A, R}, When) -> unify1(Env, {uvar, A, R}, T, When); unify1(_Env, {tvar, _, X}, {tvar, _, X}, _When) -> true; %% Rigid type variables -unify1(Env, {sub_t, _, T1}, {sub_t, _, T2}, When) -> - % TODO: should be an error - unify(Env, T1, T2, When); -unify1(Env, {sub_t, _, T1}, T2, When) -> - case {T1, T2} of - {{con, _, "Cat"}, {con, _, "Animal"}} -> true; - {{fun_t, A1, NAs1, As1, Tf1}, {fun_t, A2, NAs2, As2, Tf2}} -> - T1Sub = {fun_t, A1, NAs1, As1, {sub_t, aeso_syntax:get_ann(Tf1), Tf1}}, - T2Sub = {fun_t, A2, NAs2, [{sub_t, aeso_syntax:get_ann(Arg), Arg} || Arg <- As2], Tf2}, - unify(Env, T1Sub, T2Sub, When); - _ -> unify(Env, T1, T2, When) - end; -unify1(Env, T1, T2 = {sub_t, _, _}, When) -> - unify1(Env, T2, T1, When); unify1(Env, [A|B], [C|D], When) -> unify(Env, A, C, When) andalso unify(Env, B, D, When); unify1(_Env, X, X, _When) -> @@ -2703,8 +2685,6 @@ unify1(_Env, A, B, When) -> cannot_unify(A, B, When), false. -dereference({sub_t, Ann, T}) -> - {sub_t, Ann, dereference(T)}; dereference(T = {uvar, _, R}) -> case ets_lookup(type_vars, R) of [] -> @@ -2727,7 +2707,6 @@ occurs_check(R, T) -> occurs_check1(R, dereference(T)). occurs_check1(R, {uvar, _, R1}) -> R == R1; -occurs_check1(R, {sub_t, _, T}) -> occurs_check1(R, T); occurs_check1(_, {id, _, _}) -> false; occurs_check1(_, {con, _, _}) -> false; occurs_check1(_, {qid, _, _}) -> false; diff --git a/src/aeso_pretty.erl b/src/aeso_pretty.erl index 85ebd09..d639ce9 100644 --- a/src/aeso_pretty.erl +++ b/src/aeso_pretty.erl @@ -278,8 +278,6 @@ type({bytes_t, _, Len}) -> text(lists:concat(["bytes(", Len, ")"])); type({if_t, _, Id, Then, Else}) -> beside(text("if"), args_type([Id, Then, Else])); -type({sub_t, _, T}) -> - text("sub_t"); type({named_arg_t, _, Name, Type, _Default}) -> %% Drop the default value %% follow(hsep(typed(name(Name), Type), text("=")), expr(Default)); -- 2.30.2 From 01fb058defdf51c1e78b69dd2d66f55db097c052 Mon Sep 17 00:00:00 2001 From: Gaith Hallak Date: Wed, 29 Dec 2021 13:34:48 +0200 Subject: [PATCH 12/53] Add variance switching --- src/aeso_ast_infer_types.erl | 31 +++++++++++++++++++++++++++---- 1 file changed, 27 insertions(+), 4 deletions(-) diff --git a/src/aeso_ast_infer_types.erl b/src/aeso_ast_infer_types.erl index f034021..26d8054 100644 --- a/src/aeso_ast_infer_types.erl +++ b/src/aeso_ast_infer_types.erl @@ -134,6 +134,7 @@ , vars = [] :: [{name(), var_info()}] , typevars = unrestricted :: unrestricted | [name()] , fields = #{} :: #{ name() => [field_info()] } %% fields are global + , contract_parents = #{} :: #{ name() => [name()] } , namespace = [] :: qname() , used_namespaces = [] :: used_namespaces() , in_pattern = false :: boolean() @@ -832,9 +833,12 @@ infer(Contracts, Options) -> -spec infer1(env(), [aeso_syntax:decl()], [aeso_syntax:decl()], list(option())) -> {env(), [aeso_syntax:decl()]}. infer1(Env, [], Acc, _Options) -> {Env, lists:reverse(Acc)}; -infer1(Env, [{Contract, Ann, ConName, Impls, Code} | Rest], Acc, Options) +infer1(Env0, [{Contract, Ann, ConName, Impls, Code} | Rest], Acc, Options) when ?IS_CONTRACT_HEAD(Contract) -> %% do type inference on each contract independently. + Env = Env0#env{ contract_parents = maps:put(name(ConName), + [name(Impl) || Impl <- Impls], + Env0#env.contract_parents) }, check_scope_name_clash(Env, contract, ConName), What = case Contract of contract_main -> contract; @@ -1965,7 +1969,7 @@ infer_case(Env = #env{ namespace = NS, current_function = {id, _, Fun} }, Attrs, {guarded, Ann, NewGuards, NewBranch} end, NewGuardedBranches = lists:map(InferGuardedBranches, GuardedBranches), - unify(Env, PatType, ExprType, {case_pat, Pattern, PatType, ExprType}), + unify(Env, ExprType, PatType, {case_pat, Pattern, PatType, ExprType}), {'case', Attrs, NewPattern, NewGuardedBranches}. %% NewStmts = infer_block(Env, Attrs, Stmts, BlockType) @@ -2647,6 +2651,13 @@ unify1(_Env, {id, _, Name}, {id, _, Name}, _When) -> true; unify1(_Env, {con, _, Name}, {con, _, Name}, _When) -> true; +unify1(Env, A = {con, _, Child}, B = {con, _, Base}, When) -> + case is_subtype(Env, Child, Base) of + true -> true; + false -> + cannot_unify(A, B, When), + false + end; unify1(_Env, {qid, _, Name}, {qid, _, Name}, _When) -> true; unify1(_Env, {qcon, _, Name}, {qcon, _, Name}, _When) -> @@ -2663,8 +2674,8 @@ unify1(_Env, {fun_t, _, _, var_args, _}, {fun_t, _, _, _, _}, When) -> type_error({unify_varargs, When}); unify1(Env, {fun_t, _, Named1, Args1, Result1}, {fun_t, _, Named2, Args2, Result2}, When) when length(Args1) == length(Args2) -> - unify(Env, Named1, Named2, When) andalso - unify(Env, Args1, Args2, When) andalso unify(Env, Result1, Result2, When); + unify(Env, Named2, Named1, When) andalso + unify(Env, Args2, Args1, When) andalso unify(Env, Result1, Result2, When); unify1(Env, {app_t, _, {Tag, _, F}, Args1}, {app_t, _, {Tag, _, F}, Args2}, When) when length(Args1) == length(Args2), Tag == id orelse Tag == qid -> unify(Env, Args1, Args2, When); @@ -2685,6 +2696,18 @@ unify1(_Env, A, B, When) -> cannot_unify(A, B, When), false. +is_subtype(Env, Child, Base) -> + Parents = maps:get(Child, Env#env.contract_parents, []), + if + Parents == [] -> + false; + true -> + case lists:member(Base, Parents) of + true -> true; + false -> lists:any(fun(Parent) -> is_subtype(Env, Parent, Base) end, Parents) + end + end. + dereference(T = {uvar, _, R}) -> case ets_lookup(type_vars, R) of [] -> -- 2.30.2 From 77f163146720791e96bb567d7899c96e90bc565b Mon Sep 17 00:00:00 2001 From: Gaith Hallak Date: Wed, 29 Dec 2021 14:50:14 +0200 Subject: [PATCH 13/53] Fix broken tests --- test/aeso_compiler_tests.erl | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/test/aeso_compiler_tests.erl b/test/aeso_compiler_tests.erl index 7e8f2f0..0777f4a 100644 --- a/test/aeso_compiler_tests.erl +++ b/test/aeso_compiler_tests.erl @@ -591,14 +591,14 @@ failing_contracts() -> , ?TYPE_ERROR(bad_bytes_concat, [<>, <>, < <>, < , ?TYPE_ERROR(bad_bytes_split, [<>, @@ -628,7 +628,7 @@ failing_contracts() -> " - 'd (at line 16, column 5)">>, <>]) -- 2.30.2 From 4348affa3ead772a4bde4bc1dd07a883c08ea679 Mon Sep 17 00:00:00 2001 From: Gaith Hallak Date: Wed, 29 Dec 2021 14:58:08 +0200 Subject: [PATCH 14/53] Fix broken abi tests --- test/aeso_abi_tests.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/aeso_abi_tests.erl b/test/aeso_abi_tests.erl index 2a8faba..98a45d4 100644 --- a/test/aeso_abi_tests.erl +++ b/test/aeso_abi_tests.erl @@ -92,7 +92,7 @@ encode_calldata_neg_test() -> Code = [ "contract Foo =\n" " entrypoint f(x : int) : string = \"hello\"\n" ], - ExpErr1 = "Type error at line 5, col 34:\nCannot unify `int` and `bool`\n" + ExpErr1 = "Type error at line 5, col 34:\nCannot unify `bool` and `int`\n" "when checking the application of\n" " `f : (int) => string`\n" "to arguments\n" -- 2.30.2 From e5e7958c3f83259e3f38762e9baf7ef87a344b84 Mon Sep 17 00:00:00 2001 From: Gaith Hallak Date: Wed, 29 Dec 2021 15:24:52 +0200 Subject: [PATCH 15/53] Add tests for variance switching --- test/aeso_compiler_tests.erl | 14 ++++++ .../polymorphism_variance_switching.aes | 50 +++++++++++++++++++ 2 files changed, 64 insertions(+) create mode 100644 test/contracts/polymorphism_variance_switching.aes diff --git a/test/aeso_compiler_tests.erl b/test/aeso_compiler_tests.erl index 0777f4a..8a133a2 100644 --- a/test/aeso_compiler_tests.erl +++ b/test/aeso_compiler_tests.erl @@ -868,6 +868,20 @@ failing_contracts() -> [<> ]) + , ?TYPE_ERROR(polymorphism_variance_switching, + [< Cat\nto arguments\n x : Animal">>, + <>, + < Animal) => Cat\nto arguments\n x : (Cat) => Cat">> + ]) ]. -define(Path(File), "code_errors/" ??File). diff --git a/test/contracts/polymorphism_variance_switching.aes b/test/contracts/polymorphism_variance_switching.aes new file mode 100644 index 0000000..434ff9f --- /dev/null +++ b/test/contracts/polymorphism_variance_switching.aes @@ -0,0 +1,50 @@ +contract interface Creature = + entrypoint is_alive : () => bool + +contract interface Animal : Creature = + entrypoint sound : () => string + +contract Cat : Animal = + entrypoint sound() = "meow" + entrypoint is_alive() = true + +main contract Main = + entrypoint init() = () + + stateful function g0(x : Creature) : Cat = Chain.create() + stateful function f0(x : Cat) : Creature = g1(x) + stateful function h0() = + let a : Animal = (Chain.create() : Cat) + let c : Creature = (Chain.create() : Cat) + let c1 : Creature = a + () + + stateful function g1(x : Animal) : Cat = Chain.create() + stateful function f1(x : Cat) : Animal = g1(x) + stateful function h1() = + let x : Animal = (Chain.create() : Cat) + () + + stateful function g11(x : list(Animal)) : list(Cat) = [Chain.create()] + stateful function f11(x : list(Cat)) : list(Animal) = g11(x) + + stateful function g12(x : Animal * Animal) : Cat * Cat = (Chain.create(), Chain.create()) + stateful function f12(x : Cat * Cat) : Animal * Animal = g12(x) + + stateful function g13() : map(Cat, Cat) = { [Chain.create()] = Chain.create() } + stateful function f13() : map(Animal, Animal) = g13() + + stateful function g2(x : Cat) : Cat = Chain.create() + stateful function f2(x : Animal) : Animal = g2(x) + + stateful function g3(x : Cat) : Animal = f1(x) + stateful function f3(x : Cat) : Cat = g3(x) + + stateful function g4(x : (Cat => Animal)) : Cat = Chain.create() + stateful function f4(x : (Animal => Cat)) : Animal = g4(x) + + stateful function g44(x : list(list(Cat) => list(Animal))) : Cat = Chain.create() + stateful function f44(x : list(list(Animal) => list(Cat))) : Animal = g44(x) + + stateful function g5(x : (Animal => Animal)) : Cat = Chain.create() + stateful function f5(x : (Cat => Cat)) : Animal = g5(x) -- 2.30.2 From 3a00476f88fcfc51f5e0235440cec5674e13ddbf Mon Sep 17 00:00:00 2001 From: Gaith Hallak Date: Thu, 10 Feb 2022 01:52:36 +0400 Subject: [PATCH 16/53] Fix tests after rebase --- src/aeso_ast_infer_types.erl | 4 ++-- test/aeso_compiler_tests.erl | 41 +++++++++++++++++++----------------- 2 files changed, 24 insertions(+), 21 deletions(-) diff --git a/src/aeso_ast_infer_types.erl b/src/aeso_ast_infer_types.erl index 26d8054..6ea8dd2 100644 --- a/src/aeso_ast_infer_types.erl +++ b/src/aeso_ast_infer_types.erl @@ -3381,10 +3381,10 @@ mk_error({empty_record_definition, Ann, Name}) -> Msg = io_lib:format("Empty record definitions are not allowed. Cannot define the record `~s`", [Name]), mk_t_err(pos(Ann), Msg); mk_error({unimplemented_interface_function, ConId, InterfaceName, FunName}) -> - Msg = io_lib:format("Unimplemented function ~s from the interface ~s in the contract ~s", [FunName, InterfaceName, pp(ConId)]), + Msg = io_lib:format("Unimplemented function `~s` from the interface `~s` in the contract `~s`", [FunName, InterfaceName, pp(ConId)]), mk_t_err(pos(ConId), Msg); mk_error({referencing_undefined_interface, InterfaceId}) -> - Msg = io_lib:format("Trying to implement or extend an undefined interface ~s", [pp(InterfaceId)]), + Msg = io_lib:format("Trying to implement or extend an undefined interface `~s`", [pp(InterfaceId)]), mk_t_err(pos(InterfaceId), Msg); mk_error(Err) -> Msg = io_lib:format("Unknown error: ~p", [Err]), diff --git a/test/aeso_compiler_tests.erl b/test/aeso_compiler_tests.erl index 8a133a2..e131e95 100644 --- a/test/aeso_compiler_tests.erl +++ b/test/aeso_compiler_tests.erl @@ -565,7 +565,7 @@ failing_contracts() -> ]) , ?TYPE_ERROR(list_comp_bad_shadow, [<> ]) , ?TYPE_ERROR(map_as_map_key, @@ -830,57 +830,60 @@ failing_contracts() -> ]) , ?TYPE_ERROR(contract_interface_polymorphism_recursive, [<> + "Trying to implement or extend an undefined interface `Z`">> ]) , ?TYPE_ERROR(contract_interface_polymorphism_same_decl_multi_interface, [<> + "Unimplemented function `f` from the interface `I` in the contract `C`">> ]) , ?TYPE_ERROR(contract_interface_polymorphism_same_name_same_type, [<> + "Unimplemented function `f` from the interface `I1` in the contract `C`">> ]) , ?TYPE_ERROR(contract_interface_polymorphism_same_name_different_type, [<> ]) , ?TYPE_ERROR(contract_polymorphism_missing_implementation, [<> + "Unimplemented function `f` from the interface `I1` in the contract `C`">> ]) , ?TYPE_ERROR(contract_polymorphism_same_decl_multi_interface, [<> + "Unimplemented function `f` from the interface `J` in the contract `C`">> ]) , ?TYPE_ERROR(contract_polymorphism_undefined_interface, [<> + "Trying to implement or extend an undefined interface `I`">> ]) , ?TYPE_ERROR(contract_polymorphism_same_name_different_type_multi_interface, [<> ]) , ?TYPE_ERROR(contract_interface_polymorphism_undefined_interface, [<> + "Trying to implement or extend an undefined interface `H`">> ]) , ?TYPE_ERROR(polymorphism_variance_switching, [< Cat\nto arguments\n x : Animal">>, + "Cannot unify `Animal` and `Cat`\n" + "when checking the application of\n" + " `g2 : (Cat) => Cat`\n" + "to arguments\n" + " `x : Animal`">>, <>, + "Cannot unify `Animal` and `Cat`\n" + "when checking the type of the expression `g3(x) : Animal` against the expected type `Cat`">>, < Animal) => Cat\nto arguments\n x : (Cat) => Cat">> + "Cannot unify `Animal` and `Cat`\n" + "when checking the application of\n" + " `g5 : ((Animal) => Animal) => Cat`\n" + "to arguments\n" + " `x : (Cat) => Cat`">> ]) ]. -- 2.30.2 From 44bd16264ae48c8b304766f4187a73f8d6852f6d Mon Sep 17 00:00:00 2001 From: Gaith Hallak Date: Fri, 11 Feb 2022 02:12:44 +0400 Subject: [PATCH 17/53] Variance switching for custom datatypes --- src/aeso_ast_infer_types.erl | 169 ++++++++++++++++++++++++++--------- test/aeso_abi_tests.erl | 2 +- test/aeso_compiler_tests.erl | 16 ++-- 3 files changed, 135 insertions(+), 52 deletions(-) diff --git a/src/aeso_ast_infer_types.erl b/src/aeso_ast_infer_types.erl index 6ea8dd2..f6cabb2 100644 --- a/src/aeso_ast_infer_types.erl +++ b/src/aeso_ast_infer_types.erl @@ -113,6 +113,8 @@ -type type_constraints() :: none | bytes_concat | bytes_split | address_to_contract | bytecode_hash. +-type variance() :: invariant | covariant | contravariant | bivariant. + -type fun_info() :: {aeso_syntax:ann(), typesig() | type()}. -type type_info() :: {aeso_syntax:ann(), typedef()}. -type var_info() :: {aeso_syntax:ann(), utype()}. @@ -806,6 +808,7 @@ infer(Contracts, Options) -> create_options(Options), ets_new(defined_contracts, [bag]), ets_new(type_vars, [set]), + ets_new(type_vars_variance, [set]), ets_new(warnings, [bag]), when_warning(warn_unused_functions, fun() -> create_unused_functions() end), check_modifiers(Env, Contracts), @@ -1103,6 +1106,8 @@ check_typedef_sccs(Env, TypeMap, [{acyclic, Name} | SCCs], Acc) -> Env2 = check_fields(Env1, TypeMap, RecTy, Fields), check_typedef_sccs(Env2, TypeMap, SCCs, Acc1); {variant_t, Cons} -> + ets_insert(type_vars_variance, {Env#env.namespace ++ qname(D), + infer_type_vars_variance(Xs, Cons)}), Target = check_type(Env1, app_t(Ann, D, Xs)), ConType = fun([]) -> Target; (Args) -> {type_sig, Ann, none, [], Args, Target} end, ConTypes = [ begin @@ -1128,6 +1133,61 @@ check_typedef(Env, {variant_t, Cons}) -> {variant_t, [ {constr_t, Ann, Con, [ check_type(Env, Arg) || Arg <- Args ]} || {constr_t, Ann, Con, Args} <- Cons ]}. +-spec infer_type_vars_variance(aeso_syntax:tvar(), [{constr_t, _, _, _}]) -> [variance()]. +infer_type_vars_variance(TypeParams, Cons) -> + % args from all type constructors + FlatArgs = lists:flatten([Args || {constr_t, _, _, Args} <- Cons]), + + Vs = lists:flatten([infer_type_vars_variance(Arg) || Arg <- FlatArgs]), + lists:map(fun({tvar, _, TVar}) -> + S = sets:from_list([Variance || {TV, Variance} <- Vs, TV == TVar]), + IsInvariant = sets:is_element(invariant, S), + IsCovariant = sets:is_element(covariant, S), + IsContravariant = sets:is_element(contravariant, S), + IsBivariant = sets:is_element(bivariant, S), + case {IsInvariant, IsCovariant, IsContravariant, IsBivariant} of + {true, _, _, _} -> invariant; + {false, true, true, _} -> invariant; + {false, _, _, true} -> bivariant; + {false, true, false, _} -> covariant; + {false, false, true, _} -> contravariant; + _ -> invariant + end + end, TypeParams). + +-spec infer_type_vars_variance(utype()) -> [{name(), variance()}]. +infer_type_vars_variance(FT = {fun_t, _, [], [{app_t, _, Type, Args}], Res}) -> + Variances = case ets_lookup(type_vars_variance, qname(Type)) of + [{_, Vs}] -> Vs; + _ -> lists:duplicate(length(Args), covariant) + end, + TypeVarsVariance = [{TVar, Variance} + || {{tvar, _, TVar}, Variance} <- lists:zip(Args, Variances)], + FlipVariance = fun({TVar, covariant}) -> {TVar, contravariant}; + ({TVar, contravariant}) -> {TVar, covariant} + end, + Cur = case arrows_in_type(FT) rem 2 of + 0 -> TypeVarsVariance; + 1 -> lists:map(FlipVariance, TypeVarsVariance) + end, + Cur ++ infer_type_vars_variance(Res); +infer_type_vars_variance(FT = {fun_t, _, [], [{tvar, _, TVar}], Res}) -> + Cur = case arrows_in_type(FT) rem 2 of + 0 -> {TVar, covariant}; + 1 -> {TVar, contravariant} + end, + [Cur | infer_type_vars_variance(Res)]; +infer_type_vars_variance({fun_t, _, [], [_Arg], Res}) -> + infer_type_vars_variance(Res); +infer_type_vars_variance({tvar, _, TVar}) -> + [{TVar, covariant}]; +infer_type_vars_variance(_) -> []. + +arrows_in_type({fun_t, _, [], [_Arg], FRes}) -> + 1 + arrows_in_type(FRes); +arrows_in_type(_) -> + 0. + check_usings(Env, []) -> Env; check_usings(Env = #env{ used_namespaces = UsedNamespaces }, [{using, Ann, Con, Alias, Parts} | Rest]) -> @@ -2077,7 +2137,8 @@ next_count() -> ets_tables() -> [options, type_vars, constraints, freshen_tvars, type_errors, - defined_contracts, warnings, function_calls, all_functions]. + defined_contracts, warnings, function_calls, all_functions, + type_vars_variance]. clean_up_ets() -> [ catch ets_delete(Tab) || Tab <- ets_tables() ], @@ -2617,9 +2678,11 @@ subst_tvars1(_Env, X) -> %% Unification -unify(_, {id, _, "_"}, _, _When) -> true; -unify(_, _, {id, _, "_"}, _When) -> true; -unify(Env, A, B, When) -> +unify(Env, A, B, When) -> unify0(Env, A, B, covariant, When). + +unify0(_, {id, _, "_"}, _, _Variance, _When) -> true; +unify0(_, _, {id, _, "_"}, _Variance, _When) -> true; +unify0(Env, A, B, Variance, When) -> Options = case When of %% Improve source location for map_in_map_key errors {check_expr, E, _, _} -> [{ann, aeso_syntax:get_ann(E)}]; @@ -2627,11 +2690,11 @@ unify(Env, A, B, When) -> end, A1 = dereference(unfold_types_in_type(Env, A, Options)), B1 = dereference(unfold_types_in_type(Env, B, Options)), - unify1(Env, A1, B1, When). + unify1(Env, A1, B1, Variance, When). -unify1(_Env, {uvar, _, R}, {uvar, _, R}, _When) -> +unify1(_Env, {uvar, _, R}, {uvar, _, R}, _Variance, _When) -> true; -unify1(_Env, {uvar, A, R}, T, When) -> +unify1(_Env, {uvar, A, R}, T, _Variance, When) -> case occurs_check(R, T) of true -> cannot_unify({uvar, A, R}, T, When), @@ -2640,65 +2703,85 @@ unify1(_Env, {uvar, A, R}, T, When) -> ets_insert(type_vars, {R, T}), true end; -unify1(Env, T, {uvar, A, R}, When) -> - unify1(Env, {uvar, A, R}, T, When); -unify1(_Env, {tvar, _, X}, {tvar, _, X}, _When) -> true; %% Rigid type variables -unify1(Env, [A|B], [C|D], When) -> - unify(Env, A, C, When) andalso unify(Env, B, D, When); -unify1(_Env, X, X, _When) -> +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); +unify1(Env, [A|B], [C|D], Variance, When) -> + unify0(Env, A, C, Variance, When) andalso unify0(Env, B, D, Variance, When); +unify1(_Env, X, X, _Variance, _When) -> true; -unify1(_Env, {id, _, Name}, {id, _, Name}, _When) -> +unify1(_Env, {id, _, Name}, {id, _, Name}, _Variance, _When) -> true; -unify1(_Env, {con, _, Name}, {con, _, Name}, _When) -> - true; -unify1(Env, A = {con, _, Child}, B = {con, _, Base}, When) -> - case is_subtype(Env, Child, Base) of - true -> true; - false -> +unify1(Env, A = {con, _, NameA}, B = {con, _, NameB}, Variance, When) -> + IsSubtype = case Variance of + invariant -> NameA == NameB; + covariant -> is_subtype(Env, NameA, NameB); + contravariant -> is_subtype(Env, NameB, NameA); + bivariant -> is_subtype(Env, NameA, NameB) + end, + if + IsSubtype -> + true; + true -> cannot_unify(A, B, When), false end; -unify1(_Env, {qid, _, Name}, {qid, _, Name}, _When) -> +unify1(_Env, {qid, _, Name}, {qid, _, Name}, _Variance, _When) -> true; -unify1(_Env, {qcon, _, Name}, {qcon, _, Name}, _When) -> +unify1(_Env, {qcon, _, Name}, {qcon, _, Name}, _Variance, _When) -> true; -unify1(_Env, {bytes_t, _, Len}, {bytes_t, _, Len}, _When) -> +unify1(_Env, {bytes_t, _, Len}, {bytes_t, _, Len}, _Variance, _When) -> true; -unify1(Env, {if_t, _, {id, _, Id}, Then1, Else1}, {if_t, _, {id, _, Id}, Then2, Else2}, When) -> - unify(Env, Then1, Then2, When) andalso - unify(Env, Else1, Else2, When); +unify1(Env, {if_t, _, {id, _, Id}, Then1, Else1}, {if_t, _, {id, _, Id}, Then2, Else2}, Variance, When) -> + unify0(Env, Then1, Then2, Variance, When) andalso + unify0(Env, Else1, Else2, Variance, When); -unify1(_Env, {fun_t, _, _, _, _}, {fun_t, _, _, var_args, _}, When) -> +unify1(_Env, {fun_t, _, _, _, _}, {fun_t, _, _, var_args, _}, _Variance, When) -> type_error({unify_varargs, When}); -unify1(_Env, {fun_t, _, _, var_args, _}, {fun_t, _, _, _, _}, When) -> +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}, When) +unify1(Env, {fun_t, _, Named1, Args1, Result1}, {fun_t, _, Named2, Args2, Result2}, Variance, When) when length(Args1) == length(Args2) -> - unify(Env, Named2, Named1, When) andalso - unify(Env, Args2, Args1, When) andalso unify(Env, Result1, Result2, When); -unify1(Env, {app_t, _, {Tag, _, F}, Args1}, {app_t, _, {Tag, _, F}, Args2}, When) + OppositeVariance = case Variance of + invariant -> invariant; + covariant -> contravariant; + contravariant -> covariant; + bivariant -> bivariant + end, + unify0(Env, Named1, Named2, OppositeVariance, When) andalso + unify0(Env, Args1, Args2, OppositeVariance, When) andalso + 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 -> - unify(Env, Args1, Args2, When); -unify1(Env, {tuple_t, _, As}, {tuple_t, _, Bs}, When) + Variances = case ets_lookup(type_vars_variance, F) of + [{_, Vs}] -> Vs; + _ -> Variance + end, + unify1(Env, Args1, Args2, Variances, When); +unify1(Env, {tuple_t, _, As}, {tuple_t, _, Bs}, Variance, When) when length(As) == length(Bs) -> - unify(Env, As, Bs, When); -unify1(Env, {named_arg_t, _, Id1, Type1, _}, {named_arg_t, _, Id2, Type2, _}, When) -> - unify1(Env, Id1, Id2, {arg_name, Id1, Id2, When}), - unify1(Env, Type1, Type2, When); + unify0(Env, As, Bs, 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}), + unify1(Env, Type1, Type2, Variance, When); %% The grammar is a bit inconsistent about whether types without %% arguments are represented as applications to an empty list of %% parameters or not. We therefore allow them to unify. -unify1(Env, {app_t, _, T, []}, B, When) -> - unify(Env, T, B, When); -unify1(Env, A, {app_t, _, T, []}, When) -> - unify(Env, A, T, When); -unify1(_Env, A, B, When) -> +unify1(Env, {app_t, _, T, []}, B, Variance, When) -> + unify0(Env, T, B, Variance, When); +unify1(Env, A, {app_t, _, T, []}, Variance, When) -> + unify0(Env, A, T, Variance, When); +unify1(_Env, A, B, _Variance, When) -> cannot_unify(A, B, When), false. is_subtype(Env, Child, Base) -> Parents = maps:get(Child, Env#env.contract_parents, []), if + Child == Base -> + true; Parents == [] -> false; true -> diff --git a/test/aeso_abi_tests.erl b/test/aeso_abi_tests.erl index 98a45d4..2a8faba 100644 --- a/test/aeso_abi_tests.erl +++ b/test/aeso_abi_tests.erl @@ -92,7 +92,7 @@ encode_calldata_neg_test() -> Code = [ "contract Foo =\n" " entrypoint f(x : int) : string = \"hello\"\n" ], - ExpErr1 = "Type error at line 5, col 34:\nCannot unify `bool` and `int`\n" + ExpErr1 = "Type error at line 5, col 34:\nCannot unify `int` and `bool`\n" "when checking the application of\n" " `f : (int) => string`\n" "to arguments\n" diff --git a/test/aeso_compiler_tests.erl b/test/aeso_compiler_tests.erl index e131e95..eb4e7e7 100644 --- a/test/aeso_compiler_tests.erl +++ b/test/aeso_compiler_tests.erl @@ -591,14 +591,14 @@ failing_contracts() -> , ?TYPE_ERROR(bad_bytes_concat, [<>, <>, < <>, < , ?TYPE_ERROR(bad_bytes_split, [<>, @@ -628,7 +628,7 @@ failing_contracts() -> " - 'd (at line 16, column 5)">>, <>]) @@ -870,7 +870,7 @@ failing_contracts() -> ]) , ?TYPE_ERROR(polymorphism_variance_switching, [< Cat`\n" "to arguments\n" -- 2.30.2 From dd259fbc964f494de9e4f1d9677e72192b0747c2 Mon Sep 17 00:00:00 2001 From: Gaith Hallak Date: Fri, 11 Feb 2022 02:27:18 +0400 Subject: [PATCH 18/53] Fix dialyzer warning --- src/aeso_ast_infer_types.erl | 1 - 1 file changed, 1 deletion(-) diff --git a/src/aeso_ast_infer_types.erl b/src/aeso_ast_infer_types.erl index f6cabb2..45950d1 100644 --- a/src/aeso_ast_infer_types.erl +++ b/src/aeso_ast_infer_types.erl @@ -1133,7 +1133,6 @@ check_typedef(Env, {variant_t, Cons}) -> {variant_t, [ {constr_t, Ann, Con, [ check_type(Env, Arg) || Arg <- Args ]} || {constr_t, Ann, Con, Args} <- Cons ]}. --spec infer_type_vars_variance(aeso_syntax:tvar(), [{constr_t, _, _, _}]) -> [variance()]. infer_type_vars_variance(TypeParams, Cons) -> % args from all type constructors FlatArgs = lists:flatten([Args || {constr_t, _, _, Args} <- Cons]), -- 2.30.2 From 918346445f833f6d30c8939f0be247b5ac6a414b Mon Sep 17 00:00:00 2001 From: Gaith Hallak Date: Wed, 23 Feb 2022 19:40:25 +0400 Subject: [PATCH 19/53] Add testing for custom types variance switching --- src/aeso_ast_infer_types.erl | 30 +++- test/aeso_compiler_tests.erl | 110 +++++++++++++++ ...rphism_variance_switching_custom_types.aes | 132 ++++++++++++++++++ 3 files changed, 271 insertions(+), 1 deletion(-) create mode 100644 test/contracts/polymorphism_variance_switching_custom_types.aes diff --git a/src/aeso_ast_infer_types.erl b/src/aeso_ast_infer_types.erl index 45950d1..9c42584 100644 --- a/src/aeso_ast_infer_types.erl +++ b/src/aeso_ast_infer_types.erl @@ -809,6 +809,7 @@ infer(Contracts, Options) -> ets_new(defined_contracts, [bag]), ets_new(type_vars, [set]), ets_new(type_vars_variance, [set]), + ets_new(uvars_variance, [set]), ets_new(warnings, [bag]), when_warning(warn_unused_functions, fun() -> create_unused_functions() end), check_modifiers(Env, Contracts), @@ -1155,6 +1156,14 @@ infer_type_vars_variance(TypeParams, Cons) -> end, TypeParams). -spec infer_type_vars_variance(utype()) -> [{name(), variance()}]. +infer_type_vars_variance({app_t, _, Type, Args}) -> + Variances = case ets_lookup(type_vars_variance, qname(Type)) of + [{_, Vs}] -> Vs; + _ -> lists:duplicate(length(Args), covariant) + end, + TypeVarsVariance = [{TVar, Variance} + || {{tvar, _, TVar}, Variance} <- lists:zip(Args, Variances)], + TypeVarsVariance; infer_type_vars_variance(FT = {fun_t, _, [], [{app_t, _, Type, Args}], Res}) -> Variances = case ets_lookup(type_vars_variance, qname(Type)) of [{_, Vs}] -> Vs; @@ -1777,6 +1786,17 @@ infer_expr(Env, {app, Ann, Fun, Args0} = App) -> ResultType = fresh_uvar(Ann), when_warning(warn_unused_functions, fun() -> register_function_call(Namespace ++ qname(CurrentFun), Name) end), + case FunType of + {fun_t, _, _, _, {app_t, _, QType, TArgs}} -> + case ets_lookup(type_vars_variance, qname(QType)) of + [{_, Vs}] -> + lists:foreach(fun({{uvar, _, URef}, Variance}) -> + ets_insert(uvars_variance, {URef, invariant}) + end, lists:zip(TArgs, Vs)); + _ -> ok + end; + _ -> ok + end, unify(Env, FunType, {fun_t, [], NamedArgsVar, ArgTypes, GeneralResultType}, When), when_warning(warn_negative_spend, fun() -> warn_potential_negative_spend(Ann, NewFun1, NewArgs) end), add_constraint( @@ -2681,7 +2701,7 @@ unify(Env, A, B, When) -> unify0(Env, A, B, covariant, When). unify0(_, {id, _, "_"}, _, _Variance, _When) -> true; unify0(_, _, {id, _, "_"}, _Variance, _When) -> true; -unify0(Env, A, B, Variance, When) -> +unify0(Env, A, B, Variance0, When) -> Options = case When of %% Improve source location for map_in_map_key errors {check_expr, E, _, _} -> [{ann, aeso_syntax:get_ann(E)}]; @@ -2689,6 +2709,14 @@ unify0(Env, A, B, Variance, When) -> end, A1 = dereference(unfold_types_in_type(Env, A, Options)), B1 = dereference(unfold_types_in_type(Env, B, Options)), + Variance = case A of + {uvar, _,URef} -> + case ets_lookup(uvars_variance, URef) of + [{_, V}] -> V; + _ -> Variance0 + end; + _ -> Variance0 + end, unify1(Env, A1, B1, Variance, When). unify1(_Env, {uvar, _, R}, {uvar, _, R}, _Variance, _When) -> diff --git a/test/aeso_compiler_tests.erl b/test/aeso_compiler_tests.erl index eb4e7e7..1838f32 100644 --- a/test/aeso_compiler_tests.erl +++ b/test/aeso_compiler_tests.erl @@ -885,6 +885,116 @@ failing_contracts() -> "to arguments\n" " `x : (Cat) => Cat`">> ]) + , ?TYPE_ERROR(polymorphism_variance_switching_custom_types, + [<>, + <>, + < Animal) => tc(Animal)`\n" + "to arguments\n" + " `f_a_to_c : (Animal) => Cat`">>, + < Cat) => tc(Cat)`\n" + "to arguments\n" + " `f_c_to_a : (Cat) => Animal`">>, + <>, + <>, + < Animal) => tc(Animal)`\n" + "to arguments\n" + " `f_a_to_c : (Animal) => Cat`">>, + < Cat) => tc(Cat)`\n" + "to arguments\n" + " `f_c_to_a : (Cat) => Animal`">>, + <>, + <>, + <>, + <>, + <>, + <>, + <>, + <>, + < (unit) => Animal) => tj(Animal)`\n" + "to arguments\n" + " `f_a_to_u_to_c : (Animal) => (unit) => Cat`">>, + < (unit) => Cat) => tj(Cat)`\n" + "to arguments\n" + " `f_c_to_u_to_a : (Cat) => (unit) => Animal`">>, + <>, + < (unit) => Animal) => tj(Animal)`\n" + "to arguments\n" + " `f_a_to_u_to_c : (Animal) => (unit) => Cat`">>, + < (unit) => Cat) => tj(Cat)`\n" + "to arguments\n" + " `f_c_to_u_to_a : (Cat) => (unit) => Animal`">>, + <>, + <>, + <>, + <>, + <>, + <>, + <> + ]) ]. -define(Path(File), "code_errors/" ??File). diff --git a/test/contracts/polymorphism_variance_switching_custom_types.aes b/test/contracts/polymorphism_variance_switching_custom_types.aes new file mode 100644 index 0000000..ffb8c73 --- /dev/null +++ b/test/contracts/polymorphism_variance_switching_custom_types.aes @@ -0,0 +1,132 @@ +contract interface Animal = + entrypoint sound : () => string + +contract Cat : Animal = + entrypoint sound() = "meow" + +main contract Main = + datatype ta('a) = TA('a => unit) + datatype tb('a) = TB(unit => 'a) + datatype tc('a) = TC('a => 'a) + datatype td('a) = TD(unit => unit) + datatype te('a) = TE1('a => unit) | TE2(unit => 'a) + datatype tf('a) = TF(ta('a) => unit) + datatype tg('a) = TG(tb('a) => unit) + datatype th('a) = TH(unit => ta('a)) + datatype ti('a) = TI(unit => tb('a)) + datatype tj('a) = TJ('a => unit => 'a) + datatype tk('a, 'b) = TK('a => 'b => unit) + + function f_a_to_a_to_u(_ : Animal) : (Animal => unit) = f_a_to_u + function f_a_to_c_to_u(_ : Animal) : (Cat => unit) = f_c_to_u + function f_c_to_a_to_u(_ : Cat) : (Animal => unit) = f_a_to_u + function f_c_to_c_to_u(_ : Cat) : (Cat => unit) = f_c_to_u + + function f_u_to_u(_ : unit) : unit = () + function f_a_to_u(_ : Animal) : unit = () + function f_c_to_u(_ : Cat) : unit = () + + function f_ta_a_to_u(_ : ta(Animal)) : unit = () + function f_ta_c_to_u(_ : ta(Cat)) : unit = () + function f_tb_a_to_u(_ : tb(Animal)) : unit = () + function f_tb_c_to_u(_ : tb(Cat)) : unit = () + function f_u_to_ta_a(_ : unit) : ta(Animal) = TA(f_a_to_u) + function f_u_to_ta_c(_ : unit) : ta(Cat) = TA(f_c_to_u) + + stateful function f_c() : Cat = Chain.create() + stateful function f_a() : Animal = f_c() + + stateful function f_u_to_a(_ : unit) : Animal = f_a() + stateful function f_u_to_c(_ : unit) : Cat = f_c() + stateful function f_a_to_a(_ : Animal) : Animal = f_a() + stateful function f_a_to_c(_ : Animal) : Cat = f_c() + stateful function f_c_to_a(_ : Cat) : Animal = f_a() + stateful function f_c_to_c(_ : Cat) : Cat = f_c() + + stateful function f_a_to_u_to_a(_ : Animal) : (unit => Animal) = f_u_to_a + stateful function f_a_to_u_to_c(_ : Animal) : (unit => Cat) = f_u_to_c + stateful function f_c_to_u_to_a(_ : Cat) : (unit => Animal) = f_u_to_a + stateful function f_c_to_u_to_c(_ : Cat) : (unit => Cat) = f_u_to_c + + stateful function f_u_to_tb_a(_ : unit) : tb(Animal) = TB(f_u_to_a) + stateful function f_u_to_tb_c(_ : unit) : tb(Cat) = TB(f_u_to_c) + + stateful entrypoint init() = + let va1 : ta(Animal) = TA(f_a_to_u) // success + let va2 : ta(Animal) = TA(f_c_to_u) // fail + let va3 : ta(Cat) = TA(f_a_to_u) // success + let va4 : ta(Cat) = TA(f_c_to_u) // success + + let vb1 : tb(Animal) = TB(f_u_to_a) // success + let vb2 : tb(Animal) = TB(f_u_to_c) // success + let vb3 : tb(Cat) = TB(f_u_to_a) // fail + let vb4 : tb(Cat) = TB(f_u_to_c) // success + + let vc1 : tc(Animal) = TC(f_a_to_a) // success + let vc2 : tc(Animal) = TC(f_a_to_c) // fail + let vc3 : tc(Animal) = TC(f_c_to_a) // fail + let vc4 : tc(Animal) = TC(f_c_to_c) // fail + let vc5 : tc(Cat) = TC(f_a_to_a) // fail + let vc6 : tc(Cat) = TC(f_a_to_c) // fail + let vc7 : tc(Cat) = TC(f_c_to_a) // fail + let vc8 : tc(Cat) = TC(f_c_to_c) // success + + let vd1 : td(Animal) = TD(f_u_to_u) // success + let vd2 : td(Cat) = TD(f_u_to_u) // success + + let ve1 : te(Animal) = TE1(f_a_to_u) // success + let ve2 : te(Animal) = TE1(f_c_to_u) // fail + let ve3 : te(Animal) = TE2(f_u_to_a) // success + let ve4 : te(Animal) = TE2(f_u_to_c) // fail + let ve5 : te(Cat) = TE1(f_a_to_u) // fail + let ve6 : te(Cat) = TE1(f_c_to_u) // success + let ve7 : te(Cat) = TE2(f_u_to_a) // fail + let ve8 : te(Cat) = TE2(f_u_to_c) // success + + let vf1 : tf(Animal) = TF(f_ta_a_to_u) // success + let vf2 : tf(Animal) = TF(f_ta_c_to_u) // success + let vf3 : tf(Cat) = TF(f_ta_a_to_u) // fail + let vf4 : tf(Cat) = TF(f_ta_c_to_u) // success + + let vg1 : tg(Animal) = TG(f_tb_a_to_u) // success + let vg2 : tg(Animal) = TG(f_tb_c_to_u) // fail + let vg3 : tg(Cat) = TG(f_tb_a_to_u) // success + let vg4 : tg(Cat) = TG(f_tb_c_to_u) // success + + let vh1 : th(Animal) = TH(f_u_to_ta_a) // success + let vh2 : th(Animal) = TH(f_u_to_ta_c) // fail + let vh3 : th(Cat) = TH(f_u_to_ta_a) // success + let vh4 : th(Cat) = TH(f_u_to_ta_c) // success + + let vi1 : ti(Animal) = TI(f_u_to_tb_a) // success + let vi2 : ti(Animal) = TI(f_u_to_tb_c) // success + let vi3 : ti(Cat) = TI(f_u_to_tb_a) // fail + let vi4 : ti(Cat) = TI(f_u_to_tb_c) // success + + let vj1 : tj(Animal) = TJ(f_a_to_u_to_a) // success + let vj2 : tj(Animal) = TJ(f_a_to_u_to_c) // fail + let vj3 : tj(Animal) = TJ(f_c_to_u_to_a) // fail (TODO) + let vj4 : tj(Animal) = TJ(f_c_to_u_to_c) // success + let vj5 : tj(Cat) = TJ(f_a_to_u_to_a) // fail + let vj6 : tj(Cat) = TJ(f_a_to_u_to_c) // fail + let vj7 : tj(Cat) = TJ(f_c_to_u_to_a) // fail (TODO) + let vj8 : tj(Cat) = TJ(f_c_to_u_to_c) // success + + let vk01 : tk(Animal, Animal) = TK(f_a_to_a_to_u) // success + let vk02 : tk(Animal, Animal) = TK(f_a_to_c_to_u) // fail + let vk03 : tk(Animal, Animal) = TK(f_c_to_a_to_u) // success + let vk04 : tk(Animal, Animal) = TK(f_c_to_c_to_u) // fail + let vk05 : tk(Animal, Cat) = TK(f_a_to_a_to_u) // success + let vk06 : tk(Animal, Cat) = TK(f_a_to_c_to_u) // success + let vk07 : tk(Animal, Cat) = TK(f_c_to_a_to_u) // success + let vk08 : tk(Animal, Cat) = TK(f_c_to_c_to_u) // success + let vk09 : tk(Cat, Animal) = TK(f_a_to_a_to_u) // fail + let vk10 : tk(Cat, Animal) = TK(f_a_to_c_to_u) // fail + let vk11 : tk(Cat, Animal) = TK(f_c_to_a_to_u) // success + let vk12 : tk(Cat, Animal) = TK(f_c_to_c_to_u) // fail + let vk13 : tk(Cat, Cat) = TK(f_a_to_a_to_u) // fail + let vk14 : tk(Cat, Cat) = TK(f_a_to_c_to_u) // fail + let vk15 : tk(Cat, Cat) = TK(f_c_to_a_to_u) // success + let vk16 : tk(Cat, Cat) = TK(f_c_to_c_to_u) // success + + () -- 2.30.2 From b9af3dbe7f61b1b00fdd4ac578da98328ffc1543 Mon Sep 17 00:00:00 2001 From: Gaith Hallak Date: Thu, 24 Feb 2022 15:52:17 +0400 Subject: [PATCH 20/53] Make opposite_variance a separate function --- src/aeso_ast_infer_types.erl | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/src/aeso_ast_infer_types.erl b/src/aeso_ast_infer_types.erl index 9c42584..2f0e32d 100644 --- a/src/aeso_ast_infer_types.erl +++ b/src/aeso_ast_infer_types.erl @@ -1196,6 +1196,11 @@ arrows_in_type({fun_t, _, [], [_Arg], FRes}) -> arrows_in_type(_) -> 0. +opposite_variance(invariant) -> invariant; +opposite_variance(covariant) -> contravariant; +opposite_variance(contravariant) -> covariant; +opposite_variance(bivariant) -> bivariant. + check_usings(Env, []) -> Env; check_usings(Env = #env{ used_namespaces = UsedNamespaces }, [{using, Ann, Con, Alias, Parts} | Rest]) -> @@ -2771,14 +2776,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) -> - OppositeVariance = case Variance of - invariant -> invariant; - covariant -> contravariant; - contravariant -> covariant; - bivariant -> bivariant - end, - unify0(Env, Named1, Named2, OppositeVariance, When) andalso - unify0(Env, Args1, Args2, OppositeVariance, When) andalso + unify0(Env, Named1, Named2, opposite_variance(Variance), When) andalso + unify0(Env, Args1, Args2, opposite_variance(Variance), When) andalso 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 -> -- 2.30.2 From ae9b8f8b163fa6c3f1ddcb5971c6c3d610a73683 Mon Sep 17 00:00:00 2001 From: Gaith Hallak Date: Thu, 24 Feb 2022 16:03:47 +0400 Subject: [PATCH 21/53] Make is_subtype/4 a separate function --- src/aeso_ast_infer_types.erl | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/src/aeso_ast_infer_types.erl b/src/aeso_ast_infer_types.erl index 2f0e32d..062affa 100644 --- a/src/aeso_ast_infer_types.erl +++ b/src/aeso_ast_infer_types.erl @@ -2747,16 +2747,9 @@ unify1(_Env, X, X, _Variance, _When) -> unify1(_Env, {id, _, Name}, {id, _, Name}, _Variance, _When) -> true; unify1(Env, A = {con, _, NameA}, B = {con, _, NameB}, Variance, When) -> - IsSubtype = case Variance of - invariant -> NameA == NameB; - covariant -> is_subtype(Env, NameA, NameB); - contravariant -> is_subtype(Env, NameB, NameA); - bivariant -> is_subtype(Env, NameA, NameB) - end, - if - IsSubtype -> - true; - true -> + case is_subtype(Env, NameA, NameB, Variance) of + true -> true; + false -> cannot_unify(A, B, When), false end; @@ -2803,6 +2796,15 @@ unify1(_Env, A, B, _Variance, When) -> cannot_unify(A, B, When), false. +is_subtype(Env, NameA, NameB, invariant) -> + NameA == NameB; +is_subtype(Env, NameA, NameB, covariant) -> + is_subtype(Env, NameA, NameB); +is_subtype(Env, NameA, NameB, contravariant) -> + is_subtype(Env, NameB, NameA); +is_subtype(Env, NameA, NameB, bivariant) -> + is_subtype(Env, NameA, NameB) orelse is_subtype(Env, NameB, NameA). + is_subtype(Env, Child, Base) -> Parents = maps:get(Child, Env#env.contract_parents, []), if -- 2.30.2 From 1980eb30bfb400a80c72aade2ace382d4b3cbc91 Mon Sep 17 00:00:00 2001 From: Gaith Hallak Date: Thu, 24 Feb 2022 16:05:07 +0400 Subject: [PATCH 22/53] Fix warning --- src/aeso_ast_infer_types.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/aeso_ast_infer_types.erl b/src/aeso_ast_infer_types.erl index 062affa..14e0852 100644 --- a/src/aeso_ast_infer_types.erl +++ b/src/aeso_ast_infer_types.erl @@ -2796,7 +2796,7 @@ unify1(_Env, A, B, _Variance, When) -> cannot_unify(A, B, When), false. -is_subtype(Env, NameA, NameB, invariant) -> +is_subtype(_Env, NameA, NameB, invariant) -> NameA == NameB; is_subtype(Env, NameA, NameB, covariant) -> is_subtype(Env, NameA, NameB); -- 2.30.2 From 8e888430d92eea62a778d998a00de2b6d0b82573 Mon Sep 17 00:00:00 2001 From: Gaith Hallak Date: Thu, 24 Feb 2022 17:17:15 +0400 Subject: [PATCH 23/53] Mark tvars as invariant --- src/aeso_ast_infer_types.erl | 23 +++++++++---------- ...rphism_variance_switching_custom_types.aes | 4 ++-- 2 files changed, 13 insertions(+), 14 deletions(-) diff --git a/src/aeso_ast_infer_types.erl b/src/aeso_ast_infer_types.erl index 14e0852..655b65b 100644 --- a/src/aeso_ast_infer_types.erl +++ b/src/aeso_ast_infer_types.erl @@ -809,7 +809,7 @@ infer(Contracts, Options) -> ets_new(defined_contracts, [bag]), ets_new(type_vars, [set]), ets_new(type_vars_variance, [set]), - ets_new(uvars_variance, [set]), + ets_new(type_vars_uvar, [set]), ets_new(warnings, [bag]), when_warning(warn_unused_functions, fun() -> create_unused_functions() end), check_modifiers(Env, Contracts), @@ -1791,15 +1791,14 @@ infer_expr(Env, {app, Ann, Fun, Args0} = App) -> ResultType = fresh_uvar(Ann), when_warning(warn_unused_functions, fun() -> register_function_call(Namespace ++ qname(CurrentFun), Name) end), + % the uvars of tvars are stored so that no variance switching happens + % in between them (e.g. in TC('a => 'a), 'a should be a single type) case FunType of - {fun_t, _, _, _, {app_t, _, QType, TArgs}} -> - case ets_lookup(type_vars_variance, qname(QType)) of - [{_, Vs}] -> - lists:foreach(fun({{uvar, _, URef}, Variance}) -> - ets_insert(uvars_variance, {URef, invariant}) - end, lists:zip(TArgs, Vs)); - _ -> ok - end; + {fun_t, _, _, _, {app_t, _, _, TArgs}} -> + lists:foreach(fun({uvar, _, URef}) -> + ets_insert(type_vars_uvar, {URef}); + (_) -> ok + end, TArgs); _ -> ok end, unify(Env, FunType, {fun_t, [], NamedArgsVar, ArgTypes, GeneralResultType}, When), @@ -2716,9 +2715,9 @@ unify0(Env, A, B, Variance0, When) -> B1 = dereference(unfold_types_in_type(Env, B, Options)), Variance = case A of {uvar, _,URef} -> - case ets_lookup(uvars_variance, URef) of - [{_, V}] -> V; - _ -> Variance0 + case ets_lookup(type_vars_uvar, URef) of + [_] -> invariant; + _ -> Variance0 end; _ -> Variance0 end, diff --git a/test/contracts/polymorphism_variance_switching_custom_types.aes b/test/contracts/polymorphism_variance_switching_custom_types.aes index ffb8c73..120e07d 100644 --- a/test/contracts/polymorphism_variance_switching_custom_types.aes +++ b/test/contracts/polymorphism_variance_switching_custom_types.aes @@ -105,11 +105,11 @@ main contract Main = let vj1 : tj(Animal) = TJ(f_a_to_u_to_a) // success let vj2 : tj(Animal) = TJ(f_a_to_u_to_c) // fail - let vj3 : tj(Animal) = TJ(f_c_to_u_to_a) // fail (TODO) + let vj3 : tj(Animal) = TJ(f_c_to_u_to_a) // fail let vj4 : tj(Animal) = TJ(f_c_to_u_to_c) // success let vj5 : tj(Cat) = TJ(f_a_to_u_to_a) // fail let vj6 : tj(Cat) = TJ(f_a_to_u_to_c) // fail - let vj7 : tj(Cat) = TJ(f_c_to_u_to_a) // fail (TODO) + let vj7 : tj(Cat) = TJ(f_c_to_u_to_a) // fail let vj8 : tj(Cat) = TJ(f_c_to_u_to_c) // success let vk01 : tk(Animal, Animal) = TK(f_a_to_a_to_u) // success -- 2.30.2 From 8a9c2a61b20b4f1f6b1d0a3ad3defaa8ee995535 Mon Sep 17 00:00:00 2001 From: Gaith Hallak Date: Sun, 27 Feb 2022 17:02:58 +0400 Subject: [PATCH 24/53] Add type_vars_uvar ets table to ets_tables() --- src/aeso_ast_infer_types.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/aeso_ast_infer_types.erl b/src/aeso_ast_infer_types.erl index 655b65b..0c10b3c 100644 --- a/src/aeso_ast_infer_types.erl +++ b/src/aeso_ast_infer_types.erl @@ -2161,7 +2161,7 @@ next_count() -> ets_tables() -> [options, type_vars, constraints, freshen_tvars, type_errors, defined_contracts, warnings, function_calls, all_functions, - type_vars_variance]. + type_vars_variance, type_vars_uvar]. clean_up_ets() -> [ catch ets_delete(Tab) || Tab <- ets_tables() ], -- 2.30.2 From 94c2f998aee66a739f8f92f9ac35590c3e973565 Mon Sep 17 00:00:00 2001 From: Gaith Hallak Date: Tue, 19 Apr 2022 17:38:30 +0400 Subject: [PATCH 25/53] Don't destroy and recreate type errors table when not needed --- src/aeso_ast_infer_types.erl | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/aeso_ast_infer_types.erl b/src/aeso_ast_infer_types.erl index 0c10b3c..2310e86 100644 --- a/src/aeso_ast_infer_types.erl +++ b/src/aeso_ast_infer_types.erl @@ -863,11 +863,9 @@ infer1(Env0, [{Contract, Ann, ConName, Impls, Code} | Rest], Acc, Options) _ -> ok end end, Impls), - destroy_and_report_type_errors(Env), - create_type_errors(), case What of contract -> - ImplementedInterfaces = [proplists:get_value(Name, AllInterfaces) || Name <- ImplsNames], + ImplementedInterfaces = [I || I <- [proplists:get_value(Name, AllInterfaces) || Name <- ImplsNames], I /= undefined], check_implemented_interfaces(ImplementedInterfaces, ConName, [ Fun || Fun = {letfun, _, _, _, _, _} <- Code1 ], [], AllInterfaces); contract_interface -> ok -- 2.30.2 From 822d54709ea0fee73adae032d553350618c9e667 Mon Sep 17 00:00:00 2001 From: Gaith Hallak Date: Wed, 20 Apr 2022 13:59:30 +0400 Subject: [PATCH 26/53] Fixes from the reviews --- src/aeso_ast_infer_types.erl | 56 ++++++++++++++++++++++-------------- 1 file changed, 35 insertions(+), 21 deletions(-) diff --git a/src/aeso_ast_infer_types.erl b/src/aeso_ast_infer_types.erl index 2310e86..32b848c 100644 --- a/src/aeso_ast_infer_types.erl +++ b/src/aeso_ast_infer_types.erl @@ -855,22 +855,7 @@ infer1(Env0, [{Contract, Ann, ConName, Impls, Code} | Rest], Acc, Options) end, {Env1, Code1} = infer_contract_top(push_scope(contract, ConName, Env), What, Code, Options), Contract1 = {Contract, Ann, ConName, Impls, Code1}, - AllInterfaces = [{name(IName), I} || I = {contract_interface, _, IName, _, _} <- Acc], - ImplsNames = lists:map(fun name/1, Impls), - create_type_errors(), - lists:foreach(fun(Impl) -> case proplists:get_value(name(Impl), AllInterfaces) of - undefined -> type_error({referencing_undefined_interface, Impl}); - _ -> ok - end - end, Impls), - case What of - contract -> - ImplementedInterfaces = [I || I <- [proplists:get_value(Name, AllInterfaces) || Name <- ImplsNames], I /= undefined], - check_implemented_interfaces(ImplementedInterfaces, ConName, [ Fun || Fun = {letfun, _, _, _, _, _} <- Code1 ], [], AllInterfaces); - contract_interface -> - ok - end, - destroy_and_report_type_errors(Env), + check_implemented_interfaces(Env1, Contract1, What, Acc), Env2 = pop_scope(Env1), Env3 = bind_contract(Contract1, Env2), infer1(Env3, Rest, [Contract1 | Acc], Options); @@ -886,18 +871,47 @@ infer1(Env, [{pragma, _, _} | Rest], Acc, Options) -> %% Pragmas are checked in check_modifiers infer1(Env, Rest, Acc, Options). -check_implemented_interfaces([], _, _, _, _) -> +check_implemented_interfaces(Env, {_Contract, _Ann, ConName, Impls, Code}, What, DefinedContracts) -> + create_type_errors(), + AllInterfaces = [{name(IName), I} || I = {contract_interface, _, IName, _, _} <- DefinedContracts], + ImplsNames = lists:map(fun name/1, Impls), + + %% All implemented intrefaces should already be defined + lists:foreach(fun(Impl) -> case proplists:get_value(name(Impl), AllInterfaces) of + undefined -> type_error({referencing_undefined_interface, Impl}); + _ -> ok + end + end, Impls), + + case What of + contract -> + ImplementedInterfaces = [I || I <- [proplists:get_value(Name, AllInterfaces) || Name <- ImplsNames], + I /= undefined], + Letfuns = [ Fun || Fun = {letfun, _, _, _, _, _} <- Code ], + check_implemented_interfaces_recursive(ImplementedInterfaces, ConName, Letfuns, AllInterfaces); + contract_interface -> + ok + end, + destroy_and_report_type_errors(Env). + +check_implemented_interfaces_recursive(ImplementedInterfaces, ConId, Letfuns, AllInterfaces) -> + check_implemented_interfaces_recursive(ImplementedInterfaces, ConId, Letfuns, [], AllInterfaces). + +%% Recursively check that all directly and indirectly referenced interfaces are implemented +check_implemented_interfaces_recursive([], _, _, _, _) -> ok; -check_implemented_interfaces([{contract_interface, _, IName, Extensions, Decls} | Interfaces], ConId, Impls, Acc, AllInterfaces) -> +check_implemented_interfaces_recursive([{contract_interface, _, IName, Extensions, Decls} | Interfaces], + ConId, Impls, Acc, AllInterfaces) -> case lists:member(name(IName), Acc) of true -> - check_implemented_interfaces(Interfaces, ConId, Impls, Acc, AllInterfaces); + check_implemented_interfaces_recursive(Interfaces, ConId, Impls, Acc, AllInterfaces); false -> Unmatched = match_impls(Decls, ConId, name(IName), Impls), - NewInterfaces = [proplists:get_value(name(I), AllInterfaces) || I <- Extensions], - check_implemented_interfaces(Interfaces ++ NewInterfaces, ConId, Unmatched, [name(IName) | Acc], AllInterfaces) + NewInterfaces = Interfaces ++ [proplists:get_value(name(I), AllInterfaces) || I <- Extensions], + check_implemented_interfaces_recursive(NewInterfaces, ConId, Unmatched, [name(IName) | Acc], AllInterfaces) end. +%% Match the functions of the contract with the interfaces functions, and return unmatched functions match_impls([], _, _, Impls) -> Impls; match_impls([{fun_decl, _, {id, _, FunName}, {fun_t, _, _, ArgsTypes, RetDecl}} | Decls], ConId, IName, Impls) -> -- 2.30.2 From 78d2ce2e75694169a5550e1a113413f60834e29e Mon Sep 17 00:00:00 2001 From: Gaith Hallak Date: Wed, 20 Apr 2022 19:34:38 +0400 Subject: [PATCH 27/53] Use is_list to check if a var is a list --- src/aeso_ast_infer_types.erl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/aeso_ast_infer_types.erl b/src/aeso_ast_infer_types.erl index 32b848c..875cf7e 100644 --- a/src/aeso_ast_infer_types.erl +++ b/src/aeso_ast_infer_types.erl @@ -933,7 +933,8 @@ match_impls([{fun_decl, _, {id, _, FunName}, {fun_t, _, _, ArgsTypes, RetDecl}} -spec compare_types(T, T) -> boolean() when T :: utype() | [utype()]. -compare_types(Types1 = [_ | _], Types2 = [_ | _]) -> +compare_types(Types1, Types2) + when is_list(Types1) andalso is_list(Types2) -> length(Types1) == length(Types2) andalso lists:all(fun({T1, T2}) -> compare_types(T1, T2) end, lists:zip(Types1, Types2)); compare_types({fun_t, _, _, Types1, RetType1}, {fun_t, _, _, Types2, RetType2}) -> -- 2.30.2 From 96d9509b4439e0e8eff5b32b2e5de61d865590c4 Mon Sep 17 00:00:00 2001 From: Gaith Hallak Date: Fri, 22 Apr 2022 17:05:21 +0400 Subject: [PATCH 28/53] Compare named args in fun_t --- src/aeso_ast_infer_types.erl | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/aeso_ast_infer_types.erl b/src/aeso_ast_infer_types.erl index 875cf7e..60cdb77 100644 --- a/src/aeso_ast_infer_types.erl +++ b/src/aeso_ast_infer_types.erl @@ -937,9 +937,10 @@ compare_types(Types1, Types2) when is_list(Types1) andalso is_list(Types2) -> length(Types1) == length(Types2) andalso lists:all(fun({T1, T2}) -> compare_types(T1, T2) end, lists:zip(Types1, Types2)); -compare_types({fun_t, _, _, Types1, RetType1}, {fun_t, _, _, Types2, RetType2}) -> - % TODO: what about named_args_t and var_args? - compare_types(RetType1, RetType2) andalso compare_types(Types1, Types2); +compare_types({fun_t, _, NamedArgs1, Types1, RetType1}, {fun_t, _, NamedArgs2, Types2, RetType2}) -> + compare_types(NamedArgs1, NamedArgs2) andalso + compare_types(RetType1, RetType2) andalso + compare_types(Types1, Types2); compare_types({app_t, _, Type1, ArgsTypes1}, {app_t, _, Type2, ArgsTypes2}) -> compare_types(Type1, Type2) andalso compare_types(ArgsTypes1, ArgsTypes2); compare_types({tuple_t, _, Types1}, {tuple_t, _, Types2}) -> @@ -952,6 +953,8 @@ compare_types(T1 = {Id, _, Type}, {Id, _, Type}) when ?is_type_id(T1) -> true; compare_types({if_t, _, {id, _, Id}, Ta1, Tb1}, {if_t, _, {id, _, Id}, Ta2, Tb2}) -> compare_types(Ta1, Ta2) andalso compare_types(Tb1, Tb2); +compare_types({named_arg_t, _, {id, _, Name}, T1, _}, {named_arg_t, _, {id, _, Name}, T2, _}) -> + compare_types(T1, T2); compare_types(_, _) -> false. -- 2.30.2 From 2883b41bebafccf2428065ee5983200258d33fc3 Mon Sep 17 00:00:00 2001 From: Gaith Hallak Date: Fri, 22 Apr 2022 17:20:03 +0400 Subject: [PATCH 29/53] Test only for covariance and contravariance --- src/aeso_ast_infer_types.erl | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/src/aeso_ast_infer_types.erl b/src/aeso_ast_infer_types.erl index 60cdb77..1bbfeb0 100644 --- a/src/aeso_ast_infer_types.erl +++ b/src/aeso_ast_infer_types.erl @@ -1157,17 +1157,13 @@ infer_type_vars_variance(TypeParams, Cons) -> Vs = lists:flatten([infer_type_vars_variance(Arg) || Arg <- FlatArgs]), lists:map(fun({tvar, _, TVar}) -> S = sets:from_list([Variance || {TV, Variance} <- Vs, TV == TVar]), - IsInvariant = sets:is_element(invariant, S), IsCovariant = sets:is_element(covariant, S), IsContravariant = sets:is_element(contravariant, S), - IsBivariant = sets:is_element(bivariant, S), - case {IsInvariant, IsCovariant, IsContravariant, IsBivariant} of - {true, _, _, _} -> invariant; - {false, true, true, _} -> invariant; - {false, _, _, true} -> bivariant; - {false, true, false, _} -> covariant; - {false, false, true, _} -> contravariant; - _ -> invariant + case {IsCovariant, IsContravariant} of + {true, true} -> invariant; + {true, false} -> covariant; + {false, true} -> contravariant; + {false, false} -> bivariant end end, TypeParams). -- 2.30.2 From f92e995931e2567fbb7f9c17e019869206f5f0fc Mon Sep 17 00:00:00 2001 From: Gaith Hallak Date: Sun, 24 Apr 2022 18:58:44 +0400 Subject: [PATCH 30/53] Remove arrows_in_type and use infer_type_vars_variance instead --- src/aeso_ast_infer_types.erl | 32 +++++++++++++++----------------- 1 file changed, 15 insertions(+), 17 deletions(-) diff --git a/src/aeso_ast_infer_types.erl b/src/aeso_ast_infer_types.erl index 1bbfeb0..7863aab 100644 --- a/src/aeso_ast_infer_types.erl +++ b/src/aeso_ast_infer_types.erl @@ -1154,7 +1154,7 @@ infer_type_vars_variance(TypeParams, Cons) -> % args from all type constructors FlatArgs = lists:flatten([Args || {constr_t, _, _, Args} <- Cons]), - Vs = lists:flatten([infer_type_vars_variance(Arg) || Arg <- FlatArgs]), + Vs = lists:flatten([element(1, infer_type_vars_variance(Arg)) || Arg <- FlatArgs]), lists:map(fun({tvar, _, TVar}) -> S = sets:from_list([Variance || {TV, Variance} <- Vs, TV == TVar]), IsCovariant = sets:is_element(covariant, S), @@ -1167,7 +1167,7 @@ infer_type_vars_variance(TypeParams, Cons) -> end end, TypeParams). --spec infer_type_vars_variance(utype()) -> [{name(), variance()}]. +-spec infer_type_vars_variance(utype()) -> {[{name(), variance()}], integer()}. infer_type_vars_variance({app_t, _, Type, Args}) -> Variances = case ets_lookup(type_vars_variance, qname(Type)) of [{_, Vs}] -> Vs; @@ -1175,8 +1175,8 @@ infer_type_vars_variance({app_t, _, Type, Args}) -> end, TypeVarsVariance = [{TVar, Variance} || {{tvar, _, TVar}, Variance} <- lists:zip(Args, Variances)], - TypeVarsVariance; -infer_type_vars_variance(FT = {fun_t, _, [], [{app_t, _, Type, Args}], Res}) -> + {TypeVarsVariance, 0}; +infer_type_vars_variance({fun_t, _, [], [{app_t, _, Type, Args}], Res}) -> Variances = case ets_lookup(type_vars_variance, qname(Type)) of [{_, Vs}] -> Vs; _ -> lists:duplicate(length(Args), covariant) @@ -1186,27 +1186,25 @@ infer_type_vars_variance(FT = {fun_t, _, [], [{app_t, _, Type, Args}], Res}) -> FlipVariance = fun({TVar, covariant}) -> {TVar, contravariant}; ({TVar, contravariant}) -> {TVar, covariant} end, - Cur = case arrows_in_type(FT) rem 2 of + {TVVs, Depth} = infer_type_vars_variance(Res), + Cur = case (Depth + 1) rem 2 of 0 -> TypeVarsVariance; 1 -> lists:map(FlipVariance, TypeVarsVariance) end, - Cur ++ infer_type_vars_variance(Res); -infer_type_vars_variance(FT = {fun_t, _, [], [{tvar, _, TVar}], Res}) -> - Cur = case arrows_in_type(FT) rem 2 of + {Cur ++ TVVs, Depth + 1}; +infer_type_vars_variance({fun_t, _, [], [{tvar, _, TVar}], Res}) -> + {TVVs, Depth} = infer_type_vars_variance(Res), + Cur = case (Depth + 1) rem 2 of 0 -> {TVar, covariant}; 1 -> {TVar, contravariant} end, - [Cur | infer_type_vars_variance(Res)]; + {[Cur | TVVs], Depth + 1}; infer_type_vars_variance({fun_t, _, [], [_Arg], Res}) -> - infer_type_vars_variance(Res); + {X, Depth} = infer_type_vars_variance(Res), + {X, Depth + 1}; infer_type_vars_variance({tvar, _, TVar}) -> - [{TVar, covariant}]; -infer_type_vars_variance(_) -> []. - -arrows_in_type({fun_t, _, [], [_Arg], FRes}) -> - 1 + arrows_in_type(FRes); -arrows_in_type(_) -> - 0. + {[{TVar, covariant}], 0}; +infer_type_vars_variance(_) -> {[], 0}. opposite_variance(invariant) -> invariant; opposite_variance(covariant) -> contravariant; -- 2.30.2 From 3b7b207527a2d2532e69208183c809ce115dbcd2 Mon Sep 17 00:00:00 2001 From: Gaith Hallak Date: Mon, 25 Apr 2022 19:50:14 +0400 Subject: [PATCH 31/53] Add tests for option and type aliases --- .../contracts/polymorphism_variance_switching.aes | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/test/contracts/polymorphism_variance_switching.aes b/test/contracts/polymorphism_variance_switching.aes index 434ff9f..cfe7b80 100644 --- a/test/contracts/polymorphism_variance_switching.aes +++ b/test/contracts/polymorphism_variance_switching.aes @@ -48,3 +48,18 @@ main contract Main = stateful function g5(x : (Animal => Animal)) : Cat = Chain.create() stateful function f5(x : (Cat => Cat)) : Animal = g5(x) + + stateful function g6() : option(Cat) = Some(Chain.create()) + stateful function f6() : option(Animal) = g6() + stateful function h6() : option(Cat) = f6() + + type cat_type = Cat + type animal_type = Animal + type cat_cat_map = map(cat_type, cat_type) + type animal_animal_map = map(animal_type, animal_type) + + stateful function g71(x : animal_type) : cat_type = Chain.create() + stateful function f71(x : cat_type) : animal_type = g1(x) + + stateful function g72() : cat_cat_map = { [Chain.create()] = Chain.create() } + stateful function f72() : animal_animal_map = g13() -- 2.30.2 From 3fc6d4722378bb271784bd129db04f3e0ec1089a Mon Sep 17 00:00:00 2001 From: Gaith Hallak Date: Mon, 25 Apr 2022 19:57:51 +0400 Subject: [PATCH 32/53] Fix previous commit --- test/aeso_compiler_tests.erl | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/test/aeso_compiler_tests.erl b/test/aeso_compiler_tests.erl index 1838f32..36c48bb 100644 --- a/test/aeso_compiler_tests.erl +++ b/test/aeso_compiler_tests.erl @@ -883,7 +883,10 @@ failing_contracts() -> "when checking the application of\n" " `g5 : ((Animal) => Animal) => Cat`\n" "to arguments\n" - " `x : (Cat) => Cat`">> + " `x : (Cat) => Cat`">>, + <> ]) , ?TYPE_ERROR(polymorphism_variance_switching_custom_types, [< Date: Mon, 9 May 2022 12:48:49 +0400 Subject: [PATCH 33/53] Rename check_implemented_interfaces_recursive to check_implemented_interfaces1 --- src/aeso_ast_infer_types.erl | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/aeso_ast_infer_types.erl b/src/aeso_ast_infer_types.erl index 7863aab..d0ebb3a 100644 --- a/src/aeso_ast_infer_types.erl +++ b/src/aeso_ast_infer_types.erl @@ -888,27 +888,27 @@ check_implemented_interfaces(Env, {_Contract, _Ann, ConName, Impls, Code}, What, ImplementedInterfaces = [I || I <- [proplists:get_value(Name, AllInterfaces) || Name <- ImplsNames], I /= undefined], Letfuns = [ Fun || Fun = {letfun, _, _, _, _, _} <- Code ], - check_implemented_interfaces_recursive(ImplementedInterfaces, ConName, Letfuns, AllInterfaces); + check_implemented_interfaces1(ImplementedInterfaces, ConName, Letfuns, AllInterfaces); contract_interface -> ok end, destroy_and_report_type_errors(Env). -check_implemented_interfaces_recursive(ImplementedInterfaces, ConId, Letfuns, AllInterfaces) -> - check_implemented_interfaces_recursive(ImplementedInterfaces, ConId, Letfuns, [], AllInterfaces). +check_implemented_interfaces1(ImplementedInterfaces, ConId, Letfuns, AllInterfaces) -> + check_implemented_interfaces1(ImplementedInterfaces, ConId, Letfuns, [], AllInterfaces). %% Recursively check that all directly and indirectly referenced interfaces are implemented -check_implemented_interfaces_recursive([], _, _, _, _) -> +check_implemented_interfaces1([], _, _, _, _) -> ok; -check_implemented_interfaces_recursive([{contract_interface, _, IName, Extensions, Decls} | Interfaces], +check_implemented_interfaces1([{contract_interface, _, IName, Extensions, Decls} | Interfaces], ConId, Impls, Acc, AllInterfaces) -> case lists:member(name(IName), Acc) of true -> - check_implemented_interfaces_recursive(Interfaces, ConId, Impls, Acc, AllInterfaces); + check_implemented_interfaces1(Interfaces, ConId, Impls, Acc, AllInterfaces); false -> Unmatched = match_impls(Decls, ConId, name(IName), Impls), NewInterfaces = Interfaces ++ [proplists:get_value(name(I), AllInterfaces) || I <- Extensions], - check_implemented_interfaces_recursive(NewInterfaces, ConId, Unmatched, [name(IName) | Acc], AllInterfaces) + check_implemented_interfaces1(NewInterfaces, ConId, Unmatched, [name(IName) | Acc], AllInterfaces) end. %% Match the functions of the contract with the interfaces functions, and return unmatched functions -- 2.30.2 From 94c8bc3671af9762bd4e493b5e1cf9aedcc208dd Mon Sep 17 00:00:00 2001 From: Gaith Hallak Date: Wed, 11 May 2022 17:07:30 +0400 Subject: [PATCH 34/53] Make interfaces declare functions from extended interfaces --- src/aeso_ast_infer_types.erl | 41 +++++++------------ test/aeso_compiler_tests.erl | 29 +++++-------- .../contract_interface_polymorphism.aes | 1 + .../polymorphism_variance_switching.aes | 1 + test/contracts/test.aes | 2 +- 5 files changed, 28 insertions(+), 46 deletions(-) diff --git a/src/aeso_ast_infer_types.erl b/src/aeso_ast_infer_types.erl index d0ebb3a..63e16df 100644 --- a/src/aeso_ast_infer_types.erl +++ b/src/aeso_ast_infer_types.erl @@ -855,7 +855,7 @@ infer1(Env0, [{Contract, Ann, ConName, Impls, Code} | Rest], Acc, Options) end, {Env1, Code1} = infer_contract_top(push_scope(contract, ConName, Env), What, Code, Options), Contract1 = {Contract, Ann, ConName, Impls, Code1}, - check_implemented_interfaces(Env1, Contract1, What, Acc), + check_implemented_interfaces(Env1, Contract1, Acc), Env2 = pop_scope(Env1), Env3 = bind_contract(Contract1, Env2), infer1(Env3, Rest, [Contract1 | Acc], Options); @@ -871,7 +871,7 @@ infer1(Env, [{pragma, _, _} | Rest], Acc, Options) -> %% Pragmas are checked in check_modifiers infer1(Env, Rest, Acc, Options). -check_implemented_interfaces(Env, {_Contract, _Ann, ConName, Impls, Code}, What, DefinedContracts) -> +check_implemented_interfaces(Env, {_Contract, _Ann, ConName, Impls, Code}, DefinedContracts) -> create_type_errors(), AllInterfaces = [{name(IName), I} || I = {contract_interface, _, IName, _, _} <- DefinedContracts], ImplsNames = lists:map(fun name/1, Impls), @@ -883,43 +883,32 @@ check_implemented_interfaces(Env, {_Contract, _Ann, ConName, Impls, Code}, What, end end, Impls), - case What of - contract -> - ImplementedInterfaces = [I || I <- [proplists:get_value(Name, AllInterfaces) || Name <- ImplsNames], - I /= undefined], - Letfuns = [ Fun || Fun = {letfun, _, _, _, _, _} <- Code ], - check_implemented_interfaces1(ImplementedInterfaces, ConName, Letfuns, AllInterfaces); - contract_interface -> - ok - end, + ImplementedInterfaces = [I || I <- [proplists:get_value(Name, AllInterfaces) || Name <- ImplsNames], + I /= undefined], + Funs = [ Fun || Fun <- Code, + element(1, Fun) == letfun orelse element(1, Fun) == fun_decl ], + check_implemented_interfaces1(ImplementedInterfaces, ConName, Funs, AllInterfaces), destroy_and_report_type_errors(Env). -check_implemented_interfaces1(ImplementedInterfaces, ConId, Letfuns, AllInterfaces) -> - check_implemented_interfaces1(ImplementedInterfaces, ConId, Letfuns, [], AllInterfaces). - %% Recursively check that all directly and indirectly referenced interfaces are implemented -check_implemented_interfaces1([], _, _, _, _) -> +check_implemented_interfaces1([], _, _, _) -> ok; -check_implemented_interfaces1([{contract_interface, _, IName, Extensions, Decls} | Interfaces], - ConId, Impls, Acc, AllInterfaces) -> - case lists:member(name(IName), Acc) of - true -> - check_implemented_interfaces1(Interfaces, ConId, Impls, Acc, AllInterfaces); - false -> - Unmatched = match_impls(Decls, ConId, name(IName), Impls), - NewInterfaces = Interfaces ++ [proplists:get_value(name(I), AllInterfaces) || I <- Extensions], - check_implemented_interfaces1(NewInterfaces, ConId, Unmatched, [name(IName) | Acc], AllInterfaces) - end. +check_implemented_interfaces1([{contract_interface, _, IName, _, Decls} | Interfaces], + ConId, Impls, AllInterfaces) -> + Unmatched = match_impls(Decls, ConId, name(IName), Impls), + check_implemented_interfaces1(Interfaces, ConId, Unmatched, AllInterfaces). %% Match the functions of the contract with the interfaces functions, and return unmatched functions match_impls([], _, _, Impls) -> Impls; -match_impls([{fun_decl, _, {id, _, FunName}, {fun_t, _, _, ArgsTypes, RetDecl}} | Decls], ConId, IName, Impls) -> +match_impls([{fun_decl, _, {id, _, FunName}, FunType = {fun_t, _, _, ArgsTypes, RetDecl}} | Decls], ConId, IName, Impls) -> Match = fun({letfun, _, {id, _, FName}, Args, RetFun, _}) when FName == FunName -> length(ArgsTypes) == length(Args) andalso compare_types(RetDecl, RetFun) andalso lists:all(fun({T1, {typed, _, _, T2}}) -> compare_types(T1, T2) end, lists:zip(ArgsTypes, Args)); + ({fun_decl, _, {id, _, FName}, FunT}) when FName == FunName -> + compare_types(FunT, FunType); (_) -> false end, UnmatchedImpls = case lists:search(Match, Impls) of diff --git a/test/aeso_compiler_tests.erl b/test/aeso_compiler_tests.erl index 36c48bb..6898ed0 100644 --- a/test/aeso_compiler_tests.erl +++ b/test/aeso_compiler_tests.erl @@ -205,6 +205,8 @@ compilable_contracts() -> "contract_polymorphism", "contract_polymorphism_multi_interface", "contract_interface_polymorphism", + "contract_interface_polymorphism_same_decl_multi_interface", + "contract_interface_polymorphism_same_name_same_type", "test" % Custom general-purpose test file. Keep it last on the list. ]. @@ -832,23 +834,12 @@ failing_contracts() -> [<> ]) - , ?TYPE_ERROR(contract_interface_polymorphism_same_decl_multi_interface, - [<> - ]) - , ?TYPE_ERROR(contract_interface_polymorphism_same_name_same_type, - [<> - ]) , ?TYPE_ERROR(contract_interface_polymorphism_same_name_different_type, - [<> - ]) + [<>]) , ?TYPE_ERROR(contract_polymorphism_missing_implementation, - [<> + [<> ]) , ?TYPE_ERROR(contract_polymorphism_same_decl_multi_interface, [< "Trying to implement or extend an undefined interface `H`">> ]) , ?TYPE_ERROR(polymorphism_variance_switching, - [< Cat`\n" "to arguments\n" " `x : Animal`">>, - <>, - < Animal) => Cat`\n" "to arguments\n" " `x : (Cat) => Cat`">>, - <> ]) diff --git a/test/contracts/contract_interface_polymorphism.aes b/test/contracts/contract_interface_polymorphism.aes index 6d51bea..5d234f5 100644 --- a/test/contracts/contract_interface_polymorphism.aes +++ b/test/contracts/contract_interface_polymorphism.aes @@ -2,6 +2,7 @@ contract interface II = entrypoint f : () => unit contract interface I : II = + entrypoint f : () => unit entrypoint g : () => unit contract C : I = diff --git a/test/contracts/polymorphism_variance_switching.aes b/test/contracts/polymorphism_variance_switching.aes index cfe7b80..0ce2f55 100644 --- a/test/contracts/polymorphism_variance_switching.aes +++ b/test/contracts/polymorphism_variance_switching.aes @@ -2,6 +2,7 @@ contract interface Creature = entrypoint is_alive : () => bool contract interface Animal : Creature = + entrypoint is_alive : () => bool entrypoint sound : () => string contract Cat : Animal = diff --git a/test/contracts/test.aes b/test/contracts/test.aes index ebdd4a2..4b100a2 100644 --- a/test/contracts/test.aes +++ b/test/contracts/test.aes @@ -1,4 +1,4 @@ contract ShareTwo = record state = {s1 : int, s2 : int} entrypoint init() = {s1 = 0, s2 = 0} - stateful entrypoint buy() = () \ No newline at end of file + stateful entrypoint buy() = () -- 2.30.2 From 3b3e24fb839febfba4fcb788e0aeed11894d25a8 Mon Sep 17 00:00:00 2001 From: Gaith Hallak Date: Wed, 11 May 2022 17:13:25 +0400 Subject: [PATCH 35/53] Restore test.aes --- test/aeso_compiler_tests.erl | 1 + ...ontract_polymorphism_interface_extensions.aes | 9 +++++++++ test/contracts/test.aes | 16 ++++++++++++---- 3 files changed, 22 insertions(+), 4 deletions(-) create mode 100644 test/contracts/contract_polymorphism_interface_extensions.aes diff --git a/test/aeso_compiler_tests.erl b/test/aeso_compiler_tests.erl index 6898ed0..fc93db6 100644 --- a/test/aeso_compiler_tests.erl +++ b/test/aeso_compiler_tests.erl @@ -205,6 +205,7 @@ compilable_contracts() -> "contract_polymorphism", "contract_polymorphism_multi_interface", "contract_interface_polymorphism", + "contract_polymorphism_interface_extensions", "contract_interface_polymorphism_same_decl_multi_interface", "contract_interface_polymorphism_same_name_same_type", "test" % Custom general-purpose test file. Keep it last on the list. diff --git a/test/contracts/contract_polymorphism_interface_extensions.aes b/test/contracts/contract_polymorphism_interface_extensions.aes new file mode 100644 index 0000000..0d87481 --- /dev/null +++ b/test/contracts/contract_polymorphism_interface_extensions.aes @@ -0,0 +1,9 @@ +contract interface I0 = + entrypoint f : () => int + +contract interface I1 : I0 = + entrypoint f : () => int + entrypoint something_else : () => int + +main contract C = + entrypoint f(x : I1) = x.f() // Here we should know that x has f \ No newline at end of file diff --git a/test/contracts/test.aes b/test/contracts/test.aes index 4b100a2..d978179 100644 --- a/test/contracts/test.aes +++ b/test/contracts/test.aes @@ -1,4 +1,12 @@ -contract ShareTwo = - record state = {s1 : int, s2 : int} - entrypoint init() = {s1 = 0, s2 = 0} - stateful entrypoint buy() = () +// This is a custom test file if you need to run a compiler without +// changing aeso_compiler_tests.erl + +include "List.aes" + +contract IntegerHolder = + type state = int + entrypoint init(x) = x + entrypoint get() = state + +main contract Test = + stateful entrypoint f(c) = Chain.clone(ref=c, 123) -- 2.30.2 From d06a61cc224c5328d84644e57161bd3fd8835012 Mon Sep 17 00:00:00 2001 From: Gaith Hallak Date: Wed, 18 May 2022 19:33:11 +0400 Subject: [PATCH 36/53] Add test for variance switching in records --- ...olymorphism_variance_switching_records.aes | 50 +++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 test/contracts/polymorphism_variance_switching_records.aes diff --git a/test/contracts/polymorphism_variance_switching_records.aes b/test/contracts/polymorphism_variance_switching_records.aes new file mode 100644 index 0000000..765cf76 --- /dev/null +++ b/test/contracts/polymorphism_variance_switching_records.aes @@ -0,0 +1,50 @@ +contract interface Animal = + entrypoint sound : () => string + +contract Cat : Animal = + entrypoint sound() = "meow" + +main contract Main = + record rec_a('a) = { x : 'a } + record rec_b('a) = { x : 'a => unit } + record rec_c('a) = { x : () => 'a} + record rec_d('a) = { x : 'a => unit, + y : () => 'a } + + stateful entrypoint new_cat() : Cat = Chain.create() + stateful entrypoint new_animal() : Animal = new_cat() + stateful entrypoint animal_to_unit(_ : Animal) : unit = () + stateful entrypoint cat_to_unit(_ : Cat) : unit = () + stateful entrypoint unit_to_animal() : Animal = new_animal() + stateful entrypoint unit_to_cat() : Cat = new_cat() + + stateful entrypoint init() = + let ra : rec_a(Animal) = { x = new_animal() } + let rc : rec_a(Cat) = { x = new_cat() } + let r01 : rec_a(Animal) = ra // success + let r02 : rec_a(Animal) = rc // success + let r03 : rec_a(Cat) = ra // fail + let r04 : rec_a(Cat) = rc // sucess + + let ratu : rec_b(Animal) = { x = animal_to_unit } + let rctu : rec_b(Cat) = { x = cat_to_unit } + let r05 : rec_b(Animal) = ratu // success + let r06 : rec_b(Animal) = rctu // fail + let r07 : rec_b(Cat) = ratu // success + let r08 : rec_b(Cat) = rctu // success + + let ruta : rec_c(Animal) = { x = unit_to_animal } + let rutc : rec_c(Cat) = { x = unit_to_cat } + let r09 : rec_c(Animal) = ruta // success + let r10 : rec_c(Animal) = rutc // success + let r11 : rec_c(Cat) = ruta // fail + let r12 : rec_c(Cat) = rutc // success + + let rxaya : rec_d(Animal) = { x = animal_to_unit, y = unit_to_animal } + let rxcyc : rec_d(Cat) = { x = cat_to_unit, y = unit_to_cat } + let r13 : rec_d(Animal) = rxaya // success + let r16 : rec_d(Animal) = rxcyc // fail + let r17 : rec_d(Cat) = rxaya // fail + let r20 : rec_d(Cat) = rxcyc // success + + () \ No newline at end of file -- 2.30.2 From 136035b952818a3ad7a7e2582f4d821716fd7e22 Mon Sep 17 00:00:00 2001 From: Gaith Hallak Date: Fri, 20 May 2022 12:49:02 +0400 Subject: [PATCH 37/53] Enable variance switching for record types --- src/aeso_ast_infer_types.erl | 6 ++++-- test/aeso_compiler_tests.erl | 16 ++++++++++++++++ 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/src/aeso_ast_infer_types.erl b/src/aeso_ast_infer_types.erl index 63e16df..de75101 100644 --- a/src/aeso_ast_infer_types.erl +++ b/src/aeso_ast_infer_types.erl @@ -1107,6 +1107,8 @@ check_typedef_sccs(Env, TypeMap, [{acyclic, Name} | SCCs], Acc) -> type_error({empty_record_definition, Ann, Name}), check_typedef_sccs(Env1, TypeMap, SCCs, Acc1); {record_t, Fields} -> + ets_insert(type_vars_variance, {Env#env.namespace ++ qname(D), + infer_type_vars_variance(Xs, Fields)}), %% check_type to get qualified name RecTy = check_type(Env1, app_t(Ann, D, Xs)), Env2 = check_fields(Env1, TypeMap, RecTy, Fields), @@ -1141,7 +1143,7 @@ check_typedef(Env, {variant_t, Cons}) -> infer_type_vars_variance(TypeParams, Cons) -> % args from all type constructors - FlatArgs = lists:flatten([Args || {constr_t, _, _, Args} <- Cons]), + FlatArgs = lists:flatten([Args || {constr_t, _, _, Args} <- Cons]) ++ [Type || {field_t, _, _, Type} <- Cons], Vs = lists:flatten([element(1, infer_type_vars_variance(Arg)) || Arg <- FlatArgs]), lists:map(fun({tvar, _, TVar}) -> @@ -1188,7 +1190,7 @@ infer_type_vars_variance({fun_t, _, [], [{tvar, _, TVar}], Res}) -> 1 -> {TVar, contravariant} end, {[Cur | TVVs], Depth + 1}; -infer_type_vars_variance({fun_t, _, [], [_Arg], Res}) -> +infer_type_vars_variance({fun_t, _, [], _, Res}) -> {X, Depth} = infer_type_vars_variance(Res), {X, Depth + 1}; infer_type_vars_variance({tvar, _, TVar}) -> diff --git a/test/aeso_compiler_tests.erl b/test/aeso_compiler_tests.erl index fc93db6..31197ce 100644 --- a/test/aeso_compiler_tests.erl +++ b/test/aeso_compiler_tests.erl @@ -990,6 +990,22 @@ failing_contracts() -> "Cannot unify `Animal` and `Cat`\n" "when checking the type of the expression `TK(f_a_to_c_to_u) : tk(Animal, Cat)` against the expected type `tk(Cat, Cat)`">> ]) + , ?TYPE_ERROR(polymorphism_variance_switching_records, + [<>, + <>, + <>, + <>, + <>]) ]. -define(Path(File), "code_errors/" ??File). -- 2.30.2 From 7d5ea6a39e4aa4d0bef52253b072ce3b6bf87f78 Mon Sep 17 00:00:00 2001 From: Gaith Hallak Date: Fri, 20 May 2022 18:24:02 +0400 Subject: [PATCH 38/53] Handle builtin types type variables separately --- src/aeso_ast_infer_types.erl | 15 +++++++++++-- test/aeso_compiler_tests.erl | 43 ++++++++++++++++++++++++++++++++++++ 2 files changed, 56 insertions(+), 2 deletions(-) diff --git a/src/aeso_ast_infer_types.erl b/src/aeso_ast_infer_types.erl index de75101..ad36319 100644 --- a/src/aeso_ast_infer_types.erl +++ b/src/aeso_ast_infer_types.erl @@ -808,9 +808,15 @@ infer(Contracts, Options) -> create_options(Options), ets_new(defined_contracts, [bag]), ets_new(type_vars, [set]), - ets_new(type_vars_variance, [set]), ets_new(type_vars_uvar, [set]), ets_new(warnings, [bag]), + ets_new(type_vars_variance, [set]), + %% Set the variance for builtin types + ets_insert(type_vars_variance, {"list", [covariant]}), + ets_insert(type_vars_variance, {"option", [covariant]}), + ets_insert(type_vars_variance, {"oracle", [contravariant, covariant]}), + ets_insert(type_vars_variance, {"oracle_query", [covariant, contravariant]}), + when_warning(warn_unused_functions, fun() -> create_unused_functions() end), check_modifiers(Env, Contracts), create_type_errors(), @@ -2775,7 +2781,12 @@ unify1(Env, {fun_t, _, Named1, Args1, Result1}, {fun_t, _, Named2, Args2, Result unify1(Env, {app_t, _, {Tag, _, F}, Args1}, {app_t, _, {Tag, _, F}, Args2}, Variance, When) when length(Args1) == length(Args2), Tag == id orelse Tag == qid -> Variances = case ets_lookup(type_vars_variance, F) of - [{_, Vs}] -> Vs; + [{_, Vs}] -> + case Variance of + contravariant -> lists:map(fun opposite_variance/1, Vs); + invariant -> invariant; + _ -> Vs + end; _ -> Variance end, unify1(Env, Args1, Args2, Variances, When); diff --git a/test/aeso_compiler_tests.erl b/test/aeso_compiler_tests.erl index 31197ce..8c5ea4b 100644 --- a/test/aeso_compiler_tests.erl +++ b/test/aeso_compiler_tests.erl @@ -1006,6 +1006,49 @@ failing_contracts() -> <>]) + , ?TYPE_ERROR(polymorphism_variance_switching_oracles, + [<>, + <>, + <>, + <>, + <>, + <>, + <>, + <>, + <>, + <>, + <>, + <>, + <>, + <>]) ]. -define(Path(File), "code_errors/" ??File). -- 2.30.2 From 075fb8a65928478fc9a996b9f972dd2a62a46cf2 Mon Sep 17 00:00:00 2001 From: Gaith Hallak Date: Fri, 20 May 2022 18:24:42 +0400 Subject: [PATCH 39/53] Add tests for oracles and oracle queries --- ...olymorphism_variance_switching_oracles.aes | 47 +++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 test/contracts/polymorphism_variance_switching_oracles.aes diff --git a/test/contracts/polymorphism_variance_switching_oracles.aes b/test/contracts/polymorphism_variance_switching_oracles.aes new file mode 100644 index 0000000..2cee15b --- /dev/null +++ b/test/contracts/polymorphism_variance_switching_oracles.aes @@ -0,0 +1,47 @@ +contract interface Animal = + entrypoint sound : () => string + +contract Cat : Animal = + entrypoint sound() = "meow" + +main contract Main = + entrypoint oracle() = ok_2YNyxd6TRJPNrTcEDCe9ra59SVUdp9FR9qWC5msKZWYD9bP9z5 + + entrypoint query() = oq_2oRvyowJuJnEkxy58Ckkw77XfWJrmRgmGaLzhdqb67SKEL1gPY + + entrypoint init() = + let o01 : oracle(Animal, Animal) = oracle() : oracle(Animal, Animal) // success + let o02 : oracle(Animal, Animal) = oracle() : oracle(Animal, Cat) // success + let o03 : oracle(Animal, Animal) = oracle() : oracle(Cat, Animal) // fail + let o04 : oracle(Animal, Animal) = oracle() : oracle(Cat, Cat) // fail + let o05 : oracle(Animal, Cat) = oracle() : oracle(Animal, Animal) // fail + let o06 : oracle(Animal, Cat) = oracle() : oracle(Animal, Cat) // success + let o07 : oracle(Animal, Cat) = oracle() : oracle(Cat, Animal) // fail + let o08 : oracle(Animal, Cat) = oracle() : oracle(Cat, Cat) // fail + let o09 : oracle(Cat, Animal) = oracle() : oracle(Animal, Animal) // success + let o10 : oracle(Cat, Animal) = oracle() : oracle(Animal, Cat) // success + let o11 : oracle(Cat, Animal) = oracle() : oracle(Cat, Animal) // success + let o12 : oracle(Cat, Animal) = oracle() : oracle(Cat, Cat) // success + let o13 : oracle(Cat, Cat) = oracle() : oracle(Animal, Animal) // fail + let o14 : oracle(Cat, Cat) = oracle() : oracle(Animal, Cat) // success + let o15 : oracle(Cat, Cat) = oracle() : oracle(Cat, Animal) // fail + let o16 : oracle(Cat, Cat) = oracle() : oracle(Cat, Cat) // success + + let q01 : oracle_query(Animal, Animal) = query() : oracle_query(Animal, Animal) // success + let q02 : oracle_query(Animal, Animal) = query() : oracle_query(Animal, Cat) // fail + let q03 : oracle_query(Animal, Animal) = query() : oracle_query(Cat, Animal) // success + let q04 : oracle_query(Animal, Animal) = query() : oracle_query(Cat, Cat) // fail + let q05 : oracle_query(Animal, Cat) = query() : oracle_query(Animal, Animal) // success + let q06 : oracle_query(Animal, Cat) = query() : oracle_query(Animal, Cat) // success + let q07 : oracle_query(Animal, Cat) = query() : oracle_query(Cat, Animal) // success + let q08 : oracle_query(Animal, Cat) = query() : oracle_query(Cat, Cat) // success + let q09 : oracle_query(Cat, Animal) = query() : oracle_query(Animal, Animal) // fail + let q10 : oracle_query(Cat, Animal) = query() : oracle_query(Animal, Cat) // fail + let q11 : oracle_query(Cat, Animal) = query() : oracle_query(Cat, Animal) // success + let q12 : oracle_query(Cat, Animal) = query() : oracle_query(Cat, Cat) // fail + let q13 : oracle_query(Cat, Cat) = query() : oracle_query(Animal, Animal) // fail + let q14 : oracle_query(Cat, Cat) = query() : oracle_query(Animal, Cat) // fail + let q15 : oracle_query(Cat, Cat) = query() : oracle_query(Cat, Animal) // success + let q16 : oracle_query(Cat, Cat) = query() : oracle_query(Cat, Cat) // success + + () \ No newline at end of file -- 2.30.2 From 983e7165b038cc3419366dd2140ae71158701030 Mon Sep 17 00:00:00 2001 From: Gaith Hallak Date: Fri, 20 May 2022 18:49:52 +0400 Subject: [PATCH 40/53] Replace compare_types with non-throwing version of unify --- src/aeso_ast_infer_types.erl | 77 ++++++++++++++++-------------------- 1 file changed, 33 insertions(+), 44 deletions(-) diff --git a/src/aeso_ast_infer_types.erl b/src/aeso_ast_infer_types.erl index ad36319..5644e2c 100644 --- a/src/aeso_ast_infer_types.erl +++ b/src/aeso_ast_infer_types.erl @@ -142,6 +142,7 @@ , in_pattern = false :: boolean() , in_guard = false :: boolean() , stateful = false :: boolean() + , unify_throws = true :: boolean() , current_function = none :: none | aeso_syntax:id() , what = top :: top | namespace | contract | contract_interface }). @@ -893,28 +894,28 @@ check_implemented_interfaces(Env, {_Contract, _Ann, ConName, Impls, Code}, Defin I /= undefined], Funs = [ Fun || Fun <- Code, element(1, Fun) == letfun orelse element(1, Fun) == fun_decl ], - check_implemented_interfaces1(ImplementedInterfaces, ConName, Funs, AllInterfaces), + check_implemented_interfaces1(Env, ImplementedInterfaces, ConName, Funs, AllInterfaces), destroy_and_report_type_errors(Env). %% Recursively check that all directly and indirectly referenced interfaces are implemented -check_implemented_interfaces1([], _, _, _) -> +check_implemented_interfaces1(_, [], _, _, _) -> ok; -check_implemented_interfaces1([{contract_interface, _, IName, _, Decls} | Interfaces], - ConId, Impls, AllInterfaces) -> - Unmatched = match_impls(Decls, ConId, name(IName), Impls), - check_implemented_interfaces1(Interfaces, ConId, Unmatched, AllInterfaces). +check_implemented_interfaces1(Env, [{contract_interface, _, IName, _, Decls} | Interfaces], + ConId, Impls, AllInterfaces) -> + Unmatched = match_impls(Env, Decls, ConId, name(IName), Impls), + check_implemented_interfaces1(Env, Interfaces, ConId, Unmatched, AllInterfaces). %% Match the functions of the contract with the interfaces functions, and return unmatched functions -match_impls([], _, _, Impls) -> +match_impls(_, [], _, _, Impls) -> Impls; -match_impls([{fun_decl, _, {id, _, FunName}, FunType = {fun_t, _, _, ArgsTypes, RetDecl}} | Decls], ConId, IName, Impls) -> +match_impls(Env, [{fun_decl, _, {id, _, FunName}, FunType = {fun_t, _, _, ArgsTypes, RetDecl}} | Decls], ConId, IName, Impls) -> Match = fun({letfun, _, {id, _, FName}, Args, RetFun, _}) when FName == FunName -> length(ArgsTypes) == length(Args) andalso - compare_types(RetDecl, RetFun) andalso - lists:all(fun({T1, {typed, _, _, T2}}) -> compare_types(T1, T2) end, + unify(Env#env{unify_throws = false}, RetDecl, RetFun, unknown) andalso + lists:all(fun({T1, {typed, _, _, T2}}) -> unify(Env#env{unify_throws = false}, T1, T2, unknown) end, lists:zip(ArgsTypes, Args)); ({fun_decl, _, {id, _, FName}, FunT}) when FName == FunName -> - compare_types(FunT, FunType); + unify(Env#env{unify_throws = false}, FunT, FunType, unknown); (_) -> false end, UnmatchedImpls = case lists:search(Match, Impls) of @@ -924,34 +925,7 @@ match_impls([{fun_decl, _, {id, _, FunName}, FunType = {fun_t, _, _, ArgsTypes, type_error({unimplemented_interface_function, ConId, IName, FunName}), Impls end, - match_impls(Decls, ConId, IName, UnmatchedImpls). - - --spec compare_types(T, T) -> boolean() when T :: utype() | [utype()]. -compare_types(Types1, Types2) - when is_list(Types1) andalso is_list(Types2) -> - length(Types1) == length(Types2) andalso - lists:all(fun({T1, T2}) -> compare_types(T1, T2) end, lists:zip(Types1, Types2)); -compare_types({fun_t, _, NamedArgs1, Types1, RetType1}, {fun_t, _, NamedArgs2, Types2, RetType2}) -> - compare_types(NamedArgs1, NamedArgs2) andalso - compare_types(RetType1, RetType2) andalso - compare_types(Types1, Types2); -compare_types({app_t, _, Type1, ArgsTypes1}, {app_t, _, Type2, ArgsTypes2}) -> - compare_types(Type1, Type2) andalso compare_types(ArgsTypes1, ArgsTypes2); -compare_types({tuple_t, _, Types1}, {tuple_t, _, Types2}) -> - compare_types(Types1, Types2); -compare_types({tuple_t, _, []}, {id, _, "unit"}) -> - true; -compare_types({id, _, "unit"}, {tuple_t, _, []}) -> - true; -compare_types(T1 = {Id, _, Type}, {Id, _, Type}) when ?is_type_id(T1) -> - true; -compare_types({if_t, _, {id, _, Id}, Ta1, Tb1}, {if_t, _, {id, _, Id}, Ta2, Tb2}) -> - compare_types(Ta1, Ta2) andalso compare_types(Tb1, Tb2); -compare_types({named_arg_t, _, {id, _, Name}, T1, _}, {named_arg_t, _, {id, _, Name}, T2, _}) -> - compare_types(T1, T2); -compare_types(_, _) -> - false. + match_impls(Env, Decls, ConId, IName, UnmatchedImpls). %% Asserts that the main contract is somehow defined. identify_main_contract(Contracts, Options) -> @@ -2732,10 +2706,15 @@ unify0(Env, A, B, Variance0, When) -> unify1(_Env, {uvar, _, R}, {uvar, _, R}, _Variance, _When) -> true; -unify1(_Env, {uvar, A, R}, T, _Variance, When) -> +unify1(Env, {uvar, A, R}, T, _Variance, When) -> case occurs_check(R, T) of true -> - cannot_unify({uvar, A, R}, T, When), + if + Env#env.unify_throws -> + cannot_unify({uvar, A, R}, T, When); + true -> + ok + end, false; false -> ets_insert(type_vars, {R, T}), @@ -2756,7 +2735,12 @@ unify1(Env, A = {con, _, NameA}, B = {con, _, NameB}, Variance, When) -> case is_subtype(Env, NameA, NameB, Variance) of true -> true; false -> - cannot_unify(A, B, When), + if + Env#env.unify_throws -> + cannot_unify(A, B, When); + true -> + ok + end, false end; unify1(_Env, {qid, _, Name}, {qid, _, Name}, _Variance, _When) -> @@ -2803,8 +2787,13 @@ unify1(Env, {app_t, _, T, []}, B, Variance, When) -> unify0(Env, T, B, Variance, When); unify1(Env, A, {app_t, _, T, []}, Variance, When) -> unify0(Env, A, T, Variance, When); -unify1(_Env, A, B, _Variance, When) -> - cannot_unify(A, B, When), +unify1(Env, A, B, _Variance, When) -> + if + Env#env.unify_throws -> + cannot_unify(A, B, When); + true -> + ok + end, false. is_subtype(_Env, NameA, NameB, invariant) -> -- 2.30.2 From 08d0c7034f345f12ca3c627de4505509643d6009 Mon Sep 17 00:00:00 2001 From: Gaith Hallak Date: Tue, 24 May 2022 15:11:43 +0400 Subject: [PATCH 41/53] Add the context to unification error --- src/aeso_ast_infer_types.erl | 24 ++++++--- test/aeso_compiler_tests.erl | 102 +++++++++++++++++------------------ 2 files changed, 68 insertions(+), 58 deletions(-) diff --git a/src/aeso_ast_infer_types.erl b/src/aeso_ast_infer_types.erl index 5644e2c..9582495 100644 --- a/src/aeso_ast_infer_types.erl +++ b/src/aeso_ast_infer_types.erl @@ -2711,7 +2711,7 @@ unify1(Env, {uvar, A, R}, T, _Variance, When) -> true -> if Env#env.unify_throws -> - cannot_unify({uvar, A, R}, T, When); + cannot_unify({uvar, A, R}, T, none, When); true -> ok end, @@ -2737,7 +2737,13 @@ unify1(Env, A = {con, _, NameA}, B = {con, _, NameB}, Variance, When) -> false -> if Env#env.unify_throws -> - cannot_unify(A, B, When); + IsSubtype = is_subtype(Env, NameA, NameB, contravariant) orelse + is_subtype(Env, NameA, NameB, covariant), + Cxt = case IsSubtype of + true -> Variance; + false -> none + end, + cannot_unify(A, B, Cxt, When); true -> ok end, @@ -2790,7 +2796,7 @@ unify1(Env, A, {app_t, _, T, []}, Variance, When) -> unify1(Env, A, B, _Variance, When) -> if Env#env.unify_throws -> - cannot_unify(A, B, When); + cannot_unify(A, B, none, When); true -> ok end, @@ -3102,8 +3108,8 @@ warn_potential_negative_spend(Ann, Fun, Args) -> %% Save unification failures for error messages. -cannot_unify(A, B, When) -> - type_error({cannot_unify, A, B, When}). +cannot_unify(A, B, Cxt, When) -> + type_error({cannot_unify, A, B, Cxt, When}). type_error(Err) -> ets_insert(type_errors, Err). @@ -3172,8 +3178,12 @@ mk_error({fundecl_must_have_funtype, _Ann, Id, Type}) -> "Entrypoints and functions must have functional types" , [pp(Id), pp(instantiate(Type))]), mk_t_err(pos(Id), Msg); -mk_error({cannot_unify, A, B, When}) -> - Msg = io_lib:format("Cannot unify `~s` and `~s`", +mk_error({cannot_unify, A, B, Cxt, When}) -> + VarianceContext = case Cxt of + none -> ""; + _ -> io_lib:format(" in a ~p context", [Cxt]) + end, + Msg = io_lib:format("Cannot unify `~s` and `~s`" ++ VarianceContext, [pp(instantiate(A)), pp(instantiate(B))]), {Pos, Ctxt} = pp_when(When), mk_t_err(Pos, Msg, Ctxt); diff --git a/test/aeso_compiler_tests.erl b/test/aeso_compiler_tests.erl index 8c5ea4b..9263c45 100644 --- a/test/aeso_compiler_tests.erl +++ b/test/aeso_compiler_tests.erl @@ -862,192 +862,192 @@ failing_contracts() -> ]) , ?TYPE_ERROR(polymorphism_variance_switching, [< Cat`\n" "to arguments\n" " `x : Animal`">>, <>, < Animal) => Cat`\n" "to arguments\n" " `x : (Cat) => Cat`">>, <> ]) , ?TYPE_ERROR(polymorphism_variance_switching_custom_types, [<>, <>, < Animal) => tc(Animal)`\n" "to arguments\n" " `f_a_to_c : (Animal) => Cat`">>, < Cat) => tc(Cat)`\n" "to arguments\n" " `f_c_to_a : (Cat) => Animal`">>, <>, <>, < Animal) => tc(Animal)`\n" "to arguments\n" " `f_a_to_c : (Animal) => Cat`">>, < Cat) => tc(Cat)`\n" "to arguments\n" " `f_c_to_a : (Cat) => Animal`">>, <>, <>, <>, <>, <>, <>, <>, <>, < (unit) => Animal) => tj(Animal)`\n" "to arguments\n" " `f_a_to_u_to_c : (Animal) => (unit) => Cat`">>, < (unit) => Cat) => tj(Cat)`\n" "to arguments\n" " `f_c_to_u_to_a : (Cat) => (unit) => Animal`">>, <>, < (unit) => Animal) => tj(Animal)`\n" "to arguments\n" " `f_a_to_u_to_c : (Animal) => (unit) => Cat`">>, < (unit) => Cat) => tj(Cat)`\n" "to arguments\n" " `f_c_to_u_to_a : (Cat) => (unit) => Animal`">>, <>, <>, <>, <>, <>, <>, <> ]) , ?TYPE_ERROR(polymorphism_variance_switching_records, [<>, <>, <>, <>, <>]) , ?TYPE_ERROR(polymorphism_variance_switching_oracles, [<>, <>, <>, <>, <>, <>, <>, <>, <>, <>, <>, <>, <>, <>]) ]. -- 2.30.2 From e5c6efc8ae2ca1ffcf96b1987f1e7095b92f86ea Mon Sep 17 00:00:00 2001 From: Gaith Hallak Date: Tue, 24 May 2022 22:16:34 +0400 Subject: [PATCH 42/53] Test variance switching for bivariant records --- test/aeso_compiler_tests.erl | 10 +++++----- .../polymorphism_variance_switching_records.aes | 8 ++++++++ 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/test/aeso_compiler_tests.erl b/test/aeso_compiler_tests.erl index 9263c45..eeab5e4 100644 --- a/test/aeso_compiler_tests.erl +++ b/test/aeso_compiler_tests.erl @@ -991,19 +991,19 @@ failing_contracts() -> "when checking the type of the expression `TK(f_a_to_c_to_u) : tk(Animal, Cat)` against the expected type `tk(Cat, Cat)`">> ]) , ?TYPE_ERROR(polymorphism_variance_switching_records, - [<>, - <>, - <>, - <>, - <>]) , ?TYPE_ERROR(polymorphism_variance_switching_oracles, diff --git a/test/contracts/polymorphism_variance_switching_records.aes b/test/contracts/polymorphism_variance_switching_records.aes index 765cf76..445868d 100644 --- a/test/contracts/polymorphism_variance_switching_records.aes +++ b/test/contracts/polymorphism_variance_switching_records.aes @@ -10,6 +10,7 @@ main contract Main = record rec_c('a) = { x : () => 'a} record rec_d('a) = { x : 'a => unit, y : () => 'a } + record rec_e('a) = { x : int } stateful entrypoint new_cat() : Cat = Chain.create() stateful entrypoint new_animal() : Animal = new_cat() @@ -47,4 +48,11 @@ main contract Main = let r17 : rec_d(Cat) = rxaya // fail let r20 : rec_d(Cat) = rxcyc // success + let rba : rec_e(Animal) = { x = 1 } + let rbc : rec_e(Cat) = { x = 1 } + let r21 : rec_e(Animal) = rba // success + let r21 : rec_e(Animal) = rbc // success + let r22 : rec_e(Cat) = rba // success + let r22 : rec_e(Cat) = rbc // success + () \ No newline at end of file -- 2.30.2 From 8067d15a412af90debe2f6763fbf96702474cfce Mon Sep 17 00:00:00 2001 From: Gaith Hallak Date: Tue, 24 May 2022 22:36:45 +0400 Subject: [PATCH 43/53] Give clear names to the records in records variance switching test --- test/aeso_compiler_tests.erl | 15 ++--- ...olymorphism_variance_switching_records.aes | 67 +++++++++---------- 2 files changed, 36 insertions(+), 46 deletions(-) diff --git a/test/aeso_compiler_tests.erl b/test/aeso_compiler_tests.erl index eeab5e4..a72e0c9 100644 --- a/test/aeso_compiler_tests.erl +++ b/test/aeso_compiler_tests.erl @@ -993,19 +993,16 @@ failing_contracts() -> , ?TYPE_ERROR(polymorphism_variance_switching_records, [<>, + "when checking the type of the pattern `r03 : rec_co(Cat)` against the expected type `Main.rec_co(Animal)`">>, <>, - <>, - <>, + <>, - <>, + <>]) + "when checking the type of the pattern `r11 : rec_inv(Cat)` against the expected type `Main.rec_inv(Animal)`">>]) , ?TYPE_ERROR(polymorphism_variance_switching_oracles, [< unit } - record rec_c('a) = { x : () => 'a} - record rec_d('a) = { x : 'a => unit, - y : () => 'a } - record rec_e('a) = { x : int } + record rec_co('a) = { x : 'a , + y : () => 'a } + record rec_contra('a) = { x : 'a => unit } + record rec_inv('a) = { x : 'a => unit, + y : () => 'a } + record rec_biv('a) = { x : int } stateful entrypoint new_cat() : Cat = Chain.create() stateful entrypoint new_animal() : Animal = new_cat() @@ -20,39 +20,32 @@ main contract Main = stateful entrypoint unit_to_cat() : Cat = new_cat() stateful entrypoint init() = - let ra : rec_a(Animal) = { x = new_animal() } - let rc : rec_a(Cat) = { x = new_cat() } - let r01 : rec_a(Animal) = ra // success - let r02 : rec_a(Animal) = rc // success - let r03 : rec_a(Cat) = ra // fail - let r04 : rec_a(Cat) = rc // sucess + let ra : rec_co(Animal) = { x = new_animal(), y = unit_to_animal } + let rc : rec_co(Cat) = { x = new_cat(), y = unit_to_cat } + let r01 : rec_co(Animal) = ra // success + let r02 : rec_co(Animal) = rc // success + let r03 : rec_co(Cat) = ra // fail + let r04 : rec_co(Cat) = rc // sucess - let ratu : rec_b(Animal) = { x = animal_to_unit } - let rctu : rec_b(Cat) = { x = cat_to_unit } - let r05 : rec_b(Animal) = ratu // success - let r06 : rec_b(Animal) = rctu // fail - let r07 : rec_b(Cat) = ratu // success - let r08 : rec_b(Cat) = rctu // success + let ratu : rec_contra(Animal) = { x = animal_to_unit } + let rctu : rec_contra(Cat) = { x = cat_to_unit } + let r05 : rec_contra(Animal) = ratu // success + let r06 : rec_contra(Animal) = rctu // fail + let r07 : rec_contra(Cat) = ratu // success + let r08 : rec_contra(Cat) = rctu // success - let ruta : rec_c(Animal) = { x = unit_to_animal } - let rutc : rec_c(Cat) = { x = unit_to_cat } - let r09 : rec_c(Animal) = ruta // success - let r10 : rec_c(Animal) = rutc // success - let r11 : rec_c(Cat) = ruta // fail - let r12 : rec_c(Cat) = rutc // success + let rxaya : rec_inv(Animal) = { x = animal_to_unit, y = unit_to_animal } + let rxcyc : rec_inv(Cat) = { x = cat_to_unit, y = unit_to_cat } + let r09 : rec_inv(Animal) = rxaya // success + let r10 : rec_inv(Animal) = rxcyc // fail + let r11 : rec_inv(Cat) = rxaya // fail + let r12 : rec_inv(Cat) = rxcyc // success - let rxaya : rec_d(Animal) = { x = animal_to_unit, y = unit_to_animal } - let rxcyc : rec_d(Cat) = { x = cat_to_unit, y = unit_to_cat } - let r13 : rec_d(Animal) = rxaya // success - let r16 : rec_d(Animal) = rxcyc // fail - let r17 : rec_d(Cat) = rxaya // fail - let r20 : rec_d(Cat) = rxcyc // success - - let rba : rec_e(Animal) = { x = 1 } - let rbc : rec_e(Cat) = { x = 1 } - let r21 : rec_e(Animal) = rba // success - let r21 : rec_e(Animal) = rbc // success - let r22 : rec_e(Cat) = rba // success - let r22 : rec_e(Cat) = rbc // success + let rba : rec_biv(Animal) = { x = 1 } + let rbc : rec_biv(Cat) = { x = 1 } + let r13 : rec_biv(Animal) = rba // success + let r14 : rec_biv(Animal) = rbc // success + let r15 : rec_biv(Cat) = rba // success + let r16 : rec_biv(Cat) = rbc // success () \ No newline at end of file -- 2.30.2 From 025e0f7b8f4183de51e0b6b216c5998ce84e9333 Mon Sep 17 00:00:00 2001 From: Gaith Hallak Date: Tue, 24 May 2022 23:17:17 +0400 Subject: [PATCH 44/53] Handle comments about polymorphism_variance_switching.aes --- test/aeso_compiler_tests.erl | 8 ++++---- .../contracts/polymorphism_variance_switching.aes | 15 ++++++--------- 2 files changed, 10 insertions(+), 13 deletions(-) diff --git a/test/aeso_compiler_tests.erl b/test/aeso_compiler_tests.erl index a72e0c9..d636bc9 100644 --- a/test/aeso_compiler_tests.erl +++ b/test/aeso_compiler_tests.erl @@ -861,22 +861,22 @@ failing_contracts() -> "Trying to implement or extend an undefined interface `H`">> ]) , ?TYPE_ERROR(polymorphism_variance_switching, - [< Cat`\n" "to arguments\n" " `x : Animal`">>, - <>, - < Animal) => Cat`\n" "to arguments\n" " `x : (Cat) => Cat`">>, - <> ]) diff --git a/test/contracts/polymorphism_variance_switching.aes b/test/contracts/polymorphism_variance_switching.aes index 0ce2f55..48106c5 100644 --- a/test/contracts/polymorphism_variance_switching.aes +++ b/test/contracts/polymorphism_variance_switching.aes @@ -12,8 +12,8 @@ contract Cat : Animal = main contract Main = entrypoint init() = () - stateful function g0(x : Creature) : Cat = Chain.create() - stateful function f0(x : Cat) : Creature = g1(x) + stateful function g0(_ : Creature) : Cat = Chain.create() + stateful function f0(x : Cat) : Creature = g0(x) stateful function h0() = let a : Animal = (Chain.create() : Cat) let c : Creature = (Chain.create() : Cat) @@ -22,9 +22,6 @@ main contract Main = stateful function g1(x : Animal) : Cat = Chain.create() stateful function f1(x : Cat) : Animal = g1(x) - stateful function h1() = - let x : Animal = (Chain.create() : Cat) - () stateful function g11(x : list(Animal)) : list(Cat) = [Chain.create()] stateful function f11(x : list(Cat)) : list(Animal) = g11(x) @@ -36,10 +33,10 @@ main contract Main = stateful function f13() : map(Animal, Animal) = g13() stateful function g2(x : Cat) : Cat = Chain.create() - stateful function f2(x : Animal) : Animal = g2(x) + stateful function f2(x : Animal) : Animal = g2(x) // fail stateful function g3(x : Cat) : Animal = f1(x) - stateful function f3(x : Cat) : Cat = g3(x) + stateful function f3(x : Cat) : Cat = g3(x) // fail stateful function g4(x : (Cat => Animal)) : Cat = Chain.create() stateful function f4(x : (Animal => Cat)) : Animal = g4(x) @@ -48,11 +45,11 @@ main contract Main = stateful function f44(x : list(list(Animal) => list(Cat))) : Animal = g44(x) stateful function g5(x : (Animal => Animal)) : Cat = Chain.create() - stateful function f5(x : (Cat => Cat)) : Animal = g5(x) + stateful function f5(x : (Cat => Cat)) : Animal = g5(x) // fail stateful function g6() : option(Cat) = Some(Chain.create()) stateful function f6() : option(Animal) = g6() - stateful function h6() : option(Cat) = f6() + stateful function h6() : option(Cat) = f6() // fail type cat_type = Cat type animal_type = Animal -- 2.30.2 From ef3b0dece9b27ee6b7b21faa169ff193edecf499 Mon Sep 17 00:00:00 2001 From: Gaith Hallak Date: Wed, 25 May 2022 17:28:30 +0400 Subject: [PATCH 45/53] Rename datatypes in custom types variance switching test for readability --- test/aeso_compiler_tests.erl | 112 ++++++------ ...rphism_variance_switching_custom_types.aes | 170 +++++++++--------- 2 files changed, 141 insertions(+), 141 deletions(-) diff --git a/test/aeso_compiler_tests.erl b/test/aeso_compiler_tests.erl index d636bc9..bacc128 100644 --- a/test/aeso_compiler_tests.erl +++ b/test/aeso_compiler_tests.erl @@ -881,114 +881,114 @@ failing_contracts() -> "when checking the type of the expression `f6() : option(Animal)` against the expected type `option(Cat)`">> ]) , ?TYPE_ERROR(polymorphism_variance_switching_custom_types, - [<>, - <>, + <>, - <>, + < Animal) => tc(Animal)`\n" + " `DT_INV : ((Animal) => Animal) => dt_inv(Animal)`\n" "to arguments\n" " `f_a_to_c : (Animal) => Cat`">>, - < Cat) => tc(Cat)`\n" + " `DT_INV : ((Cat) => Cat) => dt_inv(Cat)`\n" "to arguments\n" " `f_c_to_a : (Cat) => Animal`">>, - <>, - <>, + <>, - <>, + < Animal) => tc(Animal)`\n" + " `DT_INV : ((Animal) => Animal) => dt_inv(Animal)`\n" "to arguments\n" " `f_a_to_c : (Animal) => Cat`">>, - < Cat) => tc(Cat)`\n" + " `DT_INV : ((Cat) => Cat) => dt_inv(Cat)`\n" "to arguments\n" " `f_c_to_a : (Cat) => Animal`">>, - <>, - <>, + <>, - <>, + <>, - <>, + <>, - <>, + <>, - <>, + <>, - <>, + <>, - <>, + <>, - <>, + < (unit) => Animal) => tj(Animal)`\n" + " `DT_CO_TWICE : ((Animal) => (unit) => Animal) => dt_co_twice(Animal)`\n" "to arguments\n" " `f_a_to_u_to_c : (Animal) => (unit) => Cat`">>, - < (unit) => Cat) => tj(Cat)`\n" + " `DT_CO_TWICE : ((Cat) => (unit) => Cat) => dt_co_twice(Cat)`\n" "to arguments\n" " `f_c_to_u_to_a : (Cat) => (unit) => Animal`">>, - <>, - <>, + < (unit) => Animal) => tj(Animal)`\n" + " `DT_CO_TWICE : ((Animal) => (unit) => Animal) => dt_co_twice(Animal)`\n" "to arguments\n" " `f_a_to_u_to_c : (Animal) => (unit) => Cat`">>, - < (unit) => Cat) => tj(Cat)`\n" + " `DT_CO_TWICE : ((Cat) => (unit) => Cat) => dt_co_twice(Cat)`\n" "to arguments\n" " `f_c_to_u_to_a : (Cat) => (unit) => Animal`">>, - <>, - <>, + <>, - <>, + <>, - <>, + <>, - <>, + <>, - <>, + <>, - <>, + <> + "when checking the type of the expression `DT_A_CO_B_CONTRA(f_a_to_c_to_u) : dt_a_co_b_contra(Animal, Cat)` against the expected type `dt_a_co_b_contra(Cat, Cat)`">> ]) , ?TYPE_ERROR(polymorphism_variance_switching_records, [< unit) - datatype tb('a) = TB(unit => 'a) - datatype tc('a) = TC('a => 'a) - datatype td('a) = TD(unit => unit) - datatype te('a) = TE1('a => unit) | TE2(unit => 'a) - datatype tf('a) = TF(ta('a) => unit) - datatype tg('a) = TG(tb('a) => unit) - datatype th('a) = TH(unit => ta('a)) - datatype ti('a) = TI(unit => tb('a)) - datatype tj('a) = TJ('a => unit => 'a) - datatype tk('a, 'b) = TK('a => 'b => unit) + datatype dt_contra('a) = DT_CONTRA('a => unit) + datatype dt_co('a) = DT_CO(unit => 'a) + datatype dt_inv('a) = DT_INV('a => 'a) + datatype dt_biv('a) = DT_BIV(unit => unit) + datatype dt_inv_sep('a) = DT_INV_SEP_A('a => unit) | DT_INV_SEP_B(unit => 'a) + datatype dt_co_nest_a('a) = DT_CO_NEST_A(dt_contra('a) => unit) + datatype dt_contra_nest_a('a) = DT_CONTRA_NEST_A(dt_co('a) => unit) + datatype dt_contra_nest_b('a) = DT_CONTRA_NEST_B(unit => dt_contra('a)) + datatype dt_co_nest_b('a) = DT_CO_NEST_B(unit => dt_co('a)) + datatype dt_co_twice('a) = DT_CO_TWICE('a => unit => 'a) + datatype dt_a_co_b_contra('a, 'b) = DT_A_CO_B_CONTRA('a => 'b => unit) function f_a_to_a_to_u(_ : Animal) : (Animal => unit) = f_a_to_u function f_a_to_c_to_u(_ : Animal) : (Cat => unit) = f_c_to_u @@ -26,12 +26,12 @@ main contract Main = function f_a_to_u(_ : Animal) : unit = () function f_c_to_u(_ : Cat) : unit = () - function f_ta_a_to_u(_ : ta(Animal)) : unit = () - function f_ta_c_to_u(_ : ta(Cat)) : unit = () - function f_tb_a_to_u(_ : tb(Animal)) : unit = () - function f_tb_c_to_u(_ : tb(Cat)) : unit = () - function f_u_to_ta_a(_ : unit) : ta(Animal) = TA(f_a_to_u) - function f_u_to_ta_c(_ : unit) : ta(Cat) = TA(f_c_to_u) + function f_ta_a_to_u(_ : dt_contra(Animal)) : unit = () + function f_ta_c_to_u(_ : dt_contra(Cat)) : unit = () + function f_tb_a_to_u(_ : dt_co(Animal)) : unit = () + function f_tb_c_to_u(_ : dt_co(Cat)) : unit = () + function f_u_to_ta_a(_ : unit) : dt_contra(Animal) = DT_CONTRA(f_a_to_u) + function f_u_to_ta_c(_ : unit) : dt_contra(Cat) = DT_CONTRA(f_c_to_u) stateful function f_c() : Cat = Chain.create() stateful function f_a() : Animal = f_c() @@ -48,85 +48,85 @@ main contract Main = stateful function f_c_to_u_to_a(_ : Cat) : (unit => Animal) = f_u_to_a stateful function f_c_to_u_to_c(_ : Cat) : (unit => Cat) = f_u_to_c - stateful function f_u_to_tb_a(_ : unit) : tb(Animal) = TB(f_u_to_a) - stateful function f_u_to_tb_c(_ : unit) : tb(Cat) = TB(f_u_to_c) + stateful function f_u_to_tb_a(_ : unit) : dt_co(Animal) = DT_CO(f_u_to_a) + stateful function f_u_to_tb_c(_ : unit) : dt_co(Cat) = DT_CO(f_u_to_c) stateful entrypoint init() = - let va1 : ta(Animal) = TA(f_a_to_u) // success - let va2 : ta(Animal) = TA(f_c_to_u) // fail - let va3 : ta(Cat) = TA(f_a_to_u) // success - let va4 : ta(Cat) = TA(f_c_to_u) // success + let va1 : dt_contra(Animal) = DT_CONTRA(f_a_to_u) // success + let va2 : dt_contra(Animal) = DT_CONTRA(f_c_to_u) // fail + let va3 : dt_contra(Cat) = DT_CONTRA(f_a_to_u) // success + let va4 : dt_contra(Cat) = DT_CONTRA(f_c_to_u) // success - let vb1 : tb(Animal) = TB(f_u_to_a) // success - let vb2 : tb(Animal) = TB(f_u_to_c) // success - let vb3 : tb(Cat) = TB(f_u_to_a) // fail - let vb4 : tb(Cat) = TB(f_u_to_c) // success + let vb1 : dt_co(Animal) = DT_CO(f_u_to_a) // success + let vb2 : dt_co(Animal) = DT_CO(f_u_to_c) // success + let vb3 : dt_co(Cat) = DT_CO(f_u_to_a) // fail + let vb4 : dt_co(Cat) = DT_CO(f_u_to_c) // success - let vc1 : tc(Animal) = TC(f_a_to_a) // success - let vc2 : tc(Animal) = TC(f_a_to_c) // fail - let vc3 : tc(Animal) = TC(f_c_to_a) // fail - let vc4 : tc(Animal) = TC(f_c_to_c) // fail - let vc5 : tc(Cat) = TC(f_a_to_a) // fail - let vc6 : tc(Cat) = TC(f_a_to_c) // fail - let vc7 : tc(Cat) = TC(f_c_to_a) // fail - let vc8 : tc(Cat) = TC(f_c_to_c) // success + let vc1 : dt_inv(Animal) = DT_INV(f_a_to_a) // success + let vc2 : dt_inv(Animal) = DT_INV(f_a_to_c) // fail + let vc3 : dt_inv(Animal) = DT_INV(f_c_to_a) // fail + let vc4 : dt_inv(Animal) = DT_INV(f_c_to_c) // fail + let vc5 : dt_inv(Cat) = DT_INV(f_a_to_a) // fail + let vc6 : dt_inv(Cat) = DT_INV(f_a_to_c) // fail + let vc7 : dt_inv(Cat) = DT_INV(f_c_to_a) // fail + let vc8 : dt_inv(Cat) = DT_INV(f_c_to_c) // success - let vd1 : td(Animal) = TD(f_u_to_u) // success - let vd2 : td(Cat) = TD(f_u_to_u) // success + let vd1 : dt_biv(Animal) = DT_BIV(f_u_to_u) // success + let vd2 : dt_biv(Cat) = DT_BIV(f_u_to_u) // success - let ve1 : te(Animal) = TE1(f_a_to_u) // success - let ve2 : te(Animal) = TE1(f_c_to_u) // fail - let ve3 : te(Animal) = TE2(f_u_to_a) // success - let ve4 : te(Animal) = TE2(f_u_to_c) // fail - let ve5 : te(Cat) = TE1(f_a_to_u) // fail - let ve6 : te(Cat) = TE1(f_c_to_u) // success - let ve7 : te(Cat) = TE2(f_u_to_a) // fail - let ve8 : te(Cat) = TE2(f_u_to_c) // success + let ve1 : dt_inv_sep(Animal) = DT_INV_SEP_A(f_a_to_u) // success + let ve2 : dt_inv_sep(Animal) = DT_INV_SEP_A(f_c_to_u) // fail + let ve3 : dt_inv_sep(Animal) = DT_INV_SEP_B(f_u_to_a) // success + let ve4 : dt_inv_sep(Animal) = DT_INV_SEP_B(f_u_to_c) // fail + let ve5 : dt_inv_sep(Cat) = DT_INV_SEP_A(f_a_to_u) // fail + let ve6 : dt_inv_sep(Cat) = DT_INV_SEP_A(f_c_to_u) // success + let ve7 : dt_inv_sep(Cat) = DT_INV_SEP_B(f_u_to_a) // fail + let ve8 : dt_inv_sep(Cat) = DT_INV_SEP_B(f_u_to_c) // success - let vf1 : tf(Animal) = TF(f_ta_a_to_u) // success - let vf2 : tf(Animal) = TF(f_ta_c_to_u) // success - let vf3 : tf(Cat) = TF(f_ta_a_to_u) // fail - let vf4 : tf(Cat) = TF(f_ta_c_to_u) // success + let vf1 : dt_co_nest_a(Animal) = DT_CO_NEST_A(f_ta_a_to_u) // success + let vf2 : dt_co_nest_a(Animal) = DT_CO_NEST_A(f_ta_c_to_u) // success + let vf3 : dt_co_nest_a(Cat) = DT_CO_NEST_A(f_ta_a_to_u) // fail + let vf4 : dt_co_nest_a(Cat) = DT_CO_NEST_A(f_ta_c_to_u) // success - let vg1 : tg(Animal) = TG(f_tb_a_to_u) // success - let vg2 : tg(Animal) = TG(f_tb_c_to_u) // fail - let vg3 : tg(Cat) = TG(f_tb_a_to_u) // success - let vg4 : tg(Cat) = TG(f_tb_c_to_u) // success + let vg1 : dt_contra_nest_a(Animal) = DT_CONTRA_NEST_A(f_tb_a_to_u) // success + let vg2 : dt_contra_nest_a(Animal) = DT_CONTRA_NEST_A(f_tb_c_to_u) // fail + let vg3 : dt_contra_nest_a(Cat) = DT_CONTRA_NEST_A(f_tb_a_to_u) // success + let vg4 : dt_contra_nest_a(Cat) = DT_CONTRA_NEST_A(f_tb_c_to_u) // success - let vh1 : th(Animal) = TH(f_u_to_ta_a) // success - let vh2 : th(Animal) = TH(f_u_to_ta_c) // fail - let vh3 : th(Cat) = TH(f_u_to_ta_a) // success - let vh4 : th(Cat) = TH(f_u_to_ta_c) // success + let vh1 : dt_contra_nest_b(Animal) = DT_CONTRA_NEST_B(f_u_to_ta_a) // success + let vh2 : dt_contra_nest_b(Animal) = DT_CONTRA_NEST_B(f_u_to_ta_c) // fail + let vh3 : dt_contra_nest_b(Cat) = DT_CONTRA_NEST_B(f_u_to_ta_a) // success + let vh4 : dt_contra_nest_b(Cat) = DT_CONTRA_NEST_B(f_u_to_ta_c) // success - let vi1 : ti(Animal) = TI(f_u_to_tb_a) // success - let vi2 : ti(Animal) = TI(f_u_to_tb_c) // success - let vi3 : ti(Cat) = TI(f_u_to_tb_a) // fail - let vi4 : ti(Cat) = TI(f_u_to_tb_c) // success + let vi1 : dt_co_nest_b(Animal) = DT_CO_NEST_B(f_u_to_tb_a) // success + let vi2 : dt_co_nest_b(Animal) = DT_CO_NEST_B(f_u_to_tb_c) // success + let vi3 : dt_co_nest_b(Cat) = DT_CO_NEST_B(f_u_to_tb_a) // fail + let vi4 : dt_co_nest_b(Cat) = DT_CO_NEST_B(f_u_to_tb_c) // success - let vj1 : tj(Animal) = TJ(f_a_to_u_to_a) // success - let vj2 : tj(Animal) = TJ(f_a_to_u_to_c) // fail - let vj3 : tj(Animal) = TJ(f_c_to_u_to_a) // fail - let vj4 : tj(Animal) = TJ(f_c_to_u_to_c) // success - let vj5 : tj(Cat) = TJ(f_a_to_u_to_a) // fail - let vj6 : tj(Cat) = TJ(f_a_to_u_to_c) // fail - let vj7 : tj(Cat) = TJ(f_c_to_u_to_a) // fail - let vj8 : tj(Cat) = TJ(f_c_to_u_to_c) // success + let vj1 : dt_co_twice(Animal) = DT_CO_TWICE(f_a_to_u_to_a) // success + let vj2 : dt_co_twice(Animal) = DT_CO_TWICE(f_a_to_u_to_c) // fail + let vj3 : dt_co_twice(Animal) = DT_CO_TWICE(f_c_to_u_to_a) // fail + let vj4 : dt_co_twice(Animal) = DT_CO_TWICE(f_c_to_u_to_c) // success + let vj5 : dt_co_twice(Cat) = DT_CO_TWICE(f_a_to_u_to_a) // fail + let vj6 : dt_co_twice(Cat) = DT_CO_TWICE(f_a_to_u_to_c) // fail + let vj7 : dt_co_twice(Cat) = DT_CO_TWICE(f_c_to_u_to_a) // fail + let vj8 : dt_co_twice(Cat) = DT_CO_TWICE(f_c_to_u_to_c) // success - let vk01 : tk(Animal, Animal) = TK(f_a_to_a_to_u) // success - let vk02 : tk(Animal, Animal) = TK(f_a_to_c_to_u) // fail - let vk03 : tk(Animal, Animal) = TK(f_c_to_a_to_u) // success - let vk04 : tk(Animal, Animal) = TK(f_c_to_c_to_u) // fail - let vk05 : tk(Animal, Cat) = TK(f_a_to_a_to_u) // success - let vk06 : tk(Animal, Cat) = TK(f_a_to_c_to_u) // success - let vk07 : tk(Animal, Cat) = TK(f_c_to_a_to_u) // success - let vk08 : tk(Animal, Cat) = TK(f_c_to_c_to_u) // success - let vk09 : tk(Cat, Animal) = TK(f_a_to_a_to_u) // fail - let vk10 : tk(Cat, Animal) = TK(f_a_to_c_to_u) // fail - let vk11 : tk(Cat, Animal) = TK(f_c_to_a_to_u) // success - let vk12 : tk(Cat, Animal) = TK(f_c_to_c_to_u) // fail - let vk13 : tk(Cat, Cat) = TK(f_a_to_a_to_u) // fail - let vk14 : tk(Cat, Cat) = TK(f_a_to_c_to_u) // fail - let vk15 : tk(Cat, Cat) = TK(f_c_to_a_to_u) // success - let vk16 : tk(Cat, Cat) = TK(f_c_to_c_to_u) // success + let vk01 : dt_a_co_b_contra(Animal, Animal) = DT_A_CO_B_CONTRA(f_a_to_a_to_u) // success + let vk02 : dt_a_co_b_contra(Animal, Animal) = DT_A_CO_B_CONTRA(f_a_to_c_to_u) // fail + let vk03 : dt_a_co_b_contra(Animal, Animal) = DT_A_CO_B_CONTRA(f_c_to_a_to_u) // success + let vk04 : dt_a_co_b_contra(Animal, Animal) = DT_A_CO_B_CONTRA(f_c_to_c_to_u) // fail + let vk05 : dt_a_co_b_contra(Animal, Cat) = DT_A_CO_B_CONTRA(f_a_to_a_to_u) // success + let vk06 : dt_a_co_b_contra(Animal, Cat) = DT_A_CO_B_CONTRA(f_a_to_c_to_u) // success + let vk07 : dt_a_co_b_contra(Animal, Cat) = DT_A_CO_B_CONTRA(f_c_to_a_to_u) // success + let vk08 : dt_a_co_b_contra(Animal, Cat) = DT_A_CO_B_CONTRA(f_c_to_c_to_u) // success + let vk09 : dt_a_co_b_contra(Cat, Animal) = DT_A_CO_B_CONTRA(f_a_to_a_to_u) // fail + let vk10 : dt_a_co_b_contra(Cat, Animal) = DT_A_CO_B_CONTRA(f_a_to_c_to_u) // fail + let vk11 : dt_a_co_b_contra(Cat, Animal) = DT_A_CO_B_CONTRA(f_c_to_a_to_u) // success + let vk12 : dt_a_co_b_contra(Cat, Animal) = DT_A_CO_B_CONTRA(f_c_to_c_to_u) // fail + let vk13 : dt_a_co_b_contra(Cat, Cat) = DT_A_CO_B_CONTRA(f_a_to_a_to_u) // fail + let vk14 : dt_a_co_b_contra(Cat, Cat) = DT_A_CO_B_CONTRA(f_a_to_c_to_u) // fail + let vk15 : dt_a_co_b_contra(Cat, Cat) = DT_A_CO_B_CONTRA(f_c_to_a_to_u) // success + let vk16 : dt_a_co_b_contra(Cat, Cat) = DT_A_CO_B_CONTRA(f_c_to_c_to_u) // success () -- 2.30.2 From 0c8b95c16d92202ccfa86091282043eedae0f7fa Mon Sep 17 00:00:00 2001 From: Gaith Hallak Date: Wed, 25 May 2022 17:43:58 +0400 Subject: [PATCH 46/53] Change the variance of the oracle_query type vars --- src/aeso_ast_infer_types.erl | 2 +- test/aeso_compiler_tests.erl | 20 +++++++++---------- ...olymorphism_variance_switching_oracles.aes | 12 +++++------ 3 files changed, 17 insertions(+), 17 deletions(-) diff --git a/src/aeso_ast_infer_types.erl b/src/aeso_ast_infer_types.erl index 9582495..5b69ff6 100644 --- a/src/aeso_ast_infer_types.erl +++ b/src/aeso_ast_infer_types.erl @@ -816,7 +816,7 @@ infer(Contracts, Options) -> ets_insert(type_vars_variance, {"list", [covariant]}), ets_insert(type_vars_variance, {"option", [covariant]}), ets_insert(type_vars_variance, {"oracle", [contravariant, covariant]}), - ets_insert(type_vars_variance, {"oracle_query", [covariant, contravariant]}), + ets_insert(type_vars_variance, {"oracle_query", [covariant, covariant]}), when_warning(warn_unused_functions, fun() -> create_unused_functions() end), check_modifiers(Env, Contracts), diff --git a/test/aeso_compiler_tests.erl b/test/aeso_compiler_tests.erl index bacc128..35d7485 100644 --- a/test/aeso_compiler_tests.erl +++ b/test/aeso_compiler_tests.erl @@ -1025,27 +1025,27 @@ failing_contracts() -> <>, - <>, - <>, + <>, + <>, <>, <>, - <>, <>, <>]) + "when checking the type of the pattern `q14 : oracle_query(Cat, Cat)` against the expected type `oracle_query(Animal, Cat)`">>, + <>]) ]. -define(Path(File), "code_errors/" ??File). diff --git a/test/contracts/polymorphism_variance_switching_oracles.aes b/test/contracts/polymorphism_variance_switching_oracles.aes index 2cee15b..42849d6 100644 --- a/test/contracts/polymorphism_variance_switching_oracles.aes +++ b/test/contracts/polymorphism_variance_switching_oracles.aes @@ -28,20 +28,20 @@ main contract Main = let o16 : oracle(Cat, Cat) = oracle() : oracle(Cat, Cat) // success let q01 : oracle_query(Animal, Animal) = query() : oracle_query(Animal, Animal) // success - let q02 : oracle_query(Animal, Animal) = query() : oracle_query(Animal, Cat) // fail + let q02 : oracle_query(Animal, Animal) = query() : oracle_query(Animal, Cat) // success let q03 : oracle_query(Animal, Animal) = query() : oracle_query(Cat, Animal) // success - let q04 : oracle_query(Animal, Animal) = query() : oracle_query(Cat, Cat) // fail - let q05 : oracle_query(Animal, Cat) = query() : oracle_query(Animal, Animal) // success + let q04 : oracle_query(Animal, Animal) = query() : oracle_query(Cat, Cat) // success + let q05 : oracle_query(Animal, Cat) = query() : oracle_query(Animal, Animal) // fail let q06 : oracle_query(Animal, Cat) = query() : oracle_query(Animal, Cat) // success - let q07 : oracle_query(Animal, Cat) = query() : oracle_query(Cat, Animal) // success + let q07 : oracle_query(Animal, Cat) = query() : oracle_query(Cat, Animal) // fail let q08 : oracle_query(Animal, Cat) = query() : oracle_query(Cat, Cat) // success let q09 : oracle_query(Cat, Animal) = query() : oracle_query(Animal, Animal) // fail let q10 : oracle_query(Cat, Animal) = query() : oracle_query(Animal, Cat) // fail let q11 : oracle_query(Cat, Animal) = query() : oracle_query(Cat, Animal) // success - let q12 : oracle_query(Cat, Animal) = query() : oracle_query(Cat, Cat) // fail + let q12 : oracle_query(Cat, Animal) = query() : oracle_query(Cat, Cat) // success let q13 : oracle_query(Cat, Cat) = query() : oracle_query(Animal, Animal) // fail let q14 : oracle_query(Cat, Cat) = query() : oracle_query(Animal, Cat) // fail - let q15 : oracle_query(Cat, Cat) = query() : oracle_query(Cat, Animal) // success + let q15 : oracle_query(Cat, Cat) = query() : oracle_query(Cat, Animal) // fail let q16 : oracle_query(Cat, Cat) = query() : oracle_query(Cat, Cat) // success () \ No newline at end of file -- 2.30.2 From 8d419bc54fc925a1c1efa0927dd35993ba05f59b Mon Sep 17 00:00:00 2001 From: Gaith Hallak Date: Wed, 25 May 2022 18:35:13 +0400 Subject: [PATCH 47/53] Add test for accessing maps with the wrong type --- test/aeso_compiler_tests.erl | 7 +++++-- test/contracts/polymorphism_variance_switching.aes | 12 ++++++++++++ 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/test/aeso_compiler_tests.erl b/test/aeso_compiler_tests.erl index 35d7485..8e54991 100644 --- a/test/aeso_compiler_tests.erl +++ b/test/aeso_compiler_tests.erl @@ -876,9 +876,12 @@ failing_contracts() -> " `g5 : ((Animal) => Animal) => Cat`\n" "to arguments\n" " `x : (Cat) => Cat`">>, - <> + "when checking the type of the expression `f6() : option(Animal)` against the expected type `option(Cat)`">>, + <> ]) , ?TYPE_ERROR(polymorphism_variance_switching_custom_types, [< Date: Wed, 25 May 2022 18:46:53 +0400 Subject: [PATCH 48/53] Default to invariant when the variance of the type vars is unknown --- src/aeso_ast_infer_types.erl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/aeso_ast_infer_types.erl b/src/aeso_ast_infer_types.erl index 5b69ff6..a28cf12 100644 --- a/src/aeso_ast_infer_types.erl +++ b/src/aeso_ast_infer_types.erl @@ -815,6 +815,7 @@ infer(Contracts, Options) -> %% Set the variance for builtin types ets_insert(type_vars_variance, {"list", [covariant]}), ets_insert(type_vars_variance, {"option", [covariant]}), + ets_insert(type_vars_variance, {"map", [covariant, covariant]}), ets_insert(type_vars_variance, {"oracle", [contravariant, covariant]}), ets_insert(type_vars_variance, {"oracle_query", [covariant, covariant]}), @@ -2777,7 +2778,7 @@ unify1(Env, {app_t, _, {Tag, _, F}, Args1}, {app_t, _, {Tag, _, F}, Args2}, Vari invariant -> invariant; _ -> Vs end; - _ -> Variance + _ -> invariant end, unify1(Env, Args1, Args2, Variances, When); unify1(Env, {tuple_t, _, As}, {tuple_t, _, Bs}, Variance, When) -- 2.30.2 From e4db742c72b7e76f1ba72afdc29eae9b2b1d793f Mon Sep 17 00:00:00 2001 From: Gaith Hallak Date: Wed, 25 May 2022 19:55:01 +0400 Subject: [PATCH 49/53] Rename test files to have common prefix --- test/aeso_compiler_tests.erl | 26 +++++++++---------- ...orphism_contract_implements_interface.aes} | 0 ..._contract_interface_extends_interface.aes} | 0 ...orphism_contract_interface_extensions.aes} | 0 ...morphism_contract_interface_recursive.aes} | 0 ...t_interface_same_decl_multi_interface.aes} | 0 ...ct_interface_same_name_different_type.aes} | 0 ...ontract_interface_same_name_same_type.aes} | 0 ...ontract_interface_undefined_interface.aes} | 0 ...phism_contract_missing_implementation.aes} | 0 ...polymorphism_contract_multi_interface.aes} | 0 ...sm_contract_same_decl_multi_interface.aes} | 0 ...e_name_different_type_multi_interface.aes} | 0 ...morphism_contract_undefined_interface.aes} | 0 14 files changed, 13 insertions(+), 13 deletions(-) rename test/contracts/{contract_polymorphism.aes => polymorphism_contract_implements_interface.aes} (100%) rename test/contracts/{contract_interface_polymorphism.aes => polymorphism_contract_interface_extends_interface.aes} (100%) rename test/contracts/{contract_polymorphism_interface_extensions.aes => polymorphism_contract_interface_extensions.aes} (100%) rename test/contracts/{contract_interface_polymorphism_recursive.aes => polymorphism_contract_interface_recursive.aes} (100%) rename test/contracts/{contract_interface_polymorphism_same_decl_multi_interface.aes => polymorphism_contract_interface_same_decl_multi_interface.aes} (100%) rename test/contracts/{contract_interface_polymorphism_same_name_different_type.aes => polymorphism_contract_interface_same_name_different_type.aes} (100%) rename test/contracts/{contract_interface_polymorphism_same_name_same_type.aes => polymorphism_contract_interface_same_name_same_type.aes} (100%) rename test/contracts/{contract_interface_polymorphism_undefined_interface.aes => polymorphism_contract_interface_undefined_interface.aes} (100%) rename test/contracts/{contract_polymorphism_missing_implementation.aes => polymorphism_contract_missing_implementation.aes} (100%) rename test/contracts/{contract_polymorphism_multi_interface.aes => polymorphism_contract_multi_interface.aes} (100%) rename test/contracts/{contract_polymorphism_same_decl_multi_interface.aes => polymorphism_contract_same_decl_multi_interface.aes} (100%) rename test/contracts/{contract_polymorphism_same_name_different_type_multi_interface.aes => polymorphism_contract_same_name_different_type_multi_interface.aes} (100%) rename test/contracts/{contract_polymorphism_undefined_interface.aes => polymorphism_contract_undefined_interface.aes} (100%) diff --git a/test/aeso_compiler_tests.erl b/test/aeso_compiler_tests.erl index 8e54991..5c81abd 100644 --- a/test/aeso_compiler_tests.erl +++ b/test/aeso_compiler_tests.erl @@ -202,12 +202,12 @@ compilable_contracts() -> "assign_patterns", "patterns_guards", "pipe_operator", - "contract_polymorphism", - "contract_polymorphism_multi_interface", - "contract_interface_polymorphism", - "contract_polymorphism_interface_extensions", - "contract_interface_polymorphism_same_decl_multi_interface", - "contract_interface_polymorphism_same_name_same_type", + "polymorphism_contract_implements_interface", + "polymorphism_contract_multi_interface", + "polymorphism_contract_interface_extends_interface", + "polymorphism_contract_interface_extensions", + "polymorphism_contract_interface_same_decl_multi_interface", + "polymorphism_contract_interface_same_name_same_type", "test" % Custom general-purpose test file. Keep it last on the list. ]. @@ -831,32 +831,32 @@ failing_contracts() -> <> ]) - , ?TYPE_ERROR(contract_interface_polymorphism_recursive, + , ?TYPE_ERROR(polymorphism_contract_interface_recursive, [<> ]) - , ?TYPE_ERROR(contract_interface_polymorphism_same_name_different_type, + , ?TYPE_ERROR(polymorphism_contract_interface_same_name_different_type, [<>]) - , ?TYPE_ERROR(contract_polymorphism_missing_implementation, + , ?TYPE_ERROR(polymorphism_contract_missing_implementation, [<> ]) - , ?TYPE_ERROR(contract_polymorphism_same_decl_multi_interface, + , ?TYPE_ERROR(polymorphism_contract_same_decl_multi_interface, [<> ]) - , ?TYPE_ERROR(contract_polymorphism_undefined_interface, + , ?TYPE_ERROR(polymorphism_contract_undefined_interface, [<> ]) - , ?TYPE_ERROR(contract_polymorphism_same_name_different_type_multi_interface, + , ?TYPE_ERROR(polymorphism_contract_same_name_different_type_multi_interface, [<> ]) - , ?TYPE_ERROR(contract_interface_polymorphism_undefined_interface, + , ?TYPE_ERROR(polymorphism_contract_interface_undefined_interface, [<> ]) diff --git a/test/contracts/contract_polymorphism.aes b/test/contracts/polymorphism_contract_implements_interface.aes similarity index 100% rename from test/contracts/contract_polymorphism.aes rename to test/contracts/polymorphism_contract_implements_interface.aes diff --git a/test/contracts/contract_interface_polymorphism.aes b/test/contracts/polymorphism_contract_interface_extends_interface.aes similarity index 100% rename from test/contracts/contract_interface_polymorphism.aes rename to test/contracts/polymorphism_contract_interface_extends_interface.aes diff --git a/test/contracts/contract_polymorphism_interface_extensions.aes b/test/contracts/polymorphism_contract_interface_extensions.aes similarity index 100% rename from test/contracts/contract_polymorphism_interface_extensions.aes rename to test/contracts/polymorphism_contract_interface_extensions.aes diff --git a/test/contracts/contract_interface_polymorphism_recursive.aes b/test/contracts/polymorphism_contract_interface_recursive.aes similarity index 100% rename from test/contracts/contract_interface_polymorphism_recursive.aes rename to test/contracts/polymorphism_contract_interface_recursive.aes diff --git a/test/contracts/contract_interface_polymorphism_same_decl_multi_interface.aes b/test/contracts/polymorphism_contract_interface_same_decl_multi_interface.aes similarity index 100% rename from test/contracts/contract_interface_polymorphism_same_decl_multi_interface.aes rename to test/contracts/polymorphism_contract_interface_same_decl_multi_interface.aes diff --git a/test/contracts/contract_interface_polymorphism_same_name_different_type.aes b/test/contracts/polymorphism_contract_interface_same_name_different_type.aes similarity index 100% rename from test/contracts/contract_interface_polymorphism_same_name_different_type.aes rename to test/contracts/polymorphism_contract_interface_same_name_different_type.aes diff --git a/test/contracts/contract_interface_polymorphism_same_name_same_type.aes b/test/contracts/polymorphism_contract_interface_same_name_same_type.aes similarity index 100% rename from test/contracts/contract_interface_polymorphism_same_name_same_type.aes rename to test/contracts/polymorphism_contract_interface_same_name_same_type.aes diff --git a/test/contracts/contract_interface_polymorphism_undefined_interface.aes b/test/contracts/polymorphism_contract_interface_undefined_interface.aes similarity index 100% rename from test/contracts/contract_interface_polymorphism_undefined_interface.aes rename to test/contracts/polymorphism_contract_interface_undefined_interface.aes diff --git a/test/contracts/contract_polymorphism_missing_implementation.aes b/test/contracts/polymorphism_contract_missing_implementation.aes similarity index 100% rename from test/contracts/contract_polymorphism_missing_implementation.aes rename to test/contracts/polymorphism_contract_missing_implementation.aes diff --git a/test/contracts/contract_polymorphism_multi_interface.aes b/test/contracts/polymorphism_contract_multi_interface.aes similarity index 100% rename from test/contracts/contract_polymorphism_multi_interface.aes rename to test/contracts/polymorphism_contract_multi_interface.aes diff --git a/test/contracts/contract_polymorphism_same_decl_multi_interface.aes b/test/contracts/polymorphism_contract_same_decl_multi_interface.aes similarity index 100% rename from test/contracts/contract_polymorphism_same_decl_multi_interface.aes rename to test/contracts/polymorphism_contract_same_decl_multi_interface.aes diff --git a/test/contracts/contract_polymorphism_same_name_different_type_multi_interface.aes b/test/contracts/polymorphism_contract_same_name_different_type_multi_interface.aes similarity index 100% rename from test/contracts/contract_polymorphism_same_name_different_type_multi_interface.aes rename to test/contracts/polymorphism_contract_same_name_different_type_multi_interface.aes diff --git a/test/contracts/contract_polymorphism_undefined_interface.aes b/test/contracts/polymorphism_contract_undefined_interface.aes similarity index 100% rename from test/contracts/contract_polymorphism_undefined_interface.aes rename to test/contracts/polymorphism_contract_undefined_interface.aes -- 2.30.2 From 24178f15d0e08c4323a0469e1ac1009706cdbaca Mon Sep 17 00:00:00 2001 From: Gaith Hallak Date: Wed, 25 May 2022 21:10:06 +0400 Subject: [PATCH 50/53] Rename functions in variance switching tests for readability --- test/aeso_compiler_tests.erl | 8 ++-- ...rphism_variance_switching_custom_types.aes | 48 +++++++++---------- 2 files changed, 28 insertions(+), 28 deletions(-) diff --git a/test/aeso_compiler_tests.erl b/test/aeso_compiler_tests.erl index 5c81abd..04268b6 100644 --- a/test/aeso_compiler_tests.erl +++ b/test/aeso_compiler_tests.erl @@ -934,16 +934,16 @@ failing_contracts() -> "when checking the type of the expression `DT_INV_SEP_B(f_u_to_a) : dt_inv_sep(Animal)` against the expected type `dt_inv_sep(Cat)`">>, <>, + "when checking the type of the expression `DT_CO_NEST_A(f_dt_contra_a_to_u) : dt_co_nest_a(Animal)` against the expected type `dt_co_nest_a(Cat)`">>, <>, + "when checking the type of the expression `DT_CONTRA_NEST_A(f_dt_co_c_to_u) : dt_contra_nest_a(Cat)` against the expected type `dt_contra_nest_a(Animal)`">>, <>, + "when checking the type of the expression `DT_CONTRA_NEST_B(f_u_to_dt_contra_c) : dt_contra_nest_b(Cat)` against the expected type `dt_contra_nest_b(Animal)`">>, <>, + "when checking the type of the expression `DT_CO_NEST_B(f_u_to_dt_co_a) : dt_co_nest_b(Animal)` against the expected type `dt_co_nest_b(Cat)`">>, < Animal) = f_u_to_a stateful function f_c_to_u_to_c(_ : Cat) : (unit => Cat) = f_u_to_c - stateful function f_u_to_tb_a(_ : unit) : dt_co(Animal) = DT_CO(f_u_to_a) - stateful function f_u_to_tb_c(_ : unit) : dt_co(Cat) = DT_CO(f_u_to_c) + stateful function f_u_to_dt_co_a(_ : unit) : dt_co(Animal) = DT_CO(f_u_to_a) + stateful function f_u_to_dt_co_c(_ : unit) : dt_co(Cat) = DT_CO(f_u_to_c) stateful entrypoint init() = let va1 : dt_contra(Animal) = DT_CONTRA(f_a_to_u) // success @@ -83,25 +83,25 @@ main contract Main = let ve7 : dt_inv_sep(Cat) = DT_INV_SEP_B(f_u_to_a) // fail let ve8 : dt_inv_sep(Cat) = DT_INV_SEP_B(f_u_to_c) // success - let vf1 : dt_co_nest_a(Animal) = DT_CO_NEST_A(f_ta_a_to_u) // success - let vf2 : dt_co_nest_a(Animal) = DT_CO_NEST_A(f_ta_c_to_u) // success - let vf3 : dt_co_nest_a(Cat) = DT_CO_NEST_A(f_ta_a_to_u) // fail - let vf4 : dt_co_nest_a(Cat) = DT_CO_NEST_A(f_ta_c_to_u) // success + let vf1 : dt_co_nest_a(Animal) = DT_CO_NEST_A(f_dt_contra_a_to_u) // success + let vf2 : dt_co_nest_a(Animal) = DT_CO_NEST_A(f_dt_contra_c_to_u) // success + let vf3 : dt_co_nest_a(Cat) = DT_CO_NEST_A(f_dt_contra_a_to_u) // fail + let vf4 : dt_co_nest_a(Cat) = DT_CO_NEST_A(f_dt_contra_c_to_u) // success - let vg1 : dt_contra_nest_a(Animal) = DT_CONTRA_NEST_A(f_tb_a_to_u) // success - let vg2 : dt_contra_nest_a(Animal) = DT_CONTRA_NEST_A(f_tb_c_to_u) // fail - let vg3 : dt_contra_nest_a(Cat) = DT_CONTRA_NEST_A(f_tb_a_to_u) // success - let vg4 : dt_contra_nest_a(Cat) = DT_CONTRA_NEST_A(f_tb_c_to_u) // success + let vg1 : dt_contra_nest_a(Animal) = DT_CONTRA_NEST_A(f_dt_co_a_to_u) // success + let vg2 : dt_contra_nest_a(Animal) = DT_CONTRA_NEST_A(f_dt_co_c_to_u) // fail + let vg3 : dt_contra_nest_a(Cat) = DT_CONTRA_NEST_A(f_dt_co_a_to_u) // success + let vg4 : dt_contra_nest_a(Cat) = DT_CONTRA_NEST_A(f_dt_co_c_to_u) // success - let vh1 : dt_contra_nest_b(Animal) = DT_CONTRA_NEST_B(f_u_to_ta_a) // success - let vh2 : dt_contra_nest_b(Animal) = DT_CONTRA_NEST_B(f_u_to_ta_c) // fail - let vh3 : dt_contra_nest_b(Cat) = DT_CONTRA_NEST_B(f_u_to_ta_a) // success - let vh4 : dt_contra_nest_b(Cat) = DT_CONTRA_NEST_B(f_u_to_ta_c) // success + let vh1 : dt_contra_nest_b(Animal) = DT_CONTRA_NEST_B(f_u_to_dt_contra_a) // success + let vh2 : dt_contra_nest_b(Animal) = DT_CONTRA_NEST_B(f_u_to_dt_contra_c) // fail + let vh3 : dt_contra_nest_b(Cat) = DT_CONTRA_NEST_B(f_u_to_dt_contra_a) // success + let vh4 : dt_contra_nest_b(Cat) = DT_CONTRA_NEST_B(f_u_to_dt_contra_c) // success - let vi1 : dt_co_nest_b(Animal) = DT_CO_NEST_B(f_u_to_tb_a) // success - let vi2 : dt_co_nest_b(Animal) = DT_CO_NEST_B(f_u_to_tb_c) // success - let vi3 : dt_co_nest_b(Cat) = DT_CO_NEST_B(f_u_to_tb_a) // fail - let vi4 : dt_co_nest_b(Cat) = DT_CO_NEST_B(f_u_to_tb_c) // success + let vi1 : dt_co_nest_b(Animal) = DT_CO_NEST_B(f_u_to_dt_co_a) // success + let vi2 : dt_co_nest_b(Animal) = DT_CO_NEST_B(f_u_to_dt_co_c) // success + let vi3 : dt_co_nest_b(Cat) = DT_CO_NEST_B(f_u_to_dt_co_a) // fail + let vi4 : dt_co_nest_b(Cat) = DT_CO_NEST_B(f_u_to_dt_co_c) // success let vj1 : dt_co_twice(Animal) = DT_CO_TWICE(f_a_to_u_to_a) // success let vj2 : dt_co_twice(Animal) = DT_CO_TWICE(f_a_to_u_to_c) // fail -- 2.30.2 From 113d699b01baeec6ec15da0d393d616d633eac4e Mon Sep 17 00:00:00 2001 From: Gaith Hallak Date: Fri, 27 May 2022 15:45:26 +0400 Subject: [PATCH 51/53] Fix variance inference --- src/aeso_ast_infer_types.erl | 68 ++------- test/aeso_compiler_tests.erl | 131 ++++++++---------- ...rphism_variance_switching_custom_types.aes | 63 +++++---- 3 files changed, 112 insertions(+), 150 deletions(-) diff --git a/src/aeso_ast_infer_types.erl b/src/aeso_ast_infer_types.erl index a28cf12..fffc56f 100644 --- a/src/aeso_ast_infer_types.erl +++ b/src/aeso_ast_infer_types.erl @@ -809,7 +809,6 @@ infer(Contracts, Options) -> create_options(Options), ets_new(defined_contracts, [bag]), ets_new(type_vars, [set]), - ets_new(type_vars_uvar, [set]), ets_new(warnings, [bag]), ets_new(type_vars_variance, [set]), %% Set the variance for builtin types @@ -1126,7 +1125,7 @@ infer_type_vars_variance(TypeParams, Cons) -> % args from all type constructors FlatArgs = lists:flatten([Args || {constr_t, _, _, Args} <- Cons]) ++ [Type || {field_t, _, _, Type} <- Cons], - Vs = lists:flatten([element(1, infer_type_vars_variance(Arg)) || Arg <- FlatArgs]), + Vs = lists:flatten([infer_type_vars_variance(Arg) || Arg <- FlatArgs]), lists:map(fun({tvar, _, TVar}) -> S = sets:from_list([Variance || {TV, Variance} <- Vs, TV == TVar]), IsCovariant = sets:is_element(covariant, S), @@ -1139,7 +1138,10 @@ infer_type_vars_variance(TypeParams, Cons) -> end end, TypeParams). --spec infer_type_vars_variance(utype()) -> {[{name(), variance()}], integer()}. +-spec infer_type_vars_variance(utype()) -> [{name(), variance()}]. +infer_type_vars_variance(Types) + when is_list(Types) -> + lists:flatten([infer_type_vars_variance(T) || T <- Types]); infer_type_vars_variance({app_t, _, Type, Args}) -> Variances = case ets_lookup(type_vars_variance, qname(Type)) of [{_, Vs}] -> Vs; @@ -1147,36 +1149,14 @@ infer_type_vars_variance({app_t, _, Type, Args}) -> end, TypeVarsVariance = [{TVar, Variance} || {{tvar, _, TVar}, Variance} <- lists:zip(Args, Variances)], - {TypeVarsVariance, 0}; -infer_type_vars_variance({fun_t, _, [], [{app_t, _, Type, Args}], Res}) -> - Variances = case ets_lookup(type_vars_variance, qname(Type)) of - [{_, Vs}] -> Vs; - _ -> lists:duplicate(length(Args), covariant) - end, - TypeVarsVariance = [{TVar, Variance} - || {{tvar, _, TVar}, Variance} <- lists:zip(Args, Variances)], - FlipVariance = fun({TVar, covariant}) -> {TVar, contravariant}; - ({TVar, contravariant}) -> {TVar, covariant} - end, - {TVVs, Depth} = infer_type_vars_variance(Res), - Cur = case (Depth + 1) rem 2 of - 0 -> TypeVarsVariance; - 1 -> lists:map(FlipVariance, TypeVarsVariance) - end, - {Cur ++ TVVs, Depth + 1}; -infer_type_vars_variance({fun_t, _, [], [{tvar, _, TVar}], Res}) -> - {TVVs, Depth} = infer_type_vars_variance(Res), - Cur = case (Depth + 1) rem 2 of - 0 -> {TVar, covariant}; - 1 -> {TVar, contravariant} - end, - {[Cur | TVVs], Depth + 1}; -infer_type_vars_variance({fun_t, _, [], _, Res}) -> - {X, Depth} = infer_type_vars_variance(Res), - {X, Depth + 1}; -infer_type_vars_variance({tvar, _, TVar}) -> - {[{TVar, covariant}], 0}; -infer_type_vars_variance(_) -> {[], 0}. + TypeVarsVariance; +infer_type_vars_variance({tvar, _, TVar}) -> [{TVar, covariant}]; +infer_type_vars_variance({fun_t, _, [], Args, Res}) -> + ArgsVariance = infer_type_vars_variance(Args), + ResVariance = infer_type_vars_variance(Res), + FlippedArgsVariance = lists:map(fun({TVar, Variance}) -> {TVar, opposite_variance(Variance)} end, ArgsVariance), + FlippedArgsVariance ++ ResVariance; +infer_type_vars_variance(_) -> []. opposite_variance(invariant) -> invariant; opposite_variance(covariant) -> contravariant; @@ -1773,16 +1753,6 @@ infer_expr(Env, {app, Ann, Fun, Args0} = App) -> ResultType = fresh_uvar(Ann), when_warning(warn_unused_functions, fun() -> register_function_call(Namespace ++ qname(CurrentFun), Name) end), - % the uvars of tvars are stored so that no variance switching happens - % in between them (e.g. in TC('a => 'a), 'a should be a single type) - case FunType of - {fun_t, _, _, _, {app_t, _, _, TArgs}} -> - lists:foreach(fun({uvar, _, URef}) -> - ets_insert(type_vars_uvar, {URef}); - (_) -> ok - end, TArgs); - _ -> ok - end, unify(Env, FunType, {fun_t, [], NamedArgsVar, ArgTypes, GeneralResultType}, When), when_warning(warn_negative_spend, fun() -> warn_potential_negative_spend(Ann, NewFun1, NewArgs) end), add_constraint( @@ -2143,7 +2113,7 @@ next_count() -> ets_tables() -> [options, type_vars, constraints, freshen_tvars, type_errors, defined_contracts, warnings, function_calls, all_functions, - type_vars_variance, type_vars_uvar]. + type_vars_variance]. clean_up_ets() -> [ catch ets_delete(Tab) || Tab <- ets_tables() ], @@ -2687,7 +2657,7 @@ unify(Env, A, B, When) -> unify0(Env, A, B, covariant, When). unify0(_, {id, _, "_"}, _, _Variance, _When) -> true; unify0(_, _, {id, _, "_"}, _Variance, _When) -> true; -unify0(Env, A, B, Variance0, When) -> +unify0(Env, A, B, Variance, When) -> Options = case When of %% Improve source location for map_in_map_key errors {check_expr, E, _, _} -> [{ann, aeso_syntax:get_ann(E)}]; @@ -2695,14 +2665,6 @@ unify0(Env, A, B, Variance0, When) -> end, A1 = dereference(unfold_types_in_type(Env, A, Options)), B1 = dereference(unfold_types_in_type(Env, B, Options)), - Variance = case A of - {uvar, _,URef} -> - case ets_lookup(type_vars_uvar, URef) of - [_] -> invariant; - _ -> Variance0 - end; - _ -> Variance0 - end, unify1(Env, A1, B1, Variance, When). unify1(_Env, {uvar, _, R}, {uvar, _, R}, _Variance, _When) -> diff --git a/test/aeso_compiler_tests.erl b/test/aeso_compiler_tests.erl index 04268b6..f1feeda 100644 --- a/test/aeso_compiler_tests.erl +++ b/test/aeso_compiler_tests.erl @@ -884,114 +884,105 @@ failing_contracts() -> "when checking the type of the expression `some_animal : Animal` against the expected type `Cat`">> ]) , ?TYPE_ERROR(polymorphism_variance_switching_custom_types, - [<>, - <>, - < Animal) => dt_inv(Animal)`\n" - "to arguments\n" - " `f_a_to_c : (Animal) => Cat`">>, - < Cat) => dt_inv(Cat)`\n" - "to arguments\n" - " `f_c_to_a : (Cat) => Animal`">>, < Cat) => dt_inv(Cat)`\nto arguments\n `f_c_to_a : (Cat) => Animal`">>, + <>, - <>, < Animal) => dt_inv(Animal)`\n" - "to arguments\n" - " `f_a_to_c : (Animal) => Cat`">>, + "when checking the type of the expression `DT_INV(f_a_to_a) : dt_inv(Animal)` against the expected type `dt_inv(Cat)`">>, < Cat) => dt_inv(Cat)`\n" - "to arguments\n" - " `f_c_to_a : (Cat) => Animal`">>, - <>, + < Cat) => dt_inv(Cat)`\nto arguments\n `f_c_to_a : (Cat) => Animal`">>, + <>, - <>, - <>, - <>, - <>, - <>, - <>, - <>, - < (unit) => Animal) => dt_co_twice(Animal)`\n" + " `DT_CO_TWICE : (((Cat) => unit) => Cat) => dt_co_twice(Cat)`\n" "to arguments\n" - " `f_a_to_u_to_c : (Animal) => (unit) => Cat`">>, - < (unit) => Cat) => dt_co_twice(Cat)`\n" - "to arguments\n" - " `f_c_to_u_to_a : (Cat) => (unit) => Animal`">>, - < unit) => Animal`">>, + <>, - < (unit) => Animal) => dt_co_twice(Animal)`\n" - "to arguments\n" - " `f_a_to_u_to_c : (Animal) => (unit) => Cat`">>, <>, + < (unit) => Cat) => dt_co_twice(Cat)`\n" + " `DT_CO_TWICE : (((Cat) => unit) => Cat) => dt_co_twice(Cat)`\n" "to arguments\n" - " `f_c_to_u_to_a : (Cat) => (unit) => Animal`">>, - < unit) => Animal`">>, + <>, - <>, + <>, - <>, - <>, - <>, + <>, - <>, + <>, + <>, + <>, + <>, + <>, - < (Animal) => unit) => dt_contra_twice(Animal)`\nto arguments\n `f_a_to_c_to_u : (Animal) => (Cat) => unit`">>, + <>, + <>, + <> + "when checking the application of\n" + " `DT_CONTRA_TWICE : ((Animal) => (Animal) => unit) => dt_contra_twice(Animal)`\n" + "to arguments\n" + " `f_a_to_c_to_u : (Animal) => (Cat) => unit`">> ]) , ?TYPE_ERROR(polymorphism_variance_switching_records, [< unit) datatype dt_contra_nest_b('a) = DT_CONTRA_NEST_B(unit => dt_contra('a)) datatype dt_co_nest_b('a) = DT_CO_NEST_B(unit => dt_co('a)) - datatype dt_co_twice('a) = DT_CO_TWICE('a => unit => 'a) - datatype dt_a_co_b_contra('a, 'b) = DT_A_CO_B_CONTRA('a => 'b => unit) + datatype dt_co_twice('a) = DT_CO_TWICE(('a => unit) => 'a) + datatype dt_contra_twice('a) = DT_CONTRA_TWICE('a => 'a => unit) + datatype dt_a_contra_b_contra('a, 'b) = DT_A_CONTRA_B_CONTRA('a => 'b => unit) function f_a_to_a_to_u(_ : Animal) : (Animal => unit) = f_a_to_u function f_a_to_c_to_u(_ : Animal) : (Cat => unit) = f_c_to_u @@ -43,10 +44,10 @@ main contract Main = stateful function f_c_to_a(_ : Cat) : Animal = f_a() stateful function f_c_to_c(_ : Cat) : Cat = f_c() - stateful function f_a_to_u_to_a(_ : Animal) : (unit => Animal) = f_u_to_a - stateful function f_a_to_u_to_c(_ : Animal) : (unit => Cat) = f_u_to_c - stateful function f_c_to_u_to_a(_ : Cat) : (unit => Animal) = f_u_to_a - stateful function f_c_to_u_to_c(_ : Cat) : (unit => Cat) = f_u_to_c + stateful function f_a_to_u_to_a(_ : (Animal => unit)) : Animal = f_a() + stateful function f_a_to_u_to_c(_ : (Animal => unit)) : Cat = f_c() + stateful function f_c_to_u_to_a(_ : (Cat => unit)) : Animal = f_a() + stateful function f_c_to_u_to_c(_ : (Cat => unit)) : Cat = f_c() stateful function f_u_to_dt_co_a(_ : unit) : dt_co(Animal) = DT_CO(f_u_to_a) stateful function f_u_to_dt_co_c(_ : unit) : dt_co(Cat) = DT_CO(f_u_to_c) @@ -63,7 +64,7 @@ main contract Main = let vb4 : dt_co(Cat) = DT_CO(f_u_to_c) // success let vc1 : dt_inv(Animal) = DT_INV(f_a_to_a) // success - let vc2 : dt_inv(Animal) = DT_INV(f_a_to_c) // fail + let vc2 : dt_inv(Animal) = DT_INV(f_a_to_c) // success let vc3 : dt_inv(Animal) = DT_INV(f_c_to_a) // fail let vc4 : dt_inv(Animal) = DT_INV(f_c_to_c) // fail let vc5 : dt_inv(Cat) = DT_INV(f_a_to_a) // fail @@ -71,8 +72,8 @@ main contract Main = let vc7 : dt_inv(Cat) = DT_INV(f_c_to_a) // fail let vc8 : dt_inv(Cat) = DT_INV(f_c_to_c) // success - let vd1 : dt_biv(Animal) = DT_BIV(f_u_to_u) // success - let vd2 : dt_biv(Cat) = DT_BIV(f_u_to_u) // success + let vd1 : dt_biv(Animal) = DT_BIV(f_u_to_u) : dt_biv(Cat) // success + let vd2 : dt_biv(Cat) = DT_BIV(f_u_to_u) : dt_biv(Animal) // success let ve1 : dt_inv_sep(Animal) = DT_INV_SEP_A(f_a_to_u) // success let ve2 : dt_inv_sep(Animal) = DT_INV_SEP_A(f_c_to_u) // fail @@ -104,29 +105,37 @@ main contract Main = let vi4 : dt_co_nest_b(Cat) = DT_CO_NEST_B(f_u_to_dt_co_c) // success let vj1 : dt_co_twice(Animal) = DT_CO_TWICE(f_a_to_u_to_a) // success - let vj2 : dt_co_twice(Animal) = DT_CO_TWICE(f_a_to_u_to_c) // fail + let vj2 : dt_co_twice(Animal) = DT_CO_TWICE(f_a_to_u_to_c) // success let vj3 : dt_co_twice(Animal) = DT_CO_TWICE(f_c_to_u_to_a) // fail let vj4 : dt_co_twice(Animal) = DT_CO_TWICE(f_c_to_u_to_c) // success let vj5 : dt_co_twice(Cat) = DT_CO_TWICE(f_a_to_u_to_a) // fail let vj6 : dt_co_twice(Cat) = DT_CO_TWICE(f_a_to_u_to_c) // fail let vj7 : dt_co_twice(Cat) = DT_CO_TWICE(f_c_to_u_to_a) // fail - let vj8 : dt_co_twice(Cat) = DT_CO_TWICE(f_c_to_u_to_c) // success - let vk01 : dt_a_co_b_contra(Animal, Animal) = DT_A_CO_B_CONTRA(f_a_to_a_to_u) // success - let vk02 : dt_a_co_b_contra(Animal, Animal) = DT_A_CO_B_CONTRA(f_a_to_c_to_u) // fail - let vk03 : dt_a_co_b_contra(Animal, Animal) = DT_A_CO_B_CONTRA(f_c_to_a_to_u) // success - let vk04 : dt_a_co_b_contra(Animal, Animal) = DT_A_CO_B_CONTRA(f_c_to_c_to_u) // fail - let vk05 : dt_a_co_b_contra(Animal, Cat) = DT_A_CO_B_CONTRA(f_a_to_a_to_u) // success - let vk06 : dt_a_co_b_contra(Animal, Cat) = DT_A_CO_B_CONTRA(f_a_to_c_to_u) // success - let vk07 : dt_a_co_b_contra(Animal, Cat) = DT_A_CO_B_CONTRA(f_c_to_a_to_u) // success - let vk08 : dt_a_co_b_contra(Animal, Cat) = DT_A_CO_B_CONTRA(f_c_to_c_to_u) // success - let vk09 : dt_a_co_b_contra(Cat, Animal) = DT_A_CO_B_CONTRA(f_a_to_a_to_u) // fail - let vk10 : dt_a_co_b_contra(Cat, Animal) = DT_A_CO_B_CONTRA(f_a_to_c_to_u) // fail - let vk11 : dt_a_co_b_contra(Cat, Animal) = DT_A_CO_B_CONTRA(f_c_to_a_to_u) // success - let vk12 : dt_a_co_b_contra(Cat, Animal) = DT_A_CO_B_CONTRA(f_c_to_c_to_u) // fail - let vk13 : dt_a_co_b_contra(Cat, Cat) = DT_A_CO_B_CONTRA(f_a_to_a_to_u) // fail - let vk14 : dt_a_co_b_contra(Cat, Cat) = DT_A_CO_B_CONTRA(f_a_to_c_to_u) // fail - let vk15 : dt_a_co_b_contra(Cat, Cat) = DT_A_CO_B_CONTRA(f_c_to_a_to_u) // success - let vk16 : dt_a_co_b_contra(Cat, Cat) = DT_A_CO_B_CONTRA(f_c_to_c_to_u) // success + let vk01 : dt_a_contra_b_contra(Animal, Animal) = DT_A_CONTRA_B_CONTRA(f_a_to_a_to_u) // success + let vk02 : dt_a_contra_b_contra(Animal, Animal) = DT_A_CONTRA_B_CONTRA(f_a_to_c_to_u) // fail + let vk03 : dt_a_contra_b_contra(Animal, Animal) = DT_A_CONTRA_B_CONTRA(f_c_to_a_to_u) // fail + let vk04 : dt_a_contra_b_contra(Animal, Animal) = DT_A_CONTRA_B_CONTRA(f_c_to_c_to_u) // fail + let vk05 : dt_a_contra_b_contra(Animal, Cat) = DT_A_CONTRA_B_CONTRA(f_a_to_a_to_u) // success + let vk06 : dt_a_contra_b_contra(Animal, Cat) = DT_A_CONTRA_B_CONTRA(f_a_to_c_to_u) // success + let vk07 : dt_a_contra_b_contra(Animal, Cat) = DT_A_CONTRA_B_CONTRA(f_c_to_a_to_u) // fail + let vk08 : dt_a_contra_b_contra(Animal, Cat) = DT_A_CONTRA_B_CONTRA(f_c_to_c_to_u) // fail + let vk09 : dt_a_contra_b_contra(Cat, Animal) = DT_A_CONTRA_B_CONTRA(f_a_to_a_to_u) // success + let vk10 : dt_a_contra_b_contra(Cat, Animal) = DT_A_CONTRA_B_CONTRA(f_a_to_c_to_u) // fail + let vk11 : dt_a_contra_b_contra(Cat, Animal) = DT_A_CONTRA_B_CONTRA(f_c_to_a_to_u) // success + let vk12 : dt_a_contra_b_contra(Cat, Animal) = DT_A_CONTRA_B_CONTRA(f_c_to_c_to_u) // fail + let vk13 : dt_a_contra_b_contra(Cat, Cat) = DT_A_CONTRA_B_CONTRA(f_a_to_a_to_u) // success + let vk14 : dt_a_contra_b_contra(Cat, Cat) = DT_A_CONTRA_B_CONTRA(f_a_to_c_to_u) // success + let vk15 : dt_a_contra_b_contra(Cat, Cat) = DT_A_CONTRA_B_CONTRA(f_c_to_a_to_u) // success + let vk16 : dt_a_contra_b_contra(Cat, Cat) = DT_A_CONTRA_B_CONTRA(f_c_to_c_to_u) // success + + let vl1 : dt_contra_twice(Animal) = DT_CONTRA_TWICE(f_a_to_a_to_u) // success + let vl2 : dt_contra_twice(Animal) = DT_CONTRA_TWICE(f_a_to_c_to_u) // fail + let vl3 : dt_contra_twice(Animal) = DT_CONTRA_TWICE(f_c_to_a_to_u) // fail + let vl4 : dt_contra_twice(Animal) = DT_CONTRA_TWICE(f_c_to_c_to_u) // fail + let vl5 : dt_contra_twice(Cat) = DT_CONTRA_TWICE(f_a_to_a_to_u) // success + let vl6 : dt_contra_twice(Cat) = DT_CONTRA_TWICE(f_a_to_c_to_u) // fail + let vl7 : dt_contra_twice(Cat) = DT_CONTRA_TWICE(f_c_to_a_to_u) // success + let vl8 : dt_contra_twice(Cat) = DT_CONTRA_TWICE(f_c_to_c_to_u) // success () -- 2.30.2 From 0237353ea7419cc8153c0cc641ea923489c40391 Mon Sep 17 00:00:00 2001 From: Gaith Hallak Date: Tue, 31 May 2022 00:25:53 +0400 Subject: [PATCH 52/53] Eliminate redundant tests --- test/aeso_compiler_tests.erl | 79 ++++++------------- ...rphism_variance_switching_custom_types.aes | 24 ++---- 2 files changed, 34 insertions(+), 69 deletions(-) diff --git a/test/aeso_compiler_tests.erl b/test/aeso_compiler_tests.erl index f1feeda..a67134d 100644 --- a/test/aeso_compiler_tests.erl +++ b/test/aeso_compiler_tests.erl @@ -884,105 +884,78 @@ failing_contracts() -> "when checking the type of the expression `some_animal : Animal` against the expected type `Cat`">> ]) , ?TYPE_ERROR(polymorphism_variance_switching_custom_types, - [<>, - <>, - < Cat) => dt_inv(Cat)`\nto arguments\n `f_c_to_a : (Cat) => Animal`">>, - <>, - <>, - <>, - < Cat) => dt_inv(Cat)`\nto arguments\n `f_c_to_a : (Cat) => Animal`">>, - <>, - <>, - <>, - <>, - <>, - <>, - <>, - <>, - < unit) => Cat) => dt_co_twice(Cat)`\n" - "to arguments\n" - " `f_c_to_u_to_a : ((Cat) => unit) => Animal`">>, - <>, - <>, - < unit) => Cat) => dt_co_twice(Cat)`\n" - "to arguments\n" - " `f_c_to_u_to_a : ((Cat) => unit) => Animal`">>, - <>, + <>, - <>, - <>, - <>, - <>, - <>, - <>, - < (Animal) => unit) => dt_contra_twice(Animal)`\nto arguments\n `f_a_to_c_to_u : (Animal) => (Cat) => unit`">>, - <>, - <>, - < (Animal) => unit) => dt_contra_twice(Animal)`\n" - "to arguments\n" - " `f_a_to_c_to_u : (Animal) => (Cat) => unit`">> + "when checking the type of the pattern `vl2 : dt_contra_twice(Animal)` against the expected type `dt_contra_twice(Cat)`">> ]) , ?TYPE_ERROR(polymorphism_variance_switching_records, [< unit)) : Animal = f_a() stateful function f_a_to_u_to_c(_ : (Animal => unit)) : Cat = f_c() stateful function f_c_to_u_to_a(_ : (Cat => unit)) : Animal = f_a() stateful function f_c_to_u_to_c(_ : (Cat => unit)) : Cat = f_c() @@ -104,13 +103,10 @@ main contract Main = let vi3 : dt_co_nest_b(Cat) = DT_CO_NEST_B(f_u_to_dt_co_a) // fail let vi4 : dt_co_nest_b(Cat) = DT_CO_NEST_B(f_u_to_dt_co_c) // success - let vj1 : dt_co_twice(Animal) = DT_CO_TWICE(f_a_to_u_to_a) // success - let vj2 : dt_co_twice(Animal) = DT_CO_TWICE(f_a_to_u_to_c) // success - let vj3 : dt_co_twice(Animal) = DT_CO_TWICE(f_c_to_u_to_a) // fail - let vj4 : dt_co_twice(Animal) = DT_CO_TWICE(f_c_to_u_to_c) // success - let vj5 : dt_co_twice(Cat) = DT_CO_TWICE(f_a_to_u_to_a) // fail - let vj6 : dt_co_twice(Cat) = DT_CO_TWICE(f_a_to_u_to_c) // fail - let vj7 : dt_co_twice(Cat) = DT_CO_TWICE(f_c_to_u_to_a) // fail + let vj1 : dt_co_twice(Animal) = DT_CO_TWICE(f_a_to_u_to_c : (Animal => unit) => Animal) : dt_co_twice(Animal) // success + let vj2 : dt_co_twice(Animal) = DT_CO_TWICE(f_c_to_u_to_c : (Cat => unit) => Cat ) : dt_co_twice(Cat) // success + let vj3 : dt_co_twice(Cat) = DT_CO_TWICE(f_c_to_u_to_a : (Animal => unit) => Animal) : dt_co_twice(Animal) // fail + let vj4 : dt_co_twice(Cat) = DT_CO_TWICE(f_c_to_u_to_c : (Cat => unit) => Cat ) : dt_co_twice(Cat) // success let vk01 : dt_a_contra_b_contra(Animal, Animal) = DT_A_CONTRA_B_CONTRA(f_a_to_a_to_u) // success let vk02 : dt_a_contra_b_contra(Animal, Animal) = DT_A_CONTRA_B_CONTRA(f_a_to_c_to_u) // fail @@ -129,13 +125,9 @@ main contract Main = let vk15 : dt_a_contra_b_contra(Cat, Cat) = DT_A_CONTRA_B_CONTRA(f_c_to_a_to_u) // success let vk16 : dt_a_contra_b_contra(Cat, Cat) = DT_A_CONTRA_B_CONTRA(f_c_to_c_to_u) // success - let vl1 : dt_contra_twice(Animal) = DT_CONTRA_TWICE(f_a_to_a_to_u) // success - let vl2 : dt_contra_twice(Animal) = DT_CONTRA_TWICE(f_a_to_c_to_u) // fail - let vl3 : dt_contra_twice(Animal) = DT_CONTRA_TWICE(f_c_to_a_to_u) // fail - let vl4 : dt_contra_twice(Animal) = DT_CONTRA_TWICE(f_c_to_c_to_u) // fail - let vl5 : dt_contra_twice(Cat) = DT_CONTRA_TWICE(f_a_to_a_to_u) // success - let vl6 : dt_contra_twice(Cat) = DT_CONTRA_TWICE(f_a_to_c_to_u) // fail - let vl7 : dt_contra_twice(Cat) = DT_CONTRA_TWICE(f_c_to_a_to_u) // success - let vl8 : dt_contra_twice(Cat) = DT_CONTRA_TWICE(f_c_to_c_to_u) // success + let vl1 : dt_contra_twice(Animal) = DT_CONTRA_TWICE(f_a_to_a_to_u : Animal => Animal => unit) : dt_contra_twice(Animal) // success + let vl2 : dt_contra_twice(Animal) = DT_CONTRA_TWICE(f_a_to_c_to_u : Cat => Cat => unit) : dt_contra_twice(Cat) // fail + let vl3 : dt_contra_twice(Cat) = DT_CONTRA_TWICE(f_a_to_a_to_u : Animal => Animal => unit) : dt_contra_twice(Animal) // success + let vl4 : dt_contra_twice(Cat) = DT_CONTRA_TWICE(f_c_to_a_to_u : Cat => Cat => unit) : dt_contra_twice(Cat) // success () -- 2.30.2 From 24972fe223c2b0b1fe65e093df04023ecbfcced5 Mon Sep 17 00:00:00 2001 From: Gaith Hallak Date: Tue, 31 May 2022 00:33:56 +0400 Subject: [PATCH 53/53] Test all cases for bivariant --- test/aeso_compiler_tests.erl | 46 +++++++++---------- ...rphism_variance_switching_custom_types.aes | 6 ++- 2 files changed, 27 insertions(+), 25 deletions(-) diff --git a/test/aeso_compiler_tests.erl b/test/aeso_compiler_tests.erl index a67134d..119fb63 100644 --- a/test/aeso_compiler_tests.erl +++ b/test/aeso_compiler_tests.erl @@ -905,55 +905,55 @@ failing_contracts() -> < Cat) => dt_inv(Cat)`\nto arguments\n `f_c_to_a : (Cat) => Animal`">>, - <>, <>, + <>, - <>, <>, + <>, - <>, - <>, - <>, - <>, - <>, - <>, - <>, <>, + <>, + <>, - <>, - <>, <>, + "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, Cat)`">>, <>, + <>, - <> ]) diff --git a/test/contracts/polymorphism_variance_switching_custom_types.aes b/test/contracts/polymorphism_variance_switching_custom_types.aes index d47472c..fef1cf5 100644 --- a/test/contracts/polymorphism_variance_switching_custom_types.aes +++ b/test/contracts/polymorphism_variance_switching_custom_types.aes @@ -71,8 +71,10 @@ main contract Main = let vc7 : dt_inv(Cat) = DT_INV(f_c_to_a) // fail let vc8 : dt_inv(Cat) = DT_INV(f_c_to_c) // success - let vd1 : dt_biv(Animal) = DT_BIV(f_u_to_u) : dt_biv(Cat) // success - let vd2 : dt_biv(Cat) = DT_BIV(f_u_to_u) : dt_biv(Animal) // success + let vd1 : dt_biv(Animal) = DT_BIV(f_u_to_u) : dt_biv(Animal) // success + let vd2 : dt_biv(Animal) = DT_BIV(f_u_to_u) : dt_biv(Cat) // success + let vd3 : dt_biv(Cat) = DT_BIV(f_u_to_u) : dt_biv(Animal) // success + let vd4 : dt_biv(Cat) = DT_BIV(f_u_to_u) : dt_biv(Cat) // success let ve1 : dt_inv_sep(Animal) = DT_INV_SEP_A(f_a_to_u) // success let ve2 : dt_inv_sep(Animal) = DT_INV_SEP_A(f_c_to_u) // fail -- 2.30.2