FAILURE MAP
← Case archive

FA-43951 / Borrow checking / Open access

Mutable iteration yields overlapping element capabilities · case 01

Mutable iteration yields overlapping element capabilities.

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

ROOT CAUSE

The static analyzer mishandles mutable yield disjoint: mutable iteration yields overlapping element capabilities.

VERIFIED REPAIR

Apply the specified transfer or inference rule at this site: if d['mutable'] and bool(set(d['yield_a'])&set(d['yield_b'])): errors.append('mutable-yield-disjoint').

Unsuccessful approach: The partial repair uses if d['mutable'] and set(d['yield_a'])==set(d['yield_b']) and bool(d['yield_a']): errors.append('mutable-yield-disjoint'), which still violates the stipulated analysis contract.

Case contract

Check iterator lifetime summaries. Yielded shared items borrow backing storage; lending items borrow the iterator until next call; mutable yields must have disjoint element footprints; next consumes the prior lending-item loan; double-ended iteration cannot yield a crossed element twice; chunked iteration retains remainder ownership; adapter closures cannot extend item lifetime; collection of lending references cannot outlive iteration; empty iterator yields no loan; iterator destruction releases its backing borrow but not independently owned items. 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['yield_origins'])<=set(d['backing_origins']): errors.append('yield-origin')
    if d['lending'] and d['item_end']>d['next_point']: errors.append('lending-call-bound')
    if False: errors.append('mutable-yield-disjoint')
    if d['next_called'] and d['prior_lending_live']: errors.append('next-reborrow-kill')
    if bool(set(d['front_indices'])&set(d['back_indices'])): errors.append('double-ended-crossing')
    if not set(d['remainder'])<=set(d['accounted']): errors.append('chunk-remainder')
    if d['adapter_bound']>d['item_bound']: errors.append('adapter-lifetime')
    if d['collected'] and d['collection_end']>d['iteration_end']: errors.append('lending-collection')
    if d['empty'] and bool(d['yielded_loans']): errors.append('empty-yield')
    if d['destroyed'] and bool(set(d['owned_items'])&set(d['invalidated_items'])): errors.append('iterator-drop-owned-items')
    return errors
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
base={'yield_origins': [], 'backing_origins': [], 'lending': False, 'item_end': 0, 'next_point': 0, 'yield_a': [], 'yield_b': [], 'mutable': False, 'prior_lending_live': False, 'next_called': False, 'front_indices': [], 'back_indices': [], 'remainder': [], 'accounted': [], 'adapter_bound': 0, 'item_bound': 0, 'collected': False, 'collection_end': 0, 'iteration_end': 0, 'empty': False, 'yielded_loans': [], 'destroyed': False, 'backing_live': False, 'owned_items': [], 'invalidated_items': []}
check('well formed empty obligations',solve(base),[])
check('yield-origin regression 0', solve(dict(base, **({'yield_origins':['a','b'],'backing_origins':['a']}))), ['yield-origin'])
check('yield-origin regression 1', solve(dict(base, **({'yield_origins':[N,N+1],'backing_origins':[N]}))), ['yield-origin'])
check('lending-call-bound regression 0', solve(dict(base, **({'lending':True,'item_end':N+1,'next_point':N}))), ['lending-call-bound'])
check('lending-call-bound regression 1', solve(dict(base, **({'lending':True,'item_end':N+2,'next_point':N+1}))), ['lending-call-bound'])
check('mutable-yield-disjoint regression 0', solve(dict(base, **({'mutable':True,'yield_a':[N,N+1],'yield_b':[N]}))), ['mutable-yield-disjoint'])
check('mutable-yield-disjoint regression 1', solve(dict(base, **({'mutable':True,'yield_a':[N],'yield_b':[N,N+1]}))), ['mutable-yield-disjoint'])
check('next-reborrow-kill regression 0', solve(dict(base, **({'next_called':True,'prior_lending_live':True}))), ['next-reborrow-kill'])
check('next-reborrow-kill regression 1', solve(dict(base, **({'next_called':True,'prior_lending_live':True,'next_point':N}))), ['next-reborrow-kill'])
check('double-ended-crossing regression 0', solve(dict(base, **({'front_indices':[N,N+1],'back_indices':[N+1]}))), ['double-ended-crossing'])
check('double-ended-crossing regression 1', solve(dict(base, **({'front_indices':[N],'back_indices':[N,N+1]}))), ['double-ended-crossing'])
check('chunk-remainder regression 0', solve(dict(base, **({'remainder':[N,N+1],'accounted':[N]}))), ['chunk-remainder'])
check('chunk-remainder regression 1', solve(dict(base, **({'remainder':list(range(N+1)),'accounted':[0]}))), ['chunk-remainder'])
check('adapter-lifetime regression 0', solve(dict(base, **({'adapter_bound':N+1,'item_bound':N}))), ['adapter-lifetime'])
check('adapter-lifetime regression 1', solve(dict(base, **({'adapter_bound':N+2,'item_bound':N+1}))), ['adapter-lifetime'])
check('lending-collection regression 0', solve(dict(base, **({'collected':True,'collection_end':N+1,'iteration_end':N}))), ['lending-collection'])
check('lending-collection regression 1', solve(dict(base, **({'collected':True,'collection_end':N+2,'iteration_end':N+1}))), ['lending-collection'])
check('empty-yield regression 0', solve(dict(base, **({'empty':True,'yielded_loans':['r']}))), ['empty-yield'])
check('empty-yield regression 1', solve(dict(base, **({'empty':True,'yielded_loans':[N]}))), ['empty-yield'])
check('iterator-drop-owned-items regression 0', solve(dict(base, **({'destroyed':True,'owned_items':['x'],'invalidated_items':['x']}))), ['iterator-drop-owned-items'])
check('iterator-drop-owned-items regression 1', solve(dict(base, **({'destroyed':True,'owned_items':[N],'invalidated_items':[N]}))), ['iterator-drop-owned-items'])
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
yield-origin regression 0['yield-origin']['yield-origin']Passed
yield-origin regression 1['yield-origin']['yield-origin']Passed
lending-call-bound regression 0['lending-call-bound']['lending-call-bound']Passed
lending-call-bound regression 1['lending-call-bound']['lending-call-bound']Passed
mutable-yield-disjoint regression 0[]['mutable-yield-disjoint']Failed
mutable-yield-disjoint regression 1[]['mutable-yield-disjoint']Failed
next-reborrow-kill regression 0['next-reborrow-kill']['next-reborrow-kill']Passed
next-reborrow-kill regression 1['next-reborrow-kill']['next-reborrow-kill']Passed
double-ended-crossing regression 0['double-ended-crossing']['double-ended-crossing']Passed
double-ended-crossing regression 1['double-ended-crossing']['double-ended-crossing']Passed
chunk-remainder regression 0['chunk-remainder']['chunk-remainder']Passed
chunk-remainder regression 1['chunk-remainder']['chunk-remainder']Passed
adapter-lifetime regression 0['adapter-lifetime']['adapter-lifetime']Passed
adapter-lifetime regression 1['adapter-lifetime']['adapter-lifetime']Passed
lending-collection regression 0['lending-collection']['lending-collection']Passed
lending-collection regression 1['lending-collection']['lending-collection']Passed
empty-yield regression 0['empty-yield']['empty-yield']Passed
empty-yield regression 1['empty-yield']['empty-yield']Passed
iterator-drop-owned-items regression 0['iterator-drop-owned-items']['iterator-drop-owned-items']Passed
iterator-drop-owned-items regression 1['iterator-drop-owned-items']['iterator-drop-owned-items']Passed

