Skip to content

Commit

Permalink
fix[lang]: transitive exports (vyperlang#3888)
Browse files Browse the repository at this point in the history
this commit fixes transitive exports, i.e. where `module1` exports
`module2.foo`, but it was previously not exportable from `module1`. this
is because previously exported functions were not added to the
`ModuleT`'s `exposed_functions` list.

a test had previously been written for this case, but it had been marked
xfail since the desired behavior was not clear until user feedback was
received. this commit removes the `xfail` marker from that test case.
  • Loading branch information
charles-cooper authored Mar 26, 2024
1 parent cedf708 commit ea7f081
Show file tree
Hide file tree
Showing 4 changed files with 8 additions and 8 deletions.
7 changes: 1 addition & 6 deletions tests/functional/codegen/modules/test_exports.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,3 @@
import pytest


def test_simple_export(make_input_bundle, get_contract):
lib1 = """
@external
Expand Down Expand Up @@ -131,9 +128,7 @@ def foo() -> uint256:
assert c.foo() == 5


# not sure if this one should work
@pytest.mark.xfail(reason="ambiguous spec")
def test_recursive_export(make_input_bundle, get_contract):
def test_transitive_export(make_input_bundle, get_contract):
lib1 = """
@external
def foo() -> uint256:
Expand Down
3 changes: 2 additions & 1 deletion vyper/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -127,8 +127,9 @@ def format_annotation(self, value):
return None

if isinstance(node, vy_ast.VyperNode):
module_node = node.get_ancestor(vy_ast.Module)
module_node = node.module_node

# TODO: handle cases where module is None or vy_ast.Module
if module_node.get("path") not in (None, "<unknown>"):
node_msg = f'{node_msg}contract "{module_node.path}:{node.lineno}", '

Expand Down
2 changes: 1 addition & 1 deletion vyper/semantics/analysis/module.py
Original file line number Diff line number Diff line change
Expand Up @@ -524,7 +524,7 @@ def visit_ExportsDecl(self, node):
decl_node = func_t.decl_node

if not isinstance(func_t, ContractFunctionT):
raise StructureException("not a function!", decl_node, item)
raise StructureException(f"not a function: `{func_t}`", decl_node, item)
if not func_t.is_external:
raise StructureException("can't export non-external functions!", decl_node, item)

Expand Down
4 changes: 4 additions & 0 deletions vyper/semantics/types/module.py
Original file line number Diff line number Diff line change
Expand Up @@ -296,6 +296,10 @@ def __init__(self, module: vy_ast.Module, name: Optional[str] = None):
# note: this checks for collisions
self.add_member(f.name, f._metadata["func_type"])

for item in self.exports_decls:
for fn_t in item._metadata["exports_info"].functions:
self.add_member(fn_t.name, fn_t)

for e in self.event_defs:
# add the type of the event so it can be used in call position
self.add_member(e.name, TYPE_T(e._metadata["event_type"])) # type: ignore
Expand Down

0 comments on commit ea7f081

Please sign in to comment.