internal/core/eval: allow closedness override
The old implementation was a bit too permissive handling
closedness in the API. The current implementation tracks
closedness status within the Vertex.
Some packages take apart the conjuncts of a value and then
reassemblem then using Unify. In this case, the closedness
information is invalidated. The added API allows passing the
closedness information of the original node to exactly specify
what is is allowed.
Change-Id: I63f177dcb8ce70b428a02649641b96163e257166
Reviewed-on: https://cue-review.googlesource.com/c/cue/+/6648
Reviewed-by: Marcel van Lohuizen <mpvl@golang.org>
diff --git a/internal/core/eval/eval.go b/internal/core/eval/eval.go
index 82dba9a..591e179 100644
--- a/internal/core/eval/eval.go
+++ b/internal/core/eval/eval.go
@@ -143,7 +143,7 @@
if v.Value == nil {
save := *v
// Use node itself to allow for cycle detection.
- s := e.evalVertex(c, v, adt.Partial)
+ s := e.evalVertex(c, v, adt.Partial, nil)
if d := s.disjunct; d != nil && len(d.Values) > 1 && d.NumDefaults != 1 {
v.Value = d
@@ -239,6 +239,19 @@
// Phase two: record incomplete
// Phase three: record cycle.
func (e *Evaluator) Unify(c *adt.OpContext, v *adt.Vertex, state adt.VertexStatus) {
+ e.UnifyAccept(c, v, state, nil) //v.Closed)
+}
+
+// UnifyAccept is like Unify, but takes an extra argument to override the
+// accepted set of fields.
+//
+// Instead of deriving the allowed set of fields, it verifies this set by
+// consulting the given Acceptor. This can be useful when splitting an existing
+// values into individual conjuncts and then unifying some of its components
+// back into a new value. Under normal circumstances, this may not always
+// succeed as the missing context may result in stricter closedness rules.
+func (e *Evaluator) UnifyAccept(c *adt.OpContext, v *adt.Vertex, state adt.VertexStatus, accept adt.Acceptor) {
+
// defer c.PopVertex(c.PushVertex(v))
if state <= v.Status()+1 {
@@ -252,7 +265,7 @@
return
}
- n := e.evalVertex(c, v, state)
+ n := e.evalVertex(c, v, state, accept)
switch d := n.disjunct; {
case d != nil && len(d.Values) == 1:
@@ -282,6 +295,7 @@
}
v.Arcs = nil
// v.Structs = nil // TODO: should we keep or discard the Structs?
+ v.Closed = newDisjunctionAcceptor(n.disjunct)
default:
if r := n.result(); r.Value != nil {
@@ -292,7 +306,7 @@
// Else set it to something.
if v.Value == nil {
- panic("errer")
+ panic("error")
}
// Check whether result is done.
@@ -301,13 +315,13 @@
// evalVertex computes the vertex results. The state indicates the minimum
// status to which this vertex should be evaluated. It should be either
// adt.Finalized or adt.Partial.
-func (e *Evaluator) evalVertex(c *adt.OpContext, v *adt.Vertex, state adt.VertexStatus) *nodeShared {
- // fmt.Println(debug.NodeString(c.StringIndexer, v, nil))
+func (e *Evaluator) evalVertex(c *adt.OpContext, v *adt.Vertex, state adt.VertexStatus, accept adt.Acceptor) *nodeShared {
shared := &nodeShared{
- ctx: c,
- eval: e,
- node: v,
- stack: nil, // silence linter
+ ctx: c,
+ eval: e,
+ node: v,
+ stack: nil, // silence linter
+ accept: accept,
}
saved := *v
@@ -577,7 +591,14 @@
a.Closed = m
}
if updated != nil && m.isClosed {
- if err := m.verifyArcAllowed(n.ctx, a.Label); err != nil {
+ if accept := n.nodeShared.accept; accept != nil {
+ if !accept.Accept(n.ctx, a.Label) {
+ label := a.Label.SelectorString(ctx)
+ n.node.Value = &adt.Bottom{
+ Err: errors.Newf(token.NoPos, "field `%s` not allowed by Acceptor", label),
+ }
+ }
+ } else if err := m.verifyArcAllowed(n.ctx, a.Label); err != nil {
n.node.Value = err
}
// TODO: use continue to not process already failed fields,
@@ -633,6 +654,9 @@
resultNode *nodeContext
result_ adt.Vertex
stack []int
+
+ // Closedness override.
+ accept adt.Acceptor
}
func (n *nodeShared) result() *adt.Vertex {