FAILURE MAP
← Case archive

FA-43211 / Borrow checking / Open access

A suspension-forbidden borrowed guard is missed after an ordinary guard · case 01

A suspension-forbidden borrowed guard is missed after an ordinary guard.

Verified by executionVariant 1 · 21 checks per implementationDownload source bundle ↓JSON ↗

ROOT CAUSE

The static analyzer mishandles guard yield: a suspension-forbidden borrowed guard is missed after an ordinary guard.

VERIFIED REPAIR

Apply the specified transfer or inference rule at this site: if bool(set(d['guards'])&set(d['suspend_disallowed'])): errors.append('guard-yield').

Unsuccessful approach: The partial repair uses if bool(d['guards']) and d['guards'][0] in d['suspend_disallowed']: errors.append('guard-yield'), which still violates the stipulated analysis contract.

Case contract

Validate static generator suspension descriptors. Live borrowed stack origins must be stored in the frame; movable frames cannot retain self references; pinned self references require pinned projection; a saved unique loan cannot have external aliases; suspension-disallowed guards cannot cross yield; resumed references must survive every resume point; cancellation drop order releases borrowers before owners; references returned by a completed generator cannot target its frame; loans killed before yield must not be saved; mutually exclusive variants require variant-specific frame loans. Input is a fully explicit descriptor of the stated toy IR. Return rule identifiers in declaration order; absent optional obligations use the provided neutral defaults. No rule is an assertion about a production language.

Why this case matters

A finite offline static-analysis model of ownership and borrowing; it does not execute the analyzed program.

1 / The failure

Exit 1
"""Failure Map reference implementation. Python standard library only."""
import json

