FAILURE MAP
← Case archive

FA-90541 / Garbage collector invariants / Open access

Reference processing: finalizable object's children freed · case 01

A finalizer runs against an object whose fields point at freed memory.

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

ROOT CAUSE

Only the finalizable objects themselves are resurrected, not what they reference.

VERIFIED REPAIR

Resurrect the full transitive closure of objects queued for finalization.

Unsuccessful approach: Keeping just their direct children frees the finalizable objects and anything deeper.

Case contract

Each cycle: trace strong reachability from roots (weak and phantom references are not traced); weak references whose referent is not strongly reachable are cleared; pending finalizable objects that are not strongly reachable are queued for finalization once and resurrected together with everything they reach for this cycle; phantom references are enqueued only when their referent is not reachable even through the resurrected set; everything else is freed. Return per cycle the cleared weak refs, finalized objects, enqueued phantoms and freed objects.

Why this case matters

Reference-processing order decides whether code can observe resurrected or half-dead objects.

1 / The failure

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

N = 1
observations = []
def solve(heap, roots, weak, finalizable, phantom, cycles):
    heap = {k: list(v) for k, v in heap.items()}
    weak = {r: t for r, t in weak}
    phantom = {r: t for r, t in phantom}
    pending = set(finalizable)
    out = []
    def trace(starts, seen):
        stack = list(starts)
        while stack:
            o = stack.pop()
            if o is None or o in seen or o not in heap:
                continue
            seen.add(o)
            stack.extend(heap[o])
        return seen
    for _ in range(cycles):
        strong = trace(roots, set())
        to_final = sorted(o for o in pending if o not in strong and o in heap)
        reach = set(strong) | set(to_final)
        cleared = sorted(r for r, t in weak.items() if t is not None and t not in strong)
        for r in cleared:
            weak[r] = None
        pending -= set(to_final)
        enq = sorted(r for r, t in phantom.items() if t is not None and t not in reach)
        for r in enq:
            phantom[r] = None
        freed = sorted(o for o in heap if o not in reach)
        for o in freed:
            del heap[o]
        out.append({'weak': cleared, 'finalize': to_final, 'phantom': enq, 'freed': freed})
    return out
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
cases = [[('regression: finalizable object over two cycles',
   ({11: [12], 12: [], 13: [14], 14: [15], 15: [], 16: []},
    [11],
    [['w1', 13], ['w2', 12]],
    [13],
    [['p1', 14], ['p2', 16]],
    2),
   [{'finalize': [13], 'freed': [16], 'phantom': ['p2'], 'weak': ['w1']},
    {'finalize': [], 'freed': [13, 14, 15], 'phantom': ['p1'], 'weak': []}]),
  ('weak reference to a resurrected object',
   ({11: [12], 12: [], 13: [14], 14: [15], 15: [], 16: []}, [11], [['w1', 13]], [13], [], 1),
   [{'finalize': [13], 'freed': [16], 'phantom': [], 'weak': ['w1']}]),
  ('finalizer runs only once',
   ({11: [12], 12: [], 13: [14], 14: [15], 15: [], 16: []}, [11], [], [13], [], 3),
   [{'finalize': [13], 'freed': [16], 'phantom': [], 'weak': []},
    {'finalize': [], 'freed': [13, 14, 15], 'phantom': [], 'weak': []},
    {'finalize': [], 'freed': [], 'phantom': [], 'weak': []}]),
  ('children of a finalizable object stay alive',
   ({11: [12], 12: [], 13: [14], 14: [15], 15: [], 16: []}, [11], [], [13], [['p1', 15]], 1),
   [{'finalize': [13], 'freed': [16], 'phantom': [], 'weak': []}]),
  ('phantom of an object only reachable from a finalizable one',
   ({11: [12], 12: [], 13: [14], 14: [15], 15: [], 16: []}, [11], [], [13], [['p1', 14]], 2),
   [{'finalize': [13], 'freed': [16], 'phantom': [], 'weak': []},
    {'finalize': [], 'freed': [13, 14, 15], 'phantom': ['p1'], 'weak': []}]),
  ('control: all strongly reachable',
   ({11: [12], 12: [], 13: [14], 14: [15], 15: [], 16: []},
    [11, 13, 16],
    [['w1', 13], ['w2', 12]],
    [13],
    [['p1', 14], ['p2', 16]],
    2),
   [{'finalize': [], 'freed': [], 'phantom': [], 'weak': []},
    {'finalize': [], 'freed': [], 'phantom': [], 'weak': []}])],
 [('regression: finalizable object over two cycles',
   ({21: [22], 22: [], 23: [24], 24: [25], 25: [], 26: []},
    [21],
    [['w1', 23], ['w2', 22]],
    [23],
    [['p1', 24], ['p2', 26]],
    2),
   [{'finalize': [23], 'freed': [26], 'phantom': ['p2'], 'weak': ['w1']},
    {'finalize': [], 'freed': [23, 24, 25], 'phantom': ['p1'], 'weak': []}]),
  ('weak reference to a resurrected object',
   ({21: [22], 22: [], 23: [24], 24: [25], 25: [], 26: []}, [21], [['w1', 23]], [23], [], 1),
   [{'finalize': [23], 'freed': [26], 'phantom': [], 'weak': ['w1']}]),
  ('finalizer runs only once',
   ({21: [22], 22: [], 23: [24], 24: [25], 25: [], 26: []}, [21], [], [23], [], 3),
   [{'finalize': [23], 'freed': [26], 'phantom': [], 'weak': []},
    {'finalize': [], 'freed': [23, 24, 25], 'phantom': [], 'weak': []},
    {'finalize': [], 'freed': [], 'phantom': [], 'weak': []}]),
  ('children of a finalizable object stay alive',
   ({21: [22], 22: [], 23: [24], 24: [25], 25: [], 26: []}, [21], [], [23], [['p1', 25]], 1),
   [{'finalize': [23], 'freed': [26], 'phantom': [], 'weak': []}]),
  ('phantom of an object only reachable from a finalizable one',
   ({21: [22], 22: [], 23: [24], 24: [25], 25: [], 26: []}, [21], [], [23], [['p1', 24]], 2),
   [{'finalize': [23], 'freed': [26], 'phantom': [], 'weak': []},
    {'finalize': [], 'freed': [23, 24, 25], 'phantom': ['p1'], 'weak': []}]),
  ('control: all strongly reachable',
   ({21: [22], 22: [], 23: [24], 24: [25], 25: [], 26: []},
    [21, 23, 26],
    [['w1', 23], ['w2', 22]],
    [23],
    [['p1', 24], ['p2', 26]],
    2),
   [{'finalize': [], 'freed': [], 'phantom': [], 'weak': []},
    {'finalize': [], 'freed': [], 'phantom': [], 'weak': []}])],
 [('regression: finalizable object over two cycles',
   ({31: [32], 32: [], 33: [34], 34: [35], 35: [], 36: []},
    [31],
    [['w1', 33], ['w2', 32]],
    [33],
    [['p1', 34], ['p2', 36]],
    2),
   [{'finalize': [33], 'freed': [36], 'phantom': ['p2'], 'weak': ['w1']},
    {'finalize': [], 'freed': [33, 34, 35], 'phantom': ['p1'], 'weak': []}]),
  ('weak reference to a resurrected object',
   ({31: [32], 32: [], 33: [34], 34: [35], 35: [], 36: []}, [31], [['w1', 33]], [33], [], 1),
   [{'finalize': [33], 'freed': [36], 'phantom': [], 'weak': ['w1']}]),
  ('finalizer runs only once',
   ({31: [32], 32: [], 33: [34], 34: [35], 35: [], 36: []}, [31], [], [33], [], 3),
   [{'finalize': [33], 'freed': [36], 'phantom': [], 'weak': []},
    {'finalize': [], 'freed': [33, 34, 35], 'phantom': [], 'weak': []},
    {'finalize': [], 'freed': [], 'phantom': [], 'weak': []}]),
  ('children of a finalizable object stay alive',
   ({31: [32], 32: [], 33: [34], 34: [35], 35: [], 36: []}, [31], [], [33], [['p1', 35]], 1),
   [{'finalize': [33], 'freed': [36], 'phantom': [], 'weak': []}]),
  ('phantom of an object only reachable from a finalizable one',
   ({31: [32], 32: [], 33: [34], 34: [35], 35: [], 36: []}, [31], [], [33], [['p1', 34]], 2),
   [{'finalize': [33], 'freed': [36], 'phantom': [], 'weak': []},
    {'finalize': [], 'freed': [33, 34, 35], 'phantom': ['p1'], 'weak': []}]),
  ('control: all strongly reachable',
   ({31: [32], 32: [], 33: [34], 34: [35], 35: [], 36: []},
    [31, 33, 36],
    [['w1', 33], ['w2', 32]],
    [33],
    [['p1', 34], ['p2', 36]],
    2),
   [{'finalize': [], 'freed': [], 'phantom': [], 'weak': []},
    {'finalize': [], 'freed': [], 'phantom': [], 'weak': []}])],
 [('regression: finalizable object over two cycles',
   ({41: [42], 42: [], 43: [44], 44: [45], 45: [], 46: []},
    [41],
    [['w1', 43], ['w2', 42]],
    [43],
    [['p1', 44], ['p2', 46]],
    2),
   [{'finalize': [43], 'freed': [46], 'phantom': ['p2'], 'weak': ['w1']},
    {'finalize': [], 'freed': [43, 44, 45], 'phantom': ['p1'], 'weak': []}]),
  ('weak reference to a resurrected object',
   ({41: [42], 42: [], 43: [44], 44: [45], 45: [], 46: []}, [41], [['w1', 43]], [43], [], 1),
   [{'finalize': [43], 'freed': [46], 'phantom': [], 'weak': ['w1']}]),
  ('finalizer runs only once',
   ({41: [42], 42: [], 43: [44], 44: [45], 45: [], 46: []}, [41], [], [43], [], 3),
   [{'finalize': [43], 'freed': [46], 'phantom': [], 'weak': []},
    {'finalize': [], 'freed': [43, 44, 45], 'phantom': [], 'weak': []},
    {'finalize': [], 'freed': [], 'phantom': [], 'weak': []}]),
  ('children of a finalizable object stay alive',
   ({41: [42], 42: [], 43: [44], 44: [45], 45: [], 46: []}, [41], [], [43], [['p1', 45]], 1),
   [{'finalize': [43], 'freed': [46], 'phantom': [], 'weak': []}]),
  ('phantom of an object only reachable from a finalizable one',
   ({41: [42], 42: [], 43: [44], 44: [45], 45: [], 46: []}, [41], [], [43], [['p1', 44]], 2),
   [{'finalize': [43], 'freed': [46], 'phantom': [], 'weak': []},
    {'finalize': [], 'freed': [43, 44, 45], 'phantom': ['p1'], 'weak': []}]),
  ('control: all strongly reachable',
   ({41: [42], 42: [], 43: [44], 44: [45], 45: [], 46: []},
    [41, 43, 46],
    [['w1', 43], ['w2', 42]],
    [43],
    [['p1', 44], ['p2', 46]],
    2),
   [{'finalize': [], 'freed': [], 'phantom': [], 'weak': []},
    {'finalize': [], 'freed': [], 'phantom': [], 'weak': []}])],
 [('regression: finalizable object over two cycles',
   ({51: [52], 52: [], 53: [54], 54: [55], 55: [], 56: []},
    [51],
    [['w1', 53], ['w2', 52]],
    [53],
    [['p1', 54], ['p2', 56]],
    2),
   [{'finalize': [53], 'freed': [56], 'phantom': ['p2'], 'weak': ['w1']},
    {'finalize': [], 'freed': [53, 54, 55], 'phantom': ['p1'], 'weak': []}]),
  ('weak reference to a resurrected object',
   ({51: [52], 52: [], 53: [54], 54: [55], 55: [], 56: []}, [51], [['w1', 53]], [53], [], 1),
   [{'finalize': [53], 'freed': [56], 'phantom': [], 'weak': ['w1']}]),
  ('finalizer runs only once',
   ({51: [52], 52: [], 53: [54], 54: [55], 55: [], 56: []}, [51], [], [53], [], 3),
   [{'finalize': [53], 'freed': [56], 'phantom': [], 'weak': []},
    {'finalize': [], 'freed': [53, 54, 55], 'phantom': [], 'weak': []},
    {'finalize': [], 'freed': [], 'phantom': [], 'weak': []}]),
  ('children of a finalizable object stay alive',
   ({51: [52], 52: [], 53: [54], 54: [55], 55: [], 56: []}, [51], [], [53], [['p1', 55]], 1),
   [{'finalize': [53], 'freed': [56], 'phantom': [], 'weak': []}]),
  ('phantom of an object only reachable from a finalizable one',
   ({51: [52], 52: [], 53: [54], 54: [55], 55: [], 56: []}, [51], [], [53], [['p1', 54]], 2),
   [{'finalize': [53], 'freed': [56], 'phantom': [], 'weak': []},
    {'finalize': [], 'freed': [53, 54, 55], 'phantom': ['p1'], 'weak': []}]),
  ('control: all strongly reachable',
   ({51: [52], 52: [], 53: [54], 54: [55], 55: [], 56: []},
    [51, 53, 56],
    [['w1', 53], ['w2', 52]],
    [53],
    [['p1', 54], ['p2', 56]],
    2),
   [{'finalize': [], 'freed': [], 'phantom': [], 'weak': []},
    {'finalize': [], 'freed': [], 'phantom': [], 'weak': []}])]]
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 fixtureActualExpectedOutcome
regression: finalizable object over two cycles[{'finalize': [13], 'freed': [14, 15, 16], 'phantom': ['p1', 'p2'], 'weak': ['w1']}, {'finalize': [], 'freed': [13], 'phantom': [], 'weak': []}][{'finalize': [13], 'freed': [16], 'phantom': ['p2'], 'weak': ['w1']}, {'finalize': [], 'freed': [13, 14, 15], 'phantom': ['p1'], 'weak': []}]Failed
weak reference to a resurrected object[{'finalize': [13], 'freed': [14, 15, 16], 'phantom': [], 'weak': ['w1']}][{'finalize': [13], 'freed': [16], 'phantom': [], 'weak': ['w1']}]Failed
finalizer runs only once[{'finalize': [13], 'freed': [14, 15, 16], 'phantom': [], 'weak': []}, {'finalize': [], 'freed': [13], 'phantom': [], 'weak': []}, {'finalize': [], 'freed': [], 'phantom': [], 'weak': []}][{'finalize': [13], 'freed': [16], 'phantom': [], 'weak': []}, {'finalize': [], 'freed': [13, 14, 15], 'phantom': [], 'weak': []}, {'finalize': [], 'freed': [], 'phantom': [], 'weak': []}]Failed
children of a finalizable object stay alive[{'finalize': [13], 'freed': [14, 15, 16], 'phantom': ['p1'], 'weak': []}][{'finalize': [13], 'freed': [16], 'phantom': [], 'weak': []}]Failed
phantom of an object only reachable from a finalizable one[{'finalize': [13], 'freed': [14, 15, 16], 'phantom': ['p1'], 'weak': []}, {'finalize': [], 'freed': [13], 'phantom': [], 'weak': []}][{'finalize': [13], 'freed': [16], 'phantom': [], 'weak': []}, {'finalize': [], 'freed': [13, 14, 15], 'phantom': ['p1'], 'weak': []}]Failed
control: all strongly reachable[{'finalize': [], 'freed': [], 'phantom': [], 'weak': []}, {'finalize': [], 'freed': [], 'phantom': [], 'weak': []}][{'finalize': [], 'freed': [], 'phantom': [], 'weak': []}, {'finalize': [], 'freed': [], 'phantom': [], 'weak': []}]Passed