SHA-256 / 14962ec95bc8f20ffb5e74ef2406dc3ffd9839e14c76dbb299a3a169edd77477

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['yield_origins'])<=set(d['backing_origins']): errors.append('yield-origin')
    if d['lending'] and d['item_end']>d['next_point']: errors.append('lending-call-bound')
    if d['mutable'] and set(d['yield_a'])==set(d['yield_b']) and bool(d['yield_a']): errors.append('mutable-yield-disjoint')
    if d['next_called'] and d['prior_lending_live']: errors.append('next-reborrow-kill')
    if bool(set(d['front_indices'])&set(d['back_indices'])): errors.append('double-ended-crossing')
    if not set(d['remainder'])<=set(d['accounted']): errors.append('chunk-remainder')
    if d['adapter_bound']>d['item_bound']: errors.append('adapter-lifetime')
    if d['collected'] and d['collection_end']>d['iteration_end']: errors.append('lending-collection')
    if d['empty'] and bool(d['yielded_loans']): errors.append('empty-yield')
    if d['destroyed'] and bool(set(d['owned_items'])&set(d['invalidated_items'])): errors.append('iterator-drop-owned-items')
    return errors
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
base={'yield_origins': [], 'backing_origins': [], 'lending': False, 'item_end': 0, 'next_point': 0, 'yield_a': [], 'yield_b': [], 'mutable': False, 'prior_lending_live': False, 'next_called': False, 'front_indices': [], 'back_indices': [], 'remainder': [], 'accounted': [], 'adapter_bound': 0, 'item_bound': 0, 'collected': False, 'collection_end': 0, 'iteration_end': 0, 'empty': False, 'yielded_loans': [], 'destroyed': False, 'backing_live': False, 'owned_items': [], 'invalidated_items': []}
check('well formed empty obligations',solve(base),[])
check('yield-origin regression 0', solve(dict(base, **({'yield_origins':['a','b'],'backing_origins':['a']}))), ['yield-origin'])
check('yield-origin regression 1', solve(dict(base, **({'yield_origins':[N,N+1],'backing_origins':[N]}))), ['yield-origin'])
check('lending-call-bound regression 0', solve(dict(base, **({'lending':True,'item_end':N+1,'next_point':N}))), ['lending-call-bound'])
check('lending-call-bound regression 1', solve(dict(base, **({'lending':True,'item_end':N+2,'next_point':N+1}))), ['lending-call-bound'])
check('mutable-yield-disjoint regression 0', solve(dict(base, **({'mutable':True,'yield_a':[N,N+1],'yield_b':[N]}))), ['mutable-yield-disjoint'])
check('mutable-yield-disjoint regression 1', solve(dict(base, **({'mutable':True,'yield_a':[N],'yield_b':[N,N+1]}))), ['mutable-yield-disjoint'])
check('next-reborrow-kill regression 0', solve(dict(base, **({'next_called':True,'prior_lending_live':True}))), ['next-reborrow-kill'])
check('next-reborrow-kill regression 1', solve(dict(base, **({'next_called':True,'prior_lending_live':True,'next_point':N}))), ['next-reborrow-kill'])
check('double-ended-crossing regression 0', solve(dict(base, **({'front_indices':[N,N+1],'back_indices':[N+1]}))), ['double-ended-crossing'])
check('double-ended-crossing regression 1', solve(dict(base, **({'front_indices':[N],'back_indices':[N,N+1]}))), ['double-ended-crossing'])
check('chunk-remainder regression 0', solve(dict(base, **({'remainder':[N,N+1],'accounted':[N]}))), ['chunk-remainder'])
check('chunk-remainder regression 1', solve(dict(base, **({'remainder':list(range(N+1)),'accounted':[0]}))), ['chunk-remainder'])
check('adapter-lifetime regression 0', solve(dict(base, **({'adapter_bound':N+1,'item_bound':N}))), ['adapter-lifetime'])
check('adapter-lifetime regression 1', solve(dict(base, **({'adapter_bound':N+2,'item_bound':N+1}))), ['adapter-lifetime'])
check('lending-collection regression 0', solve(dict(base, **({'collected':True,'collection_end':N+1,'iteration_end':N}))), ['lending-collection'])
check('lending-collection regression 1', solve(dict(base, **({'collected':True,'collection_end':N+2,'iteration_end':N+1}))), ['lending-collection'])
check('empty-yield regression 0', solve(dict(base, **({'empty':True,'yielded_loans':['r']}))), ['empty-yield'])
check('empty-yield regression 1', solve(dict(base, **({'empty':True,'yielded_loans':[N]}))), ['empty-yield'])
check('iterator-drop-owned-items regression 0', solve(dict(base, **({'destroyed':True,'owned_items':['x'],'invalidated_items':['x']}))), ['iterator-drop-owned-items'])
check('iterator-drop-owned-items regression 1', solve(dict(base, **({'destroyed':True,'owned_items':[N],'invalidated_items':[N]}))), ['iterator-drop-owned-items'])
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
yield-origin regression 0['yield-origin']['yield-origin']Passed
yield-origin regression 1['yield-origin']['yield-origin']Passed
lending-call-bound regression 0['lending-call-bound']['lending-call-bound']Passed
lending-call-bound regression 1['lending-call-bound']['lending-call-bound']Passed
mutable-yield-disjoint regression 0[]['mutable-yield-disjoint']Failed
mutable-yield-disjoint regression 1[]['mutable-yield-disjoint']Failed
next-reborrow-kill regression 0['next-reborrow-kill']['next-reborrow-kill']Passed
next-reborrow-kill regression 1['next-reborrow-kill']['next-reborrow-kill']Passed
double-ended-crossing regression 0['double-ended-crossing']['double-ended-crossing']Passed
double-ended-crossing regression 1['double-ended-crossing']['double-ended-crossing']Passed
chunk-remainder regression 0['chunk-remainder']['chunk-remainder']Passed
chunk-remainder regression 1['chunk-remainder']['chunk-remainder']Passed
adapter-lifetime regression 0['adapter-lifetime']['adapter-lifetime']Passed
adapter-lifetime regression 1['adapter-lifetime']['adapter-lifetime']Passed
lending-collection regression 0['lending-collection']['lending-collection']Passed
lending-collection regression 1['lending-collection']['lending-collection']Passed
empty-yield regression 0['empty-yield']['empty-yield']Passed
empty-yield regression 1['empty-yield']['empty-yield']Passed
iterator-drop-owned-items regression 0['iterator-drop-owned-items']['iterator-drop-owned-items']Passed
iterator-drop-owned-items regression 1['iterator-drop-owned-items']['iterator-drop-owned-items']Passed

