diff --git a/src/aeso_ast_infer_types.erl b/src/aeso_ast_infer_types.erl index b99a6bf..4d87e70 100644 --- a/src/aeso_ast_infer_types.erl +++ b/src/aeso_ast_infer_types.erl @@ -103,6 +103,8 @@ -type typesig() :: {type_sig, aeso_syntax:ann(), type_constraints(), [aeso_syntax:named_arg_t()], [type()], type()}. -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. @@ -128,7 +130,7 @@ , typevars = unrestricted :: unrestricted | [name()] , fields = #{} :: #{ name() => [field_info()] } %% fields are global , namespace = [] :: qname() - , used_namespaces = [] :: [{qname(), namespace_alias()}] + , used_namespaces = [] :: used_namespaces() , in_pattern = false :: boolean() , stateful = false :: boolean() , 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) end, Ret1 = [ lists:sublist(Current, I) ++ Q || I <- lists:seq(0, length(Current)), Q <- NewQuals ], - Ret2 = [ Namespace ++ Q || {Namespace, none} <- UsedNamespaces, Q <- NewQuals ], - Ret1 ++ Ret2. + Ret2 = [ Namespace ++ Q || {Namespace, none, _} <- UsedNamespaces, Q <- NewQuals ], + 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()}. lookup_type(Env, Id) -> @@ -352,7 +385,7 @@ lookup_env(Env, Kind, Ann, Name) -> end. -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), Name = lists:last(QName), AllowPrivate = lists:prefix(Qual, Current), @@ -376,7 +409,11 @@ lookup_env1(#env{ namespace = Current, scopes = Scopes }, Kind, Ann, QName) -> {Ann1, _} = E -> %% Check that it's not private (or we can see private funs) 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 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), Namespace1 = {namespace, Ann, Name, Code1}, 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(Env, [{pragma, _, _} | Rest], Acc, Options) -> %% Pragmas are checked in check_modifiers @@ -872,7 +909,7 @@ infer_contract(Env0, What, Defs0, Options) -> ({letfun, _, _, _, _, _}) -> function; ({fun_clauses, _, _, _, _}) -> function; ({fun_decl, _, _, _}) -> prototype; - ({using, _, _, _}) -> using; + ({using, _, _, _, _}) -> using; (_) -> unexpected 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, []) -> 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 none -> none; @@ -1020,10 +1057,27 @@ check_usings(Env = #env{ used_namespaces = UsedNamespaces }, [{using, Ann, Con, create_type_errors(), type_error({using_undefined_namespace, Ann, qname(Con)}), destroy_and_report_type_errors(Env); - _ -> - check_usings(Env#env{ used_namespaces = UsedNamespaces ++ [{qname(Con), AliasName}] }, Rest) + Scope -> + 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; -check_usings(Env, Using = {using, _, _, _}) -> +check_usings(Env, Using = {using, _, _, _, _}) -> check_usings(Env, [Using]). check_unexpected(Xs) -> @@ -1055,7 +1109,7 @@ check_modifiers_(Env, [{namespace, _, _, Decls} | Rest]) -> check_modifiers_(Env, [{pragma, Ann, Pragma} | Rest]) -> check_pragma(Env, Ann, Pragma), check_modifiers_(Env, Rest); -check_modifiers_(Env, [{using, _, _, _} | Rest]) -> +check_modifiers_(Env, [{using, _, _, _, _} | Rest]) -> check_modifiers_(Env, Rest); check_modifiers_(Env, [Decl | Rest]) -> 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}, _}} = infer_case(Env, Attrs, Pattern, PatType, {block, Attrs, Rest}, BlockType), [{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(Env, Attrs, [E|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}) -> Msg = io_lib:format("Cannot use undefined namespace ~s", [Namespace]), 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) -> Msg = io_lib:format("Unknown error: ~p\n", [Err]), mk_t_err(pos(0, 0), Msg).