# HG changeset patch # User Paul Boddie # Date 1559509826 -7200 # Node ID 938887b82033d33ceaf9e9a462f1aabf5b7c8df2 # Parent dc210430ce4c10b10bffa48cab52c1d01f2a005d Prevent "null" usage of assigned names from escaping via the exception handler. diff -r dc210430ce4c -r 938887b82033 common.py --- a/common.py Mon May 27 11:54:56 2019 +0200 +++ b/common.py Sun Jun 02 23:10:26 2019 +0200 @@ -563,6 +563,8 @@ self.next_temporary() t2 = self.get_temporary_name() self.next_temporary() + t3 = self.get_temporary_name() + self.next_temporary() node = compiler.ast.Stmt([ @@ -583,9 +585,10 @@ # try: # while True: # try: - # ... = () + # = () # except StopIteration: # raise LoopExit + # ... = # {n.body} # except LoopExit: # {n.else_} @@ -601,13 +604,16 @@ compiler.ast.Stmt([ compiler.ast.TryExcept( compiler.ast.Assign( - [n.assign], + [compiler.ast.AssName(t3, "OP_ASSIGN")], compiler.ast.CallFunc( compiler.ast.Name(t2), [])), [(compiler.ast.Name("StopIteration"), None, compiler.ast.Raise(compiler.ast.Name("LoopExit")))], None), + compiler.ast.Assign( + [n.assign], + compiler.ast.Name(t3)), n.body]), None), [(compiler.ast.Name("LoopExit"), None, n.else_ or compiler.ast.Pass())], diff -r dc210430ce4c -r 938887b82033 internal_tests/branches.py --- a/internal_tests/branches.py Mon May 27 11:54:56 2019 +0200 +++ b/internal_tests/branches.py Sun Jun 02 23:10:26 2019 +0200 @@ -687,4 +687,77 @@ bt.get_assignment_positions_for_branches("a", ar) names.append(bt.assignments["a"]) +# This demonstrates why the assignment in a "for" loop construct must appear +# outside the inner "try" body: null usage escapes the loop via the exception +# handler and the raise statement, even though the assignment would only be +# valid otherwise. + +# Equivalent to... +# +# try: +# while ...: +# try: +# a = ... +# except: +# raise ... +# a.p +# except: +# pass + +bt = branching.BranchTracker() +bt.new_branchpoint() # begin (try) +bt.new_branchpoint(True) # begin (while) +bt.new_branch(True) # while ... +bt.new_branchpoint() # begin (try) +a = bt.assign_names(["a"]) +bt.resume_abandoned_branches() # except +bt.abandon_branch() # raise +bt.shelve_branch() +bt.new_branch() # (null) +bt.shelve_branch() +bt.merge_branches() # end (try) +ap = bt.use_attribute("a", "p") +bt.resume_continuing_branches() +bt.shelve_branch(True) +bt.new_branch() # (null) +bt.shelve_branch() +bt.merge_branches() # end (while) +bt.resume_broken_branches() +bt.resume_abandoned_branches() # except +bt.shelve_branch() +bt.new_branch() # (null) +bt.shelve_branch() +bt.merge_branches() # end (try) + +print simple_usage(a) == \ + {'a' : set([('p',), ()])}, simple_usage(a) +print bt.get_assignment_positions_for_branches("a", ap) == [0], \ + bt.get_assignment_positions_for_branches("a", ap) +names.append(bt.assignments["a"]) + +# Equivalent to... +# +# b = ... +# while ...: +# a = ... +# a.p + +bt = branching.BranchTracker() +bt.new_branchpoint(True) # begin +b = bt.assign_names(["b"]) +bt.new_branch(True) # while ... +a = bt.assign_names(["a"]) +ap = bt.use_attribute("a", "p") +bt.resume_continuing_branches() +bt.shelve_branch(True) +bt.new_branch() # (null) +bt.shelve_branch() +bt.merge_branches() # end + +print simple_usage(a) == \ + {'a' : set([('p',)])}, simple_usage(a) +print bt.get_assignment_positions_for_branches("a", ap) == [0], \ + bt.get_assignment_positions_for_branches("a", ap) +names.append(bt.assignments["a"]) + # vim: tabstop=4 expandtab shiftwidth=4