FA-90201 / Bytecode virtual machines / Open access
Generators: generator survives an uncaught throw · case 01
After throw() propagates out, next() resumes the dead frame and faults.
ROOT CAUSE
The unwinding path returns the exception without marking the generator finished.
VERIFIED REPAIR
An exception escaping the frame finishes the generator.
Unsuccessful approach: Clearing the handler stack still leaves the frame resumable.
Case contract
A generator frame runs body ops push v, pop, add, yield (pop and hand out a value, suspend), return (pop, finish with StopIteration value), try exc target / endtry (handler stack; handler "Exception" catches any thrown name). Commands: next (= send None) and send v resume; resuming a suspended frame pushes the sent value as the result of its yield; sending non-None to a never-started generator is ["TypeError"]; a finished generator answers ["StopIteration", None]. throw into a created or finished generator finishes it with ["raised", exc]; otherwise the innermost matching handler is jumped to, or the generator finishes and re-raises. close finishes the generator. Frame faults are ["vm-error"].
Why this case matters
Generator state transitions (created/suspended/done) and send/throw semantics are easy to get subtly wrong.
1 / The failure
Exit 1"""Failure Map reference implementation. Python standard library only."""
import json
N = 1
observations = []
def solve(body, commands):
state = 'created'
pc = 0
stack = []
handlers = []
out = []
def run(inject, exc):
nonlocal pc, state
if exc is not None:
while handlers and handlers[-1][0] not in (exc, 'Exception'):
handlers.pop()
if not handlers:
return ['raised', exc]
pc = handlers.pop()[1]
elif state == 'suspended':
stack.append(inject)
state = 'running'
try:
while True:
ins = body[pc]
op = ins[0]
pc += 1
if op == 'push':
stack.append(ins[1])
elif op == 'pop':
stack.pop()
elif op == 'add':
b = stack.pop()
a = stack.pop()
stack.append(a + b)
elif op == 'yield':
state = 'suspended'
return ['yielded', stack.pop()]
elif op == 'return':
state = 'done'
return ['StopIteration', stack.pop()]
elif op == 'try':
handlers.append((ins[1], ins[2]))
elif op == 'endtry':
handlers.pop()
except (IndexError, TypeError):
state = 'done'
return ['vm-error']
for cmd in commands:
kind = cmd[0]
if kind in ('next', 'send'):
value = cmd[1] if kind == 'send' else None
if state == 'done':
out.append(['StopIteration', None])
elif state == 'created' and value is not None:
out.append(['TypeError'])
else:
out.append(run(value, None))
elif kind == 'throw':
if state == 'created' or state == 'done':
state = 'done'
out.append(['raised', cmd[1]])
else:
out.append(run(None, cmd[1]))
else:
state = 'done'
out.append(['closed'])
return out
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
cases = [[('regression: send values through two yields',
([['push', 1], ['yield'], ['push', 10], ['add'], ['yield'], ['pop'], ['push', 6], ['return']],
[['next'], ['send', 7], ['next'], ['next']]),
[['yielded', 1], ['yielded', 17], ['StopIteration', 6], ['StopIteration', None]]),
('non-None send to a fresh generator',
([['push', 1], ['yield'], ['push', 10], ['add'], ['yield'], ['pop'], ['push', 6], ['return']],
[['send', 0], ['next'], ['send', 1]]),
[['TypeError'], ['yielded', 1], ['yielded', 11]]),
('sending zero to a suspended generator',
([['push', 1], ['yield'], ['push', 10], ['add'], ['yield'], ['pop'], ['push', 6], ['return']],
[['next'], ['send', 0], ['next']]),
[['yielded', 1], ['yielded', 10], ['StopIteration', 6]]),
('yield hands out and removes the value',
([['push', 5], ['push', 1], ['yield'], ['add'], ['return']], [['next'], ['send', 3], ['next']]),
[['yielded', 1], ['StopIteration', 8], ['StopIteration', None]]),
('thrown exception caught by catch-all handler',
([['try', 'Exception', 7],
['push', 1],
['yield'],
['pop'],
['endtry'],
['push', -1],
['return'],
['push', 43],
['yield'],
['pop'],
['push', 0],
['return']],
[['next'], ['throw', 'ValueError'], ['next']]),
[['yielded', 1], ['yielded', 43], ['StopIteration', 0]]),
('uncaught throw finishes the generator',
([['push', 5], ['push', 1], ['yield'], ['add'], ['return']], [['next'], ['throw', 'KeyError'], ['next']]),
[['yielded', 1], ['raised', 'KeyError'], ['StopIteration', None]]),
('closing a fresh generator',
([['push', 5], ['push', 1], ['yield'], ['add'], ['return']], [['close'], ['next']]),
[['closed'], ['StopIteration', None]]),
('closing a suspended generator',
([['push', 5], ['push', 1], ['yield'], ['add'], ['return']], [['next'], ['close'], ['next']]),
[['yielded', 1], ['closed'], ['StopIteration', None]]),
('control: throw into a fresh generator',
([['push', 1], ['yield'], ['push', 10], ['add'], ['yield'], ['pop'], ['push', 6], ['return']],
[['throw', 'ValueError'], ['next']]),
[['raised', 'ValueError'], ['StopIteration', None]])],
[('regression: send values through two yields',
([['push', 1], ['yield'], ['push', 10], ['add'], ['yield'], ['pop'], ['push', 7], ['return']],
[['next'], ['send', 14], ['next'], ['next']]),
[['yielded', 1], ['yielded', 24], ['StopIteration', 7], ['StopIteration', None]]),
('non-None send to a fresh generator',
([['push', 1], ['yield'], ['push', 10], ['add'], ['yield'], ['pop'], ['push', 7], ['return']],
[['send', 0], ['next'], ['send', 2]]),
[['TypeError'], ['yielded', 1], ['yielded', 12]]),
('sending zero to a suspended generator',
([['push', 1], ['yield'], ['push', 10], ['add'], ['yield'], ['pop'], ['push', 7], ['return']],
[['next'], ['send', 0], ['next']]),
[['yielded', 1], ['yielded', 10], ['StopIteration', 7]]),
('yield hands out and removes the value',
([['push', 10], ['push', 1], ['yield'], ['add'], ['return']], [['next'], ['send', 3], ['next']]),
[['yielded', 1], ['StopIteration', 13], ['StopIteration', None]]),
('thrown exception caught by catch-all handler',
([['try', 'Exception', 7],
['push', 1],
['yield'],
['pop'],
['endtry'],
['push', -1],
['return'],
['push', 44],
['yield'],
['pop'],
['push', 0],
['return']],
[['next'], ['throw', 'ValueError'], ['next']]),
[['yielded', 1], ['yielded', 44], ['StopIteration', 0]]),
('uncaught throw finishes the generator',
([['push', 10], ['push', 1], ['yield'], ['add'], ['return']], [['next'], ['throw', 'KeyError'], ['next']]),
[['yielded', 1], ['raised', 'KeyError'], ['StopIteration', None]]),
('closing a fresh generator',
([['push', 10], ['push', 1], ['yield'], ['add'], ['return']], [['close'], ['next']]),
[['closed'], ['StopIteration', None]]),
('closing a suspended generator',
([['push', 10], ['push', 1], ['yield'], ['add'], ['return']], [['next'], ['close'], ['next']]),
[['yielded', 1], ['closed'], ['StopIteration', None]]),
('control: throw into a fresh generator',
([['push', 1], ['yield'], ['push', 10], ['add'], ['yield'], ['pop'], ['push', 7], ['return']],
[['throw', 'ValueError'], ['next']]),
[['raised', 'ValueError'], ['StopIteration', None]])],
[('regression: send values through two yields',
([['push', 1], ['yield'], ['push', 10], ['add'], ['yield'], ['pop'], ['push', 8], ['return']],
[['next'], ['send', 21], ['next'], ['next']]),
[['yielded', 1], ['yielded', 31], ['StopIteration', 8], ['StopIteration', None]]),
('non-None send to a fresh generator',
([['push', 1], ['yield'], ['push', 10], ['add'], ['yield'], ['pop'], ['push', 8], ['return']],
[['send', 0], ['next'], ['send', 3]]),
[['TypeError'], ['yielded', 1], ['yielded', 13]]),
('sending zero to a suspended generator',
([['push', 1], ['yield'], ['push', 10], ['add'], ['yield'], ['pop'], ['push', 8], ['return']],
[['next'], ['send', 0], ['next']]),
[['yielded', 1], ['yielded', 10], ['StopIteration', 8]]),
('yield hands out and removes the value',
([['push', 15], ['push', 1], ['yield'], ['add'], ['return']], [['next'], ['send', 3], ['next']]),
[['yielded', 1], ['StopIteration', 18], ['StopIteration', None]]),
('thrown exception caught by catch-all handler',
([['try', 'Exception', 7],
['push', 1],
['yield'],
['pop'],
['endtry'],
['push', -1],
['return'],
['push', 45],
['yield'],
['pop'],
['push', 0],
['return']],
[['next'], ['throw', 'ValueError'], ['next']]),
[['yielded', 1], ['yielded', 45], ['StopIteration', 0]]),
('uncaught throw finishes the generator',
([['push', 15], ['push', 1], ['yield'], ['add'], ['return']], [['next'], ['throw', 'KeyError'], ['next']]),
[['yielded', 1], ['raised', 'KeyError'], ['StopIteration', None]]),
('closing a fresh generator',
([['push', 15], ['push', 1], ['yield'], ['add'], ['return']], [['close'], ['next']]),
[['closed'], ['StopIteration', None]]),
('closing a suspended generator',
([['push', 15], ['push', 1], ['yield'], ['add'], ['return']], [['next'], ['close'], ['next']]),
[['yielded', 1], ['closed'], ['StopIteration', None]]),
('control: throw into a fresh generator',
([['push', 1], ['yield'], ['push', 10], ['add'], ['yield'], ['pop'], ['push', 8], ['return']],
[['throw', 'ValueError'], ['next']]),
[['raised', 'ValueError'], ['StopIteration', None]])],
[('regression: send values through two yields',
([['push', 1], ['yield'], ['push', 10], ['add'], ['yield'], ['pop'], ['push', 9], ['return']],
[['next'], ['send', 28], ['next'], ['next']]),
[['yielded', 1], ['yielded', 38], ['StopIteration', 9], ['StopIteration', None]]),
('non-None send to a fresh generator',
([['push', 1], ['yield'], ['push', 10], ['add'], ['yield'], ['pop'], ['push', 9], ['return']],
[['send', 0], ['next'], ['send', 4]]),
[['TypeError'], ['yielded', 1], ['yielded', 14]]),
('sending zero to a suspended generator',
([['push', 1], ['yield'], ['push', 10], ['add'], ['yield'], ['pop'], ['push', 9], ['return']],
[['next'], ['send', 0], ['next']]),
[['yielded', 1], ['yielded', 10], ['StopIteration', 9]]),
('yield hands out and removes the value',
([['push', 20], ['push', 1], ['yield'], ['add'], ['return']], [['next'], ['send', 3], ['next']]),
[['yielded', 1], ['StopIteration', 23], ['StopIteration', None]]),
('thrown exception caught by catch-all handler',
([['try', 'Exception', 7],
['push', 1],
['yield'],
['pop'],
['endtry'],
['push', -1],
['return'],
['push', 46],
['yield'],
['pop'],
['push', 0],
['return']],
[['next'], ['throw', 'ValueError'], ['next']]),
[['yielded', 1], ['yielded', 46], ['StopIteration', 0]]),
('uncaught throw finishes the generator',
([['push', 20], ['push', 1], ['yield'], ['add'], ['return']], [['next'], ['throw', 'KeyError'], ['next']]),
[['yielded', 1], ['raised', 'KeyError'], ['StopIteration', None]]),
('closing a fresh generator',
([['push', 20], ['push', 1], ['yield'], ['add'], ['return']], [['close'], ['next']]),
[['closed'], ['StopIteration', None]]),
('closing a suspended generator',
([['push', 20], ['push', 1], ['yield'], ['add'], ['return']], [['next'], ['close'], ['next']]),
[['yielded', 1], ['closed'], ['StopIteration', None]]),
('control: throw into a fresh generator',
([['push', 1], ['yield'], ['push', 10], ['add'], ['yield'], ['pop'], ['push', 9], ['return']],
[['throw', 'ValueError'], ['next']]),
[['raised', 'ValueError'], ['StopIteration', None]])],
[('regression: send values through two yields',
([['push', 1], ['yield'], ['push', 10], ['add'], ['yield'], ['pop'], ['push', 10], ['return']],
[['next'], ['send', 35], ['next'], ['next']]),
[['yielded', 1], ['yielded', 45], ['StopIteration', 10], ['StopIteration', None]]),
('non-None send to a fresh generator',
([['push', 1], ['yield'], ['push', 10], ['add'], ['yield'], ['pop'], ['push', 10], ['return']],
[['send', 0], ['next'], ['send', 5]]),
[['TypeError'], ['yielded', 1], ['yielded', 15]]),
('sending zero to a suspended generator',
([['push', 1], ['yield'], ['push', 10], ['add'], ['yield'], ['pop'], ['push', 10], ['return']],
[['next'], ['send', 0], ['next']]),
[['yielded', 1], ['yielded', 10], ['StopIteration', 10]]),
('yield hands out and removes the value',
([['push', 25], ['push', 1], ['yield'], ['add'], ['return']], [['next'], ['send', 3], ['next']]),
[['yielded', 1], ['StopIteration', 28], ['StopIteration', None]]),
('thrown exception caught by catch-all handler',
([['try', 'Exception', 7],
['push', 1],
['yield'],
['pop'],
['endtry'],
['push', -1],
['return'],
['push', 47],
['yield'],
['pop'],
['push', 0],
['return']],
[['next'], ['throw', 'ValueError'], ['next']]),
[['yielded', 1], ['yielded', 47], ['StopIteration', 0]]),
('uncaught throw finishes the generator',
([['push', 25], ['push', 1], ['yield'], ['add'], ['return']], [['next'], ['throw', 'KeyError'], ['next']]),
[['yielded', 1], ['raised', 'KeyError'], ['StopIteration', None]]),
('closing a fresh generator',
([['push', 25], ['push', 1], ['yield'], ['add'], ['return']], [['close'], ['next']]),
[['closed'], ['StopIteration', None]]),
('closing a suspended generator',
([['push', 25], ['push', 1], ['yield'], ['add'], ['return']], [['next'], ['close'], ['next']]),
[['yielded', 1], ['closed'], ['StopIteration', None]]),
('control: throw into a fresh generator',
([['push', 1], ['yield'], ['push', 10], ['add'], ['yield'], ['pop'], ['push', 10], ['return']],
[['throw', 'ValueError'], ['next']]),
[['raised', 'ValueError'], ['StopIteration', None]])]]
for label, args, expected in cases[N - 1]:
check(label, solve(*args), expected)
print(json.dumps({"observations": observations, "passed": all(x["passed"] for x in observations)}, ensure_ascii=False))
raise SystemExit(0 if all(x["passed"] for x in observations) else 1)
| Boundary fixture | Actual | Expected | Outcome |
|---|---|---|---|
| regression: send values through two yields | [['yielded', 1], ['yielded', 17], ['StopIteration', 6], ['StopIteration', None]] | [['yielded', 1], ['yielded', 17], ['StopIteration', 6], ['StopIteration', None]] | Passed |
| non-None send to a fresh generator | [['TypeError'], ['yielded', 1], ['yielded', 11]] | [['TypeError'], ['yielded', 1], ['yielded', 11]] | Passed |
| sending zero to a suspended generator | [['yielded', 1], ['yielded', 10], ['StopIteration', 6]] | [['yielded', 1], ['yielded', 10], ['StopIteration', 6]] | Passed |
| yield hands out and removes the value | [['yielded', 1], ['StopIteration', 8], ['StopIteration', None]] | [['yielded', 1], ['StopIteration', 8], ['StopIteration', None]] | Passed |
| thrown exception caught by catch-all handler | [['yielded', 1], ['yielded', 43], ['StopIteration', 0]] | [['yielded', 1], ['yielded', 43], ['StopIteration', 0]] | Passed |
| uncaught throw finishes the generator | [['yielded', 1], ['raised', 'KeyError'], ['vm-error']] | [['yielded', 1], ['raised', 'KeyError'], ['StopIteration', None]] | Failed |
| closing a fresh generator | [['closed'], ['StopIteration', None]] | [['closed'], ['StopIteration', None]] | Passed |
| closing a suspended generator | [['yielded', 1], ['closed'], ['StopIteration', None]] | [['yielded', 1], ['closed'], ['StopIteration', None]] | Passed |
| control: throw into a fresh generator | [['raised', 'ValueError'], ['StopIteration', None]] | [['raised', 'ValueError'], ['StopIteration', None]] | Passed |
SHA-256 / cced5d97c847f516d789c70259bebc884c30da244f43a9b4940c5675f71876f2
2 / The unsuccessful fix
Exit 1"""Failure Map reference implementation. Python standard library only."""
import json
N = 1
observations = []
def solve(body, commands):
state = 'created'
pc = 0
stack = []
handlers = []
out = []
def run(inject, exc):
nonlocal pc, state
if exc is not None:
while handlers and handlers[-1][0] not in (exc, 'Exception'):
handlers.pop()
if not handlers:
handlers.clear()
return ['raised', exc]
pc = handlers.pop()[1]
elif state == 'suspended':
stack.append(inject)
state = 'running'
try:
while True:
ins = body[pc]
op = ins[0]
pc += 1
if op == 'push':
stack.append(ins[1])
elif op == 'pop':
stack.pop()
elif op == 'add':
b = stack.pop()
a = stack.pop()
stack.append(a + b)
elif op == 'yield':
state = 'suspended'
return ['yielded', stack.pop()]
elif op == 'return':
state = 'done'
return ['StopIteration', stack.pop()]
elif op == 'try':
handlers.append((ins[1], ins[2]))
elif op == 'endtry':
handlers.pop()
except (IndexError, TypeError):
state = 'done'
return ['vm-error']
for cmd in commands:
kind = cmd[0]
if kind in ('next', 'send'):
value = cmd[1] if kind == 'send' else None
if state == 'done':
out.append(['StopIteration', None])
elif state == 'created' and value is not None:
out.append(['TypeError'])
else:
out.append(run(value, None))
elif kind == 'throw':
if state == 'created' or state == 'done':
state = 'done'
out.append(['raised', cmd[1]])
else:
out.append(run(None, cmd[1]))
else:
state = 'done'
out.append(['closed'])
return out
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
cases = [[('regression: send values through two yields',
([['push', 1], ['yield'], ['push', 10], ['add'], ['yield'], ['pop'], ['push', 6], ['return']],
[['next'], ['send', 7], ['next'], ['next']]),
[['yielded', 1], ['yielded', 17], ['StopIteration', 6], ['StopIteration', None]]),
('non-None send to a fresh generator',
([['push', 1], ['yield'], ['push', 10], ['add'], ['yield'], ['pop'], ['push', 6], ['return']],
[['send', 0], ['next'], ['send', 1]]),
[['TypeError'], ['yielded', 1], ['yielded', 11]]),
('sending zero to a suspended generator',
([['push', 1], ['yield'], ['push', 10], ['add'], ['yield'], ['pop'], ['push', 6], ['return']],
[['next'], ['send', 0], ['next']]),
[['yielded', 1], ['yielded', 10], ['StopIteration', 6]]),
('yield hands out and removes the value',
([['push', 5], ['push', 1], ['yield'], ['add'], ['return']], [['next'], ['send', 3], ['next']]),
[['yielded', 1], ['StopIteration', 8], ['StopIteration', None]]),
('thrown exception caught by catch-all handler',
([['try', 'Exception', 7],
['push', 1],
['yield'],
['pop'],
['endtry'],
['push', -1],
['return'],
['push', 43],
['yield'],
['pop'],
['push', 0],
['return']],
[['next'], ['throw', 'ValueError'], ['next']]),
[['yielded', 1], ['yielded', 43], ['StopIteration', 0]]),
('uncaught throw finishes the generator',
([['push', 5], ['push', 1], ['yield'], ['add'], ['return']], [['next'], ['throw', 'KeyError'], ['next']]),
[['yielded', 1], ['raised', 'KeyError'], ['StopIteration', None]]),
('closing a fresh generator',
([['push', 5], ['push', 1], ['yield'], ['add'], ['return']], [['close'], ['next']]),
[['closed'], ['StopIteration', None]]),
('closing a suspended generator',
([['push', 5], ['push', 1], ['yield'], ['add'], ['return']], [['next'], ['close'], ['next']]),
[['yielded', 1], ['closed'], ['StopIteration', None]]),
('control: throw into a fresh generator',
([['push', 1], ['yield'], ['push', 10], ['add'], ['yield'], ['pop'], ['push', 6], ['return']],
[['throw', 'ValueError'], ['next']]),
[['raised', 'ValueError'], ['StopIteration', None]])],
[('regression: send values through two yields',
([['push', 1], ['yield'], ['push', 10], ['add'], ['yield'], ['pop'], ['push', 7], ['return']],
[['next'], ['send', 14], ['next'], ['next']]),
[['yielded', 1], ['yielded', 24], ['StopIteration', 7], ['StopIteration', None]]),
('non-None send to a fresh generator',
([['push', 1], ['yield'], ['push', 10], ['add'], ['yield'], ['pop'], ['push', 7], ['return']],
[['send', 0], ['next'], ['send', 2]]),
[['TypeError'], ['yielded', 1], ['yielded', 12]]),
('sending zero to a suspended generator',
([['push', 1], ['yield'], ['push', 10], ['add'], ['yield'], ['pop'], ['push', 7], ['return']],
[['next'], ['send', 0], ['next']]),
[['yielded', 1], ['yielded', 10], ['StopIteration', 7]]),
('yield hands out and removes the value',
([['push', 10], ['push', 1], ['yield'], ['add'], ['return']], [['next'], ['send', 3], ['next']]),
[['yielded', 1], ['StopIteration', 13], ['StopIteration', None]]),
('thrown exception caught by catch-all handler',
([['try', 'Exception', 7],
['push', 1],
['yield'],
['pop'],
['endtry'],
['push', -1],
['return'],
['push', 44],
['yield'],
['pop'],
['push', 0],
['return']],
[['next'], ['throw', 'ValueError'], ['next']]),
[['yielded', 1], ['yielded', 44], ['StopIteration', 0]]),
('uncaught throw finishes the generator',
([['push', 10], ['push', 1], ['yield'], ['add'], ['return']], [['next'], ['throw', 'KeyError'], ['next']]),
[['yielded', 1], ['raised', 'KeyError'], ['StopIteration', None]]),
('closing a fresh generator',
([['push', 10], ['push', 1], ['yield'], ['add'], ['return']], [['close'], ['next']]),
[['closed'], ['StopIteration', None]]),
('closing a suspended generator',
([['push', 10], ['push', 1], ['yield'], ['add'], ['return']], [['next'], ['close'], ['next']]),
[['yielded', 1], ['closed'], ['StopIteration', None]]),
('control: throw into a fresh generator',
([['push', 1], ['yield'], ['push', 10], ['add'], ['yield'], ['pop'], ['push', 7], ['return']],
[['throw', 'ValueError'], ['next']]),
[['raised', 'ValueError'], ['StopIteration', None]])],
[('regression: send values through two yields',
([['push', 1], ['yield'], ['push', 10], ['add'], ['yield'], ['pop'], ['push', 8], ['return']],
[['next'], ['send', 21], ['next'], ['next']]),
[['yielded', 1], ['yielded', 31], ['StopIteration', 8], ['StopIteration', None]]),
('non-None send to a fresh generator',
([['push', 1], ['yield'], ['push', 10], ['add'], ['yield'], ['pop'], ['push', 8], ['return']],
[['send', 0], ['next'], ['send', 3]]),
[['TypeError'], ['yielded', 1], ['yielded', 13]]),
('sending zero to a suspended generator',
([['push', 1], ['yield'], ['push', 10], ['add'], ['yield'], ['pop'], ['push', 8], ['return']],
[['next'], ['send', 0], ['next']]),
[['yielded', 1], ['yielded', 10], ['StopIteration', 8]]),
('yield hands out and removes the value',
([['push', 15], ['push', 1], ['yield'], ['add'], ['return']], [['next'], ['send', 3], ['next']]),
[['yielded', 1], ['StopIteration', 18], ['StopIteration', None]]),
('thrown exception caught by catch-all handler',
([['try', 'Exception', 7],
['push', 1],
['yield'],
['pop'],
['endtry'],
['push', -1],
['return'],
['push', 45],
['yield'],
['pop'],
['push', 0],
['return']],
[['next'], ['throw', 'ValueError'], ['next']]),
[['yielded', 1], ['yielded', 45], ['StopIteration', 0]]),
('uncaught throw finishes the generator',
([['push', 15], ['push', 1], ['yield'], ['add'], ['return']], [['next'], ['throw', 'KeyError'], ['next']]),
[['yielded', 1], ['raised', 'KeyError'], ['StopIteration', None]]),
('closing a fresh generator',
([['push', 15], ['push', 1], ['yield'], ['add'], ['return']], [['close'], ['next']]),
[['closed'], ['StopIteration', None]]),
('closing a suspended generator',
([['push', 15], ['push', 1], ['yield'], ['add'], ['return']], [['next'], ['close'], ['next']]),
[['yielded', 1], ['closed'], ['StopIteration', None]]),
('control: throw into a fresh generator',
([['push', 1], ['yield'], ['push', 10], ['add'], ['yield'], ['pop'], ['push', 8], ['return']],
[['throw', 'ValueError'], ['next']]),
[['raised', 'ValueError'], ['StopIteration', None]])],
[('regression: send values through two yields',
([['push', 1], ['yield'], ['push', 10], ['add'], ['yield'], ['pop'], ['push', 9], ['return']],
[['next'], ['send', 28], ['next'], ['next']]),
[['yielded', 1], ['yielded', 38], ['StopIteration', 9], ['StopIteration', None]]),
('non-None send to a fresh generator',
([['push', 1], ['yield'], ['push', 10], ['add'], ['yield'], ['pop'], ['push', 9], ['return']],
[['send', 0], ['next'], ['send', 4]]),
[['TypeError'], ['yielded', 1], ['yielded', 14]]),
('sending zero to a suspended generator',
([['push', 1], ['yield'], ['push', 10], ['add'], ['yield'], ['pop'], ['push', 9], ['return']],
[['next'], ['send', 0], ['next']]),
[['yielded', 1], ['yielded', 10], ['StopIteration', 9]]),
('yield hands out and removes the value',
([['push', 20], ['push', 1], ['yield'], ['add'], ['return']], [['next'], ['send', 3], ['next']]),
[['yielded', 1], ['StopIteration', 23], ['StopIteration', None]]),
('thrown exception caught by catch-all handler',
([['try', 'Exception', 7],
['push', 1],
['yield'],
['pop'],
['endtry'],
['push', -1],
['return'],
['push', 46],
['yield'],
['pop'],
['push', 0],
['return']],
[['next'], ['throw', 'ValueError'], ['next']]),
[['yielded', 1], ['yielded', 46], ['StopIteration', 0]]),
('uncaught throw finishes the generator',
([['push', 20], ['push', 1], ['yield'], ['add'], ['return']], [['next'], ['throw', 'KeyError'], ['next']]),
[['yielded', 1], ['raised', 'KeyError'], ['StopIteration', None]]),
('closing a fresh generator',
([['push', 20], ['push', 1], ['yield'], ['add'], ['return']], [['close'], ['next']]),
[['closed'], ['StopIteration', None]]),
('closing a suspended generator',
([['push', 20], ['push', 1], ['yield'], ['add'], ['return']], [['next'], ['close'], ['next']]),
[['yielded', 1], ['closed'], ['StopIteration', None]]),
('control: throw into a fresh generator',
([['push', 1], ['yield'], ['push', 10], ['add'], ['yield'], ['pop'], ['push', 9], ['return']],
[['throw', 'ValueError'], ['next']]),
[['raised', 'ValueError'], ['StopIteration', None]])],
[('regression: send values through two yields',
([['push', 1], ['yield'], ['push', 10], ['add'], ['yield'], ['pop'], ['push', 10], ['return']],
[['next'], ['send', 35], ['next'], ['next']]),
[['yielded', 1], ['yielded', 45], ['StopIteration', 10], ['StopIteration', None]]),
('non-None send to a fresh generator',
([['push', 1], ['yield'], ['push', 10], ['add'], ['yield'], ['pop'], ['push', 10], ['return']],
[['send', 0], ['next'], ['send', 5]]),
[['TypeError'], ['yielded', 1], ['yielded', 15]]),
('sending zero to a suspended generator',
([['push', 1], ['yield'], ['push', 10], ['add'], ['yield'], ['pop'], ['push', 10], ['return']],
[['next'], ['send', 0], ['next']]),
[['yielded', 1], ['yielded', 10], ['StopIteration', 10]]),
('yield hands out and removes the value',
([['push', 25], ['push', 1], ['yield'], ['add'], ['return']], [['next'], ['send', 3], ['next']]),
[['yielded', 1], ['StopIteration', 28], ['StopIteration', None]]),
('thrown exception caught by catch-all handler',
([['try', 'Exception', 7],
['push', 1],
['yield'],
['pop'],
['endtry'],
['push', -1],
['return'],
['push', 47],
['yield'],
['pop'],
['push', 0],
['return']],
[['next'], ['throw', 'ValueError'], ['next']]),
[['yielded', 1], ['yielded', 47], ['StopIteration', 0]]),
('uncaught throw finishes the generator',
([['push', 25], ['push', 1], ['yield'], ['add'], ['return']], [['next'], ['throw', 'KeyError'], ['next']]),
[['yielded', 1], ['raised', 'KeyError'], ['StopIteration', None]]),
('closing a fresh generator',
([['push', 25], ['push', 1], ['yield'], ['add'], ['return']], [['close'], ['next']]),
[['closed'], ['StopIteration', None]]),
('closing a suspended generator',
([['push', 25], ['push', 1], ['yield'], ['add'], ['return']], [['next'], ['close'], ['next']]),
[['yielded', 1], ['closed'], ['StopIteration', None]]),
('control: throw into a fresh generator',
([['push', 1], ['yield'], ['push', 10], ['add'], ['yield'], ['pop'], ['push', 10], ['return']],
[['throw', 'ValueError'], ['next']]),
[['raised', 'ValueError'], ['StopIteration', None]])]]
for label, args, expected in cases[N - 1]:
check(label, solve(*args), expected)
print(json.dumps({"observations": observations, "passed": all(x["passed"] for x in observations)}, ensure_ascii=False))
raise SystemExit(0 if all(x["passed"] for x in observations) else 1)
| Boundary fixture | Actual | Expected | Outcome |
|---|---|---|---|
| regression: send values through two yields | [['yielded', 1], ['yielded', 17], ['StopIteration', 6], ['StopIteration', None]] | [['yielded', 1], ['yielded', 17], ['StopIteration', 6], ['StopIteration', None]] | Passed |
| non-None send to a fresh generator | [['TypeError'], ['yielded', 1], ['yielded', 11]] | [['TypeError'], ['yielded', 1], ['yielded', 11]] | Passed |
| sending zero to a suspended generator | [['yielded', 1], ['yielded', 10], ['StopIteration', 6]] | [['yielded', 1], ['yielded', 10], ['StopIteration', 6]] | Passed |
| yield hands out and removes the value | [['yielded', 1], ['StopIteration', 8], ['StopIteration', None]] | [['yielded', 1], ['StopIteration', 8], ['StopIteration', None]] | Passed |
| thrown exception caught by catch-all handler | [['yielded', 1], ['yielded', 43], ['StopIteration', 0]] | [['yielded', 1], ['yielded', 43], ['StopIteration', 0]] | Passed |
| uncaught throw finishes the generator | [['yielded', 1], ['raised', 'KeyError'], ['vm-error']] | [['yielded', 1], ['raised', 'KeyError'], ['StopIteration', None]] | Failed |
| closing a fresh generator | [['closed'], ['StopIteration', None]] | [['closed'], ['StopIteration', None]] | Passed |
| closing a suspended generator | [['yielded', 1], ['closed'], ['StopIteration', None]] | [['yielded', 1], ['closed'], ['StopIteration', None]] | Passed |
| control: throw into a fresh generator | [['raised', 'ValueError'], ['StopIteration', None]] | [['raised', 'ValueError'], ['StopIteration', None]] | Passed |
SHA-256 / 359511c53c96cd3f4ba552cf2e3f63c3eccd176405144e541c756d394e964e90
3 / The verified repair
Exit 0"""Failure Map reference implementation. Python standard library only."""
import json
N = 1
observations = []
def solve(body, commands):
state = 'created'
pc = 0
stack = []
handlers = []
out = []
def run(inject, exc):
nonlocal pc, state
if exc is not None:
while handlers and handlers[-1][0] not in (exc, 'Exception'):
handlers.pop()
if not handlers:
state = 'done'
return ['raised', exc]
pc = handlers.pop()[1]
elif state == 'suspended':
stack.append(inject)
state = 'running'
try:
while True:
ins = body[pc]
op = ins[0]
pc += 1
if op == 'push':
stack.append(ins[1])
elif op == 'pop':
stack.pop()
elif op == 'add':
b = stack.pop()
a = stack.pop()
stack.append(a + b)
elif op == 'yield':
state = 'suspended'
return ['yielded', stack.pop()]
elif op == 'return':
state = 'done'
return ['StopIteration', stack.pop()]
elif op == 'try':
handlers.append((ins[1], ins[2]))
elif op == 'endtry':
handlers.pop()
except (IndexError, TypeError):
state = 'done'
return ['vm-error']
for cmd in commands:
kind = cmd[0]
if kind in ('next', 'send'):
value = cmd[1] if kind == 'send' else None
if state == 'done':
out.append(['StopIteration', None])
elif state == 'created' and value is not None:
out.append(['TypeError'])
else:
out.append(run(value, None))
elif kind == 'throw':
if state == 'created' or state == 'done':
state = 'done'
out.append(['raised', cmd[1]])
else:
out.append(run(None, cmd[1]))
else:
state = 'done'
out.append(['closed'])
return out
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
cases = [[('regression: send values through two yields',
([['push', 1], ['yield'], ['push', 10], ['add'], ['yield'], ['pop'], ['push', 6], ['return']],
[['next'], ['send', 7], ['next'], ['next']]),
[['yielded', 1], ['yielded', 17], ['StopIteration', 6], ['StopIteration', None]]),
('non-None send to a fresh generator',
([['push', 1], ['yield'], ['push', 10], ['add'], ['yield'], ['pop'], ['push', 6], ['return']],
[['send', 0], ['next'], ['send', 1]]),
[['TypeError'], ['yielded', 1], ['yielded', 11]]),
('sending zero to a suspended generator',
([['push', 1], ['yield'], ['push', 10], ['add'], ['yield'], ['pop'], ['push', 6], ['return']],
[['next'], ['send', 0], ['next']]),
[['yielded', 1], ['yielded', 10], ['StopIteration', 6]]),
('yield hands out and removes the value',
([['push', 5], ['push', 1], ['yield'], ['add'], ['return']], [['next'], ['send', 3], ['next']]),
[['yielded', 1], ['StopIteration', 8], ['StopIteration', None]]),
('thrown exception caught by catch-all handler',
([['try', 'Exception', 7],
['push', 1],
['yield'],
['pop'],
['endtry'],
['push', -1],
['return'],
['push', 43],
['yield'],
['pop'],
['push', 0],
['return']],
[['next'], ['throw', 'ValueError'], ['next']]),
[['yielded', 1], ['yielded', 43], ['StopIteration', 0]]),
('uncaught throw finishes the generator',
([['push', 5], ['push', 1], ['yield'], ['add'], ['return']], [['next'], ['throw', 'KeyError'], ['next']]),
[['yielded', 1], ['raised', 'KeyError'], ['StopIteration', None]]),
('closing a fresh generator',
([['push', 5], ['push', 1], ['yield'], ['add'], ['return']], [['close'], ['next']]),
[['closed'], ['StopIteration', None]]),
('closing a suspended generator',
([['push', 5], ['push', 1], ['yield'], ['add'], ['return']], [['next'], ['close'], ['next']]),
[['yielded', 1], ['closed'], ['StopIteration', None]]),
('control: throw into a fresh generator',
([['push', 1], ['yield'], ['push', 10], ['add'], ['yield'], ['pop'], ['push', 6], ['return']],
[['throw', 'ValueError'], ['next']]),
[['raised', 'ValueError'], ['StopIteration', None]])],
[('regression: send values through two yields',
([['push', 1], ['yield'], ['push', 10], ['add'], ['yield'], ['pop'], ['push', 7], ['return']],
[['next'], ['send', 14], ['next'], ['next']]),
[['yielded', 1], ['yielded', 24], ['StopIteration', 7], ['StopIteration', None]]),
('non-None send to a fresh generator',
([['push', 1], ['yield'], ['push', 10], ['add'], ['yield'], ['pop'], ['push', 7], ['return']],
[['send', 0], ['next'], ['send', 2]]),
[['TypeError'], ['yielded', 1], ['yielded', 12]]),
('sending zero to a suspended generator',
([['push', 1], ['yield'], ['push', 10], ['add'], ['yield'], ['pop'], ['push', 7], ['return']],
[['next'], ['send', 0], ['next']]),
[['yielded', 1], ['yielded', 10], ['StopIteration', 7]]),
('yield hands out and removes the value',
([['push', 10], ['push', 1], ['yield'], ['add'], ['return']], [['next'], ['send', 3], ['next']]),
[['yielded', 1], ['StopIteration', 13], ['StopIteration', None]]),
('thrown exception caught by catch-all handler',
([['try', 'Exception', 7],
['push', 1],
['yield'],
['pop'],
['endtry'],
['push', -1],
['return'],
['push', 44],
['yield'],
['pop'],
['push', 0],
['return']],
[['next'], ['throw', 'ValueError'], ['next']]),
[['yielded', 1], ['yielded', 44], ['StopIteration', 0]]),
('uncaught throw finishes the generator',
([['push', 10], ['push', 1], ['yield'], ['add'], ['return']], [['next'], ['throw', 'KeyError'], ['next']]),
[['yielded', 1], ['raised', 'KeyError'], ['StopIteration', None]]),
('closing a fresh generator',
([['push', 10], ['push', 1], ['yield'], ['add'], ['return']], [['close'], ['next']]),
[['closed'], ['StopIteration', None]]),
('closing a suspended generator',
([['push', 10], ['push', 1], ['yield'], ['add'], ['return']], [['next'], ['close'], ['next']]),
[['yielded', 1], ['closed'], ['StopIteration', None]]),
('control: throw into a fresh generator',
([['push', 1], ['yield'], ['push', 10], ['add'], ['yield'], ['pop'], ['push', 7], ['return']],
[['throw', 'ValueError'], ['next']]),
[['raised', 'ValueError'], ['StopIteration', None]])],
[('regression: send values through two yields',
([['push', 1], ['yield'], ['push', 10], ['add'], ['yield'], ['pop'], ['push', 8], ['return']],
[['next'], ['send', 21], ['next'], ['next']]),
[['yielded', 1], ['yielded', 31], ['StopIteration', 8], ['StopIteration', None]]),
('non-None send to a fresh generator',
([['push', 1], ['yield'], ['push', 10], ['add'], ['yield'], ['pop'], ['push', 8], ['return']],
[['send', 0], ['next'], ['send', 3]]),
[['TypeError'], ['yielded', 1], ['yielded', 13]]),
('sending zero to a suspended generator',
([['push', 1], ['yield'], ['push', 10], ['add'], ['yield'], ['pop'], ['push', 8], ['return']],
[['next'], ['send', 0], ['next']]),
[['yielded', 1], ['yielded', 10], ['StopIteration', 8]]),
('yield hands out and removes the value',
([['push', 15], ['push', 1], ['yield'], ['add'], ['return']], [['next'], ['send', 3], ['next']]),
[['yielded', 1], ['StopIteration', 18], ['StopIteration', None]]),
('thrown exception caught by catch-all handler',
([['try', 'Exception', 7],
['push', 1],
['yield'],
['pop'],
['endtry'],
['push', -1],
['return'],
['push', 45],
['yield'],
['pop'],
['push', 0],
['return']],
[['next'], ['throw', 'ValueError'], ['next']]),
[['yielded', 1], ['yielded', 45], ['StopIteration', 0]]),
('uncaught throw finishes the generator',
([['push', 15], ['push', 1], ['yield'], ['add'], ['return']], [['next'], ['throw', 'KeyError'], ['next']]),
[['yielded', 1], ['raised', 'KeyError'], ['StopIteration', None]]),
('closing a fresh generator',
([['push', 15], ['push', 1], ['yield'], ['add'], ['return']], [['close'], ['next']]),
[['closed'], ['StopIteration', None]]),
('closing a suspended generator',
([['push', 15], ['push', 1], ['yield'], ['add'], ['return']], [['next'], ['close'], ['next']]),
[['yielded', 1], ['closed'], ['StopIteration', None]]),
('control: throw into a fresh generator',
([['push', 1], ['yield'], ['push', 10], ['add'], ['yield'], ['pop'], ['push', 8], ['return']],
[['throw', 'ValueError'], ['next']]),
[['raised', 'ValueError'], ['StopIteration', None]])],
[('regression: send values through two yields',
([['push', 1], ['yield'], ['push', 10], ['add'], ['yield'], ['pop'], ['push', 9], ['return']],
[['next'], ['send', 28], ['next'], ['next']]),
[['yielded', 1], ['yielded', 38], ['StopIteration', 9], ['StopIteration', None]]),
('non-None send to a fresh generator',
([['push', 1], ['yield'], ['push', 10], ['add'], ['yield'], ['pop'], ['push', 9], ['return']],
[['send', 0], ['next'], ['send', 4]]),
[['TypeError'], ['yielded', 1], ['yielded', 14]]),
('sending zero to a suspended generator',
([['push', 1], ['yield'], ['push', 10], ['add'], ['yield'], ['pop'], ['push', 9], ['return']],
[['next'], ['send', 0], ['next']]),
[['yielded', 1], ['yielded', 10], ['StopIteration', 9]]),
('yield hands out and removes the value',
([['push', 20], ['push', 1], ['yield'], ['add'], ['return']], [['next'], ['send', 3], ['next']]),
[['yielded', 1], ['StopIteration', 23], ['StopIteration', None]]),
('thrown exception caught by catch-all handler',
([['try', 'Exception', 7],
['push', 1],
['yield'],
['pop'],
['endtry'],
['push', -1],
['return'],
['push', 46],
['yield'],
['pop'],
['push', 0],
['return']],
[['next'], ['throw', 'ValueError'], ['next']]),
[['yielded', 1], ['yielded', 46], ['StopIteration', 0]]),
('uncaught throw finishes the generator',
([['push', 20], ['push', 1], ['yield'], ['add'], ['return']], [['next'], ['throw', 'KeyError'], ['next']]),
[['yielded', 1], ['raised', 'KeyError'], ['StopIteration', None]]),
('closing a fresh generator',
([['push', 20], ['push', 1], ['yield'], ['add'], ['return']], [['close'], ['next']]),
[['closed'], ['StopIteration', None]]),
('closing a suspended generator',
([['push', 20], ['push', 1], ['yield'], ['add'], ['return']], [['next'], ['close'], ['next']]),
[['yielded', 1], ['closed'], ['StopIteration', None]]),
('control: throw into a fresh generator',
([['push', 1], ['yield'], ['push', 10], ['add'], ['yield'], ['pop'], ['push', 9], ['return']],
[['throw', 'ValueError'], ['next']]),
[['raised', 'ValueError'], ['StopIteration', None]])],
[('regression: send values through two yields',
([['push', 1], ['yield'], ['push', 10], ['add'], ['yield'], ['pop'], ['push', 10], ['return']],
[['next'], ['send', 35], ['next'], ['next']]),
[['yielded', 1], ['yielded', 45], ['StopIteration', 10], ['StopIteration', None]]),
('non-None send to a fresh generator',
([['push', 1], ['yield'], ['push', 10], ['add'], ['yield'], ['pop'], ['push', 10], ['return']],
[['send', 0], ['next'], ['send', 5]]),
[['TypeError'], ['yielded', 1], ['yielded', 15]]),
('sending zero to a suspended generator',
([['push', 1], ['yield'], ['push', 10], ['add'], ['yield'], ['pop'], ['push', 10], ['return']],
[['next'], ['send', 0], ['next']]),
[['yielded', 1], ['yielded', 10], ['StopIteration', 10]]),
('yield hands out and removes the value',
([['push', 25], ['push', 1], ['yield'], ['add'], ['return']], [['next'], ['send', 3], ['next']]),
[['yielded', 1], ['StopIteration', 28], ['StopIteration', None]]),
('thrown exception caught by catch-all handler',
([['try', 'Exception', 7],
['push', 1],
['yield'],
['pop'],
['endtry'],
['push', -1],
['return'],
['push', 47],
['yield'],
['pop'],
['push', 0],
['return']],
[['next'], ['throw', 'ValueError'], ['next']]),
[['yielded', 1], ['yielded', 47], ['StopIteration', 0]]),
('uncaught throw finishes the generator',
([['push', 25], ['push', 1], ['yield'], ['add'], ['return']], [['next'], ['throw', 'KeyError'], ['next']]),
[['yielded', 1], ['raised', 'KeyError'], ['StopIteration', None]]),
('closing a fresh generator',
([['push', 25], ['push', 1], ['yield'], ['add'], ['return']], [['close'], ['next']]),
[['closed'], ['StopIteration', None]]),
('closing a suspended generator',
([['push', 25], ['push', 1], ['yield'], ['add'], ['return']], [['next'], ['close'], ['next']]),
[['yielded', 1], ['closed'], ['StopIteration', None]]),
('control: throw into a fresh generator',
([['push', 1], ['yield'], ['push', 10], ['add'], ['yield'], ['pop'], ['push', 10], ['return']],
[['throw', 'ValueError'], ['next']]),
[['raised', 'ValueError'], ['StopIteration', None]])]]
for label, args, expected in cases[N - 1]:
check(label, solve(*args), expected)
print(json.dumps({"observations": observations, "passed": all(x["passed"] for x in observations)}, ensure_ascii=False))
raise SystemExit(0 if all(x["passed"] for x in observations) else 1)
| Boundary fixture | Actual | Expected | Outcome |
|---|---|---|---|
| regression: send values through two yields | [['yielded', 1], ['yielded', 17], ['StopIteration', 6], ['StopIteration', None]] | [['yielded', 1], ['yielded', 17], ['StopIteration', 6], ['StopIteration', None]] | Passed |
| non-None send to a fresh generator | [['TypeError'], ['yielded', 1], ['yielded', 11]] | [['TypeError'], ['yielded', 1], ['yielded', 11]] | Passed |
| sending zero to a suspended generator | [['yielded', 1], ['yielded', 10], ['StopIteration', 6]] | [['yielded', 1], ['yielded', 10], ['StopIteration', 6]] | Passed |
| yield hands out and removes the value | [['yielded', 1], ['StopIteration', 8], ['StopIteration', None]] | [['yielded', 1], ['StopIteration', 8], ['StopIteration', None]] | Passed |
| thrown exception caught by catch-all handler | [['yielded', 1], ['yielded', 43], ['StopIteration', 0]] | [['yielded', 1], ['yielded', 43], ['StopIteration', 0]] | Passed |
| uncaught throw finishes the generator | [['yielded', 1], ['raised', 'KeyError'], ['StopIteration', None]] | [['yielded', 1], ['raised', 'KeyError'], ['StopIteration', None]] | Passed |
| closing a fresh generator | [['closed'], ['StopIteration', None]] | [['closed'], ['StopIteration', None]] | Passed |
| closing a suspended generator | [['yielded', 1], ['closed'], ['StopIteration', None]] | [['yielded', 1], ['closed'], ['StopIteration', None]] | Passed |
| control: throw into a fresh generator | [['raised', 'ValueError'], ['StopIteration', None]] | [['raised', 'ValueError'], ['StopIteration', None]] | Passed |
SHA-256 / f8a019999cd82806d62c44a3a1ff8aaf08de22e15580af70a42780f489ed07cb
Verification & scope
A deterministic, bounded teaching model of one bytecode virtual machine mechanism with a stipulated instruction encoding; it is not a production VM and claims no conformance to any real specification. This reproducer isolates one failure mechanism. Results cover the supplied fixtures. Variants within a family share a test contract and should remain grouped when constructing evaluation splits. Related mechanisms with a shared evaluation_group must also remain together; these controlled models are not independent production incidents.
Observations recorded using Python 3.12.14 at 2026-09-29T14:51:24.527431+00:00.
Case digest / fb19b4131c63ae177fcdf964216d361bd7c9c3c0813334c2173d1d997470eccb