SHA-256 / d58fdc454981c9a0c3da5bb4bd6fdba20d7c2462d8731a4199200c00c557d824

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['yield_origins'])<=set(d['backing_origins']): errors.append('yield-origin')
    if d['lending'] and d['item_end']>d['next_point']: errors.append('lending-call-bound')
    if d['mutable'] and bool(set(d['yield_a'])&set(d['yield_b'])): errors.append('mutable-yield-disjoint')
    if d['next_called'] and d['prior_lending_live']: errors.append('next-reborrow-kill')
    if bool(set(d['front_indices'])&set(d['back_indices'])): errors.append('double-ended-crossing')
    if not set(d['remainder'])<=set(d['accounted']): errors.append('chunk-remainder')
    if d['adapter_bound']>d['item_bound']: errors.append('adapter-lifetime')
    if d['collected'] and d['collection_end']>d['iteration_end']: errors.append('lending-collection')
    if d['empty'] and bool(d['yielded_loans']): errors.append('empty-yield')
    if d['destroyed'] and bool(set(d['owned_items'])&set(d['invalidated_items'])): errors.append('iterator-drop-owned-items')
    return errors
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
base={'yield_origins': [], 'backing_origins': [], 'lending': False, 'item_end': 0, 'next_point': 0, 'yield_a': [], 'yield_b': [], 'mutable': False, 'prior_lending_live': False, 'next_called': False, 'front_indices': [], 'back_indices': [], 'remainder': [], 'accounted': [], 'adapter_bound': 0, 'item_bound': 0, 'collected': False, 'collection_end': 0, 'iteration_end': 0, 'empty': False, 'yielded_loans': [], 'destroyed': False, 'backing_live': False, 'owned_items': [], 'invalidated_items': []}
check('well formed empty obligations',solve(base),[])
check('yield-origin regression 0', solve(dict(base, **({'yield_origins':['a','b'],'backing_origins':['a']}))), ['yield-origin'])
check('yield-origin regression 1', solve(dict(base, **({'yield_origins':[N,N+1],'backing_origins':[N]}))), ['yield-origin'])
check('lending-call-bound regression 0', solve(dict(base, **({'lending':True,'item_end':N+1,'next_point':N}))), ['lending-call-bound'])
check('lending-call-bound regression 1', solve(dict(base, **({'lending':True,'item_end':N+2,'next_point':N+1}))), ['lending-call-bound'])
check('mutable-yield-disjoint regression 0', solve(dict(base, **({'mutable':True,'yield_a':[N,N+1],'yield_b':[N]}))), ['mutable-yield-disjoint'])
check('mutable-yield-disjoint regression 1', solve(dict(base, **({'mutable':True,'yield_a':[N],'yield_b':[N,N+1]}))), ['mutable-yield-disjoint'])
check('next-reborrow-kill regression 0', solve(dict(base, **({'next_called':True,'prior_lending_live':True}))), ['next-reborrow-kill'])
check('next-reborrow-kill regression 1', solve(dict(base, **({'next_called':True,'prior_lending_live':True,'next_point':N}))), ['next-reborrow-kill'])
check('double-ended-crossing regression 0', solve(dict(base, **({'front_indices':[N,N+1],'back_indices':[N+1]}))), ['double-ended-crossing'])
check('double-ended-crossing regression 1', solve(dict(base, **({'front_indices':[N],'back_indices':[N,N+1]}))), ['double-ended-crossing'])
check('chunk-remainder regression 0', solve(dict(base, **({'remainder':[N,N+1],'accounted':[N]}))), ['chunk-remainder'])
check('chunk-remainder regression 1', solve(dict(base, **({'remainder':list(range(N+1)),'accounted':[0]}))), ['chunk-remainder'])
check('adapter-lifetime regression 0', solve(dict(base, **({'adapter_bound':N+1,'item_bound':N}))), ['adapter-lifetime'])
check('adapter-lifetime regression 1', solve(dict(base, **({'adapter_bound':N+2,'item_bound':N+1}))), ['adapter-lifetime'])
check('lending-collection regression 0', solve(dict(base, **({'collected':True,'collection_end':N+1,'iteration_end':N}))), ['lending-collection'])
check('lending-collection regression 1', solve(dict(base, **({'collected':True,'collection_end':N+2,'iteration_end':N+1}))), ['lending-collection'])
check('empty-yield regression 0', solve(dict(base, **({'empty':True,'yielded_loans':['r']}))), ['empty-yield'])
check('empty-yield regression 1', solve(dict(base, **({'empty':True,'yielded_loans':[N]}))), ['empty-yield'])
check('iterator-drop-owned-items regression 0', solve(dict(base, **({'destroyed':True,'owned_items':['x'],'invalidated_items':['x']}))), ['iterator-drop-owned-items'])
check('iterator-drop-owned-items regression 1', solve(dict(base, **({'destroyed':True,'owned_items':[N],'invalidated_items':[N]}))), ['iterator-drop-owned-items'])
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
yield-origin regression 0['yield-origin']['yield-origin']Passed
yield-origin regression 1['yield-origin']['yield-origin']Passed
lending-call-bound regression 0['lending-call-bound']['lending-call-bound']Passed
lending-call-bound regression 1['lending-call-bound']['lending-call-bound']Passed
mutable-yield-disjoint regression 0['mutable-yield-disjoint']['mutable-yield-disjoint']Passed
mutable-yield-disjoint regression 1['mutable-yield-disjoint']['mutable-yield-disjoint']Passed
next-reborrow-kill regression 0['next-reborrow-kill']['next-reborrow-kill']Passed
next-reborrow-kill regression 1['next-reborrow-kill']['next-reborrow-kill']Passed
double-ended-crossing regression 0['double-ended-crossing']['double-ended-crossing']Passed
double-ended-crossing regression 1['double-ended-crossing']['double-ended-crossing']Passed
chunk-remainder regression 0['chunk-remainder']['chunk-remainder']Passed
chunk-remainder regression 1['chunk-remainder']['chunk-remainder']Passed
adapter-lifetime regression 0['adapter-lifetime']['adapter-lifetime']Passed
adapter-lifetime regression 1['adapter-lifetime']['adapter-lifetime']Passed
lending-collection regression 0['lending-collection']['lending-collection']Passed
lending-collection regression 1['lending-collection']['lending-collection']Passed
empty-yield regression 0['empty-yield']['empty-yield']Passed
empty-yield regression 1['empty-yield']['empty-yield']Passed
iterator-drop-owned-items regression 0['iterator-drop-owned-items']['iterator-drop-owned-items']Passed
iterator-drop-owned-items regression 1['iterator-drop-owned-items']['iterator-drop-owned-items']Passed

SHA-256 / 823152520bf9a812147baf7d5ac93ee9cce3f0701272736156f0a92269dd0af9

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:44:07.316474+00:00.

Case digest / 698b7aa9da4b9cc239dc9857a88e6257ecde1df80c3f625a53108918f30c284a