Add using namespace parts to ast type inference

This commit is contained in:
Gaith Hallak 2021-08-20 19:11:32 +03:00
parent 81e4429585
commit c29e9a7b2e

View File

@ -103,6 +103,8 @@
-type typesig() :: {type_sig, aeso_syntax:ann(), type_constraints(), [aeso_syntax:named_arg_t()], [type()], type()}. -type typesig() :: {type_sig, aeso_syntax:ann(), type_constraints(), [aeso_syntax:named_arg_t()], [type()], type()}.
-type namespace_alias() :: none | name(). -type namespace_alias() :: none | name().
-type namespace_parts() :: none | {for, [name()]} | {hiding, [name()]}.
-type used_namespaces() :: [{qname(), namespace_alias(), namespace_parts()}].
-type type_constraints() :: none | bytes_concat | bytes_split | address_to_contract | bytecode_hash. -type type_constraints() :: none | bytes_concat | bytes_split | address_to_contract | bytecode_hash.
@ -128,7 +130,7 @@
, typevars = unrestricted :: unrestricted | [name()] , typevars = unrestricted :: unrestricted | [name()]
, fields = #{} :: #{ name() => [field_info()] } %% fields are global , fields = #{} :: #{ name() => [field_info()] } %% fields are global
, namespace = [] :: qname() , namespace = [] :: qname()
, used_namespaces = [] :: [{qname(), namespace_alias()}] , used_namespaces = [] :: used_namespaces()
, in_pattern = false :: boolean() , in_pattern = false :: boolean()
, stateful = false :: boolean() , stateful = false :: boolean()
, current_function = none :: none | aeso_syntax:id() , current_function = none :: none | aeso_syntax:id()
@ -324,8 +326,39 @@ possible_scopes(#env{ namespace = Current, used_namespaces = UsedNamespaces }, N
lists:map(fun(X) -> element(1, X) end, Namespaces) lists:map(fun(X) -> element(1, X) end, Namespaces)
end, end,
Ret1 = [ lists:sublist(Current, I) ++ Q || I <- lists:seq(0, length(Current)), Q <- NewQuals ], Ret1 = [ lists:sublist(Current, I) ++ Q || I <- lists:seq(0, length(Current)), Q <- NewQuals ],
Ret2 = [ Namespace ++ Q || {Namespace, none} <- UsedNamespaces, Q <- NewQuals ], Ret2 = [ Namespace ++ Q || {Namespace, none, _} <- UsedNamespaces, Q <- NewQuals ],
Ret1 ++ Ret2. lists:usort(Ret1 ++ Ret2).
-spec visible_in_used_namespaces(used_namespaces(), qname()) -> boolean().
visible_in_used_namespaces(UsedNamespaces, QName) ->
Qual = lists:droplast(QName),
Name = lists:last(QName),
case lists:filter(fun({Ns, _, _}) -> Qual == Ns end, UsedNamespaces) of
[] ->
true;
Namespaces ->
IsVisible = fun(Namespace) ->
case Namespace of
{_, _, {for, Names}} ->
case lists:member(Name, Names) of
true ->
true;
false ->
false
end;
{_, _, {hiding, Names}} ->
case lists:member(Name, Names) of
true ->
false;
false ->
true
end;
_ ->
true
end
end,
lists:any(IsVisible, Namespaces)
end.
-spec lookup_type(env(), type_id()) -> false | {qname(), type_info()}. -spec lookup_type(env(), type_id()) -> false | {qname(), type_info()}.
lookup_type(Env, Id) -> lookup_type(Env, Id) ->
@ -352,7 +385,7 @@ lookup_env(Env, Kind, Ann, Name) ->
end. end.
-spec lookup_env1(env(), type | term, aeso_syntax:ann(), qname()) -> false | {qname(), fun_info()}. -spec lookup_env1(env(), type | term, aeso_syntax:ann(), qname()) -> false | {qname(), fun_info()}.
lookup_env1(#env{ namespace = Current, scopes = Scopes }, Kind, Ann, QName) -> lookup_env1(#env{ namespace = Current, used_namespaces = UsedNamespaces, scopes = Scopes }, Kind, Ann, QName) ->
Qual = lists:droplast(QName), Qual = lists:droplast(QName),
Name = lists:last(QName), Name = lists:last(QName),
AllowPrivate = lists:prefix(Qual, Current), AllowPrivate = lists:prefix(Qual, Current),
@ -376,7 +409,11 @@ lookup_env1(#env{ namespace = Current, scopes = Scopes }, Kind, Ann, QName) ->
{Ann1, _} = E -> {Ann1, _} = E ->
%% Check that it's not private (or we can see private funs) %% Check that it's not private (or we can see private funs)
case not is_private(Ann1) orelse AllowPrivate of case not is_private(Ann1) orelse AllowPrivate of
true -> {QName, E}; true ->
case visible_in_used_namespaces(UsedNamespaces, QName) of
true -> {QName, E};
false -> false
end;
false -> false false -> false
end end
end end
@ -814,7 +851,7 @@ infer1(Env, [{namespace, Ann, Name, Code} | Rest], Acc, Options) ->
{Env1, Code1} = infer_contract_top(push_scope(namespace, Name, Env), namespace, Code, Options), {Env1, Code1} = infer_contract_top(push_scope(namespace, Name, Env), namespace, Code, Options),
Namespace1 = {namespace, Ann, Name, Code1}, Namespace1 = {namespace, Ann, Name, Code1},
infer1(pop_scope(Env1), Rest, [Namespace1 | Acc], Options); infer1(pop_scope(Env1), Rest, [Namespace1 | Acc], Options);
infer1(Env, [Using = {using, _, _, _} | Rest], Acc, Options) -> infer1(Env, [Using = {using, _, _, _, _} | Rest], Acc, Options) ->
infer1(check_usings(Env, Using), Rest, Acc, Options); infer1(check_usings(Env, Using), Rest, Acc, Options);
infer1(Env, [{pragma, _, _} | Rest], Acc, Options) -> infer1(Env, [{pragma, _, _} | Rest], Acc, Options) ->
%% Pragmas are checked in check_modifiers %% Pragmas are checked in check_modifiers
@ -872,7 +909,7 @@ infer_contract(Env0, What, Defs0, Options) ->
({letfun, _, _, _, _, _}) -> function; ({letfun, _, _, _, _, _}) -> function;
({fun_clauses, _, _, _, _}) -> function; ({fun_clauses, _, _, _, _}) -> function;
({fun_decl, _, _, _}) -> prototype; ({fun_decl, _, _, _}) -> prototype;
({using, _, _, _}) -> using; ({using, _, _, _, _}) -> using;
(_) -> unexpected (_) -> unexpected
end, end,
Get = fun(K, In) -> [ Def || Def <- In, Kind(Def) == K ] end, Get = fun(K, In) -> [ Def || Def <- In, Kind(Def) == K ] end,
@ -1008,7 +1045,7 @@ check_typedef(Env, {variant_t, Cons}) ->
check_usings(Env, []) -> check_usings(Env, []) ->
Env; Env;
check_usings(Env = #env{ used_namespaces = UsedNamespaces }, [{using, Ann, Con, Alias} | Rest]) -> check_usings(Env = #env{ used_namespaces = UsedNamespaces }, [{using, Ann, Con, Alias, Parts} | Rest]) ->
AliasName = case Alias of AliasName = case Alias of
none -> none ->
none; none;
@ -1020,10 +1057,27 @@ check_usings(Env = #env{ used_namespaces = UsedNamespaces }, [{using, Ann, Con,
create_type_errors(), create_type_errors(),
type_error({using_undefined_namespace, Ann, qname(Con)}), type_error({using_undefined_namespace, Ann, qname(Con)}),
destroy_and_report_type_errors(Env); destroy_and_report_type_errors(Env);
_ -> Scope ->
check_usings(Env#env{ used_namespaces = UsedNamespaces ++ [{qname(Con), AliasName}] }, Rest) Nsp = case Parts of
none ->
{qname(Con), AliasName, none};
{ForOrHiding, Ids} ->
IsUndefined = fun(Id) ->
proplists:lookup(name(Id), Scope#scope.funs) == none
end,
UndefinedIds = lists:filter(IsUndefined, Ids),
case UndefinedIds of
[] ->
{qname(Con), AliasName, {ForOrHiding, lists:map(fun name/1, Ids)}};
_ ->
create_type_errors(),
type_error({using_undefined_namespace_parts, Ann, qname(Con), lists:map(fun qname/1, UndefinedIds)}),
destroy_and_report_type_errors(Env)
end
end,
check_usings(Env#env{ used_namespaces = UsedNamespaces ++ [Nsp] }, Rest)
end; end;
check_usings(Env, Using = {using, _, _, _}) -> check_usings(Env, Using = {using, _, _, _, _}) ->
check_usings(Env, [Using]). check_usings(Env, [Using]).
check_unexpected(Xs) -> check_unexpected(Xs) ->
@ -1055,7 +1109,7 @@ check_modifiers_(Env, [{namespace, _, _, Decls} | Rest]) ->
check_modifiers_(Env, [{pragma, Ann, Pragma} | Rest]) -> check_modifiers_(Env, [{pragma, Ann, Pragma} | Rest]) ->
check_pragma(Env, Ann, Pragma), check_pragma(Env, Ann, Pragma),
check_modifiers_(Env, Rest); check_modifiers_(Env, Rest);
check_modifiers_(Env, [{using, _, _, _} | Rest]) -> check_modifiers_(Env, [{using, _, _, _, _} | Rest]) ->
check_modifiers_(Env, Rest); check_modifiers_(Env, Rest);
check_modifiers_(Env, [Decl | Rest]) -> check_modifiers_(Env, [Decl | Rest]) ->
type_error({bad_top_level_decl, Decl}), type_error({bad_top_level_decl, Decl}),
@ -1810,7 +1864,7 @@ infer_block(Env, _, [{letval, Attrs, Pattern, E}|Rest], BlockType) ->
{'case', _, NewPattern, {typed, _, {block, _, NewRest}, _}} = {'case', _, NewPattern, {typed, _, {block, _, NewRest}, _}} =
infer_case(Env, Attrs, Pattern, PatType, {block, Attrs, Rest}, BlockType), infer_case(Env, Attrs, Pattern, PatType, {block, Attrs, Rest}, BlockType),
[{letval, Attrs, NewPattern, NewE}|NewRest]; [{letval, Attrs, NewPattern, NewE}|NewRest];
infer_block(Env, Attrs, [Using = {using, _, _, _} | Rest], BlockType) -> infer_block(Env, Attrs, [Using = {using, _, _, _, _} | Rest], BlockType) ->
infer_block(check_usings(Env, Using), Attrs, Rest, BlockType); infer_block(check_usings(Env, Using), Attrs, Rest, BlockType);
infer_block(Env, Attrs, [E|Rest], BlockType) -> infer_block(Env, Attrs, [E|Rest], BlockType) ->
[infer_expr(Env, E)|infer_block(Env, Attrs, Rest, BlockType)]. [infer_expr(Env, E)|infer_block(Env, Attrs, Rest, BlockType)].
@ -3036,6 +3090,10 @@ mk_error({ambiguous_name, QIds = [{qid, Ann, _} | _]}) ->
mk_error({using_undefined_namespace, Ann, Namespace}) -> mk_error({using_undefined_namespace, Ann, Namespace}) ->
Msg = io_lib:format("Cannot use undefined namespace ~s", [Namespace]), Msg = io_lib:format("Cannot use undefined namespace ~s", [Namespace]),
mk_t_err(pos(Ann), Msg); mk_t_err(pos(Ann), Msg);
mk_error({using_undefined_namespace_parts, Ann, Namespace, Parts}) ->
PartsStr = lists:concat(lists:join(", ", Parts)),
Msg = io_lib:format("The namespace ~s does not define the following names: ~s", [Namespace, PartsStr]),
mk_t_err(pos(Ann), Msg);
mk_error(Err) -> mk_error(Err) ->
Msg = io_lib:format("Unknown error: ~p\n", [Err]), Msg = io_lib:format("Unknown error: ~p\n", [Err]),
mk_t_err(pos(0, 0), Msg). mk_t_err(pos(0, 0), Msg).