Make interfaces declare functions from extended interfaces

This commit is contained in:
Gaith Hallak 2022-05-11 17:07:30 +04:00
parent 5624279d6d
commit 94c8bc3671
5 changed files with 28 additions and 46 deletions

View File

@ -855,7 +855,7 @@ infer1(Env0, [{Contract, Ann, ConName, Impls, Code} | Rest], Acc, Options)
end, end,
{Env1, Code1} = infer_contract_top(push_scope(contract, ConName, Env), What, Code, Options), {Env1, Code1} = infer_contract_top(push_scope(contract, ConName, Env), What, Code, Options),
Contract1 = {Contract, Ann, ConName, Impls, Code1}, Contract1 = {Contract, Ann, ConName, Impls, Code1},
check_implemented_interfaces(Env1, Contract1, What, Acc), check_implemented_interfaces(Env1, Contract1, Acc),
Env2 = pop_scope(Env1), Env2 = pop_scope(Env1),
Env3 = bind_contract(Contract1, Env2), Env3 = bind_contract(Contract1, Env2),
infer1(Env3, Rest, [Contract1 | Acc], Options); infer1(Env3, Rest, [Contract1 | Acc], Options);
@ -871,7 +871,7 @@ infer1(Env, [{pragma, _, _} | Rest], Acc, Options) ->
%% Pragmas are checked in check_modifiers %% Pragmas are checked in check_modifiers
infer1(Env, Rest, Acc, Options). 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(), create_type_errors(),
AllInterfaces = [{name(IName), I} || I = {contract_interface, _, IName, _, _} <- DefinedContracts], AllInterfaces = [{name(IName), I} || I = {contract_interface, _, IName, _, _} <- DefinedContracts],
ImplsNames = lists:map(fun name/1, Impls), ImplsNames = lists:map(fun name/1, Impls),
@ -883,43 +883,32 @@ check_implemented_interfaces(Env, {_Contract, _Ann, ConName, Impls, Code}, What,
end end
end, Impls), end, Impls),
case What of ImplementedInterfaces = [I || I <- [proplists:get_value(Name, AllInterfaces) || Name <- ImplsNames],
contract -> I /= undefined],
ImplementedInterfaces = [I || I <- [proplists:get_value(Name, AllInterfaces) || Name <- ImplsNames], Funs = [ Fun || Fun <- Code,
I /= undefined], element(1, Fun) == letfun orelse element(1, Fun) == fun_decl ],
Letfuns = [ Fun || Fun = {letfun, _, _, _, _, _} <- Code ], check_implemented_interfaces1(ImplementedInterfaces, ConName, Funs, AllInterfaces),
check_implemented_interfaces1(ImplementedInterfaces, ConName, Letfuns, AllInterfaces);
contract_interface ->
ok
end,
destroy_and_report_type_errors(Env). 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 %% Recursively check that all directly and indirectly referenced interfaces are implemented
check_implemented_interfaces1([], _, _, _, _) -> check_implemented_interfaces1([], _, _, _) ->
ok; ok;
check_implemented_interfaces1([{contract_interface, _, IName, Extensions, Decls} | Interfaces], check_implemented_interfaces1([{contract_interface, _, IName, _, Decls} | Interfaces],
ConId, Impls, Acc, AllInterfaces) -> ConId, Impls, AllInterfaces) ->
case lists:member(name(IName), Acc) of Unmatched = match_impls(Decls, ConId, name(IName), Impls),
true -> check_implemented_interfaces1(Interfaces, ConId, Unmatched, 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_interfaces1(NewInterfaces, ConId, Unmatched, [name(IName) | Acc], AllInterfaces)
end.
%% Match the functions of the contract with the interfaces functions, and return unmatched functions %% Match the functions of the contract with the interfaces functions, and return unmatched functions
match_impls([], _, _, Impls) -> match_impls([], _, _, 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 -> Match = fun({letfun, _, {id, _, FName}, Args, RetFun, _}) when FName == FunName ->
length(ArgsTypes) == length(Args) andalso length(ArgsTypes) == length(Args) andalso
compare_types(RetDecl, RetFun) andalso compare_types(RetDecl, RetFun) andalso
lists:all(fun({T1, {typed, _, _, T2}}) -> compare_types(T1, T2) end, lists:all(fun({T1, {typed, _, _, T2}}) -> compare_types(T1, T2) end,
lists:zip(ArgsTypes, Args)); lists:zip(ArgsTypes, Args));
({fun_decl, _, {id, _, FName}, FunT}) when FName == FunName ->
compare_types(FunT, FunType);
(_) -> false (_) -> false
end, end,
UnmatchedImpls = case lists:search(Match, Impls) of UnmatchedImpls = case lists:search(Match, Impls) of

View File

@ -205,6 +205,8 @@ compilable_contracts() ->
"contract_polymorphism", "contract_polymorphism",
"contract_polymorphism_multi_interface", "contract_polymorphism_multi_interface",
"contract_interface_polymorphism", "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. "test" % Custom general-purpose test file. Keep it last on the list.
]. ].
@ -832,23 +834,12 @@ failing_contracts() ->
[<<?Pos(1,24) [<<?Pos(1,24)
"Trying to implement or extend an undefined interface `Z`">> "Trying to implement or extend an undefined interface `Z`">>
]) ])
, ?TYPE_ERROR(contract_interface_polymorphism_same_decl_multi_interface,
[<<?Pos(7,10)
"Unimplemented function `f` from the interface `I` in the contract `C`">>
])
, ?TYPE_ERROR(contract_interface_polymorphism_same_name_same_type,
[<<?Pos(7,10)
"Unimplemented function `f` from the interface `I1` in the contract `C`">>
])
, ?TYPE_ERROR(contract_interface_polymorphism_same_name_different_type, , ?TYPE_ERROR(contract_interface_polymorphism_same_name_different_type,
[<<?Pos(9,5) [<<?Pos(4,20)
"Duplicate definitions of `f` at\n" "Unimplemented function `f` from the interface `I1` in the contract `I2`">>])
" - line 8, column 5\n"
" - line 9, column 5">>
])
, ?TYPE_ERROR(contract_polymorphism_missing_implementation, , ?TYPE_ERROR(contract_polymorphism_missing_implementation,
[<<?Pos(7,10) [<<?Pos(4,20)
"Unimplemented function `f` from the interface `I1` in the contract `C`">> "Unimplemented function `f` from the interface `I1` in the contract `I2`">>
]) ])
, ?TYPE_ERROR(contract_polymorphism_same_decl_multi_interface, , ?TYPE_ERROR(contract_polymorphism_same_decl_multi_interface,
[<<?Pos(7,10) [<<?Pos(7,10)
@ -869,22 +860,22 @@ failing_contracts() ->
"Trying to implement or extend an undefined interface `H`">> "Trying to implement or extend an undefined interface `H`">>
]) ])
, ?TYPE_ERROR(polymorphism_variance_switching, , ?TYPE_ERROR(polymorphism_variance_switching,
[<<?Pos(38,49) [<<?Pos(39,49)
"Cannot unify `Cat` and `Animal`\n" "Cannot unify `Cat` and `Animal`\n"
"when checking the application of\n" "when checking the application of\n"
" `g2 : (Cat) => Cat`\n" " `g2 : (Cat) => Cat`\n"
"to arguments\n" "to arguments\n"
" `x : Animal`">>, " `x : Animal`">>,
<<?Pos(41,43) <<?Pos(42,43)
"Cannot unify `Animal` and `Cat`\n" "Cannot unify `Animal` and `Cat`\n"
"when checking the type of the expression `g3(x) : Animal` against the expected type `Cat`">>, "when checking the type of the expression `g3(x) : Animal` against the expected type `Cat`">>,
<<?Pos(50,55) <<?Pos(51,55)
"Cannot unify `Animal` and `Cat`\n" "Cannot unify `Animal` and `Cat`\n"
"when checking the application of\n" "when checking the application of\n"
" `g5 : ((Animal) => Animal) => Cat`\n" " `g5 : ((Animal) => Animal) => Cat`\n"
"to arguments\n" "to arguments\n"
" `x : (Cat) => Cat`">>, " `x : (Cat) => Cat`">>,
<<?Pos(54, 44) <<?Pos(55, 44)
"Cannot unify `Animal` and `Cat`\n" "Cannot unify `Animal` and `Cat`\n"
"when checking the type of the expression `f6() : option(Animal)` against the expected type `option(Cat)`">> "when checking the type of the expression `f6() : option(Animal)` against the expected type `option(Cat)`">>
]) ])

View File

@ -2,6 +2,7 @@ contract interface II =
entrypoint f : () => unit entrypoint f : () => unit
contract interface I : II = contract interface I : II =
entrypoint f : () => unit
entrypoint g : () => unit entrypoint g : () => unit
contract C : I = contract C : I =

View File

@ -2,6 +2,7 @@ contract interface Creature =
entrypoint is_alive : () => bool entrypoint is_alive : () => bool
contract interface Animal : Creature = contract interface Animal : Creature =
entrypoint is_alive : () => bool
entrypoint sound : () => string entrypoint sound : () => string
contract Cat : Animal = contract Cat : Animal =

View File

@ -1,4 +1,4 @@
contract ShareTwo = contract ShareTwo =
record state = {s1 : int, s2 : int} record state = {s1 : int, s2 : int}
entrypoint init() = {s1 = 0, s2 = 0} entrypoint init() = {s1 = 0, s2 = 0}
stateful entrypoint buy() = () stateful entrypoint buy() = ()