N = 1
observations = []
def solve(d):
    errors=[]
    if not set(d['live_origins'])<=set(d['frame_slots']): errors.append('frame-promotion')
    if d['movable'] and bool(d['self_refs']): errors.append('self-move')
    if not set(d['self_refs'])<=set(d['pinned']): errors.append('pin-projection')
    if bool(set(d['unique_saved'])&set(d['external_aliases'])): errors.append('suspended-unique-alias')
    if False: errors.append('guard-yield')
    if not set(d['resume'])<=set(d['valid']): errors.append('resume-lifetime')
    if any(d['drop_order'].index(b)>d['drop_order'].index(o) for b,o in d['dependencies']): errors.append('cancel-drop-order')
    if d['completed'] and bool(set(d['returned'])&set(d['frame_slots'])): errors.append('completed-frame-return')
    if bool(set(d['saved'])&set(d['killed'])): errors.append('dead-loan-spill')
    if any(v!=d['active_variant'] and bool(set(ls)&set(d['common_loans'])) for v,ls in d['variant_loans'].items()): errors.append('variant-separation')
    return errors
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
base={'live_origins': [], 'frame_slots': [], 'self_refs': [], 'movable': False, 'pinned': [], 'unique_saved': [], 'external_aliases': [], 'guards': [], 'suspend_disallowed': [], 'resume': [], 'valid': [], 'drop_order': [], 'dependencies': [], 'returned': [], 'completed': False, 'saved': [], 'killed': [], 'variant_loans': {}, 'active_variant': None, 'common_loans': []}
check('well formed empty obligations',solve(base),[])
check('frame-promotion regression 0', solve(dict(base, **({'live_origins':['a','b'],'frame_slots':['a']}))), ['frame-promotion'])
check('frame-promotion regression 1', solve(dict(base, **({'live_origins':['a',N],'frame_slots':['a']}))), ['frame-promotion'])
check('self-move regression 0', solve(dict(base, **({'movable':True,'self_refs':['r'],'pinned':['r']}))), ['self-move'])
check('self-move regression 1', solve(dict(base, **({'movable':True,'self_refs':[N],'pinned':[N]}))), ['self-move'])
check('pin-projection regression 0', solve(dict(base, **({'self_refs':['a','b'],'pinned':['a']}))), ['pin-projection'])
check('pin-projection regression 1', solve(dict(base, **({'self_refs':[N],'pinned':[N+1]}))), ['pin-projection'])
check('suspended-unique-alias regression 0', solve(dict(base, **({'unique_saved':['a'],'external_aliases':['a']}))), ['suspended-unique-alias'])
check('suspended-unique-alias regression 1', solve(dict(base, **({'unique_saved':[N],'external_aliases':[N]}))), ['suspended-unique-alias'])
check('guard-yield regression 0', solve(dict(base, **({'guards':['a','b'],'suspend_disallowed':['b']}))), ['guard-yield'])
check('guard-yield regression 1', solve(dict(base, **({'guards':['a',N],'suspend_disallowed':[N]}))), ['guard-yield'])
check('resume-lifetime regression 0', solve(dict(base, **({'resume':[N],'valid':[N+1]}))), ['resume-lifetime'])
check('resume-lifetime regression 1', solve(dict(base, **({'resume':[N,N+1],'valid':[N+1,N+2]}))), ['resume-lifetime'])
check('cancel-drop-order regression 0', solve(dict(base, **({'drop_order':['o','b'],'dependencies':[('b','o')]}))), ['cancel-drop-order'])
check('cancel-drop-order regression 1', solve(dict(base, **({'drop_order':['x','o','b'],'dependencies':[('b','o')]}))), ['cancel-drop-order'])
check('completed-frame-return regression 0', solve(dict(base, **({'completed':True,'returned':['x','r'],'frame_slots':['r']}))), ['completed-frame-return'])
check('completed-frame-return regression 1', solve(dict(base, **({'completed':True,'returned':['x',N],'frame_slots':[N]}))), ['completed-frame-return'])
check('dead-loan-spill regression 0', solve(dict(base, **({'saved':['a','b'],'killed':['b']}))), ['dead-loan-spill'])
check('dead-loan-spill regression 1', solve(dict(base, **({'saved':[N,N+1],'killed':[N+1]}))), ['dead-loan-spill'])
check('variant-separation regression 0', solve(dict(base, **({'active_variant':'a','variant_loans':{'b':['r']},'common_loans':['r']}))), ['variant-separation'])
check('variant-separation regression 1', solve(dict(base, **({'active_variant':N,'variant_loans':{N+1:['r']},'common_loans':['r']}))), ['variant-separation'])
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 fixtureActualExpectedOutcome
well formed empty obligations[][]Passed
frame-promotion regression 0['frame-promotion']['frame-promotion']Passed
frame-promotion regression 1['frame-promotion']['frame-promotion']Passed
self-move regression 0['self-move']['self-move']Passed
self-move regression 1['self-move']['self-move']Passed
pin-projection regression 0['pin-projection']['pin-projection']Passed
pin-projection regression 1['pin-projection']['pin-projection']Passed
suspended-unique-alias regression 0['suspended-unique-alias']['suspended-unique-alias']Passed
suspended-unique-alias regression 1['suspended-unique-alias']['suspended-unique-alias']Passed
guard-yield regression 0[]['guard-yield']Failed
guard-yield regression 1[]['guard-yield']Failed
resume-lifetime regression 0['resume-lifetime']['resume-lifetime']Passed
resume-lifetime regression 1['resume-lifetime']['resume-lifetime']Passed
cancel-drop-order regression 0['cancel-drop-order']['cancel-drop-order']Passed
cancel-drop-order regression 1['cancel-drop-order']['cancel-drop-order']Passed
completed-frame-return regression 0['completed-frame-return']['completed-frame-return']Passed
completed-frame-return regression 1['completed-frame-return']['completed-frame-return']Passed
dead-loan-spill regression 0['dead-loan-spill']['dead-loan-spill']Passed
dead-loan-spill regression 1['dead-loan-spill']['dead-loan-spill']Passed
variant-separation regression 0['variant-separation']['variant-separation']Passed
variant-separation regression 1['variant-separation']['variant-separation']Passed

SHA-256 / 2dd3b096ac4992220a1f6b95019da30d39f65d9a9c11cf88ad37f5c20b7900ba

2 / The unsuccessful fix

Exit 1
"""Failure Map reference implementation. Python standard library only."""
import json