SHA-256 / 9f12be84653553f8cc54a45c57e4cff219d3195be08bd81cdb7237969aecc32a

2 / The unsuccessful fix

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

N = 1
observations = []
def solve(heap, roots, weak, finalizable, phantom, cycles):
    heap = {k: list(v) for k, v in heap.items()}
    weak = {r: t for r, t in weak}
    phantom = {r: t for r, t in phantom}
    pending = set(finalizable)
    out = []
    def trace(starts, seen):
        stack = list(starts)
        while stack:
            o = stack.pop()
            if o is None or o in seen or o not in heap:
                continue
            seen.add(o)
            stack.extend(heap[o])
        return seen
    for _ in range(cycles):
        strong = trace(roots, set())
        to_final = sorted(o for o in pending if o not in strong and o in heap)
        reach = strong | {c for o in to_final for c in heap[o] if c is not None}
        cleared = sorted(r for r, t in weak.items() if t is not None and t not in strong)
        for r in cleared:
            weak[r] = None
        pending -= set(to_final)
        enq = sorted(r for r, t in phantom.items() if t is not None and t not in reach)
        for r in enq:
            phantom[r] = None
        freed = sorted(o for o in heap if o not in reach)
        for o in freed:
            del heap[o]
        out.append({'weak': cleared, 'finalize': to_final, 'phantom': enq, 'freed': freed})
    return out
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
cases = [[('regression: finalizable object over two cycles',
   ({11: [12], 12: [], 13: [14], 14: [15], 15: [], 16: []},
    [11],
    [['w1', 13], ['w2', 12]],
    [13],
    [['p1', 14], ['p2', 16]],
    2),
   [{'finalize': [13], 'freed': [16], 'phantom': ['p2'], 'weak': ['w1']},
    {'finalize': [], 'freed': [13, 14, 15], 'phantom': ['p1'], 'weak': []}]),
  ('weak reference to a resurrected object',
   ({11: [12], 12: [], 13: [14], 14: [15], 15: [], 16: []}, [11], [['w1', 13]], [13], [], 1),
   [{'finalize': [13], 'freed': [16], 'phantom': [], 'weak': ['w1']}]),
  ('finalizer runs only once',
   ({11: [12], 12: [], 13: [14], 14: [15], 15: [], 16: []}, [11], [], [13], [], 3),
   [{'finalize': [13], 'freed': [16], 'phantom': [], 'weak': []},
    {'finalize': [], 'freed': [13, 14, 15], 'phantom': [], 'weak': []},
    {'finalize': [], 'freed': [], 'phantom': [], 'weak': []}]),
  ('children of a finalizable object stay alive',
   ({11: [12], 12: [], 13: [14], 14: [15], 15: [], 16: []}, [11], [], [13], [['p1', 15]], 1),
   [{'finalize': [13], 'freed': [16], 'phantom': [], 'weak': []}]),
  ('phantom of an object only reachable from a finalizable one',
   ({11: [12], 12: [], 13: [14], 14: [15], 15: [], 16: []}, [11], [], [13], [['p1', 14]], 2),
   [{'finalize': [13], 'freed': [16], 'phantom': [], 'weak': []},
    {'finalize': [], 'freed': [13, 14, 15], 'phantom': ['p1'], 'weak': []}]),
  ('control: all strongly reachable',
   ({11: [12], 12: [], 13: [14], 14: [15], 15: [], 16: []},
    [11, 13, 16],
    [['w1', 13], ['w2', 12]],
    [13],
    [['p1', 14], ['p2', 16]],
    2),
   [{'finalize': [], 'freed': [], 'phantom': [], 'weak': []},
    {'finalize': [], 'freed': [], 'phantom': [], 'weak': []}])],
 [('regression: finalizable object over two cycles',
   ({21: [22], 22: [], 23: [24], 24: [25], 25: [], 26: []},
    [21],
    [['w1', 23], ['w2', 22]],
    [23],
    [['p1', 24], ['p2', 26]],
    2),
   [{'finalize': [23], 'freed': [26], 'phantom': ['p2'], 'weak': ['w1']},
    {'finalize': [], 'freed': [23, 24, 25], 'phantom': ['p1'], 'weak': []}]),
  ('weak reference to a resurrected object',
   ({21: [22], 22: [], 23: [24], 24: [25], 25: [], 26: []}, [21], [['w1', 23]], [23], [], 1),
   [{'finalize': [23], 'freed': [26], 'phantom': [], 'weak': ['w1']}]),
  ('finalizer runs only once',
   ({21: [22], 22: [], 23: [24], 24: [25], 25: [], 26: []}, [21], [], [23], [], 3),
   [{'finalize': [23], 'freed': [26], 'phantom': [], 'weak': []},
    {'finalize': [], 'freed': [23, 24, 25], 'phantom': [], 'weak': []},
    {'finalize': [], 'freed': [], 'phantom': [], 'weak': []}]),
  ('children of a finalizable object stay alive',
   ({21: [22], 22: [], 23: [24], 24: [25], 25: [], 26: []}, [21], [], [23], [['p1', 25]], 1),
   [{'finalize': [23], 'freed': [26], 'phantom': [], 'weak': []}]),
  ('phantom of an object only reachable from a finalizable one',
   ({21: [22], 22: [], 23: [24], 24: [25], 25: [], 26: []}, [21], [], [23], [['p1', 24]], 2),
   [{'finalize': [23], 'freed': [26], 'phantom': [], 'weak': []},
    {'finalize': [], 'freed': [23, 24, 25], 'phantom': ['p1'], 'weak': []}]),
  ('control: all strongly reachable',
   ({21: [22], 22: [], 23: [24], 24: [25], 25: [], 26: []},
    [21, 23, 26],
    [['w1', 23], ['w2', 22]],
    [23],
    [['p1', 24], ['p2', 26]],
    2),
   [{'finalize': [], 'freed': [], 'phantom': [], 'weak': []},
    {'finalize': [], 'freed': [], 'phantom': [], 'weak': []}])],
 [('regression: finalizable object over two cycles',
   ({31: [32], 32: [], 33: [34], 34: [35], 35: [], 36: []},
    [31],
    [['w1', 33], ['w2', 32]],
    [33],
    [['p1', 34], ['p2', 36]],
    2),
   [{'finalize': [33], 'freed': [36], 'phantom': ['p2'], 'weak': ['w1']},
    {'finalize': [], 'freed': [33, 34, 35], 'phantom': ['p1'], 'weak': []}]),
  ('weak reference to a resurrected object',
   ({31: [32], 32: [], 33: [34], 34: [35], 35: [], 36: []}, [31], [['w1', 33]], [33], [], 1),
   [{'finalize': [33], 'freed': [36], 'phantom': [], 'weak': ['w1']}]),
  ('finalizer runs only once',
   ({31: [32], 32: [], 33: [34], 34: [35], 35: [], 36: []}, [31], [], [33], [], 3),
   [{'finalize': [33], 'freed': [36], 'phantom': [], 'weak': []},
    {'finalize': [], 'freed': [33, 34, 35], 'phantom': [], 'weak': []},
    {'finalize': [], 'freed': [], 'phantom': [], 'weak': []}]),
  ('children of a finalizable object stay alive',
   ({31: [32], 32: [], 33: [34], 34: [35], 35: [], 36: []}, [31], [], [33], [['p1', 35]], 1),
   [{'finalize': [33], 'freed': [36], 'phantom': [], 'weak': []}]),
  ('phantom of an object only reachable from a finalizable one',
   ({31: [32], 32: [], 33: [34], 34: [35], 35: [], 36: []}, [31], [], [33], [['p1', 34]], 2),
   [{'finalize': [33], 'freed': [36], 'phantom': [], 'weak': []},
    {'finalize': [], 'freed': [33, 34, 35], 'phantom': ['p1'], 'weak': []}]),
  ('control: all strongly reachable',
   ({31: [32], 32: [], 33: [34], 34: [35], 35: [], 36: []},
    [31, 33, 36],
    [['w1', 33], ['w2', 32]],
    [33],
    [['p1', 34], ['p2', 36]],
    2),
   [{'finalize': [], 'freed': [], 'phantom': [], 'weak': []},
    {'finalize': [], 'freed': [], 'phantom': [], 'weak': []}])],
 [('regression: finalizable object over two cycles',
   ({41: [42], 42: [], 43: [44], 44: [45], 45: [], 46: []},
    [41],
    [['w1', 43], ['w2', 42]],
    [43],
    [['p1', 44], ['p2', 46]],
    2),
   [{'finalize': [43], 'freed': [46], 'phantom': ['p2'], 'weak': ['w1']},
    {'finalize': [], 'freed': [43, 44, 45], 'phantom': ['p1'], 'weak': []}]),
  ('weak reference to a resurrected object',
   ({41: [42], 42: [], 43: [44], 44: [45], 45: [], 46: []}, [41], [['w1', 43]], [43], [], 1),
   [{'finalize': [43], 'freed': [46], 'phantom': [], 'weak': ['w1']}]),
  ('finalizer runs only once',
   ({41: [42], 42: [], 43: [44], 44: [45], 45: [], 46: []}, [41], [], [43], [], 3),
   [{'finalize': [43], 'freed': [46], 'phantom': [], 'weak': []},
    {'finalize': [], 'freed': [43, 44, 45], 'phantom': [], 'weak': []},
    {'finalize': [], 'freed': [], 'phantom': [], 'weak': []}]),
  ('children of a finalizable object stay alive',
   ({41: [42], 42: [], 43: [44], 44: [45], 45: [], 46: []}, [41], [], [43], [['p1', 45]], 1),
   [{'finalize': [43], 'freed': [46], 'phantom': [], 'weak': []}]),
  ('phantom of an object only reachable from a finalizable one',
   ({41: [42], 42: [], 43: [44], 44: [45], 45: [], 46: []}, [41], [], [43], [['p1', 44]], 2),
   [{'finalize': [43], 'freed': [46], 'phantom': [], 'weak': []},
    {'finalize': [], 'freed': [43, 44, 45], 'phantom': ['p1'], 'weak': []}]),
  ('control: all strongly reachable',
   ({41: [42], 42: [], 43: [44], 44: [45], 45: [], 46: []},
    [41, 43, 46],
    [['w1', 43], ['w2', 42]],
    [43],
    [['p1', 44], ['p2', 46]],
    2),
   [{'finalize': [], 'freed': [], 'phantom': [], 'weak': []},
    {'finalize': [], 'freed': [], 'phantom': [], 'weak': []}])],
 [('regression: finalizable object over two cycles',
   ({51: [52], 52: [], 53: [54], 54: [55], 55: [], 56: []},
    [51],
    [['w1', 53], ['w2', 52]],
    [53],
    [['p1', 54], ['p2', 56]],
    2),
   [{'finalize': [53], 'freed': [56], 'phantom': ['p2'], 'weak': ['w1']},
    {'finalize': [], 'freed': [53, 54, 55], 'phantom': ['p1'], 'weak': []}]),
  ('weak reference to a resurrected object',
   ({51: [52], 52: [], 53: [54], 54: [55], 55: [], 56: []}, [51], [['w1', 53]], [53], [], 1),
   [{'finalize': [53], 'freed': [56], 'phantom': [], 'weak': ['w1']}]),
  ('finalizer runs only once',
   ({51: [52], 52: [], 53: [54], 54: [55], 55: [], 56: []}, [51], [], [53], [], 3),
   [{'finalize': [53], 'freed': [56], 'phantom': [], 'weak': []},
    {'finalize': [], 'freed': [53, 54, 55], 'phantom': [], 'weak': []},
    {'finalize': [], 'freed': [], 'phantom': [], 'weak': []}]),
  ('children of a finalizable object stay alive',
   ({51: [52], 52: [], 53: [54], 54: [55], 55: [], 56: []}, [51], [], [53], [['p1', 55]], 1),
   [{'finalize': [53], 'freed': [56], 'phantom': [], 'weak': []}]),
  ('phantom of an object only reachable from a finalizable one',
   ({51: [52], 52: [], 53: [54], 54: [55], 55: [], 56: []}, [51], [], [53], [['p1', 54]], 2),
   [{'finalize': [53], 'freed': [56], 'phantom': [], 'weak': []},
    {'finalize': [], 'freed': [53, 54, 55], 'phantom': ['p1'], 'weak': []}]),
  ('control: all strongly reachable',
   ({51: [52], 52: [], 53: [54], 54: [55], 55: [], 56: []},
    [51, 53, 56],
    [['w1', 53], ['w2', 52]],
    [53],
    [['p1', 54], ['p2', 56]],
    2),
   [{'finalize': [], 'freed': [], 'phantom': [], 'weak': []},
    {'finalize': [], 'freed': [], 'phantom': [], 'weak': []}])]]
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 fixtureActualExpectedOutcome
regression: finalizable object over two cycles[{'finalize': [13], 'freed': [13, 15, 16], 'phantom': ['p2'], 'weak': ['w1']}, {'finalize': [], 'freed': [14], 'phantom': ['p1'], 'weak': []}][{'finalize': [13], 'freed': [16], 'phantom': ['p2'], 'weak': ['w1']}, {'finalize': [], 'freed': [13, 14, 15], 'phantom': ['p1'], 'weak': []}]Failed
weak reference to a resurrected object[{'finalize': [13], 'freed': [13, 15, 16], 'phantom': [], 'weak': ['w1']}][{'finalize': [13], 'freed': [16], 'phantom': [], 'weak': ['w1']}]Failed
finalizer runs only once[{'finalize': [13], 'freed': [13, 15, 16], 'phantom': [], 'weak': []}, {'finalize': [], 'freed': [14], 'phantom': [], 'weak': []}, {'finalize': [], 'freed': [], 'phantom': [], 'weak': []}][{'finalize': [13], 'freed': [16], 'phantom': [], 'weak': []}, {'finalize': [], 'freed': [13, 14, 15], 'phantom': [], 'weak': []}, {'finalize': [], 'freed': [], 'phantom': [], 'weak': []}]Failed
children of a finalizable object stay alive[{'finalize': [13], 'freed': [13, 15, 16], 'phantom': ['p1'], 'weak': []}][{'finalize': [13], 'freed': [16], 'phantom': [], 'weak': []}]Failed
phantom of an object only reachable from a finalizable one[{'finalize': [13], 'freed': [13, 15, 16], 'phantom': [], 'weak': []}, {'finalize': [], 'freed': [14], 'phantom': ['p1'], 'weak': []}][{'finalize': [13], 'freed': [16], 'phantom': [], 'weak': []}, {'finalize': [], 'freed': [13, 14, 15], 'phantom': ['p1'], 'weak': []}]Failed
control: all strongly reachable[{'finalize': [], 'freed': [], 'phantom': [], 'weak': []}, {'finalize': [], 'freed': [], 'phantom': [], 'weak': []}][{'finalize': [], 'freed': [], 'phantom': [], 'weak': []}, {'finalize': [], 'freed': [], 'phantom': [], 'weak': []}]Passed

