FAILURE MAP
← Case archive

FA-43966 / Borrow checking / Open access

Chunk iterator lowering drops the remainder borrow capability · case 01

Chunk iterator lowering drops the remainder borrow capability.

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

ROOT CAUSE

The static analyzer mishandles chunk remainder: chunk iterator lowering drops the remainder borrow capability.

VERIFIED REPAIR

Apply the specified transfer or inference rule at this site: if not set(d['remainder'])<=set(d['accounted']): errors.append('chunk-remainder').

Unsuccessful approach: The partial repair uses if not d['accounted'] and bool(d['remainder']): errors.append('chunk-remainder'), 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 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 False: 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']Failed
chunk-remainder regression 1[]['chunk-remainder']Failed
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 / 26030ddf420b59838b3029790b4a5bb2458433c233ea747df27cfe2db546a1d7

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 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 d['accounted'] and bool(d['remainder']): 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']Failed
chunk-remainder regression 1[]['chunk-remainder']Failed
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 / 42db14162135c16404cfbcfe363e351448b547b8f13262bef8329c95380af184

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

Case digest / f38ee28e5ac6d4965912111d44d8f231f1f4e19bb636f16bd0812ca70ad5ef90