Cover a calc operand by what it renders
The fence now asks the right question for each kind of hold. A path head keeps containment, since a path may read into anything the level carries. An operand head takes the render closure, because a calc renders its field and that draw fixes only the value the render produces. That stops rejecting a reference to a sibling the operand never renders, which had loaded fine before the fence widened, and starts rejecting a reference that reaches what the operand renders through. A head whose cover is empty — a fixed string — skips the walk. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
+33
-2
@@ -60,8 +60,19 @@ func checkBoundLevelsHeld(root map[string]node) error {
|
|||||||
}
|
}
|
||||||
sort.Strings(heads) // so which overlap is reported does not vary
|
sort.Strings(heads) // so which overlap is reported does not vary
|
||||||
for _, head := range heads {
|
for _, head := range heads {
|
||||||
|
// What one draw answers for depends on how the draw is read. A path may
|
||||||
|
// read into anything the level contains; a calc renders its operand, so
|
||||||
|
// that draw fixes exactly the value the render produces.
|
||||||
held := map[node]bool{}
|
held := map[node]bool{}
|
||||||
|
reader, isPath := t.bound[head]
|
||||||
|
if isPath {
|
||||||
cover(t.fields[head], held)
|
cover(t.fields[head], held)
|
||||||
|
} else {
|
||||||
|
coverRendered(t.fields[head], held)
|
||||||
|
}
|
||||||
|
if len(held) == 0 {
|
||||||
|
continue // a fixed string, which cannot disagree with itself
|
||||||
|
}
|
||||||
// One seen set across the edges: a node that cannot reach the level
|
// One seen set across the edges: a node that cannot reach the level
|
||||||
// cannot reach it by another route either, so it is walked once here.
|
// cannot reach it by another route either, so it is walked once here.
|
||||||
seen := map[node]bool{}
|
seen := map[node]bool{}
|
||||||
@@ -70,10 +81,10 @@ func checkBoundLevelsHeld(root map[string]node) error {
|
|||||||
continue // a token or operand reading this draw, the routes allowed
|
continue // a token or operand reading this draw, the routes allowed
|
||||||
}
|
}
|
||||||
if renders(e.to, held, seen) {
|
if renders(e.to, held, seen) {
|
||||||
if reader, isPath := t.bound[head]; isPath {
|
if isPath {
|
||||||
return fmt.Errorf("%s: {%s} renders %q, which {%s} reads a path into; name the fields you want instead", path, e.label, head, reader)
|
return fmt.Errorf("%s: {%s} renders %q, which {%s} reads a path into; name the fields you want instead", path, e.label, head, reader)
|
||||||
}
|
}
|
||||||
return fmt.Errorf("%s: {%s} renders %q, which a {calc()} also reads; spell it {%s} so both read one draw", path, e.label, head, head)
|
return fmt.Errorf("%s: {%s} renders %q, which a {calc()} also reads; reach it one way so it is drawn once", path, e.label, head)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -95,6 +106,26 @@ func cover(n node, into map[node]bool) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// coverRendered collects what one held draw of a calc operand answers for: what
|
||||||
|
// rendering it reaches, following the same edges expand does. An operand is only
|
||||||
|
// ever rendered — checkNoOverlap rejects a path into one — so its draw fixes that
|
||||||
|
// value and nothing else. That is narrower than containment where a sibling the
|
||||||
|
// operand's format never renders cannot disagree with it, and wider where the
|
||||||
|
// operand renders through a reference, which is part of the value it produces.
|
||||||
|
// Literals are left out for the reason cover leaves them out.
|
||||||
|
func coverRendered(n node, into map[node]bool) {
|
||||||
|
if _, fixed := n.(literal); fixed {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if into[n] {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
into[n] = true
|
||||||
|
for _, e := range renderEdges(n) {
|
||||||
|
coverRendered(e.to, into)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// renders reports whether rendering n can reach anything in want, following the
|
// renders reports whether rendering n can reach anything in want, following the
|
||||||
// same edges expand does. seen keeps a node shared by several routes from being
|
// same edges expand does. seen keeps a node shared by several routes from being
|
||||||
// walked twice; checkNoCycles has already proved the graph is a DAG, so the walk
|
// walked twice; checkNoCycles has already proved the graph is a DAG, so the walk
|
||||||
|
|||||||
Reference in New Issue
Block a user