N = 1
observations = []
def solve(d):
    errors=[]
    if not set(d['live_origins'])<=set(d['frame_slots']): errors.append('frame-promotion')
    if d['movable'] and bool(d['self_refs']): errors.append('self-move')
    if not set(d['self_refs'])<=set(d['pinned']): errors.append('pin-projection')
    if bool(set(d['unique_saved'])&set(d['external_aliases'])): errors.append('suspended-unique-alias')
    if bool(d['guards']) and d['guards'][0] in d['suspend_disallowed']: errors.append('guard-yield')
    if not set(d['resume'])<=set(d['valid']): errors.append('resume-lifetime')
    if any(d['drop_order'].index(b)>d['drop_order'].index(o) for b,o in d['dependencies']): errors.append('cancel-drop-order')
    if d['completed'] and bool(set(d['returned'])&set(d['frame_slots'])): errors.append('completed-frame-return')
    if bool(set(d['saved'])&set(d['killed'])): errors.append('dead-loan-spill')
    if any(v!=d['active_variant'] and bool(set(ls)&set(d['common_loans'])) for v,ls in d['variant_loans'].items()): errors.append('variant-separation')
    return errors
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
base={'live_origins': [], 'frame_slots': [], 'self_refs': [], 'movable': False, 'pinned': [], 'unique_saved': [], 'external_aliases': [], 'guards': [], 'suspend_disallowed': [], 'resume': [], 'valid': [], 'drop_order': [], 'dependencies': [], 'returned': [], 'completed': False, 'saved': [], 'killed': [], 'variant_loans': {}, 'active_variant': None, 'common_loans': []}
check('well formed empty obligations',solve(base),[])
check('frame-promotion regression 0', solve(dict(base, **({'live_origins':['a','b'],'frame_slots':['a']}))), ['frame-promotion'])
check('frame-promotion regression 1', solve(dict(base, **({'live_origins':['a',N],'frame_slots':['a']}))), ['frame-promotion'])
check('self-move regression 0', solve(dict(base, **({'movable':True,'self_refs':['r'],'pinned':['r']}))), ['self-move'])
check('self-move regression 1', solve(dict(base, **({'movable':True,'self_refs':[N],'pinned':[N]}))), ['self-move'])
check('pin-projection regression 0', solve(dict(base, **({'self_refs':['a','b'],'pinned':['a']}))), ['pin-projection'])
check('pin-projection regression 1', solve(dict(base, **({'self_refs':[N],'pinned':[N+1]}))), ['pin-projection'])
check('suspended-unique-alias regression 0', solve(dict(base, **({'unique_saved':['a'],'external_aliases':['a']}))), ['suspended-unique-alias'])
check('suspended-unique-alias regression 1', solve(dict(base, **({'unique_saved':[N],'external_aliases':[N]}))), ['suspended-unique-alias'])
check('guard-yield regression 0', solve(dict(base, **({'guards':['a','b'],'suspend_disallowed':['b']}))), ['guard-yield'])
check('guard-yield regression 1', solve(dict(base, **({'guards':['a',N],'suspend_disallowed':[N]}))), ['guard-yield'])
check('resume-lifetime regression 0', solve(dict(base, **({'resume':[N],'valid':[N+1]}))), ['resume-lifetime'])
check('resume-lifetime regression 1', solve(dict(base, **({'resume':[N,N+1],'valid':[N+1,N+2]}))), ['resume-lifetime'])
check('cancel-drop-order regression 0', solve(dict(base, **({'drop_order':['o','b'],'dependencies':[('b','o')]}))), ['cancel-drop-order'])
check('cancel-drop-order regression 1', solve(dict(base, **({'drop_order':['x','o','b'],'dependencies':[('b','o')]}))), ['cancel-drop-order'])
check('completed-frame-return regression 0', solve(dict(base, **({'completed':True,'returned':['x','r'],'frame_slots':['r']}))), ['completed-frame-return'])
check('completed-frame-return regression 1', solve(dict(base, **({'completed':True,'returned':['x',N],'frame_slots':[N]}))), ['completed-frame-return'])
check('dead-loan-spill regression 0', solve(dict(base, **({'saved':['a','b'],'killed':['b']}))), ['dead-loan-spill'])
check('dead-loan-spill regression 1', solve(dict(base, **({'saved':[N,N+1],'killed':[N+1]}))), ['dead-loan-spill'])
check('variant-separation regression 0', solve(dict(base, **({'active_variant':'a','variant_loans':{'b':['r']},'common_loans':['r']}))), ['variant-separation'])
check('variant-separation regression 1', solve(dict(base, **({'active_variant':N,'variant_loans':{N+1:['r']},'common_loans':['r']}))), ['variant-separation'])
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 fixtureActualExpectedOutcome
well formed empty obligations[][]Passed
frame-promotion regression 0['frame-promotion']['frame-promotion']Passed
frame-promotion regression 1['frame-promotion']['frame-promotion']Passed
self-move regression 0['self-move']['self-move']Passed
self-move regression 1['self-move']['self-move']Passed
pin-projection regression 0['pin-projection']['pin-projection']Passed
pin-projection regression 1['pin-projection']['pin-projection']Passed
suspended-unique-alias regression 0['suspended-unique-alias']['suspended-unique-alias']Passed
suspended-unique-alias regression 1['suspended-unique-alias']['suspended-unique-alias']Passed
guard-yield regression 0[]['guard-yield']Failed
guard-yield regression 1[]['guard-yield']Failed
resume-lifetime regression 0['resume-lifetime']['resume-lifetime']Passed
resume-lifetime regression 1['resume-lifetime']['resume-lifetime']Passed
cancel-drop-order regression 0['cancel-drop-order']['cancel-drop-order']Passed
cancel-drop-order regression 1['cancel-drop-order']['cancel-drop-order']Passed
completed-frame-return regression 0['completed-frame-return']['completed-frame-return']Passed
completed-frame-return regression 1['completed-frame-return']['completed-frame-return']Passed
dead-loan-spill regression 0['dead-loan-spill']['dead-loan-spill']Passed
dead-loan-spill regression 1['dead-loan-spill']['dead-loan-spill']Passed
variant-separation regression 0['variant-separation']['variant-separation']Passed
variant-separation regression 1['variant-separation']['variant-separation']Passed

