Found while profiling bolt lint (autoresearch branch autoresearch/bolt-lint-perf, autoresearch/loop-260924-2130/summary.md). This was the largest single cost in the run, and no current rule sees it.
Shape
A recursive def walks list A and carries list B through unchanged (+uses, +read, +seen). On every step it passes B to a walk: a looping def of the file, or a Base walk such as List.contains or List.find. That's O(|A|·|B|), and the output is still right.
def check.go(binds: List<&2, Bind.Bind>, +uses: List<&2, Bind.Use>, ..) -> List<&2, F.Finding>:
match binds:
case Con{Bind.Bind{+name, +line, +col, +kind, note}, rest}:
+more = check.go(rest, uses, fl, path)
... Bool.not(used(uses, line, col)) ... # a scan of every use, per binder
Cost here
unused on bolt/rules/PROOF.bend: 22k binders × 94k uses took 135 s of a 208 s run. With a trie of use targets as a sound fast path (a scan only on a miss), it takes 0.4 s.
- The coverage/unsafe closure's
fresh asked has(read, d) of every file for each edge. Asking the short lists first and stopping early took coverage from 10.2 s to 2.75 s.
Proposed rule (suspicious)
Report a def that calls itself and, in the same step, passes one of its parameters to a walk, where that parameter goes into the self-call unchanged (the same name, in the same slot). The rule would be quiet in these cases:
- the carried list is a literal, or a fixed table (that's
table/hoist territory);
- laws and proofs;
- the call sits in a thunk (
_u => ..) that only runs on a rare branch;
- possibly, a size hint in the def says the list is small.
Message idea: "used walks uses on every step of check.go, which carries it unchanged: that's |binds|·|uses|. Index it once outside the loop, or merge two sorted walks."
A new code (U013?) under suspicious.
Found while profiling bolt lint (autoresearch branch
autoresearch/bolt-lint-perf,autoresearch/loop-260924-2130/summary.md). This was the largest single cost in the run, and no current rule sees it.Shape
A recursive def walks list A and carries list B through unchanged (
+uses,+read,+seen). On every step it passes B to a walk: a looping def of the file, or a Base walk such asList.containsorList.find. That's O(|A|·|B|), and the output is still right.Cost here
unusedonbolt/rules/PROOF.bend: 22k binders × 94k uses took 135 s of a 208 s run. With a trie of use targets as a sound fast path (a scan only on a miss), it takes 0.4 s.freshaskedhas(read, d)of every file for each edge. Asking the short lists first and stopping early took coverage from 10.2 s to 2.75 s.Proposed rule (suspicious)
Report a def that calls itself and, in the same step, passes one of its parameters to a walk, where that parameter goes into the self-call unchanged (the same name, in the same slot). The rule would be quiet in these cases:
table/hoistterritory);_u => ..) that only runs on a rare branch;Message idea: "
usedwalksuseson every step ofcheck.go, which carries it unchanged: that's |binds|·|uses|. Index it once outside the loop, or merge two sorted walks."A new code (U013?) under
suspicious.