Skip to content

Commit

Permalink
fix: unreachable code analysis inside for loops (vyperlang#3731)
Browse files Browse the repository at this point in the history
unreachable code analysis did not analyze for loop bodies.
fix: in `find_terminating_node()`, recurse into the bodies of for loops.
  • Loading branch information
charles-cooper authored Jan 15, 2024
1 parent 88d9c22 commit 81c6d8e
Show file tree
Hide file tree
Showing 3 changed files with 50 additions and 1 deletion.
40 changes: 40 additions & 0 deletions tests/functional/syntax/test_unbalanced_return.py
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,39 @@ def foo() -> uint256:
""",
StructureException,
),
(
"""
@internal
def foo() -> uint256:
for i: uint256 in range(10):
if i == 11:
return 1
""",
FunctionDeclarationException,
),
(
"""
@internal
def foo() -> uint256:
for i: uint256 in range(9):
if i == 11:
return 1
if block.number % 2 == 0:
return 1
""",
FunctionDeclarationException,
),
(
"""
@internal
def foo() -> uint256:
for i: uint256 in range(10):
return 1
pass # unreachable
return 5
""",
StructureException,
),
]


Expand Down Expand Up @@ -187,6 +220,13 @@ def foo() -> int128:
else:
raw_revert(b"vyper")
""",
"""
@external
def foo() -> int128:
for i: uint256 in range(1):
return 1
return 0
""",
]


Expand Down
6 changes: 5 additions & 1 deletion vyper/ast/nodes.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -257,6 +257,10 @@ class IfExp(ExprNode):
body: ExprNode = ...
orelse: ExprNode = ...

class For(VyperNode): ...
class For(VyperNode):
target: ExprNode
iter: ExprNode
body: list[VyperNode]

class Break(VyperNode): ...
class Continue(VyperNode): ...
5 changes: 5 additions & 0 deletions vyper/semantics/analysis/local.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ def find_terminating_node(node_list: list) -> Optional[vy_ast.VyperNode]:
for node in node_list:
if ret is not None:
raise StructureException("Unreachable code!", node)

if node.is_terminus:
ret = node

Expand All @@ -87,6 +88,10 @@ def find_terminating_node(node_list: list) -> Optional[vy_ast.VyperNode]:
if body_terminates is not None and else_terminates is not None:
ret = else_terminates

if isinstance(node, vy_ast.For):
# call find_terminating_node for its side effects
find_terminating_node(node.body)

return ret


Expand Down

0 comments on commit 81c6d8e

Please sign in to comment.