FA-90186 / Bytecode virtual machines / Open access
Generators: falsy non-None first send accepted · case 01
send(0) to a fresh generator starts it instead of raising TypeError.
ROOT CAUSE
The just-started check tests the value's truthiness rather than comparing it with None.
VERIFIED REPAIR
Reject any value other than None when the generator has not started.
Unsuccessful approach: Rejecting every non-None send also breaks legitimate sends to suspended generators.
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:
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:
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 | [['yielded', 1], ['vm-error'], ['StopIteration', None]] | [['TypeError'], ['yielded', 1], ['yielded', 11]] | Failed |
| 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 / fbc4b7cbd70856bc4c402c59cec399e4b2aec034c9814417f005c7a9b16f74f9
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:
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 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], ['TypeError'], ['vm-error'], ['StopIteration', None]] | [['yielded', 1], ['yielded', 17], ['StopIteration', 6], ['StopIteration', None]] | Failed |
| non-None send to a fresh generator | [['TypeError'], ['yielded', 1], ['TypeError']] | [['TypeError'], ['yielded', 1], ['yielded', 11]] | Failed |
| sending zero to a suspended generator | [['yielded', 1], ['TypeError'], ['vm-error']] | [['yielded', 1], ['yielded', 10], ['StopIteration', 6]] | Failed |
| yield hands out and removes the value | [['yielded', 1], ['TypeError'], ['vm-error']] | [['yielded', 1], ['StopIteration', 8], ['StopIteration', None]] | Failed |
| 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 / 616bf482ba9d8a98b13e06703d4224a249b61aac670b0e5e02964f6bd94910fa
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.438333+00:00.
Case digest / 803f8ccd0587793bfede54ff105111acb030514f0661a4206c002e390eef5e7a