SHA-256 / 83b12c6931c28a3124cb67b2f7b7db957da88fad777d2ce0088d148332e3b1fd

3 / The verified repair

Exit 0
"""Failure Map reference implementation. Python standard library only."""
import json

N = 1
observations = []
def solve(d):
    errors=[]
    if not set(d['live_origins'])<=set(d['frame_slots']): errors.append('frame-promotion')
    if d['movable'] and bool(d['self_refs']): errors.append('self-move')
    if not set(d['self_refs'])<=set(d['pinned']): errors.append('pin-projection')
    if bool(set(d['unique_saved'])&set(d['external_aliases'])): errors.append('suspended-unique-alias')
    if bool(set(d['guards'])&set(d['suspend_disallowed'])): errors.append('guard-yield')
    if not set(d['resume'])<=set(d['valid']): errors.append('resume-lifetime')
    if any(d['drop_order'].index(b)>d['drop_order'].index(o) for b,o in d['dependencies']): errors.append('cancel-drop-order')
    if d['completed'] and bool(set(d['returned'])&set(d['frame_slots'])): errors.append('completed-frame-return')
    if bool(set(d['saved'])&set(d['killed'])): errors.append('dead-loan-spill')
    if any(v!=d['active_variant'] and bool(set(ls)&set(d['common_loans'])) for v,ls in d['variant_loans'].items()): errors.append('variant-separation')
    return errors
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
base={'live_origins': [], 'frame_slots': [], 'self_refs': [], 'movable': False, 'pinned': [], 'unique_saved': [], 'external_aliases': [], 'guards': [], 'suspend_disallowed': [], 'resume': [], 'valid': [], 'drop_order': [], 'dependencies': [], 'returned': [], 'completed': False, 'saved': [], 'killed': [], 'variant_loans': {}, 'active_variant': None, 'common_loans': []}
check('well formed empty obligations',solve(base),[])
check('frame-promotion regression 0', solve(dict(base, **({'live_origins':['a','b'],'frame_slots':['a']}))), ['frame-promotion'])
check('frame-promotion regression 1', solve(dict(base, **({'live_origins':['a',N],'frame_slots':['a']}))), ['frame-promotion'])
check('self-move regression 0', solve(dict(base, **({'movable':True,'self_refs':['r'],'pinned':['r']}))), ['self-move'])
check('self-move regression 1', solve(dict(base, **({'movable':True,'self_refs':[N],'pinned':[N]}))), ['self-move'])
check('pin-projection regression 0', solve(dict(base, **({'self_refs':['a','b'],'pinned':['a']}))), ['pin-projection'])
check('pin-projection regression 1', solve(dict(base, **({'self_refs':[N],'pinned':[N+1]}))), ['pin-projection'])
check('suspended-unique-alias regression 0', solve(dict(base, **({'unique_saved':['a'],'external_aliases':['a']}))), ['suspended-unique-alias'])
check('suspended-unique-alias regression 1', solve(dict(base, **({'unique_saved':[N],'external_aliases':[N]}))), ['suspended-unique-alias'])
check('guard-yield regression 0', solve(dict(base, **({'guards':['a','b'],'suspend_disallowed':['b']}))), ['guard-yield'])
check('guard-yield regression 1', solve(dict(base, **({'guards':['a',N],'suspend_disallowed':[N]}))), ['guard-yield'])
check('resume-lifetime regression 0', solve(dict(base, **({'resume':[N],'valid':[N+1]}))), ['resume-lifetime'])
check('resume-lifetime regression 1', solve(dict(base, **({'resume':[N,N+1],'valid':[N+1,N+2]}))), ['resume-lifetime'])
check('cancel-drop-order regression 0', solve(dict(base, **({'drop_order':['o','b'],'dependencies':[('b','o')]}))), ['cancel-drop-order'])
check('cancel-drop-order regression 1', solve(dict(base, **({'drop_order':['x','o','b'],'dependencies':[('b','o')]}))), ['cancel-drop-order'])
check('completed-frame-return regression 0', solve(dict(base, **({'completed':True,'returned':['x','r'],'frame_slots':['r']}))), ['completed-frame-return'])
check('completed-frame-return regression 1', solve(dict(base, **({'completed':True,'returned':['x',N],'frame_slots':[N]}))), ['completed-frame-return'])
check('dead-loan-spill regression 0', solve(dict(base, **({'saved':['a','b'],'killed':['b']}))), ['dead-loan-spill'])
check('dead-loan-spill regression 1', solve(dict(base, **({'saved':[N,N+1],'killed':[N+1]}))), ['dead-loan-spill'])
check('variant-separation regression 0', solve(dict(base, **({'active_variant':'a','variant_loans':{'b':['r']},'common_loans':['r']}))), ['variant-separation'])
check('variant-separation regression 1', solve(dict(base, **({'active_variant':N,'variant_loans':{N+1:['r']},'common_loans':['r']}))), ['variant-separation'])
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 fixtureActualExpectedOutcome
well formed empty obligations[][]Passed
frame-promotion regression 0['frame-promotion']['frame-promotion']Passed
frame-promotion regression 1['frame-promotion']['frame-promotion']Passed
self-move regression 0['self-move']['self-move']Passed
self-move regression 1['self-move']['self-move']Passed
pin-projection regression 0['pin-projection']['pin-projection']Passed
pin-projection regression 1['pin-projection']['pin-projection']Passed
suspended-unique-alias regression 0['suspended-unique-alias']['suspended-unique-alias']Passed
suspended-unique-alias regression 1['suspended-unique-alias']['suspended-unique-alias']Passed
guard-yield regression 0['guard-yield']['guard-yield']Passed
guard-yield regression 1['guard-yield']['guard-yield']Passed
resume-lifetime regression 0['resume-lifetime']['resume-lifetime']Passed
resume-lifetime regression 1['resume-lifetime']['resume-lifetime']Passed
cancel-drop-order regression 0['cancel-drop-order']['cancel-drop-order']Passed
cancel-drop-order regression 1['cancel-drop-order']['cancel-drop-order']Passed
completed-frame-return regression 0['completed-frame-return']['completed-frame-return']Passed
completed-frame-return regression 1['completed-frame-return']['completed-frame-return']Passed
dead-loan-spill regression 0['dead-loan-spill']['dead-loan-spill']Passed
dead-loan-spill regression 1['dead-loan-spill']['dead-loan-spill']Passed
variant-separation regression 0['variant-separation']['variant-separation']Passed
variant-separation regression 1['variant-separation']['variant-separation']Passed

SHA-256 / 684d3fc28a8f7e28246833cdfe2fd0838ad7fbcf930d67ade0a7b02e8d3d2abb

Verification & scope

The explicitly stated toy language is the complete scope; this is not a production compiler or a claim about Rust semantics. 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:43:59.406918+00:00.

Case digest / 4454c95f42ee31d8ed8f505ec797289ab9b65358748d8cf403e3b057b9cb4338