Allow exceptions in code that will be staged. Consider all exceptions to be exiting the CFG, with no explicit support for exception-based control-flow. It may incidentally work to use exception-based control flow in code that is never staged.
PiperOrigin-RevId: 232719435
This commit is contained in:
parent
78840bb864
commit
3f8dcd3e28
@ -125,6 +125,10 @@ class SideEffectGuardTransformer(converter.Base):
|
|||||||
node.orelse = self._visit_and_reindent(node.orelse)
|
node.orelse = self._visit_and_reindent(node.orelse)
|
||||||
return node
|
return node
|
||||||
|
|
||||||
|
# TODO(b/123995141) Remove once ExceptionHandlers are in the CFG
|
||||||
|
def visit_ExceptHandler(self, node):
|
||||||
|
return node
|
||||||
|
|
||||||
def visit_Expr(self, node):
|
def visit_Expr(self, node):
|
||||||
self.generic_visit(node)
|
self.generic_visit(node)
|
||||||
if isinstance(node.value, gast.Call):
|
if isinstance(node.value, gast.Call):
|
||||||
|
@ -393,6 +393,8 @@ class GraphBuilder(object):
|
|||||||
def _connect_jump_to_finally_sections(self, node):
|
def _connect_jump_to_finally_sections(self, node):
|
||||||
"""Connects a jump node to the finally sections protecting it."""
|
"""Connects a jump node to the finally sections protecting it."""
|
||||||
cursor = set((node,))
|
cursor = set((node,))
|
||||||
|
if node not in self.finally_sections:
|
||||||
|
return cursor
|
||||||
for guard_section_id in self.finally_sections[node]:
|
for guard_section_id in self.finally_sections[node]:
|
||||||
guard_begin, guard_ends = self.finally_section_subgraphs[guard_section_id]
|
guard_begin, guard_ends = self.finally_section_subgraphs[guard_section_id]
|
||||||
self._connect_nodes(cursor, guard_begin)
|
self._connect_nodes(cursor, guard_begin)
|
||||||
@ -620,10 +622,10 @@ class AstToCfg(gast.NodeVisitor):
|
|||||||
leaving_node = self.lexical_scopes.pop()
|
leaving_node = self.lexical_scopes.pop()
|
||||||
assert node == leaving_node
|
assert node == leaving_node
|
||||||
|
|
||||||
def _get_enclosing_scopes(self, include, stop_at):
|
def _get_enclosing_finally_scopes(self, stop_at):
|
||||||
included = []
|
included = []
|
||||||
for node in reversed(self.lexical_scopes):
|
for node in reversed(self.lexical_scopes):
|
||||||
if isinstance(node, include):
|
if isinstance(node, gast.Try) and node.finalbody:
|
||||||
included.append(node)
|
included.append(node)
|
||||||
if isinstance(node, stop_at):
|
if isinstance(node, stop_at):
|
||||||
return node, included
|
return node, included
|
||||||
@ -635,10 +637,8 @@ class AstToCfg(gast.NodeVisitor):
|
|||||||
|
|
||||||
def _process_exit_statement(self, node, *exits_nodes_of_type):
|
def _process_exit_statement(self, node, *exits_nodes_of_type):
|
||||||
# Note: this is safe because we process functions separately.
|
# Note: this is safe because we process functions separately.
|
||||||
try_node, guards = self._get_enclosing_scopes(
|
try_node, guards = self._get_enclosing_finally_scopes(
|
||||||
include=(gast.Try,),
|
tuple(exits_nodes_of_type))
|
||||||
stop_at=tuple(exits_nodes_of_type),
|
|
||||||
)
|
|
||||||
if try_node is None:
|
if try_node is None:
|
||||||
raise ValueError(
|
raise ValueError(
|
||||||
'%s that is not enclosed by any of %s' % (node, exits_nodes_of_type))
|
'%s that is not enclosed by any of %s' % (node, exits_nodes_of_type))
|
||||||
@ -646,10 +646,8 @@ class AstToCfg(gast.NodeVisitor):
|
|||||||
|
|
||||||
def _process_continue_statement(self, node, *loops_to_nodes_of_type):
|
def _process_continue_statement(self, node, *loops_to_nodes_of_type):
|
||||||
# Note: this is safe because we process functions separately.
|
# Note: this is safe because we process functions separately.
|
||||||
try_node, guards = self._get_enclosing_scopes(
|
try_node, guards = self._get_enclosing_finally_scopes(
|
||||||
include=(gast.Try,),
|
tuple(loops_to_nodes_of_type))
|
||||||
stop_at=tuple(loops_to_nodes_of_type),
|
|
||||||
)
|
|
||||||
if try_node is None:
|
if try_node is None:
|
||||||
raise ValueError('%s that is not enclosed by any of %s' %
|
raise ValueError('%s that is not enclosed by any of %s' %
|
||||||
(node, loops_to_nodes_of_type))
|
(node, loops_to_nodes_of_type))
|
||||||
@ -698,10 +696,7 @@ class AstToCfg(gast.NodeVisitor):
|
|||||||
self._process_basic_statement(node)
|
self._process_basic_statement(node)
|
||||||
|
|
||||||
def visit_Raise(self, node):
|
def visit_Raise(self, node):
|
||||||
try_node, guards = self._get_enclosing_scopes(
|
try_node, guards = self._get_enclosing_finally_scopes((gast.FunctionDef,))
|
||||||
include=(gast.Try,),
|
|
||||||
stop_at=(gast.FunctionDef,),
|
|
||||||
)
|
|
||||||
if try_node is None:
|
if try_node is None:
|
||||||
raise ValueError('%s that is not enclosed by any FunctionDef' % node)
|
raise ValueError('%s that is not enclosed by any FunctionDef' % node)
|
||||||
self.builder.add_error_node(node, guards)
|
self.builder.add_error_node(node, guards)
|
||||||
@ -797,16 +792,13 @@ class AstToCfg(gast.NodeVisitor):
|
|||||||
for stmt in node.orelse:
|
for stmt in node.orelse:
|
||||||
self.visit(stmt)
|
self.visit(stmt)
|
||||||
|
|
||||||
if node.handlers:
|
|
||||||
# TODO(mdan): Should we still support bare try/except? Might be confusing.
|
|
||||||
raise NotImplementedError('exceptions are not yet supported')
|
|
||||||
|
|
||||||
self._exit_lexical_scope(node)
|
self._exit_lexical_scope(node)
|
||||||
|
|
||||||
self.builder.enter_finally_section(node)
|
if node.finalbody:
|
||||||
for stmt in node.finalbody:
|
self.builder.enter_finally_section(node)
|
||||||
self.visit(stmt)
|
for stmt in node.finalbody:
|
||||||
self.builder.exit_finally_section(node)
|
self.visit(stmt)
|
||||||
|
self.builder.exit_finally_section(node)
|
||||||
|
|
||||||
def visit_With(self, node):
|
def visit_With(self, node):
|
||||||
# TODO(mdan): Mark the context manager's exit call as exit guard.
|
# TODO(mdan): Mark the context manager's exit call as exit guard.
|
||||||
|
@ -219,6 +219,10 @@ class Annotator(transformer.Base):
|
|||||||
frozenset(self.current_analyzer.out[cfg_node]))
|
frozenset(self.current_analyzer.out[cfg_node]))
|
||||||
return node
|
return node
|
||||||
|
|
||||||
|
def visit_ExceptHandler(self, node):
|
||||||
|
# TODO(b/123995141) Add Exception Handlers to the CFG
|
||||||
|
return node
|
||||||
|
|
||||||
|
|
||||||
def resolve(node, source_info, graphs):
|
def resolve(node, source_info, graphs):
|
||||||
"""Resolves the live symbols at the exit of control flow statements.
|
"""Resolves the live symbols at the exit of control flow statements.
|
||||||
|
@ -223,6 +223,10 @@ class TreeAnnotator(transformer.Base):
|
|||||||
def visit_global(self, node):
|
def visit_global(self, node):
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
def visit_ExceptHandler(self, node):
|
||||||
|
# TODO(b/123995141) Add Exception Handlers to the CFG
|
||||||
|
return node
|
||||||
|
|
||||||
def visit_Name(self, node):
|
def visit_Name(self, node):
|
||||||
if self.current_analyzer is None:
|
if self.current_analyzer is None:
|
||||||
# Names may appear outside function defs - for example in class
|
# Names may appear outside function defs - for example in class
|
||||||
@ -232,7 +236,8 @@ class TreeAnnotator(transformer.Base):
|
|||||||
analyzer = self.current_analyzer
|
analyzer = self.current_analyzer
|
||||||
cfg_node = self.current_cfg_node
|
cfg_node = self.current_cfg_node
|
||||||
|
|
||||||
assert cfg_node is not None, 'name node outside of any statement?'
|
assert cfg_node is not None, ('name node, %s, outside of any statement?'
|
||||||
|
% node.id)
|
||||||
|
|
||||||
qn = anno.getanno(node, anno.Basic.QN)
|
qn = anno.getanno(node, anno.Basic.QN)
|
||||||
if isinstance(node.ctx, gast.Load):
|
if isinstance(node.ctx, gast.Load):
|
||||||
|
Loading…
x
Reference in New Issue
Block a user