Refuse a selector on a template before its level fence, give pathCheck one entry over its own tail, and scope the probe decision to descend

This commit is contained in:
2026-09-28 00:36:33 +02:00
committed by Lillem4n
parent d95045f4ac
commit 060fb42fee
3 changed files with 12 additions and 7 deletions
+5 -5
View File
@@ -688,8 +688,8 @@ though `hasRead` states the same stop rule. Valid while the two differ in where
2026-09-28, larv-review on systems-architect's finding; awaiting approval. Goals 2 and 2026-09-28, larv-review on systems-architect's finding; awaiting approval. Goals 2 and
11: `pathCheck`, `pathCover`, `pathProbe` and `pathDraw` each loop over `stepInto` and 11: `pathCheck`, `pathCover`, `pathProbe` and `pathDraw` each loop over `stepInto` and
`table.route` themselves, and `descend` and `tableReadOf` build their probe in place, so `table.route` themselves, and `descend` builds its probe in place, so the probe's
its `drawn` map stays on the stack. One walk calling them through an interface or a type parameter `drawn` map stays on the stack. One walk calling them through an interface or a type
leaks the walker and the pins it holds, an allocation per reference read. Valid while Go's parameter leaks the walker and the pins it holds, an allocation per reference read.
escape analysis treats a call through an interface or a type parameter as leaking its Valid while Go's escape analysis treats a call through an interface or a type parameter
receiver. as leaking its receiver.
+1 -1
View File
@@ -165,7 +165,7 @@ func renderEdges(n node) []renderEdge {
// already proved the tail resolves in every variant. // already proved the tail resolves in every variant.
func pathLeaves(n node, tail []string) []node { func pathLeaves(n node, tail []string) []node {
w := &pathCheck{tail: tail} w := &pathCheck{tail: tail}
_, _ = w.walk(n, tail) _, _ = w.run(n)
return w.leaves return w.leaves
} }
+6 -1
View File
@@ -196,6 +196,8 @@ type pathCheck struct {
leaves []node leaves []node
} }
func (w *pathCheck) run(n node) (node, error) { return w.walk(n, w.tail) }
func (w *pathCheck) walk(n node, tail []string) (node, error) { func (w *pathCheck) walk(n node, tail []string) (node, error) {
for descended := false; len(tail) > 0 || descended; { for descended := false; len(tail) > 0 || descended; {
var err error var err error
@@ -215,6 +217,9 @@ func (w *pathCheck) walk(n node, tail []string) (node, error) {
n, tail, descended = r.next, r.rest, r.descends n, tail, descended = r.next, r.rest, r.descends
continue continue
case *template: case *template:
if isSelector(tail[0]) {
break
}
if err := w.enter(x, tail); err != nil { if err := w.enter(x, tail); err != nil {
return nil, err return nil, err
} }
@@ -452,6 +457,6 @@ func unreachableInChoice(c *choice, want string) error {
// checkPath proves a dotted tail as pathCheck does, so a path that validates here // checkPath proves a dotted tail as pathCheck does, so a path that validates here
// resolves on every render, and a typo is a New-time error. // resolves on every render, and a typo is a New-time error.
func checkPath(n node, tail []string, level string) error { func checkPath(n node, tail []string, level string) error {
_, err := (&pathCheck{level: level, tail: tail}).walk(n, tail) _, err := (&pathCheck{level: level, tail: tail}).run(n)
return err return err
} }