Ban using contracts as namespaces (#428)
* Ban calling contracts functions as functions namespaces * Ban using contracts as namespaces * Add tests * Update CHANGELOG * Separate guards with a semicolon
This commit is contained in:
parent
256df25af4
commit
5adeb6c93e
@ -17,6 +17,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
### Changed
|
### Changed
|
||||||
- Type definitions serialised to ACI as `typedefs` field instead of `type_defs` to increase compatibility.
|
- Type definitions serialised to ACI as `typedefs` field instead of `type_defs` to increase compatibility.
|
||||||
- Check contracts and entrypoints modifiers when implementing interfaces.
|
- Check contracts and entrypoints modifiers when implementing interfaces.
|
||||||
|
- Contracts can no longer be used as namespaces.
|
||||||
### Removed
|
### Removed
|
||||||
### Fixed
|
### Fixed
|
||||||
- Typechecker crashes if Chain.create or Chain.clone are used without arguments.
|
- Typechecker crashes if Chain.create or Chain.clone are used without arguments.
|
||||||
|
@ -229,7 +229,7 @@ force_bind_fun(X, Type, Env = #env{ what = What }) ->
|
|||||||
NoCode = get_option(no_code, false),
|
NoCode = get_option(no_code, false),
|
||||||
Entry = if X == "init", What == contract, not NoCode ->
|
Entry = if X == "init", What == contract, not NoCode ->
|
||||||
{reserved_init, Ann, Type};
|
{reserved_init, Ann, Type};
|
||||||
What == contract_interface -> {contract_fun, Ann, Type};
|
What == contract; What == contract_interface -> {contract_fun, Ann, Type};
|
||||||
true -> {Ann, Type}
|
true -> {Ann, Type}
|
||||||
end,
|
end,
|
||||||
on_current_scope(Env, fun(Scope = #scope{ funs = Funs }) ->
|
on_current_scope(Env, fun(Scope = #scope{ funs = Funs }) ->
|
||||||
@ -426,6 +426,7 @@ lookup_env(Env, Kind, Ann, Name) ->
|
|||||||
lookup_env1(#env{ namespace = Current, used_namespaces = UsedNamespaces, 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),
|
||||||
|
QNameIsEvent = lists:suffix(["Chain", "event"], QName),
|
||||||
AllowPrivate = lists:prefix(Qual, Current),
|
AllowPrivate = lists:prefix(Qual, Current),
|
||||||
%% Get the scope
|
%% Get the scope
|
||||||
case maps:get(Qual, Scopes, false) of
|
case maps:get(Qual, Scopes, false) of
|
||||||
@ -441,6 +442,8 @@ lookup_env1(#env{ namespace = Current, used_namespaces = UsedNamespaces, scopes
|
|||||||
{reserved_init, Ann1, Type} ->
|
{reserved_init, Ann1, Type} ->
|
||||||
type_error({cannot_call_init_function, Ann}),
|
type_error({cannot_call_init_function, Ann}),
|
||||||
{QName, {Ann1, Type}}; %% Return the type to avoid an extra not-in-scope error
|
{QName, {Ann1, Type}}; %% Return the type to avoid an extra not-in-scope error
|
||||||
|
{contract_fun, Ann1, Type} when AllowPrivate orelse QNameIsEvent ->
|
||||||
|
{QName, {Ann1, Type}};
|
||||||
{contract_fun, Ann1, Type} ->
|
{contract_fun, Ann1, Type} ->
|
||||||
type_error({contract_treated_as_namespace, Ann, QName}),
|
type_error({contract_treated_as_namespace, Ann, QName}),
|
||||||
{QName, {Ann1, Type}};
|
{QName, {Ann1, Type}};
|
||||||
@ -1254,6 +1257,10 @@ 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{kind = contract} ->
|
||||||
|
create_type_errors(),
|
||||||
|
type_error({using_undefined_namespace, Ann, qname(Con)}),
|
||||||
|
destroy_and_report_type_errors(Env);
|
||||||
Scope ->
|
Scope ->
|
||||||
Nsp = case Parts of
|
Nsp = case Parts of
|
||||||
none ->
|
none ->
|
||||||
|
@ -1175,6 +1175,13 @@ failing_contracts() ->
|
|||||||
[<<?Pos(5,16)
|
[<<?Pos(5,16)
|
||||||
"`f` must be payable because the entrypoint `f` in the interface `I` is payable">>
|
"`f` must be payable because the entrypoint `f` in the interface `I` is payable">>
|
||||||
])
|
])
|
||||||
|
, ?TYPE_ERROR(calling_child_contract_entrypoint,
|
||||||
|
[<<?Pos(5,20)
|
||||||
|
"Invalid call to contract entrypoint `F.g`.\n"
|
||||||
|
"It must be called as `c.g` for some `c : F`.">>])
|
||||||
|
, ?TYPE_ERROR(using_contract_as_namespace,
|
||||||
|
[<<?Pos(5,3)
|
||||||
|
"Cannot use undefined namespace F">>])
|
||||||
].
|
].
|
||||||
|
|
||||||
validation_test_() ->
|
validation_test_() ->
|
||||||
|
5
test/contracts/calling_child_contract_entrypoint.aes
Normal file
5
test/contracts/calling_child_contract_entrypoint.aes
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
contract F =
|
||||||
|
entrypoint g() = 1
|
||||||
|
|
||||||
|
main contract C =
|
||||||
|
entrypoint f() = F.g()
|
7
test/contracts/using_contract_as_namespace.aes
Normal file
7
test/contracts/using_contract_as_namespace.aes
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
contract F =
|
||||||
|
entrypoint g() = 1
|
||||||
|
|
||||||
|
main contract C =
|
||||||
|
using F for [g]
|
||||||
|
|
||||||
|
entrypoint f() = g()
|
Loading…
x
Reference in New Issue
Block a user