~/bend-docscommunity

src/rules/suspicious/hoist.bend source

src/rules/suspicious/hoist.bend on the hub · documented module

# rule hoist: a fixed table built inside a def that calls itself, from inputs# that do not change, and then indexed. The build runs again on every step.# Build it once, outside the recursion. A build is a list or array literal,# `List.replicate` / `List.range` / `List.map` / `Array.new` / `Array.map`, or# a call to a def of this file whose body is itself one fixed table of more# than eight cells; a def whose body cannot be seen (another module's) is not# a build. A table of eight cells or fewer is left alone, and so is a build# whose arguments depend on the step. A name that is not a carried parameter# (a pattern binder, a value computed on the step) keeps the build; a case arm# that does not recurse is cold; laws and proofs do not run (a def with no# type at all fills a law: it is a proof). `List.get` itself stays with# `index`.import Baseimport ../../src.bend as Srcimport ../../finding.bend as Fimport ../../syntax/lex.bend as Leximport ../../syntax/tree.bend as Treeimport ../calls.bend as Callsimport ../../lazy/lazy.bend as Lazyimport ./table.bend as Table# where an accessor reads its collection, when it is onetype Slot is Data:  Slot{on: Bool, at: Nat}# a `name = expr` whose left side is one plain nametype Got is Data:  Got{nm: String, expr: Tree.Node}# which argument a get or set indexesdef slot_pick(+list: Bool, +str: Bool, +arr: Bool) -> Slot:  match list str arr:    case True{} b c:      Slot{True{}, 2n}    case False{} True{} c:      Slot{True{}, 0n}    case False{} False{} True{}:      Slot{True{}, 1n}    case False{} False{} False{}:      Slot{False{}, 0n}# List.get / List.set read argument 2, String.get argument 0, Array.get / Array.set argument 1def slot_of(+tt: String) -> Slot:  slot_pick(    Bool.or(String.eq(tt, "List.get"), String.eq(tt, "List.set")),    String.eq(tt, "String.get"),    Bool.or(String.eq(tt, "Array.get"), String.eq(tt, "Array.set")))# the self-calls' argument listsdef pushes(nn: Tree.Node, +self: String) -> List<&2, List<&2, Tree.Node>>:  match nn:    case Tree.NCons{Tree.Leaf{Lex.Tok{k, +t, l, c}}, Tree.NCons{Tree.Group{Lex.Tok{_, +o, _, _}, +kids, _}, rest}}:      +me = Bool.and(String.eq(o, "("), String.eq(t, self))      +more = List.concat(&2, List<&2, Tree.Node>, [pushes(kids, self), pushes(rest, self)])      Bool.pick(List<&2, List<&2, Tree.Node>>, me, Calls.args(kids) <> more, more)    case Tree.NCons{Tree.Group{open, +kids, close}, rest}:      List.concat(&2, List<&2, Tree.Node>, [pushes(kids, self), pushes(rest, self)])    case Tree.NCons{Tree.Stmt{kind, +kids, body}, rest}:      List.concat(&2, List<&2, Tree.Node>, [pushes(kids, self), pushes(body, self), pushes(rest, self)])    case Tree.NCons{h, rest}:      pushes(rest, self)    case other:      Nil{}# is the chain exactly this name?def lone_eq(nn: Tree.Node, +name: String) -> Bool:  match nn:    case Tree.NCons{Tree.Leaf{Lex.Tok{Lex.TName{}, +t, l, c}}, Tree.NNil{}}:      String.eq(t, name)    case other:      False{}# every self-call passes this same name at this argumentdef all_same(cs: List<&2, List<&2, Tree.Node>>, +ii: Nat, +name: String) -> Bool:  match cs:    case Nil{}:      True{}    case Con{as, rest}:      +here = lone_eq(Calls.arg(as, ii), name)      +more = all_same(rest, ii, name)      Bool.and(here, more)# parameters every self-call passes unchanged, when there is a self-calldef carried.go(  ps: List<&2, Tree.Node>,  +cs: List<&2, List<&2, Tree.Node>>,  +ii: Nat,  +acc: List<&2, String>) -> List<&2, String>:  match ps:    case Nil{}:      List.reverse(&2, String, acc)    case Con{p, rest}:      +nm = Calls.param_name(p)      +keep = Bool.and(Bool.not(String.is_empty(nm)), all_same(cs, ii, nm))      carried.go(rest, cs, (ii + 1n : Nat), Bool.pick(List<&2, String>, keep, nm <> acc, acc))# parameters passed through unchanged; empty when nothing recursesdef carried_of(cs: List<&2, List<&2, Tree.Node>>, +sig: Tree.Node) -> List<&2, String>:  match cs:    case Nil{}:      Nil{}    case Con{h, t}:      carried.go(Calls.params(sig), h <> t, 0n, [])# parameters passed through unchanged; empty when nothing recursesdef carried(sig: Tree.Node, body: Tree.Node, +self: String) -> List<&2, String>:  carried_of(pushes(body, self), sig)# a plain name is closed only when it is a carried parameter; a type or a dotted name isdef name_ok(kk: Lex.TokKind, +tt: String, +carried: List<&2, String>) -> Bool:  match kk:    case Lex.TName{}:      List.contains(~String, ~String.eq, carried, tt)    case other:      True{}# a token after an operator: a template argument is not a value, anything else isdef leaf_ok(+skip: Bool, tok: Lex.Tok, +carried: List<&2, String>) -> Bool:  match skip:    case True{}:      True{}    case False{}:      Lex.Tok{+k, +t, l, c} = tok      name_ok(k, t, carried)# an application is closed in its arguments; a bracketed name is a valuedef value_ok(+app: Bool, kk: Lex.TokKind, +tt: String, +carried: List<&2, String>) -> Bool:  match app:    case True{}:      True{}    case False{}:      name_ok(kk, tt, carried)# every value name under the node is a carried parameter (callees and templates are not values)def closed(nn: Tree.Node, +carried: List<&2, String>) -> Bool:  match nn:    case Tree.NCons{Tree.Leaf{Lex.Tok{Lex.TOp{}, +op, l, c}}, Tree.NCons{Tree.Leaf{+tok}, tail}}:      +ok = leaf_ok(String.eq(op, "~"), tok, carried)      +aft = closed(tail, carried)      Bool.and(ok, aft)    case Tree.NCons{Tree.Leaf{Lex.Tok{Lex.TOp{}, op, l, c}}, rest}:      closed(rest, carried)    case Tree.NCons{Tree.Leaf{Lex.Tok{+k, +t, l, c}}, Tree.NCons{Tree.Group{Lex.Tok{_, +o, _, _}, +kids, _}, rest}}:      +ok = value_ok(String.eq(o, "("), k, t, carried)      +inn = closed(kids, carried)      +aft = closed(rest, carried)      Bool.and(ok, Bool.and(inn, aft))    case Tree.NCons{Tree.Leaf{Lex.Tok{Lex.TName{}, +t, l, c}}, rest}:      +ok = List.contains(~String, ~String.eq, carried, t)      +aft = closed(rest, carried)      Bool.and(ok, aft)    case Tree.NCons{Tree.Leaf{tok}, rest}:      closed(rest, carried)    case Tree.NCons{Tree.Group{open, +kids, close}, rest}:      +inn = closed(kids, carried)      +aft = closed(rest, carried)      Bool.and(inn, aft)    case Tree.NCons{Tree.Stmt{kind, +kids, body}, rest}:      +inn = closed(kids, carried)      +bod = closed(body, carried)      +aft = closed(rest, carried)      Bool.and(inn, Bool.and(bod, aft))    case Tree.NCons{h, rest}:      closed(rest, carried)    case Tree.Leaf{Lex.Tok{Lex.TName{}, +t, l, c}}:      List.contains(~String, ~String.eq, carried, t)    case other:      True{}# a top-level token of this text, not one nested in a groupdef top_mark(nn: Tree.Node, +want: String) -> Bool:  match nn:    case Tree.NCons{Tree.Group{open, kids, close}, rest}:      top_mark(rest, want)    case Tree.NCons{Tree.Leaf{Lex.Tok{k, +t, l, c}}, rest}:      +more = top_mark(rest, want)      Bool.or(String.eq(t, want), more)    case Tree.NCons{h, rest}:      top_mark(rest, want)    case other:      False{}# `[v : T*n]` or `[v : T^d]`: an array, not a listdef is_array(+kids: Tree.Node) -> Bool:  +col = top_mark(kids, ":")  +star = top_mark(kids, "*")  +hat = top_mark(kids, "^")  Bool.and(col, Bool.or(star, hat))# how many top-level copies of this tokendef top_count(nn: Tree.Node, +want: String, +got: Nat) -> Nat:  match nn:    case Tree.NCons{Tree.Group{open, kids, close}, rest}:      top_count(rest, want, got)    case Tree.NCons{Tree.Leaf{Lex.Tok{k, +t, l, c}}, rest}:      +next = Bool.pick(Nat, String.eq(t, want), (got + 1n : Nat), got)      top_count(rest, want, next)    case Tree.NCons{h, rest}:      top_count(rest, want, got)    case other:      got# a number literal of eight or fewerdef small_num(+tt: String) -> Bool:  List.contains(~String, ~String.eq,    ["0", "1", "2", "3", "4", "5", "6", "7", "8", "0n", "1n", "2n", "3n", "4n", "5n", "6n", "7n", "8n"], tt)# the size after `*` is a literal of eight or fewer, or the depth after `^` (2^d slots) three or fewerdef small_size(nn: Tree.Node) -> Bool:  match nn:    case Tree.NCons{Tree.Leaf{Lex.Tok{Lex.TOp{}, +op, l, c}},        Tree.NCons{Tree.Leaf{Lex.Tok{Lex.TNum{}, +t, l2, c2}}, rest}}:      +here = Bool.or(Bool.and(String.eq(op, "*"), small_num(t)),        Bool.and(String.eq(op, "^"), List.contains(~String, ~String.eq, ["0", "1", "2", "3", "0n", "1n", "2n", "3n"], t)))      +more = small_size(rest)      Bool.or(here, more)    case Tree.NCons{Tree.Group{open, kids, close}, rest}:      small_size(rest)    case Tree.NCons{h, rest}:      small_size(rest)    case other:      False{}# more than eight cells: nine or more list elements, or an array whose size is not that smalldef wide_shape(+arr: Bool, +kids: Tree.Node) -> Bool:  match arr:    case True{}:      Bool.not(small_size(kids))    case False{}:      Nat.is_gt(top_count(kids, ",", 0n), 7n)# constructors that build a fixed table rather than walk onedef allowed(+tt: String) -> Bool:  List.contains(~String, ~String.eq,    ["List.replicate", "List.range", "List.map", "Array.new", "Array.map"], tt)# a Base constructor on the allow list, or a def of this file whose body is# itself a wide table (`wides`); a def whose body cannot be seen is not onedef builder(kk: Lex.TokKind, +tt: String, +self: String, +wides: List<&2, String>) -> Bool:  match kk:    case Lex.TName{}:      Bool.and(Bool.not(String.eq(tt, self)), List.contains(~String, ~String.eq, wides, tt))    case Lex.TDotted{}:      Bool.or(allowed(tt), List.contains(~String, ~String.eq, wides, tt))    case other:      False{}# a bracket group that is a closed list or array of more than eight cellsdef group_yes(+arr: Bool, +kids: Tree.Node, +carried: List<&2, String>) -> Bool:  +shape = Bool.or(top_mark(kids, ","), arr)  +ok = closed(kids, carried)  +wide = wide_shape(arr, kids)  Bool.and(shape, Bool.and(ok, wide))# a bracket group that is a closed list or arraydef table_group(+brack: Bool, +kids: Tree.Node, +carried: List<&2, String>) -> Bool:  match brack:    case False{}:      False{}    case True{}:      group_yes(is_array(kids), kids, carried)# the chain is exactly one number; its text, or emptydef num_text(nn: Tree.Node) -> String:  match nn:    case Tree.NCons{Tree.Leaf{Lex.Tok{Lex.TNum{}, +t, l, c}}, Tree.NNil{}}:      t    case other:      ""# a count is wide when it is not a literal of eight or fewer (a name may be large)def count_wide(aa: Tree.Node) -> Bool:  Bool.not(small_num(num_text(aa)))# the last argumentdef last_node(as: List<&2, Tree.Node>) -> Tree.Node:  match as:    case Nil{}:      Tree.NNil{}    case Con{h, Nil{}}:      h    case Con{h, rest}:      last_node(rest)# `List.replicate` / `Array.new` take argument 2; anything else is wide unless it is `List.range`def call_arm(rep: Bool, +as: List<&2, Tree.Node>, +end: Tree.Node, +range: Bool) -> Bool:  match rep:    case True{}:      count_wide(Calls.arg(as, 2n))    case False{}:      Bool.pick(Bool, range, count_wide(end), True{})# `List.replicate` / `Array.new` / `List.range` are wide unless their count is a small literaldef call_wide(+tt: String, kids: Tree.Node) -> Bool:  +as = Calls.args(kids)  call_arm(Bool.or(String.eq(tt, "List.replicate"), String.eq(tt, "Array.new")), as, last_node(as), String.eq(tt, "List.range"))# a call that builds a table from closed arguments, and is not a small literal countdef table_built(+ok: Bool, +wide: Bool, kids: Tree.Node, +carried: List<&2, String>) -> Bool:  match ok wide:    case True{} True{}:      closed(kids, carried)    case True{} False{}:      False{}    case False{} b:      False{}# a call that builds a table from closed argumentsdef table_call(  +app: Bool,  kk: Lex.TokKind,  +tt: String,  +kids: Tree.Node,  +self: String,  +wides: List<&2, String>,  +carried: List<&2, String>) -> Bool:  match app:    case False{}:      False{}    case True{}:      table_built(builder(kk, tt, self, wides), call_wide(tt, kids), kids, carried)# a fixed table of more than eight cells, whatever its cells readdef wide_of(nn: Tree.Node) -> Bool:  match nn:    case Tree.NCons{Tree.Group{Lex.Tok{k, +o, l, c}, +kids, close}, Tree.NNil{}}:      wide_shape(is_array(kids), kids)    case Tree.NCons{Tree.Leaf{Lex.Tok{k, +t, l, c}},        Tree.NCons{Tree.Group{Lex.Tok{_, +o, _, _}, +kids, _}, Tree.NNil{}}}:      call_wide(t, kids)    case other:      False{}# a def whose body is one wide fixed table (`Table.built`, and more than eight cells)def wide_one(body: Tree.Node, +name: String, +acc: List<&2, String>) -> List<&2, String>:  match body:    case Tree.NCons{Tree.Stmt{kind, +kids, inn}, Tree.NNil{}}:      Bool.pick(List<&2, String>, Bool.and(Table.built(kids), wide_of(kids)), name <> acc, acc)    case other:      acc# defs of this file whose body is one wide fixed tabledef wide_defs(ds: List<&2, Calls.Def>, acc: List<&2, String>) -> List<&2, String>:  match ds:    case Nil{}:      acc    case Con{Calls.Def{+name, sig, +body}, rest}:      wide_defs(rest, wide_one(body, name, acc))# a list, an array, or a builder call, closed over carried parametersdef is_table(nn: Tree.Node, +self: String, +wides: List<&2, String>, +carried: List<&2, String>) -> Bool:  match nn:    case Tree.NCons{Tree.Group{Lex.Tok{k, +o, l, c}, +kids, close}, Tree.NNil{}}:      table_group(String.eq(o, "["), kids, carried)    case Tree.NCons{Tree.Leaf{Lex.Tok{+k, +t, l, c}},        Tree.NCons{Tree.Group{Lex.Tok{_, +o, _, _}, +kids, _}, Tree.NNil{}}}:      table_call(String.eq(o, "("), k, t, kids, self, wides, carried)    case other:      False{}# drop the right-hand sidedef skip_expr(nn: Tree.Node) -> Maybe<&2, Got>:  match nn:    case other:      None{}# the chain after `=`, when the left side is one plain name and nothing elsedef got_bad(+bad: Bool, +nm: String, expr: Tree.Node) -> Maybe<&2, Got>:  match bad:    case True{}:      skip_expr(expr)    case False{}:      Some{Got{nm, expr}}# the chain after `=`, when exactly one plain name stands before itdef got_at(+seen: Bool, +bad: Bool, +nm: String, expr: Tree.Node) -> Maybe<&2, Got>:  match seen:    case False{}:      skip_expr(expr)    case True{}:      got_bad(bad, nm, expr)# a second name, a constructor or a group on the left is not a plain binderdef eat(nn: Tree.Node, +seen: Bool, +bad: Bool, +nm: String) -> Maybe<&2, Got>:  match nn:    case Tree.NCons{Tree.Leaf{Lex.Tok{Lex.TEq{}, t, l, c}}, rest}:      got_at(seen, bad, nm, rest)    case Tree.NCons{Tree.Leaf{Lex.Tok{Lex.TName{}, +t, l, c}}, rest}:      eat(rest, True{}, Bool.or(bad, seen), t)    case Tree.NCons{Tree.Leaf{Lex.Tok{Lex.TUpper{}, t, l, c}}, rest}:      eat(rest, seen, True{}, nm)    case Tree.NCons{Tree.Group{open, kids, close}, rest}:      eat(rest, seen, True{}, nm)    case Tree.NCons{h, rest}:      eat(rest, seen, bad, nm)    case other:      None{}# a finding on the table expressiondef cite(+nn: Tree.Node, +path: String) -> List<&2, F.Finding>:  [F.Finding{path, Tree.line(nn), Tree.col(nn), U32.from_nat(String.length(Tree.text(nn))), "hoist",    "This table is rebuilt on every step from inputs that do not change; build it once, outside the recursion."}]# a name, and nothing after itdef wrapped_inside(nn: Tree.Node, +binder: String) -> Bool:  match nn:    case Tree.NCons{Tree.Leaf{Lex.Tok{Lex.TName{}, +t, l, c}}, Tree.NNil{}}:      String.eq(t, binder)    case other:      False{}# inside one pair of parenthesesdef wrapped_open(+paren: Bool, kids: Tree.Node, +binder: String) -> Bool:  match paren:    case True{}:      wrapped_inside(kids, binder)    case False{}:      False{}# the chain is exactly this name, or that name in parenthesesdef wrapped(nn: Tree.Node, +binder: String) -> Bool:  match nn:    case Tree.NCons{Tree.Leaf{Lex.Tok{Lex.TName{}, +t, l, c}}, Tree.NNil{}}:      String.eq(t, binder)    case Tree.NCons{Tree.Group{Lex.Tok{k, +o, l, c}, +kids, close}, Tree.NNil{}}:      wrapped_open(String.eq(o, "("), kids, binder)    case other:      False{}# the collection argument is this namedef uses_arg(ss: Slot, +as: List<&2, Tree.Node>, +binder: String) -> Bool:  Slot{on, at} = ss  match on:    case False{}:      False{}    case True{}:      wrapped(Calls.arg(as, at), binder)# `name[` is an index; a call is an index when its collection argument is the namedef uses_here_call(+call: Bool, +tt: String, kids: Tree.Node, +binder: String) -> Bool:  match call:    case False{}:      False{}    case True{}:      uses_arg(slot_of(tt), Calls.args(kids), binder)# `name[` is an index; a call is an index when its collection argument is the namedef uses_here(+brack: Bool, +call: Bool, +tt: String, kids: Tree.Node, +binder: String) -> Bool:  match brack:    case True{}:      String.eq(tt, binder)    case False{}:      uses_here_call(call, tt, kids, binder)# the name is indexed later: `name[`, or the collection of a get or setdef uses_name(nn: Tree.Node, +binder: String) -> Bool:  match nn:    case Tree.NCons{Tree.Leaf{Lex.Tok{k, +t, l, c}}, Tree.NCons{Tree.Group{Lex.Tok{_, +o, _, _}, +kids, _}, rest}}:      +here = uses_here(String.eq(o, "["), String.eq(o, "("), t, kids, binder)      +inn = uses_name(kids, binder)      +aft = uses_name(rest, binder)      Bool.or(here, Bool.or(inn, aft))    case Tree.NCons{Tree.Group{open, +kids, close}, rest}:      +inn = uses_name(kids, binder)      +aft = uses_name(rest, binder)      Bool.or(inn, aft)    case Tree.NCons{Tree.Stmt{kind, +kids, body}, rest}:      +inn = uses_name(kids, binder)      +bod = uses_name(body, binder)      +aft = uses_name(rest, binder)      Bool.or(inn, Bool.or(bod, aft))    case Tree.NCons{h, rest}:      uses_name(rest, binder)    case other:      False{}# indexed in the let's body or in a following statementdef uses_or(body: Tree.Node, rest: Tree.Node, +binder: String) -> Bool:  +inn = uses_name(body, binder)  +aft = uses_name(rest, binder)  Bool.or(inn, aft)# a finding when the table is also indexeddef let_used(+used: Bool, +expr: Tree.Node, +path: String) -> List<&2, F.Finding>:  match used:    case True{}:      cite(expr, path)    case False{}:      Nil{}# a finding when the right-hand side is a closed table that is indexeddef let_use(+tab: Bool, +used: Bool, +expr: Tree.Node, +path: String) -> List<&2, F.Finding>:  match tab:    case False{}:      Nil{}    case True{}:      let_used(used, expr, path)# a plain let of a tabledef let_of(  mm: Maybe<&2, Got>,  +body: Tree.Node,  +rest: Tree.Node,  +self: String,  +wides: List<&2, String>,  +carried: List<&2, String>,  +path: String) -> List<&2, F.Finding>:  match mm:    case None{}:      Nil{}    case Some{Got{+nm, +expr}}:      let_use(is_table(expr, self, wides, carried), uses_or(body, rest, nm), expr, path)# a hot plain let of a table that is indexed afterwardsdef let_hit(  +hot: Bool,  kids: Tree.Node,  +body: Tree.Node,  +rest: Tree.Node,  +self: String,  +wides: List<&2, String>,  +carried: List<&2, String>,  +path: String) -> List<&2, F.Finding>:  match hot:    case False{}:      Nil{}    case True{}:      let_of(eat(kids, False{}, False{}, ""), body, rest, self, wides, carried, path)# a finding when this argument is itself a closed tabledef one_yes(+yes: Bool, +aa: Tree.Node, +path: String) -> List<&2, F.Finding>:  match yes:    case True{}:      cite(aa, path)    case False{}:      Nil{}# the collection argument, when this call indexes onedef arg_table(  ss: Slot,  +as: List<&2, Tree.Node>,  +self: String,  +wides: List<&2, String>,  +carried: List<&2, String>,  +path: String) -> List<&2, F.Finding>:  Slot{on, at} = ss  match on:    case False{}:      Nil{}    case True{}:      +aa = Calls.arg(as, at)      one_yes(is_table(aa, self, wides, carried), aa, path)# an inline table passed straight to a get or set, when the group is a calldef call_open(  +open: Bool,  +tt: String,  kids: Tree.Node,  +self: String,  +wides: List<&2, String>,  +carried: List<&2, String>,  +path: String) -> List<&2, F.Finding>:  match open:    case False{}:      Nil{}    case True{}:      arg_table(slot_of(tt), Calls.args(kids), self, wides, carried, path)# an inline table passed straight to a get or setdef call_table(  +hot: Bool,  +open: Bool,  +tt: String,  kids: Tree.Node,  +self: String,  +wides: List<&2, String>,  +carried: List<&2, String>,  +path: String) -> List<&2, F.Finding>:  match hot:    case False{}:      Nil{}    case True{}:      call_open(open, tt, kids, self, wides, carried, path)# a `=>` cools the rest of its chain: a lambda's body runs when it is called, not per stepdef warm(+hot: Bool, kk: Lex.TokKind) -> Bool:  match kk:    case Lex.TLam{}:      False{}    case other:      hot# tables built in a hot regiondef walk(  nn: Tree.Node,  +self: String,  +wides: List<&2, String>,  +carried: List<&2, String>,  +path: String,  +hot: Bool) -> List<&2, F.Finding>:  match nn:    case Tree.NCons{Tree.Leaf{Lex.Tok{k, +t, l, c}}, Tree.NCons{Tree.Group{Lex.Tok{_, +o, _, _}, +kids, _}, rest}}:      +heat = warm(hot, k)      +own = call_table(heat, String.eq(o, "("), t, kids, self, wides, carried, path)      +inn = walk(kids, self, wides, carried, path, heat)      +aft = walk(rest, self, wides, carried, path, heat)      List.concat(&2, F.Finding, [own, inn, aft])    case Tree.NCons{Tree.Leaf{Lex.Tok{k, t, l, c}}, rest}:      walk(rest, self, wides, carried, path, warm(hot, k))    case Tree.NCons{Tree.Group{open, +kids, close}, rest}:      List.concat(&2, F.Finding,        [walk(kids, self, wides, carried, path, hot), walk(rest, self, wides, carried, path, hot)])    case Tree.NCons{Tree.Stmt{Tree.SCase{}, kids, +body}, rest}:      +arm = Bool.and(hot, Calls.calls(body, self))      List.concat(&2, F.Finding, [walk(kids, self, wides, carried, path, False{}),        walk(body, self, wides, carried, path, arm), walk(rest, self, wides, carried, path, hot)])    case Tree.NCons{Tree.Stmt{kind, +kids, +body}, +rest}:      +own = let_hit(hot, kids, body, rest, self, wides, carried, path)      List.concat(&2, F.Finding, [own, walk(kids, self, wides, carried, path, hot),        walk(body, self, wides, carried, path, hot), walk(rest, self, wides, carried, path, hot)])    case Tree.NCons{h, rest}:      walk(rest, self, wides, carried, path, hot)    case other:      Nil{}def check.go(  ds: List<&2, Calls.Def>,  +wide: List<&2, String>,  +path: String,  acc: List<&2, List<&2, F.Finding>>) -> List<&2, F.Finding>:  match ds:    case Nil{}:      List.concat(&2, F.Finding, List.reverse(&2, List<&2, F.Finding>, acc))    case Con{Calls.Def{+name, +sig, +body}, rest}:      check.go(rest, wide, path,        Lazy.stop(List<&2, F.Finding>,          Bool.not(Bool.and(Calls.calls(body, name), Bool.not(Calls.exempt(path, sig)))), [],          _u => walk(body, name, wide, carried(sig, body, name), path, True{})) <> acc)# the ruledef check(ss: Src.Src) -> List<&2, F.Finding>:  Src.Src{path, text, toks, tree, bound, items} = ss  +ds = Calls.defs(tree)  check.go(ds, wide_defs(ds, []), path, [])