Found while profiling bolt lint (branch autoresearch/bolt-lint-perf).
Shape
pick tells you to "bind the call once above the pick (+more = go(rest))". That's right for a filter, where both branches use more. For a first-match search, though, the then-branch never needs more, and binding it just moves the full walk out of the rule's sight:
def deps_of(es: List<&2, Edge>, +pp: String) -> List<&2, String>:
match es:
case Con{Edge{+ep, ds}, rest}:
+more = deps_of(rest, pp)
Bool.pick(List<&2, String>, String.eq(ep, pp), ds, more) # the hit at the head still walks to the end
The same shape appears in Bind.find (the environment search) and Laws.res.all-style searches. pick's own description names this bug ("a search never stops early"), but it only catches the unbound form.
Cost here
Making deps_of stop early (Lazy.stop(.., hit, ds, _u => deps_of(rest, pp))) took coverage over bolt's tree from 10.2 s to 9.4 s on its own. It was one of the lookups in the closure-walk fix (10.2 s to 2.75 s overall). Honestly the gain depends on the data: the same change to Bind.find measured nothing, because environments are short.
Proposal
Report +x = self(..) where x is read only in the else branch of a Bool.pick that follows it (the then-branch doesn't mention x). Suggest Lazy.stop(T, hit, value, _u => self(..)). A filter (d <> more, more) uses x in both branches, so it's never reported.
Found while profiling bolt lint (branch
autoresearch/bolt-lint-perf).Shape
picktells you to "bind the call once above the pick (+more = go(rest))". That's right for a filter, where both branches usemore. For a first-match search, though, the then-branch never needsmore, and binding it just moves the full walk out of the rule's sight:The same shape appears in
Bind.find(the environment search) andLaws.res.all-style searches.pick's own description names this bug ("a search never stops early"), but it only catches the unbound form.Cost here
Making
deps_ofstop early (Lazy.stop(.., hit, ds, _u => deps_of(rest, pp))) took coverage over bolt's tree from 10.2 s to 9.4 s on its own. It was one of the lookups in the closure-walk fix (10.2 s to 2.75 s overall). Honestly the gain depends on the data: the same change toBind.findmeasured nothing, because environments are short.Proposal
Report
+x = self(..)wherexis read only in the else branch of aBool.pickthat follows it (the then-branch doesn't mentionx). SuggestLazy.stop(T, hit, value, _u => self(..)). A filter (d <> more, more) usesxin both branches, so it's never reported.