Python: preserve bindingset on new-CFG node dominance wrappers

The `Cfg::ControlFlowNode` facade re-exports the shared CFG library's
`dominates`/`strictlyDominates` predicates, which are declared
`bindingset[this, that]` + `pragma[inline_late]` and are meant to be used
as bound-pair membership checks. The facade wrappers dropped these
annotations (using plain `pragma[inline]`), so even though the only
callers — the `with` / `async with` taint steps in DataFlowPrivate.qll
and TaintTrackingPrivate.qll — bind both endpoints, the optimizer was
free to materialise `Cfg::ControlFlowNode.strictlyDominates/1` as a full
O(nodes^2) relation over the (larger) shared-CFG node set.

On some projects this dominated analysis time entirely (DCA showed e.g.
ICTU/quality-time and biosimulations regressing ~75-160x). Restoring
`bindingset[this, other]` + `pragma[inline_late]` on the wrappers turns
the predicate back into a bound-pair check and is result-preserving (only
binding annotations change, the predicate body is unchanged).

Reproduced on ICTU/quality-time: full python-security-extended suite went
from stalling >20min on `strictlyDominates` to completing in ~6min; all
ControlFlow and dataflow/coverage library tests pass.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
yoff
2026-07-02 15:15:28 +00:00
parent b7f01fb510
commit c2f439a38f

View File

@@ -110,11 +110,13 @@ class ControlFlowNode extends CfgImpl::ControlFlowNode {
}
/** Holds if this strictly dominates `other`. */
pragma[inline]
bindingset[this, other]
pragma[inline_late]
predicate strictlyDominates(ControlFlowNode other) { super.strictlyDominates(other) }
/** Holds if this dominates `other` (reflexively). */
pragma[inline]
bindingset[this, other]
pragma[inline_late]
predicate dominates(ControlFlowNode other) { super.dominates(other) }
/** Holds if this is the first node in its enclosing scope. */