Implement loading namespaces with the using keyword (#338)

* Add using namespace as to scanner and parser

* Change the alias from id() to con()

* Add using namespace to AST type inference

* Allow using namespace to appear in the top level

* Allow using namespace to appear inside functions

* Add a compiler test for using namespace

* Handle name collisions

* Implement mk_error for ambiguous_name

* Add failing test for ambiguous names

* Limit the scope of the used namespaces

* Add test for wrong scope of using namespace

* Use a single using declaration

* Split long line

* Forbid using undefined namespaces

* Add a test for using undefined namespaces

* Change the type of used_namespaces

* Add using namespace parts to scanner and parser

* Add using namespace parts to ast type inference

* Add tests for using namespace parts

* Update CHANGELOG.md

* Code cleaning

* Update the docs

* Update the docs about the same alias for multiple namespaces
This commit is contained in:
Gaith Hallak 2021-09-07 17:45:28 +03:00 committed by GitHub
parent 262452fb70
commit a7b7aafced
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 310 additions and 18 deletions

View File

@ -8,6 +8,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Added ### Added
- `Set` stdlib - `Set` stdlib
- `Option.force_msg` - `Option.force_msg`
- Loading namespaces into the current scope (e.g. `using Pair`)
### Changed ### Changed
### Removed ### Removed

View File

@ -248,6 +248,66 @@ Functions in namespaces have access to the same environment (including the
with the exception of `state`, `put` and `Chain.event` since these are with the exception of `state`, `put` and `Chain.event` since these are
dependent on the specific state and event types of the contract. 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)
```
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:
```
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 ## Splitting code over multiple files
Code from another file can be included in a contract using an `include` Code from another file can be included in a contract using an `include`

View File

@ -102,6 +102,10 @@
-type qname() :: [string()]. -type qname() :: [string()].
-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_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.
-type fun_info() :: {aeso_syntax:ann(), typesig() | type()}. -type fun_info() :: {aeso_syntax:ann(), typesig() | type()}.
@ -121,15 +125,16 @@
-type scope() :: #scope{}. -type scope() :: #scope{}.
-record(env, -record(env,
{ scopes = #{ [] => #scope{}} :: #{ qname() => scope() } { scopes = #{ [] => #scope{}} :: #{ qname() => scope() }
, vars = [] :: [{name(), var_info()}] , vars = [] :: [{name(), var_info()}]
, 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()
, in_pattern = false :: boolean() , used_namespaces = [] :: used_namespaces()
, stateful = false :: boolean() , in_pattern = false :: boolean()
, current_function = none :: none | aeso_syntax:id() , stateful = false :: boolean()
, what = top :: top | namespace | contract | contract_interface , current_function = none :: none | aeso_syntax:id()
, what = top :: top | namespace | contract | contract_interface
}). }).
-type env() :: #env{}. -type env() :: #env{}.
@ -312,9 +317,38 @@ bind_contract({Contract, Ann, Id, Contents}, Env)
%% What scopes could a given name come from? %% What scopes could a given name come from?
-spec possible_scopes(env(), qname()) -> [qname()]. -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), Qual = lists:droplast(Name),
[ lists:sublist(Current, I) ++ Qual || I <- lists:seq(0, length(Current)) ]. 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,
Ret1 = [ lists:sublist(Current, I) ++ Q || I <- lists:seq(0, length(Current)), Q <- NewQuals ],
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}} ->
lists:member(Name, Names);
{_, _, {hiding, Names}} ->
not lists:member(Name, Names);
_ ->
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) ->
@ -341,7 +375,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),
@ -365,7 +399,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
@ -803,6 +841,8 @@ 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(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
infer1(Env, Rest, Acc, Options). infer1(Env, Rest, Acc, Options).
@ -859,10 +899,13 @@ infer_contract(Env0, What, Defs0, Options) ->
({letfun, _, _, _, _, _}) -> function; ({letfun, _, _, _, _, _}) -> function;
({fun_clauses, _, _, _, _}) -> function; ({fun_clauses, _, _, _, _}) -> function;
({fun_decl, _, _, _}) -> prototype; ({fun_decl, _, _, _}) -> prototype;
({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,
{Env1, TypeDefs} = check_typedefs(Env, Get(type, Defs)), OldUsedNamespaces = Env#env.used_namespaces,
Env01 = check_usings(Env, Get(using, Defs)),
{Env1, TypeDefs} = check_typedefs(Env01, Get(type, Defs)),
create_type_errors(), create_type_errors(),
check_unexpected(Get(unexpected, Defs)), check_unexpected(Get(unexpected, Defs)),
Env2 = Env2 =
@ -884,11 +927,13 @@ infer_contract(Env0, What, Defs0, Options) ->
DepGraph = maps:map(fun(_, Def) -> aeso_syntax_utils:used_ids(Def) end, FunMap), DepGraph = maps:map(fun(_, Def) -> aeso_syntax_utils:used_ids(Def) end, FunMap),
SCCs = aeso_utils:scc(DepGraph), SCCs = aeso_utils:scc(DepGraph),
{Env4, Defs1} = check_sccs(Env3, FunMap, SCCs, []), {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 that `init` doesn't read or write the state
check_state_dependencies(Env4, Defs1), check_state_dependencies(Env4, Defs1),
destroy_and_report_type_errors(Env4), destroy_and_report_type_errors(Env4),
%% Add inferred types of definitions %% Add inferred types of definitions
{Env4, TypeDefs ++ Decls ++ Defs1}. {Env5, TypeDefs ++ Decls ++ Defs1}.
%% Restructure blocks into multi-clause fundefs (`fun_clauses`). %% Restructure blocks into multi-clause fundefs (`fun_clauses`).
-spec process_blocks([aeso_syntax:decl()]) -> [aeso_syntax:decl()]. -spec process_blocks([aeso_syntax:decl()]) -> [aeso_syntax:decl()].
@ -988,6 +1033,43 @@ check_typedef(Env, {variant_t, Cons}) ->
{variant_t, [ {constr_t, Ann, Con, [ check_type(Env, Arg) || Arg <- Args ]} {variant_t, [ {constr_t, Ann, Con, [ check_type(Env, Arg) || Arg <- Args ]}
|| {constr_t, Ann, Con, Args} <- Cons ]}. || {constr_t, Ann, Con, Args} <- Cons ]}.
check_usings(Env, []) ->
Env;
check_usings(Env = #env{ used_namespaces = UsedNamespaces }, [{using, Ann, Con, Alias, Parts} | Rest]) ->
AliasName = case Alias of
none ->
none;
_ ->
qname(Alias)
end,
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);
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]).
check_unexpected(Xs) -> check_unexpected(Xs) ->
[ type_error(X) || X <- Xs ]. [ type_error(X) || X <- Xs ].
@ -1017,6 +1099,8 @@ 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, 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}),
check_modifiers_(Env, Rest); check_modifiers_(Env, Rest);
@ -1770,6 +1854,8 @@ 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(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)].
@ -2987,6 +3073,17 @@ mk_error({contract_lacks_definition, Type, When}) ->
), ),
{Pos, Ctxt} = pp_when(When), {Pos, Ctxt} = pp_when(When),
mk_t_err(Pos, Msg, Ctxt); 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({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) -> 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).

View File

@ -109,6 +109,7 @@ decl() ->
, ?RULE(keyword(namespace), con(), tok('='), maybe_block(decl()), {namespace, _1, _2, _4}) , ?RULE(keyword(namespace), con(), tok('='), maybe_block(decl()), {namespace, _1, _2, _4})
, ?RULE(keyword(include), str(), {include, get_ann(_1), _2}) , ?RULE(keyword(include), str(), {include, get_ann(_1), _2})
, using()
, pragma() , pragma()
%% Type declarations TODO: format annotation for "type bla" vs "type bla()" %% Type declarations TODO: format annotation for "type bla" vs "type bla()"
@ -135,6 +136,21 @@ fundef_or_decl() ->
choice([?RULE(id(), tok(':'), type(), {fun_decl, get_ann(_1), _1, _3}), choice([?RULE(id(), tok(':'), type(), {fun_decl, get_ann(_1), _1, _3}),
fundef()]). fundef()]).
using() ->
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, 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() -> pragma() ->
Op = choice([token(T) || T <- ['<', '=<', '==', '>=', '>']]), Op = choice([token(T) || T <- ['<', '=<', '==', '>=', '>']]),
?RULE(tok('@'), id("compiler"), Op, version(), {pragma, get_ann(_1), {compiler, element(1, _3), _4}}). ?RULE(tok('@'), id("compiler"), Op, version(), {pragma, get_ann(_1), {compiler, element(1, _3), _4}}).
@ -254,7 +270,8 @@ body() ->
stmt() -> stmt() ->
?LAZY_P(choice( ?LAZY_P(choice(
[ expr() [ using()
, expr()
, letdecl() , letdecl()
, {switch, keyword(switch), parens(expr()), maybe_block(branch())} , {switch, keyword(switch), parens(expr()), maybe_block(branch())}
, {'if', keyword('if'), parens(expr()), body()} , {'if', keyword('if'), parens(expr()), body()}

View File

@ -45,7 +45,7 @@ lexer() ->
Keywords = ["contract", "include", "let", "switch", "type", "record", "datatype", "if", "elif", "else", "function", Keywords = ["contract", "include", "let", "switch", "type", "record", "datatype", "if", "elif", "else", "function",
"stateful", "payable", "true", "false", "mod", "public", "entrypoint", "private", "indexed", "namespace", "stateful", "payable", "true", "false", "mod", "public", "entrypoint", "private", "indexed", "namespace",
"interface", "main" "interface", "main", "using", "as", "for", "hiding"
], ],
KW = string:join(Keywords, "|"), KW = string:join(Keywords, "|"),

View File

@ -35,6 +35,9 @@
-type qcon() :: {qcon, ann(), [name()]}. -type qcon() :: {qcon, ann(), [name()]}.
-type tvar() :: {tvar, ann(), name()}. -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()]} -type decl() :: {contract_main, ann(), con(), [decl()]}
| {contract_child, ann(), con(), [decl()]} | {contract_child, ann(), con(), [decl()]}
| {contract_interface, ann(), con(), [decl()]} | {contract_interface, ann(), con(), [decl()]}
@ -44,6 +47,7 @@
| {type_def, ann(), id(), [tvar()], typedef()} | {type_def, ann(), id(), [tvar()], typedef()}
| {fun_clauses, ann(), id(), type(), [letfun() | fundecl()]} | {fun_clauses, ann(), id(), type(), [letfun() | fundecl()]}
| {block, ann(), [decl()]} | {block, ann(), [decl()]}
| {using, ann(), con(), namespace_alias(), namespace_parts()}
| fundecl() | fundecl()
| letfun() | letfun()
| letval(). % Only for error msgs | letval(). % Only for error msgs
@ -52,7 +56,6 @@
-type pragma() :: {compiler, '==' | '<' | '>' | '=<' | '>=', compiler_version()}. -type pragma() :: {compiler, '==' | '<' | '>' | '=<' | '>=', compiler_version()}.
-type letval() :: {letval, ann(), pat(), expr()}. -type letval() :: {letval, ann(), pat(), expr()}.
-type letfun() :: {letfun, ann(), id(), [pat()], type(), expr()}. -type letfun() :: {letfun, ann(), id(), [pat()], type(), expr()}.
-type fundecl() :: {fun_decl, ann(), id(), type()}. -type fundecl() :: {fun_decl, ann(), id(), type()}.

View File

@ -200,6 +200,7 @@ compilable_contracts() ->
"clone_simple", "clone_simple",
"create", "create",
"child_contract_init_bug", "child_contract_init_bug",
"using_namespace",
"test" % Custom general-purpose test file. Keep it last on the list. "test" % Custom general-purpose test file. Keep it last on the list.
]. ].
@ -781,6 +782,30 @@ failing_contracts() ->
[<<?Pos(1,6) [<<?Pos(1,6)
"Only one main contract can be defined.">> "Only one main contract can be defined.">>
]) ])
, ?TYPE_ERROR(using_namespace_ambiguous_name,
[ <<?Pos(2,3)
"Ambiguous name: Xa.f at line 2, column 3\nXb.f at line 5, column 3">>
, <<?Pos(13,23)
"Unbound variable A.f at line 13, column 23">>
])
, ?TYPE_ERROR(using_namespace_wrong_scope,
[ <<?Pos(19,5)
"Unbound variable f at line 19, column 5">>
, <<?Pos(21,23)
"Unbound variable f at line 21, column 23">>
])
, ?TYPE_ERROR(using_namespace_undefined,
[<<?Pos(2,3)
"Cannot use undefined namespace MyUndefinedNamespace">>
])
, ?TYPE_ERROR(using_namespace_undefined_parts,
[<<?Pos(5,3)
"The namespace Nsp does not define the following names: a">>
])
, ?TYPE_ERROR(using_namespace_hidden_parts,
[<<?Pos(8,23)
"Unbound variable g at line 8, column 23">>
])
]. ].
-define(Path(File), "code_errors/" ??File). -define(Path(File), "code_errors/" ??File).

View File

@ -0,0 +1,36 @@
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, 3, 4))
if (is_some(op))
thd(force(op)) == 4
else
false
contract Cntr =
using Nsp
entrypoint init() = ()
function f() =
let p = (1, 2)
if (h())
fst(p)
else
snd(p)
function g() =
using String for [concat]
let s1 = "abc"
let s2 = "def"
concat(s1, s2)

View File

@ -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()

View File

@ -0,0 +1,8 @@
namespace Nsp =
function f() = 1
function g() = 2
contract Cntr =
using Nsp for [f]
entrypoint init() = g()

View File

@ -0,0 +1,4 @@
contract C =
using MyUndefinedNamespace
entrypoint init() = ()

View File

@ -0,0 +1,7 @@
namespace Nsp =
function f() = 1
contract Cntr =
using Nsp for [a]
entrypoint init() = f()

View File

@ -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()