SHA-256 / a3ec62707df180aba33bb4011cfc6e2b7c4a1d4f35bb9a3383ec5a0b312b1b29

3 / The verified repair

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

N = 1
observations = []
def solve(heap, roots, weak, finalizable, phantom, cycles):
    heap = {k: list(v) for k, v in heap.items()}
    weak = {r: t for r, t in weak}
    phantom = {r: t for r, t in phantom}
    pending = set(finalizable)
    out = []
    def trace(starts, seen):
        stack = list(starts)
        while stack:
            o = stack.pop()
            if o is None or o in seen or o not in heap:
                continue
            seen.add(o)
            stack.extend(heap[o])
        return seen
    for _ in range(cycles):
        strong = trace(roots, set())
        to_final = sorted(o for o in pending if o not in strong and o in heap)
        reach = trace(to_final, set(strong))
        cleared = sorted(r for r, t in weak.items() if t is not None and t not in strong)
        for r in cleared:
            weak[r] = None
        pending -= set(to_final)
        enq = sorted(r for r, t in phantom.items() if t is not None and t not in reach)
        for r in enq:
            phantom[r] = None
        freed = sorted(o for o in heap if o not in reach)
        for o in freed:
            del heap[o]
        out.append({'weak': cleared, 'finalize': to_final, 'phantom': enq, 'freed': freed})
    return out
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
cases = [[('regression: finalizable object over two cycles',
   ({11: [12], 12: [], 13: [14], 14: [15], 15: [], 16: []},
    [11],
    [['w1', 13], ['w2', 12]],
    [13],
    [['p1', 14], ['p2', 16]],
    2),
   [{'finalize': [13], 'freed': [16], 'phantom': ['p2'], 'weak': ['w1']},
    {'finalize': [], 'freed': [13, 14, 15], 'phantom': ['p1'], 'weak': []}]),
  ('weak reference to a resurrected object',
   ({11: [12], 12: [], 13: [14], 14: [15], 15: [], 16: []}, [11], [['w1', 13]], [13], [], 1),
   [{'finalize': [13], 'freed': [16], 'phantom': [], 'weak': ['w1']}]),
  ('finalizer runs only once',
   ({11: [12], 12: [], 13: [14], 14: [15], 15: [], 16: []}, [11], [], [13], [], 3),
   [{'finalize': [13], 'freed': [16], 'phantom': [], 'weak': []},
    {'finalize': [], 'freed': [13, 14, 15], 'phantom': [], 'weak': []},
    {'finalize': [], 'freed': [], 'phantom': [], 'weak': []}]),
  ('children of a finalizable object stay alive',
   ({11: [12], 12: [], 13: [14], 14: [15], 15: [], 16: []}, [11], [], [13], [['p1', 15]], 1),
   [{'finalize': [13], 'freed': [16], 'phantom': [], 'weak': []}]),
  ('phantom of an object only reachable from a finalizable one',
   ({11: [12], 12: [], 13: [14], 14: [15], 15: [], 16: []}, [11], [], [13], [['p1', 14]], 2),
   [{'finalize': [13], 'freed': [16], 'phantom': [], 'weak': []},
    {'finalize': [], 'freed': [13, 14, 15], 'phantom': ['p1'], 'weak': []}]),
  ('control: all strongly reachable',
   ({11: [12], 12: [], 13: [14], 14: [15], 15: [], 16: []},
    [11, 13, 16],
    [['w1', 13], ['w2', 12]],
    [13],
    [['p1', 14], ['p2', 16]],
    2),
   [{'finalize': [], 'freed': [], 'phantom': [], 'weak': []},
    {'finalize': [], 'freed': [], 'phantom': [], 'weak': []}])],
 [('regression: finalizable object over two cycles',
   ({21: [22], 22: [], 23: [24], 24: [25], 25: [], 26: []},
    [21],
    [['w1', 23], ['w2', 22]],
    [23],
    [['p1', 24], ['p2', 26]],
    2),
   [{'finalize': [23], 'freed': [26], 'phantom': ['p2'], 'weak': ['w1']},
    {'finalize': [], 'freed': [23, 24, 25], 'phantom': ['p1'], 'weak': []}]),
  ('weak reference to a resurrected object',
   ({21: [22], 22: [], 23: [24], 24: [25], 25: [], 26: []}, [21], [['w1', 23]], [23], [], 1),
   [{'finalize': [23], 'freed': [26], 'phantom': [], 'weak': ['w1']}]),
  ('finalizer runs only once',
   ({21: [22], 22: [], 23: [24], 24: [25], 25: [], 26: []}, [21], [], [23], [], 3),
   [{'finalize': [23], 'freed': [26], 'phantom': [], 'weak': []},
    {'finalize': [], 'freed': [23, 24, 25], 'phantom': [], 'weak': []},
    {'finalize': [], 'freed': [], 'phantom': [], 'weak': []}]),
  ('children of a finalizable object stay alive',
   ({21: [22], 22: [], 23: [24], 24: [25], 25: [], 26: []}, [21], [], [23], [['p1', 25]], 1),
   [{'finalize': [23], 'freed': [26], 'phantom': [], 'weak': []}]),
  ('phantom of an object only reachable from a finalizable one',
   ({21: [22], 22: [], 23: [24], 24: [25], 25: [], 26: []}, [21], [], [23], [['p1', 24]], 2),
   [{'finalize': [23], 'freed': [26], 'phantom': [], 'weak': []},
    {'finalize': [], 'freed': [23, 24, 25], 'phantom': ['p1'], 'weak': []}]),
  ('control: all strongly reachable',
   ({21: [22], 22: [], 23: [24], 24: [25], 25: [], 26: []},
    [21, 23, 26],
    [['w1', 23], ['w2', 22]],
    [23],
    [['p1', 24], ['p2', 26]],
    2),
   [{'finalize': [], 'freed': [], 'phantom': [], 'weak': []},
    {'finalize': [], 'freed': [], 'phantom': [], 'weak': []}])],
 [('regression: finalizable object over two cycles',
   ({31: [32], 32: [], 33: [34], 34: [35], 35: [], 36: []},
    [31],
    [['w1', 33], ['w2', 32]],
    [33],
    [['p1', 34], ['p2', 36]],
    2),
   [{'finalize': [33], 'freed': [36], 'phantom': ['p2'], 'weak': ['w1']},
    {'finalize': [], 'freed': [33, 34, 35], 'phantom': ['p1'], 'weak': []}]),
  ('weak reference to a resurrected object',
   ({31: [32], 32: [], 33: [34], 34: [35], 35: [], 36: []}, [31], [['w1', 33]], [33], [], 1),
   [{'finalize': [33], 'freed': [36], 'phantom': [], 'weak': ['w1']}]),
  ('finalizer runs only once',
   ({31: [32], 32: [], 33: [34], 34: [35], 35: [], 36: []}, [31], [], [33], [], 3),
   [{'finalize': [33], 'freed': [36], 'phantom': [], 'weak': []},
    {'finalize': [], 'freed': [33, 34, 35], 'phantom': [], 'weak': []},
    {'finalize': [], 'freed': [], 'phantom': [], 'weak': []}]),
  ('children of a finalizable object stay alive',
   ({31: [32], 32: [], 33: [34], 34: [35], 35: [], 36: []}, [31], [], [33], [['p1', 35]], 1),
   [{'finalize': [33], 'freed': [36], 'phantom': [], 'weak': []}]),
  ('phantom of an object only reachable from a finalizable one',
   ({31: [32], 32: [], 33: [34], 34: [35], 35: [], 36: []}, [31], [], [33], [['p1', 34]], 2),
   [{'finalize': [33], 'freed': [36], 'phantom': [], 'weak': []},
    {'finalize': [], 'freed': [33, 34, 35], 'phantom': ['p1'], 'weak': []}]),
  ('control: all strongly reachable',
   ({31: [32], 32: [], 33: [34], 34: [35], 35: [], 36: []},
    [31, 33, 36],
    [['w1', 33], ['w2', 32]],
    [33],
    [['p1', 34], ['p2', 36]],
    2),
   [{'finalize': [], 'freed': [], 'phantom': [], 'weak': []},
    {'finalize': [], 'freed': [], 'phantom': [], 'weak': []}])],
 [('regression: finalizable object over two cycles',
   ({41: [42], 42: [], 43: [44], 44: [45], 45: [], 46: []},
    [41],
    [['w1', 43], ['w2', 42]],
    [43],
    [['p1', 44], ['p2', 46]],
    2),
   [{'finalize': [43], 'freed': [46], 'phantom': ['p2'], 'weak': ['w1']},
    {'finalize': [], 'freed': [43, 44, 45], 'phantom': ['p1'], 'weak': []}]),
  ('weak reference to a resurrected object',
   ({41: [42], 42: [], 43: [44], 44: [45], 45: [], 46: []}, [41], [['w1', 43]], [43], [], 1),
   [{'finalize': [43], 'freed': [46], 'phantom': [], 'weak': ['w1']}]),
  ('finalizer runs only once',
   ({41: [42], 42: [], 43: [44], 44: [45], 45: [], 46: []}, [41], [], [43], [], 3),
   [{'finalize': [43], 'freed': [46], 'phantom': [], 'weak': []},
    {'finalize': [], 'freed': [43, 44, 45], 'phantom': [], 'weak': []},
    {'finalize': [], 'freed': [], 'phantom': [], 'weak': []}]),
  ('children of a finalizable object stay alive',
   ({41: [42], 42: [], 43: [44], 44: [45], 45: [], 46: []}, [41], [], [43], [['p1', 45]], 1),
   [{'finalize': [43], 'freed': [46], 'phantom': [], 'weak': []}]),
  ('phantom of an object only reachable from a finalizable one',
   ({41: [42], 42: [], 43: [44], 44: [45], 45: [], 46: []}, [41], [], [43], [['p1', 44]], 2),
   [{'finalize': [43], 'freed': [46], 'phantom': [], 'weak': []},
    {'finalize': [], 'freed': [43, 44, 45], 'phantom': ['p1'], 'weak': []}]),
  ('control: all strongly reachable',
   ({41: [42], 42: [], 43: [44], 44: [45], 45: [], 46: []},
    [41, 43, 46],
    [['w1', 43], ['w2', 42]],
    [43],
    [['p1', 44], ['p2', 46]],
    2),
   [{'finalize': [], 'freed': [], 'phantom': [], 'weak': []},
    {'finalize': [], 'freed': [], 'phantom': [], 'weak': []}])],
 [('regression: finalizable object over two cycles',
   ({51: [52], 52: [], 53: [54], 54: [55], 55: [], 56: []},
    [51],
    [['w1', 53], ['w2', 52]],
    [53],
    [['p1', 54], ['p2', 56]],
    2),
   [{'finalize': [53], 'freed': [56], 'phantom': ['p2'], 'weak': ['w1']},
    {'finalize': [], 'freed': [53, 54, 55], 'phantom': ['p1'], 'weak': []}]),
  ('weak reference to a resurrected object',
   ({51: [52], 52: [], 53: [54], 54: [55], 55: [], 56: []}, [51], [['w1', 53]], [53], [], 1),
   [{'finalize': [53], 'freed': [56], 'phantom': [], 'weak': ['w1']}]),
  ('finalizer runs only once',
   ({51: [52], 52: [], 53: [54], 54: [55], 55: [], 56: []}, [51], [], [53], [], 3),
   [{'finalize': [53], 'freed': [56], 'phantom': [], 'weak': []},
    {'finalize': [], 'freed': [53, 54, 55], 'phantom': [], 'weak': []},
    {'finalize': [], 'freed': [], 'phantom': [], 'weak': []}]),
  ('children of a finalizable object stay alive',
   ({51: [52], 52: [], 53: [54], 54: [55], 55: [], 56: []}, [51], [], [53], [['p1', 55]], 1),
   [{'finalize': [53], 'freed': [56], 'phantom': [], 'weak': []}]),
  ('phantom of an object only reachable from a finalizable one',
   ({51: [52], 52: [], 53: [54], 54: [55], 55: [], 56: []}, [51], [], [53], [['p1', 54]], 2),
   [{'finalize': [53], 'freed': [56], 'phantom': [], 'weak': []},
    {'finalize': [], 'freed': [53, 54, 55], 'phantom': ['p1'], 'weak': []}]),
  ('control: all strongly reachable',
   ({51: [52], 52: [], 53: [54], 54: [55], 55: [], 56: []},
    [51, 53, 56],
    [['w1', 53], ['w2', 52]],
    [53],
    [['p1', 54], ['p2', 56]],
    2),
   [{'finalize': [], 'freed': [], 'phantom': [], 'weak': []},
    {'finalize': [], 'freed': [], 'phantom': [], 'weak': []}])]]
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 fixtureActualExpectedOutcome
regression: finalizable object over two cycles[{'finalize': [13], 'freed': [16], 'phantom': ['p2'], 'weak': ['w1']}, {'finalize': [], 'freed': [13, 14, 15], 'phantom': ['p1'], 'weak': []}][{'finalize': [13], 'freed': [16], 'phantom': ['p2'], 'weak': ['w1']}, {'finalize': [], 'freed': [13, 14, 15], 'phantom': ['p1'], 'weak': []}]Passed
weak reference to a resurrected object[{'finalize': [13], 'freed': [16], 'phantom': [], 'weak': ['w1']}][{'finalize': [13], 'freed': [16], 'phantom': [], 'weak': ['w1']}]Passed
finalizer runs only once[{'finalize': [13], 'freed': [16], 'phantom': [], 'weak': []}, {'finalize': [], 'freed': [13, 14, 15], 'phantom': [], 'weak': []}, {'finalize': [], 'freed': [], 'phantom': [], 'weak': []}][{'finalize': [13], 'freed': [16], 'phantom': [], 'weak': []}, {'finalize': [], 'freed': [13, 14, 15], 'phantom': [], 'weak': []}, {'finalize': [], 'freed': [], 'phantom': [], 'weak': []}]Passed
children of a finalizable object stay alive[{'finalize': [13], 'freed': [16], 'phantom': [], 'weak': []}][{'finalize': [13], 'freed': [16], 'phantom': [], 'weak': []}]Passed
phantom of an object only reachable from a finalizable one[{'finalize': [13], 'freed': [16], 'phantom': [], 'weak': []}, {'finalize': [], 'freed': [13, 14, 15], 'phantom': ['p1'], 'weak': []}][{'finalize': [13], 'freed': [16], 'phantom': [], 'weak': []}, {'finalize': [], 'freed': [13, 14, 15], 'phantom': ['p1'], 'weak': []}]Passed
control: all strongly reachable[{'finalize': [], 'freed': [], 'phantom': [], 'weak': []}, {'finalize': [], 'freed': [], 'phantom': [], 'weak': []}][{'finalize': [], 'freed': [], 'phantom': [], 'weak': []}, {'finalize': [], 'freed': [], 'phantom': [], 'weak': []}]Passed

SHA-256 / 1bc257fc71f29ad925cf0532cf3655eb6fabe21082ed79dfc30904232ea303fa

Verification & scope

A deterministic, bounded teaching model of one garbage-collector mechanism with stipulated rules; it is not a production collector and claims no conformance to any particular runtime. 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:27.703510+00:00.

Case digest / b78903d571d9af6009bf94c23056d29f58602bea938d3fbfa8c05f60a45b89cc