mirror of
https://github.com/abhigyanpatwari/GitNexus.git
synced 2026-10-10 03:27:59 +00:00
fix(cfg): wire Swift multi-catch throw edges to every handler (#2195)
visitDo routed the protected body's throw edge only to handlerEntries[0], so a
do { try r() } catch A {} catch {} left the 2nd..Nth catch handlers UNREACHABLE
from ENTRY — orphaned blocks whose error bindings + def/use facts were stranded
in a dead component (a soundness gap for idiomatic Swift typed multi-catch).
Swift tries the catch clauses in order and the thrown type is unknown at CFG
time, so every protected block may reach ANY clause: edge each protected block
to every handlerEntry. Found by the per-language CFG/CDG verification swarm
(reproduced: 2-catch=1, 3-catch=3 unreachable blocks). swift suite 26 passed,
tsc clean.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
c97d1b2dfc
commit
5d284b8f5f
2 changed files with 18 additions and 3 deletions
|
|
@ -740,10 +740,14 @@ class SwiftCfgWalk {
|
|||
const bodyRes = bodyNode ? this.visitSeq(this.statementsOf(bodyNode)) : null;
|
||||
this.handlers.pop();
|
||||
|
||||
// Conservative exceptional edges: every protected-region block → the handler.
|
||||
if (catchBlocks.length > 0) {
|
||||
// Conservative exceptional edges: every protected-region block → EACH catch
|
||||
// handler. Swift tries the catch clauses in order until one matches; the
|
||||
// thrown type is unknown at CFG time, so any protected block may reach ANY
|
||||
// clause. Edging only the first handler orphaned the 2nd..Nth catch blocks
|
||||
// (unreachable from ENTRY, stranding their error bindings + def/use facts).
|
||||
if (handlerEntries.length > 0) {
|
||||
for (let b = protectedStart; b < this.builder.blockCount; b++) {
|
||||
this.builder.edge(b, doHandler, 'throw');
|
||||
for (const handler of handlerEntries) this.builder.edge(b, handler, 'throw');
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -238,6 +238,17 @@ describe('Swift CfgVisitor — do/catch (error handling)', () => {
|
|||
expect(reaches(cfg, thr, block(cfg, 'done()'))).toBe(false);
|
||||
expect(reachable(cfg, block(cfg, 'done()'))).toBe(true); // via the if false branch
|
||||
});
|
||||
|
||||
it('multi-catch: EVERY catch handler is reachable from ENTRY (#2195)', () => {
|
||||
// The protected body can throw an error matching ANY clause, so the 2nd..Nth
|
||||
// catch must not be orphaned — the bug routed the throw edge only to the
|
||||
// first handler, leaving later handlers unreachable from ENTRY.
|
||||
const cfg = swift.cfgOf(`func f() { do { try r() } catch A { ha() } catch { hb() } }`);
|
||||
expect(reachable(cfg, block(cfg, 'ha()'))).toBe(true);
|
||||
expect(reachable(cfg, block(cfg, 'hb()'))).toBe(true);
|
||||
// the protected `try r()` reaches both handlers.
|
||||
expect(reaches(cfg, block(cfg, 'try r()'), block(cfg, 'hb()'))).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Swift CfgVisitor — defer (LIFO scope-exit)', () => {
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue