From 53ba62e423bcc19392365513fdeb2dbbcbe594df Mon Sep 17 00:00:00 2001 From: Gaith Hallak Date: Fri, 13 Aug 2021 20:03:20 +0300 Subject: [PATCH 01/23] Add using namespace as to scanner and parser --- src/aeso_parser.erl | 9 +++++++++ src/aeso_scan.erl | 2 +- src/aeso_syntax.erl | 3 +++ 3 files changed, 13 insertions(+), 1 deletion(-) diff --git a/src/aeso_parser.erl b/src/aeso_parser.erl index c29403d..e833ca0 100644 --- a/src/aeso_parser.erl +++ b/src/aeso_parser.erl @@ -109,6 +109,7 @@ decl() -> , ?RULE(keyword(namespace), con(), tok('='), maybe_block(decl()), {namespace, _1, _2, _4}) , ?RULE(keyword(include), str(), {include, get_ann(_1), _2}) + , using() , pragma() %% Type declarations TODO: format annotation for "type bla" vs "type bla()" @@ -135,6 +136,14 @@ fundef_or_decl() -> choice([?RULE(id(), tok(':'), type(), {fun_decl, get_ann(_1), _1, _3}), fundef()]). +using() -> + ?RULE(keyword(using), con(), optional({keyword(as), id()}), using(get_ann(_1), _2, _3)). + +using(Ann, Con, none) -> + {using, Ann, Con}; +using(Ann, Con, {ok, {_, Id}}) -> + {using, Ann, Con, Id}. + pragma() -> Op = choice([token(T) || T <- ['<', '=<', '==', '>=', '>']]), ?RULE(tok('@'), id("compiler"), Op, version(), {pragma, get_ann(_1), {compiler, element(1, _3), _4}}). diff --git a/src/aeso_scan.erl b/src/aeso_scan.erl index 2c5d301..ec47e4f 100644 --- a/src/aeso_scan.erl +++ b/src/aeso_scan.erl @@ -45,7 +45,7 @@ lexer() -> Keywords = ["contract", "include", "let", "switch", "type", "record", "datatype", "if", "elif", "else", "function", "stateful", "payable", "true", "false", "mod", "public", "entrypoint", "private", "indexed", "namespace", - "interface", "main" + "interface", "main", "using", "as" ], KW = string:join(Keywords, "|"), diff --git a/src/aeso_syntax.erl b/src/aeso_syntax.erl index cab8edc..44c8b7c 100644 --- a/src/aeso_syntax.erl +++ b/src/aeso_syntax.erl @@ -44,6 +44,7 @@ | {type_def, ann(), id(), [tvar()], typedef()} | {fun_clauses, ann(), id(), type(), [letfun() | fundecl()]} | {block, ann(), [decl()]} + | using() | fundecl() | letfun() | letval(). % Only for error msgs @@ -52,6 +53,8 @@ -type pragma() :: {compiler, '==' | '<' | '>' | '=<' | '>=', compiler_version()}. +-type using() :: {using, ann(), con()} + | {using, ann(), con(), id()}. -type letval() :: {letval, ann(), pat(), expr()}. -type letfun() :: {letfun, ann(), id(), [pat()], type(), expr()}. -- 2.30.2 From 2d450397f4e0ea616b5651537844098304b4477d Mon Sep 17 00:00:00 2001 From: Gaith Hallak Date: Tue, 17 Aug 2021 10:18:48 +0300 Subject: [PATCH 02/23] Change the alias from id() to con() --- src/aeso_parser.erl | 6 +++--- src/aeso_syntax.erl | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/aeso_parser.erl b/src/aeso_parser.erl index e833ca0..4257eb6 100644 --- a/src/aeso_parser.erl +++ b/src/aeso_parser.erl @@ -137,12 +137,12 @@ fundef_or_decl() -> fundef()]). using() -> - ?RULE(keyword(using), con(), optional({keyword(as), id()}), using(get_ann(_1), _2, _3)). + ?RULE(keyword(using), con(), optional({keyword(as), con()}), using(get_ann(_1), _2, _3)). using(Ann, Con, none) -> {using, Ann, Con}; -using(Ann, Con, {ok, {_, Id}}) -> - {using, Ann, Con, Id}. +using(Ann, Con, {ok, {_, Alias}}) -> + {using, Ann, Con, Alias}. pragma() -> Op = choice([token(T) || T <- ['<', '=<', '==', '>=', '>']]), diff --git a/src/aeso_syntax.erl b/src/aeso_syntax.erl index 44c8b7c..13f0930 100644 --- a/src/aeso_syntax.erl +++ b/src/aeso_syntax.erl @@ -54,7 +54,7 @@ -type pragma() :: {compiler, '==' | '<' | '>' | '=<' | '>=', compiler_version()}. -type using() :: {using, ann(), con()} - | {using, ann(), con(), id()}. + | {using, ann(), con(), con()}. -type letval() :: {letval, ann(), pat(), expr()}. -type letfun() :: {letfun, ann(), id(), [pat()], type(), expr()}. -- 2.30.2 From 3a4b63baa2973157fb55849bd7cb2aa49f895a42 Mon Sep 17 00:00:00 2001 From: Gaith Hallak Date: Tue, 17 Aug 2021 11:21:13 +0300 Subject: [PATCH 03/23] Add using namespace to AST type inference --- src/aeso_ast_infer_types.erl | 44 ++++++++++++++++++++++++++---------- 1 file changed, 32 insertions(+), 12 deletions(-) diff --git a/src/aeso_ast_infer_types.erl b/src/aeso_ast_infer_types.erl index bc049cf..212eb64 100644 --- a/src/aeso_ast_infer_types.erl +++ b/src/aeso_ast_infer_types.erl @@ -102,6 +102,8 @@ -type qname() :: [string()]. -type typesig() :: {type_sig, aeso_syntax:ann(), type_constraints(), [aeso_syntax:named_arg_t()], [type()], type()}. +-type namespace_alias() :: none | name(). + -type type_constraints() :: none | bytes_concat | bytes_split | address_to_contract | bytecode_hash. -type fun_info() :: {aeso_syntax:ann(), typesig() | type()}. @@ -121,15 +123,16 @@ -type scope() :: #scope{}. -record(env, - { scopes = #{ [] => #scope{}} :: #{ qname() => scope() } - , vars = [] :: [{name(), var_info()}] - , typevars = unrestricted :: unrestricted | [name()] - , fields = #{} :: #{ name() => [field_info()] } %% fields are global - , namespace = [] :: qname() - , in_pattern = false :: boolean() - , stateful = false :: boolean() - , current_function = none :: none | aeso_syntax:id() - , what = top :: top | namespace | contract | contract_interface + { scopes = #{ [] => #scope{}} :: #{ qname() => scope() } + , vars = [] :: [{name(), var_info()}] + , typevars = unrestricted :: unrestricted | [name()] + , fields = #{} :: #{ name() => [field_info()] } %% fields are global + , namespace = [] :: qname() + , used_namespaces = [] :: list({qname(), namespace_alias()}) + , in_pattern = false :: boolean() + , stateful = false :: boolean() + , current_function = none :: none | aeso_syntax:id() + , what = top :: top | namespace | contract | contract_interface }). -type env() :: #env{}. @@ -312,9 +315,16 @@ bind_contract({Contract, Ann, Id, Contents}, Env) %% What scopes could a given name come from? -spec possible_scopes(env(), qname()) -> [qname()]. -possible_scopes(#env{ namespace = Current}, Name) -> +possible_scopes(#env{ namespace = Current, used_namespaces = UsedNamespaces }, Name) -> Qual = lists:droplast(Name), - [ lists:sublist(Current, I) ++ Qual || I <- lists:seq(0, length(Current)) ]. + NewQual = case lists:keyfind(Qual, 2, UsedNamespaces) of + {Namespace, _} -> + Namespace; + false -> + Qual + end, + Ret = [ lists:sublist(Current, I) ++ NewQual || I <- lists:seq(0, length(Current)) ] ++ [ Namespace ++ NewQual || {Namespace, none} <- UsedNamespaces ], + Ret. -spec lookup_type(env(), type_id()) -> false | {qname(), type_info()}. lookup_type(Env, Id) -> @@ -859,10 +869,13 @@ 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, - {Env1, TypeDefs} = check_typedefs(Env, Get(type, Defs)), + Env01 = check_usings(Env, Get(using, Defs)), + {Env1, TypeDefs} = check_typedefs(Env01, Get(type, Defs)), create_type_errors(), check_unexpected(Get(unexpected, Defs)), Env2 = @@ -988,6 +1001,13 @@ check_typedef(Env, {variant_t, Cons}) -> {variant_t, [ {constr_t, Ann, Con, [ check_type(Env, Arg) || Arg <- Args ]} || {constr_t, Ann, Con, Args} <- Cons ]}. +check_usings(Env, []) -> + Env; +check_usings(Env = #env{ used_namespaces = UsedNamespaces }, [{using, Ann, Con} | Rest]) -> + check_usings(Env#env{ used_namespaces = UsedNamespaces ++ [{qname(Con), none}] }, Rest); +check_usings(Env = #env{ used_namespaces = UsedNamespaces }, [{using, Ann, Con, Alias} | Rest]) -> + check_usings(Env#env{ used_namespaces = UsedNamespaces ++ [{qname(Con), qname(Alias)}] }, Rest). + check_unexpected(Xs) -> [ type_error(X) || X <- Xs ]. -- 2.30.2 From 519e60039e8d75e15990988ae987776c38642cf3 Mon Sep 17 00:00:00 2001 From: Gaith Hallak Date: Tue, 17 Aug 2021 18:34:15 +0300 Subject: [PATCH 04/23] Allow using namespace to appear in the top level --- src/aeso_ast_infer_types.erl | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/aeso_ast_infer_types.erl b/src/aeso_ast_infer_types.erl index 212eb64..573bc34 100644 --- a/src/aeso_ast_infer_types.erl +++ b/src/aeso_ast_infer_types.erl @@ -813,6 +813,10 @@ 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(check_usings(Env, [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 infer1(Env, Rest, Acc, Options). @@ -1003,9 +1007,9 @@ check_typedef(Env, {variant_t, Cons}) -> check_usings(Env, []) -> Env; -check_usings(Env = #env{ used_namespaces = UsedNamespaces }, [{using, Ann, Con} | Rest]) -> +check_usings(Env = #env{ used_namespaces = UsedNamespaces }, [{using, _, Con} | Rest]) -> check_usings(Env#env{ used_namespaces = UsedNamespaces ++ [{qname(Con), none}] }, Rest); -check_usings(Env = #env{ used_namespaces = UsedNamespaces }, [{using, Ann, Con, Alias} | Rest]) -> +check_usings(Env = #env{ used_namespaces = UsedNamespaces }, [{using, _, Con, Alias} | Rest]) -> check_usings(Env#env{ used_namespaces = UsedNamespaces ++ [{qname(Con), qname(Alias)}] }, Rest). check_unexpected(Xs) -> @@ -1037,6 +1041,10 @@ 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, Rest); +check_modifiers_(Env, [{using, _, _, _} | Rest]) -> + check_modifiers_(Env, Rest); check_modifiers_(Env, [Decl | Rest]) -> type_error({bad_top_level_decl, Decl}), check_modifiers_(Env, Rest); -- 2.30.2 From 976f6c42baf5fde78a3a4df3565608cb771acba3 Mon Sep 17 00:00:00 2001 From: Gaith Hallak Date: Tue, 17 Aug 2021 18:44:02 +0300 Subject: [PATCH 05/23] Allow using namespace to appear inside functions --- src/aeso_ast_infer_types.erl | 4 ++++ src/aeso_parser.erl | 3 ++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/aeso_ast_infer_types.erl b/src/aeso_ast_infer_types.erl index 573bc34..b46f279 100644 --- a/src/aeso_ast_infer_types.erl +++ b/src/aeso_ast_infer_types.erl @@ -1798,6 +1798,10 @@ 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(check_usings(Env, [Using]), Attrs, 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)]. diff --git a/src/aeso_parser.erl b/src/aeso_parser.erl index 4257eb6..15ece13 100644 --- a/src/aeso_parser.erl +++ b/src/aeso_parser.erl @@ -263,7 +263,8 @@ body() -> stmt() -> ?LAZY_P(choice( - [ expr() + [ using() + , expr() , letdecl() , {switch, keyword(switch), parens(expr()), maybe_block(branch())} , {'if', keyword('if'), parens(expr()), body()} -- 2.30.2 From 06dc68a7f29eebe240d7510b0e86535cc2526ba1 Mon Sep 17 00:00:00 2001 From: Gaith Hallak Date: Tue, 17 Aug 2021 19:20:16 +0300 Subject: [PATCH 06/23] Add a compiler test for using namespace --- test/aeso_compiler_tests.erl | 1 + test/contracts/using_namespace.aes | 31 ++++++++++++++++++++++++++++++ 2 files changed, 32 insertions(+) create mode 100644 test/contracts/using_namespace.aes diff --git a/test/aeso_compiler_tests.erl b/test/aeso_compiler_tests.erl index 1df9bf3..8b531e7 100644 --- a/test/aeso_compiler_tests.erl +++ b/test/aeso_compiler_tests.erl @@ -200,6 +200,7 @@ compilable_contracts() -> "clone_simple", "create", "child_contract_init_bug", + "using_namespace", "test" % Custom general-purpose test file. Keep it last on the list. ]. diff --git a/test/contracts/using_namespace.aes b/test/contracts/using_namespace.aes new file mode 100644 index 0000000..ae20706 --- /dev/null +++ b/test/contracts/using_namespace.aes @@ -0,0 +1,31 @@ +include "Option.aes" +include "Pair.aes" +include "String.aes" + +using Pair + +namespace Nsp = + using Option + + function h() = + let op = Some(2) + is_some(op) + +contract Cntr = + using Nsp + + entrypoint init() = () + + function f() = + let p = (1, 2) + if (h()) + fst(p) + else + snd(p) + + function g() = + using String + + let s1 = "abc" + let s2 = "def" + concat(s1, s2) -- 2.30.2 From 0782f439f5095b910fc19c7a01ad29ff9a2be372 Mon Sep 17 00:00:00 2001 From: Gaith Hallak Date: Wed, 18 Aug 2021 11:43:33 +0300 Subject: [PATCH 07/23] Handle name collisions --- 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 b46f279..f1397ca 100644 --- a/src/aeso_ast_infer_types.erl +++ b/src/aeso_ast_infer_types.erl @@ -317,13 +317,13 @@ bind_contract({Contract, Ann, Id, Contents}, Env) -spec possible_scopes(env(), qname()) -> [qname()]. possible_scopes(#env{ namespace = Current, used_namespaces = UsedNamespaces }, Name) -> Qual = lists:droplast(Name), - NewQual = case lists:keyfind(Qual, 2, UsedNamespaces) of - {Namespace, _} -> - Namespace; - false -> - Qual - end, - Ret = [ lists:sublist(Current, I) ++ NewQual || I <- lists:seq(0, length(Current)) ] ++ [ Namespace ++ NewQual || {Namespace, none} <- UsedNamespaces ], + NewQuals = case lists:filter(fun(X) -> element(2, X) == Qual end, UsedNamespaces) of + [] -> + [Qual]; + Namespaces -> + lists:map(fun(X) -> element(1, X) end, Namespaces) + end, + Ret = [ lists:sublist(Current, I) ++ Q || I <- lists:seq(0, length(Current)), Q <- NewQuals ] ++ [ Namespace ++ Q || {Namespace, none} <- UsedNamespaces, Q <- NewQuals ], Ret. -spec lookup_type(env(), type_id()) -> false | {qname(), type_info()}. -- 2.30.2 From 8ac4156c8795c730b3f96f95dca73624289b9808 Mon Sep 17 00:00:00 2001 From: Gaith Hallak Date: Wed, 18 Aug 2021 13:50:43 +0300 Subject: [PATCH 08/23] Implement mk_error for ambiguous_name --- src/aeso_ast_infer_types.erl | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/aeso_ast_infer_types.erl b/src/aeso_ast_infer_types.erl index f1397ca..74dd14b 100644 --- a/src/aeso_ast_infer_types.erl +++ b/src/aeso_ast_infer_types.erl @@ -3019,6 +3019,10 @@ mk_error({contract_lacks_definition, Type, When}) -> ), {Pos, Ctxt} = pp_when(When), mk_t_err(Pos, Msg, Ctxt); +mk_error({ambiguous_name, QIds = [{qid, Ann, _} | _]}) -> + Names = lists:map(fun(QId) -> io_lib:format("~s at ~s\n", [pp(QId), pp_loc(QId)]) end, QIds), + Msg = "Ambiguous name: " ++ lists:concat(Names), + 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). -- 2.30.2 From 52b1c99ea7ccdb996240d196775d69a87d902ca0 Mon Sep 17 00:00:00 2001 From: Gaith Hallak Date: Wed, 18 Aug 2021 13:57:40 +0300 Subject: [PATCH 09/23] Add failing test for ambiguous names --- test/aeso_compiler_tests.erl | 6 ++++++ test/contracts/using_namespace_ambiguous_name.aes | 13 +++++++++++++ 2 files changed, 19 insertions(+) create mode 100644 test/contracts/using_namespace_ambiguous_name.aes diff --git a/test/aeso_compiler_tests.erl b/test/aeso_compiler_tests.erl index 8b531e7..d7f0b30 100644 --- a/test/aeso_compiler_tests.erl +++ b/test/aeso_compiler_tests.erl @@ -782,6 +782,12 @@ failing_contracts() -> [<> ]) + , ?TYPE_ERROR(using_namespace_ambiguous_name, + [ <> + , <> + ]) ]. -define(Path(File), "code_errors/" ??File). diff --git a/test/contracts/using_namespace_ambiguous_name.aes b/test/contracts/using_namespace_ambiguous_name.aes new file mode 100644 index 0000000..e86e8ad --- /dev/null +++ b/test/contracts/using_namespace_ambiguous_name.aes @@ -0,0 +1,13 @@ +namespace Xa = + function f() = 1 + +namespace Xb = + function f() = 2 + +contract Cntr = + using Xa as A + using Xb as A + + type state = int + + entrypoint init() = A.f() -- 2.30.2 From c823c712aeada1bf995ebdb3ffbc970e9a7ca71a Mon Sep 17 00:00:00 2001 From: Gaith Hallak Date: Wed, 18 Aug 2021 16:50:56 +0300 Subject: [PATCH 10/23] Limit the scope of the used namespaces --- src/aeso_ast_infer_types.erl | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/aeso_ast_infer_types.erl b/src/aeso_ast_infer_types.erl index 74dd14b..acb9b8b 100644 --- a/src/aeso_ast_infer_types.erl +++ b/src/aeso_ast_infer_types.erl @@ -878,6 +878,7 @@ infer_contract(Env0, What, Defs0, Options) -> (_) -> unexpected end, Get = fun(K, In) -> [ Def || Def <- In, Kind(Def) == K ] end, + OldUsedNamespaces = Env#env.used_namespaces, Env01 = check_usings(Env, Get(using, Defs)), {Env1, TypeDefs} = check_typedefs(Env01, Get(type, Defs)), create_type_errors(), @@ -901,11 +902,13 @@ infer_contract(Env0, What, Defs0, Options) -> DepGraph = maps:map(fun(_, Def) -> aeso_syntax_utils:used_ids(Def) end, FunMap), SCCs = aeso_utils:scc(DepGraph), {Env4, Defs1} = check_sccs(Env3, FunMap, SCCs, []), + %% Remove namespaces used in the current namespace + Env5 = Env4#env{ used_namespaces = OldUsedNamespaces }, %% Check that `init` doesn't read or write the state check_state_dependencies(Env4, Defs1), destroy_and_report_type_errors(Env4), %% Add inferred types of definitions - {Env4, TypeDefs ++ Decls ++ Defs1}. + {Env5, TypeDefs ++ Decls ++ Defs1}. %% Restructure blocks into multi-clause fundefs (`fun_clauses`). -spec process_blocks([aeso_syntax:decl()]) -> [aeso_syntax:decl()]. -- 2.30.2 From 21c16158ce4d68dc98e0d155309fdc19d9ac53fa Mon Sep 17 00:00:00 2001 From: Gaith Hallak Date: Wed, 18 Aug 2021 17:09:49 +0300 Subject: [PATCH 11/23] Add test for wrong scope of using namespace --- test/aeso_compiler_tests.erl | 6 ++++++ .../contracts/using_namespace_wrong_scope.aes | 21 +++++++++++++++++++ 2 files changed, 27 insertions(+) create mode 100644 test/contracts/using_namespace_wrong_scope.aes diff --git a/test/aeso_compiler_tests.erl b/test/aeso_compiler_tests.erl index d7f0b30..646177b 100644 --- a/test/aeso_compiler_tests.erl +++ b/test/aeso_compiler_tests.erl @@ -788,6 +788,12 @@ failing_contracts() -> , <> ]) + , ?TYPE_ERROR(using_namespace_wrong_scope, + [ <> + , <> + ]) ]. -define(Path(File), "code_errors/" ??File). diff --git a/test/contracts/using_namespace_wrong_scope.aes b/test/contracts/using_namespace_wrong_scope.aes new file mode 100644 index 0000000..8e8c298 --- /dev/null +++ b/test/contracts/using_namespace_wrong_scope.aes @@ -0,0 +1,21 @@ +namespace Nsp1 = + function f() = 1 + +namespace Nsp2 = + using Nsp1 + + function g() = 1 + +contract Cntr = + using Nsp2 + + type state = int + + function x() = + using Nsp1 + f() + + function y() = + f() + + entrypoint init() = f() -- 2.30.2 From 2a581a5800f5d5d6b1a6136395dbeb7dfef177be Mon Sep 17 00:00:00 2001 From: Gaith Hallak Date: Wed, 18 Aug 2021 18:52:18 +0300 Subject: [PATCH 12/23] Use a single using declaration --- src/aeso_ast_infer_types.erl | 23 +++++++++++------------ src/aeso_parser.erl | 2 +- src/aeso_syntax.erl | 7 +++---- 3 files changed, 15 insertions(+), 17 deletions(-) diff --git a/src/aeso_ast_infer_types.erl b/src/aeso_ast_infer_types.erl index acb9b8b..7c081f2 100644 --- a/src/aeso_ast_infer_types.erl +++ b/src/aeso_ast_infer_types.erl @@ -813,10 +813,8 @@ 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(check_usings(Env, [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) -> %% Pragmas are checked in check_modifiers infer1(Env, Rest, Acc, Options). @@ -873,7 +871,6 @@ infer_contract(Env0, What, Defs0, Options) -> ({letfun, _, _, _, _, _}) -> function; ({fun_clauses, _, _, _, _}) -> function; ({fun_decl, _, _, _}) -> prototype; - ({using, _, _}) -> using; ({using, _, _, _}) -> using; (_) -> unexpected end, @@ -1010,10 +1007,16 @@ check_typedef(Env, {variant_t, Cons}) -> check_usings(Env, []) -> Env; -check_usings(Env = #env{ used_namespaces = UsedNamespaces }, [{using, _, Con} | Rest]) -> - check_usings(Env#env{ used_namespaces = UsedNamespaces ++ [{qname(Con), none}] }, Rest); check_usings(Env = #env{ used_namespaces = UsedNamespaces }, [{using, _, Con, Alias} | Rest]) -> - check_usings(Env#env{ used_namespaces = UsedNamespaces ++ [{qname(Con), qname(Alias)}] }, Rest). + AliasName = case Alias of + none -> + none; + _ -> + qname(Alias) + end, + check_usings(Env#env{ used_namespaces = UsedNamespaces ++ [{qname(Con), AliasName}] }, Rest); +check_usings(Env, Using = {using, _, _, _}) -> + check_usings(Env, [Using]). check_unexpected(Xs) -> [ type_error(X) || X <- Xs ]. @@ -1044,8 +1047,6 @@ 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, Rest); check_modifiers_(Env, [{using, _, _, _} | Rest]) -> check_modifiers_(Env, Rest); check_modifiers_(Env, [Decl | Rest]) -> @@ -1801,10 +1802,8 @@ 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(check_usings(Env, [Using]), Attrs, 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_expr(Env, E)|infer_block(Env, Attrs, Rest, BlockType)]. diff --git a/src/aeso_parser.erl b/src/aeso_parser.erl index 15ece13..b5fd8b9 100644 --- a/src/aeso_parser.erl +++ b/src/aeso_parser.erl @@ -140,7 +140,7 @@ using() -> ?RULE(keyword(using), con(), optional({keyword(as), con()}), using(get_ann(_1), _2, _3)). using(Ann, Con, none) -> - {using, Ann, Con}; + {using, Ann, Con, none}; using(Ann, Con, {ok, {_, Alias}}) -> {using, Ann, Con, Alias}. diff --git a/src/aeso_syntax.erl b/src/aeso_syntax.erl index 13f0930..5fb02bb 100644 --- a/src/aeso_syntax.erl +++ b/src/aeso_syntax.erl @@ -35,6 +35,8 @@ -type qcon() :: {qcon, ann(), [name()]}. -type tvar() :: {tvar, ann(), name()}. +-type namespace_alias() :: none | con(). + -type decl() :: {contract_main, ann(), con(), [decl()]} | {contract_child, ann(), con(), [decl()]} | {contract_interface, ann(), con(), [decl()]} @@ -44,7 +46,7 @@ | {type_def, ann(), id(), [tvar()], typedef()} | {fun_clauses, ann(), id(), type(), [letfun() | fundecl()]} | {block, ann(), [decl()]} - | using() + | {using, ann(), con(), namespace_alias()} | fundecl() | letfun() | letval(). % Only for error msgs @@ -53,9 +55,6 @@ -type pragma() :: {compiler, '==' | '<' | '>' | '=<' | '>=', compiler_version()}. --type using() :: {using, ann(), con()} - | {using, ann(), con(), con()}. - -type letval() :: {letval, ann(), pat(), expr()}. -type letfun() :: {letfun, ann(), id(), [pat()], type(), expr()}. -type fundecl() :: {fun_decl, ann(), id(), type()}. -- 2.30.2 From dfe0ad9c38cdb6f4a843ee904f2c53de570d0fe6 Mon Sep 17 00:00:00 2001 From: Gaith Hallak Date: Wed, 18 Aug 2021 18:59:56 +0300 Subject: [PATCH 13/23] Split long line --- src/aeso_ast_infer_types.erl | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/aeso_ast_infer_types.erl b/src/aeso_ast_infer_types.erl index 7c081f2..48c412b 100644 --- a/src/aeso_ast_infer_types.erl +++ b/src/aeso_ast_infer_types.erl @@ -323,8 +323,9 @@ possible_scopes(#env{ namespace = Current, used_namespaces = UsedNamespaces }, N Namespaces -> lists:map(fun(X) -> element(1, X) end, Namespaces) end, - Ret = [ lists:sublist(Current, I) ++ Q || I <- lists:seq(0, length(Current)), Q <- NewQuals ] ++ [ Namespace ++ Q || {Namespace, none} <- UsedNamespaces, Q <- NewQuals ], - Ret. + Ret1 = [ lists:sublist(Current, I) ++ Q || I <- lists:seq(0, length(Current)), Q <- NewQuals ], + Ret2 = [ Namespace ++ Q || {Namespace, none} <- UsedNamespaces, Q <- NewQuals ], + Ret1 ++ Ret2. -spec lookup_type(env(), type_id()) -> false | {qname(), type_info()}. lookup_type(Env, Id) -> -- 2.30.2 From 9acd1b1d8868f6816333f450292c9399d2d0465f Mon Sep 17 00:00:00 2001 From: Gaith Hallak Date: Wed, 18 Aug 2021 19:26:41 +0300 Subject: [PATCH 14/23] Forbid using undefined namespaces --- src/aeso_ast_infer_types.erl | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/aeso_ast_infer_types.erl b/src/aeso_ast_infer_types.erl index 48c412b..42be32a 100644 --- a/src/aeso_ast_infer_types.erl +++ b/src/aeso_ast_infer_types.erl @@ -1008,14 +1008,21 @@ check_typedef(Env, {variant_t, Cons}) -> check_usings(Env, []) -> Env; -check_usings(Env = #env{ used_namespaces = UsedNamespaces }, [{using, _, Con, Alias} | Rest]) -> +check_usings(Env = #env{ used_namespaces = UsedNamespaces }, [{using, Ann, Con, Alias} | Rest]) -> AliasName = case Alias of none -> none; _ -> qname(Alias) end, - check_usings(Env#env{ used_namespaces = UsedNamespaces ++ [{qname(Con), AliasName}] }, Rest); + case get_scope(Env, qname(Con)) of + false -> + 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) + end; check_usings(Env, Using = {using, _, _, _}) -> check_usings(Env, [Using]). @@ -3026,6 +3033,9 @@ mk_error({ambiguous_name, QIds = [{qid, Ann, _} | _]}) -> Names = lists:map(fun(QId) -> io_lib:format("~s at ~s\n", [pp(QId), pp_loc(QId)]) end, QIds), Msg = "Ambiguous name: " ++ lists:concat(Names), mk_t_err(pos(Ann), Msg); +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(Err) -> Msg = io_lib:format("Unknown error: ~p\n", [Err]), mk_t_err(pos(0, 0), Msg). -- 2.30.2 From 5ef068f1ff19fdbe28a9d388d4eb1a0c10534828 Mon Sep 17 00:00:00 2001 From: Gaith Hallak Date: Wed, 18 Aug 2021 19:27:02 +0300 Subject: [PATCH 15/23] Add a test for using undefined namespaces --- test/aeso_compiler_tests.erl | 4 ++++ test/contracts/using_namespace_undefined.aes | 4 ++++ 2 files changed, 8 insertions(+) create mode 100644 test/contracts/using_namespace_undefined.aes diff --git a/test/aeso_compiler_tests.erl b/test/aeso_compiler_tests.erl index 646177b..ab66966 100644 --- a/test/aeso_compiler_tests.erl +++ b/test/aeso_compiler_tests.erl @@ -794,6 +794,10 @@ failing_contracts() -> , <> ]) + , ?TYPE_ERROR(using_namespace_undefined, + [<> + ]) ]. -define(Path(File), "code_errors/" ??File). diff --git a/test/contracts/using_namespace_undefined.aes b/test/contracts/using_namespace_undefined.aes new file mode 100644 index 0000000..130fd92 --- /dev/null +++ b/test/contracts/using_namespace_undefined.aes @@ -0,0 +1,4 @@ +contract C = + using MyUndefinedNamespace + + entrypoint init() = () -- 2.30.2 From db60be0af31067597c98ad28d90fc4012541314f Mon Sep 17 00:00:00 2001 From: Gaith Hallak Date: Thu, 19 Aug 2021 17:36:53 +0300 Subject: [PATCH 16/23] Change the type of used_namespaces --- 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 42be32a..b99a6bf 100644 --- a/src/aeso_ast_infer_types.erl +++ b/src/aeso_ast_infer_types.erl @@ -128,7 +128,7 @@ , typevars = unrestricted :: unrestricted | [name()] , fields = #{} :: #{ name() => [field_info()] } %% fields are global , namespace = [] :: qname() - , used_namespaces = [] :: list({qname(), namespace_alias()}) + , used_namespaces = [] :: [{qname(), namespace_alias()}] , in_pattern = false :: boolean() , stateful = false :: boolean() , current_function = none :: none | aeso_syntax:id() -- 2.30.2 From 81e442958526671f894a97c83aaae6ba982a6cc0 Mon Sep 17 00:00:00 2001 From: Gaith Hallak Date: Thu, 19 Aug 2021 17:09:21 +0300 Subject: [PATCH 17/23] Add using namespace parts to scanner and parser --- src/aeso_parser.erl | 17 ++++++++++++----- src/aeso_scan.erl | 2 +- src/aeso_syntax.erl | 3 ++- 3 files changed, 15 insertions(+), 7 deletions(-) diff --git a/src/aeso_parser.erl b/src/aeso_parser.erl index b5fd8b9..80002ca 100644 --- a/src/aeso_parser.erl +++ b/src/aeso_parser.erl @@ -137,12 +137,19 @@ fundef_or_decl() -> fundef()]). using() -> - ?RULE(keyword(using), con(), optional({keyword(as), con()}), using(get_ann(_1), _2, _3)). + Alias = {keyword(as), con()}, + For = ?RULE(keyword(for), bracket_list(id()), {for, _2}), + Hiding = ?RULE(keyword(hiding), bracket_list(id()), {hiding, _2}), + ?RULE(keyword(using), con(), optional(Alias), optional(choice(For, Hiding)), using(get_ann(_1), _2, _3, _4)). -using(Ann, Con, none) -> - {using, Ann, Con, none}; -using(Ann, Con, {ok, {_, Alias}}) -> - {using, Ann, Con, Alias}. +using(Ann, Con, none, none) -> + {using, Ann, Con, none, none}; +using(Ann, Con, {ok, {_, Alias}}, none) -> + {using, Ann, Con, Alias, none}; +using(Ann, Con, none, {ok, List}) -> + {using, Ann, Con, none, List}; +using(Ann, Con, {ok, {_, Alias}}, {ok, List}) -> + {using, Ann, Con, Alias, List}. pragma() -> Op = choice([token(T) || T <- ['<', '=<', '==', '>=', '>']]), diff --git a/src/aeso_scan.erl b/src/aeso_scan.erl index ec47e4f..4587efa 100644 --- a/src/aeso_scan.erl +++ b/src/aeso_scan.erl @@ -45,7 +45,7 @@ lexer() -> Keywords = ["contract", "include", "let", "switch", "type", "record", "datatype", "if", "elif", "else", "function", "stateful", "payable", "true", "false", "mod", "public", "entrypoint", "private", "indexed", "namespace", - "interface", "main", "using", "as" + "interface", "main", "using", "as", "for", "hiding" ], KW = string:join(Keywords, "|"), diff --git a/src/aeso_syntax.erl b/src/aeso_syntax.erl index 5fb02bb..8148a1a 100644 --- a/src/aeso_syntax.erl +++ b/src/aeso_syntax.erl @@ -36,6 +36,7 @@ -type tvar() :: {tvar, ann(), name()}. -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()]} @@ -46,7 +47,7 @@ | {type_def, ann(), id(), [tvar()], typedef()} | {fun_clauses, ann(), id(), type(), [letfun() | fundecl()]} | {block, ann(), [decl()]} - | {using, ann(), con(), namespace_alias()} + | {using, ann(), con(), namespace_alias(), namespace_parts()} | fundecl() | letfun() | letval(). % Only for error msgs -- 2.30.2 From c29e9a7b2eceb698398f55d5ae2270ac4ab3f36d Mon Sep 17 00:00:00 2001 From: Gaith Hallak Date: Fri, 20 Aug 2021 19:11:32 +0300 Subject: [PATCH 18/23] Add using namespace parts to ast type inference --- src/aeso_ast_infer_types.erl | 84 ++++++++++++++++++++++++++++++------ 1 file changed, 71 insertions(+), 13 deletions(-) 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). -- 2.30.2 From 2d916733d82e68397d9530adce77a465450bc22e Mon Sep 17 00:00:00 2001 From: Gaith Hallak Date: Fri, 20 Aug 2021 19:33:09 +0300 Subject: [PATCH 19/23] Add tests for using namespace parts --- test/aeso_compiler_tests.erl | 8 ++++++++ test/contracts/using_namespace.aes | 11 ++++++++--- test/contracts/using_namespace_hidden_parts.aes | 8 ++++++++ test/contracts/using_namespace_undefined_parts.aes | 7 +++++++ 4 files changed, 31 insertions(+), 3 deletions(-) create mode 100644 test/contracts/using_namespace_hidden_parts.aes create mode 100644 test/contracts/using_namespace_undefined_parts.aes diff --git a/test/aeso_compiler_tests.erl b/test/aeso_compiler_tests.erl index ab66966..72b10cc 100644 --- a/test/aeso_compiler_tests.erl +++ b/test/aeso_compiler_tests.erl @@ -798,6 +798,14 @@ failing_contracts() -> [<> ]) + , ?TYPE_ERROR(using_namespace_undefined_parts, + [<> + ]) + , ?TYPE_ERROR(using_namespace_hidden_parts, + [<> + ]) ]. -define(Path(File), "code_errors/" ??File). diff --git a/test/contracts/using_namespace.aes b/test/contracts/using_namespace.aes index ae20706..95d40e5 100644 --- a/test/contracts/using_namespace.aes +++ b/test/contracts/using_namespace.aes @@ -1,15 +1,20 @@ include "Option.aes" include "Pair.aes" include "String.aes" +include "Triple.aes" using Pair +using Triple hiding [fst, snd] namespace Nsp = using Option function h() = - let op = Some(2) - is_some(op) + let op = Some((2, 3, 4)) + if (is_some(op)) + thd(force(op)) == 4 + else + false contract Cntr = using Nsp @@ -24,7 +29,7 @@ contract Cntr = snd(p) function g() = - using String + using String for [concat] let s1 = "abc" let s2 = "def" diff --git a/test/contracts/using_namespace_hidden_parts.aes b/test/contracts/using_namespace_hidden_parts.aes new file mode 100644 index 0000000..5469e95 --- /dev/null +++ b/test/contracts/using_namespace_hidden_parts.aes @@ -0,0 +1,8 @@ +namespace Nsp = + function f() = 1 + function g() = 2 + +contract Cntr = + using Nsp for [f] + + entrypoint init() = g() diff --git a/test/contracts/using_namespace_undefined_parts.aes b/test/contracts/using_namespace_undefined_parts.aes new file mode 100644 index 0000000..a728641 --- /dev/null +++ b/test/contracts/using_namespace_undefined_parts.aes @@ -0,0 +1,7 @@ +namespace Nsp = + function f() = 1 + +contract Cntr = + using Nsp for [a] + + entrypoint init() = f() -- 2.30.2 From fc55cfe05978c7e48e6e1bd4a5483ee8d2ed066c Mon Sep 17 00:00:00 2001 From: Gaith Hallak Date: Mon, 23 Aug 2021 18:30:33 +0300 Subject: [PATCH 20/23] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index f5f6ee0..9d97cd9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added - `Set` stdlib - `Option.force_msg` +- Loading namespaces into the current scope (e.g. `using Pair`) ### Changed ### Removed -- 2.30.2 From 5725307643789be5df8332a661a1d99e16871131 Mon Sep 17 00:00:00 2001 From: Gaith Hallak Date: Tue, 24 Aug 2021 12:52:39 +0300 Subject: [PATCH 21/23] Code cleaning --- src/aeso_ast_infer_types.erl | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/src/aeso_ast_infer_types.erl b/src/aeso_ast_infer_types.erl index 4d87e70..a0c07fa 100644 --- a/src/aeso_ast_infer_types.erl +++ b/src/aeso_ast_infer_types.erl @@ -340,19 +340,9 @@ visible_in_used_namespaces(UsedNamespaces, QName) -> IsVisible = fun(Namespace) -> case Namespace of {_, _, {for, Names}} -> - case lists:member(Name, Names) of - true -> - true; - false -> - false - end; + lists:member(Name, Names); {_, _, {hiding, Names}} -> - case lists:member(Name, Names) of - true -> - false; - false -> - true - end; + not lists:member(Name, Names); _ -> true end -- 2.30.2 From c948591c867a4168066a31556060eaaa96d0f963 Mon Sep 17 00:00:00 2001 From: Gaith Hallak Date: Mon, 23 Aug 2021 18:30:57 +0300 Subject: [PATCH 22/23] Update the docs --- docs/sophia_features.md | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/docs/sophia_features.md b/docs/sophia_features.md index 6be7ac4..da5be7c 100644 --- a/docs/sophia_features.md +++ b/docs/sophia_features.md @@ -248,6 +248,39 @@ Functions in namespaces have access to the same environment (including the with the exception of `state`, `put` and `Chain.event` since these are dependent on the specific state and event types of the contract. +To avoid mentioning the namespace every time it is used, Sophia allows +including the namespace in the current scope with the `using` keyword: +``` +include "Pair.aes" +using Pair +contract C = + type state = int + entrypoint init() = + let p = (1, 2) + fst(p) // this is the same as Pair.fst(p) +``` + +It is also possible to make an alias for the namespace with the `as` keyword: +``` +include "Pair.aes" +contract C = + using Pair as P + type state = int + entrypoint init() = + let p = (1, 2) + P.fst(p) // this is the same as Pair.fst(p) +``` + +Importing specific parts of a namespace or hiding these parts can also be +done like this: +``` +using Pair for [fst, snd] // this will only import fst and snd +using Triple hiding [fst, snd] // this will import everything except for fst and snd +``` + +Note that it is possible to use a namespace in the top level of the file, in the +contract level, namespace level, or in the function level. + ## Splitting code over multiple files Code from another file can be included in a contract using an `include` -- 2.30.2 From 7fabbff11e07da8451df3d57a281f9bae7b3d739 Mon Sep 17 00:00:00 2001 From: Gaith Hallak Date: Mon, 6 Sep 2021 17:53:25 +0300 Subject: [PATCH 23/23] Update the docs about the same alias for multiple namespaces --- docs/sophia_features.md | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/docs/sophia_features.md b/docs/sophia_features.md index da5be7c..d6d062b 100644 --- a/docs/sophia_features.md +++ b/docs/sophia_features.md @@ -271,6 +271,33 @@ contract C = P.fst(p) // this is the same as Pair.fst(p) ``` +Having the same alias for multiple namespaces is possible and it allows +referening functions that are defined in different namespaces and have +different names with the same alias: +``` +namespace Xa = function f() = 1 +namespace Xb = function g() = 2 +contract Cntr = + using Xa as A + using Xb as A + type state = int + entrypoint init() = A.f() + A.g() +``` + +Note that using functions with the same name would result in an ambiguous name +error: +``` +namespace Xa = function f() = 1 +namespace Xb = function f() = 2 +contract Cntr = + using Xa as A + using Xb as A + type state = int + + // the next line has an error because f is defined in both Xa and Xb + entrypoint init() = A.f() +``` + Importing specific parts of a namespace or hiding these parts can also be done like this: ``` -- 2.30.2