cuelang.org/go@v0.10.1/internal/core/adt/fields.go (about)

     1  // Copyright 2023 CUE Authors
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package adt
    16  
    17  import (
    18  	"fmt"
    19  )
    20  
    21  // This file holds the logic for the insertion of fields and pattern
    22  // constraints, including tracking closedness.
    23  //
    24  //
    25  // DESIGN GOALS
    26  //
    27  // Key to performance is to fail early during evaluation. This is especially
    28  // true for disjunctions. In CUE evaluation, conjuncts may be evaluated in a
    29  // fairly arbitrary order. We want to retain this flexibility while also failing
    30  // on disallowed fields as soon as we have enough data to tell for certain.
    31  //
    32  // Keeping track of which fields are allowed means keeping provenance data on
    33  // whether certain conjuncts originate from embeddings or definitions, as well
    34  // as how they group together with other conjuncts. These data structures should
    35  // allow for a "mark and unwind" approach to allow for backtracking when
    36  // computing disjunctions.
    37  //
    38  // References to the same CUE value may be added as conjuncts through various
    39  // paths. For instance, a reference to a definition may be added directly, or
    40  // through embedding. How they are added affects which set of fields are
    41  // allowed. This can make the removal of duplicate conjuncts hard. A solution
    42  // should make it straightforward to deduplicate conjuncts if they have the same
    43  // impact on field inclusion.
    44  //
    45  // All conjuncts associated with field constraints, including optional fields
    46  // and pattern constraints, should be collated, deduplicated, and evaluated as
    47  // if they were regular fields. This allows comparisons between values to be
    48  // meaningful and helps to filter disjuncts.
    49  //
    50  // The provenance data generated by this algorithm should ideally be easily
    51  // usable in external APIs.
    52  //
    53  //
    54  // DATA STRUCTURES
    55  //
    56  // Conjuncts
    57  //
    58  // To keep track of conjunct provenance, each conjunct has a few flags that
    59  // indicates whether it originates from
    60  //   - an embedding
    61  //   - a definition
    62  //   - a reference (optional and unimplemented)
    63  //
    64  // Conjuncts with the same origin are represented as a single Conjunct in the
    65  // Vertex, where this conjunct is a list of these conjuncts. In other words, the
    66  // conjuncts of a Vertex are really a forest (group of trees) of conjuncts that,
    67  // recursively, reflect the provenance of the conjuncts contained within it.
    68  //
    69  // The current implementation uses a Vertex for listing conjuncts with the same
    70  // origin. This Vertex is marked as "Dynamic", as it does not have a CUE path
    71  // that leads to them.
    72  //
    73  //
    74  // Constraints
    75  //
    76  // Vertex values separately keep track of pattern constraints. These consist of
    77  // a list of patterns with associated conjuncts, and a CUE expression that
    78  // represents the set of allowed fields. This information is mostly for equality
    79  // checking: by the time this data is produced, conjuncts associated with
    80  // patterns are already inserted into the computed subfields.
    81  //
    82  // Note that this representation assumes that patterns are always accrued
    83  // cumulatively: a field that is allowed will accrue the conjuncts of any
    84  // matched pattern, even if it originates from an embedding that itself does not
    85  // allow this field.
    86  //
    87  //
    88  // ALGORITHM
    89  //
    90  // When processing the conjuncts of a Vertex, subfields are tracked per
    91  // "grouping" (the list of conjuncts of the same origin). Each grouping keeps a
    92  // counter of the number of unprocessed conjuncts and subgroups associated with
    93  // it. Field inclusion (closedness) can be computed as soon as all subconjuncts
    94  // and subgroups are processed.
    95  //
    96  // Conjuncts of subfields are inserted in such a way that they reflect the same
    97  // grouping as the parent Vertex, plus any grouping that may be added by the
    98  // subfield itself.
    99  //
   100  // It would be possible, though, to collapse certain (combinations of) groups
   101  // that contain only a single conjunct. This can limit the size of such conjunct
   102  // trees.
   103  //
   104  // As conjuncts are added within their grouping context, it is possible to
   105  // uniquely identify conjuncts only by Vertex and expression pointer,
   106  // disregarding the Environment.
   107  //
   108  //
   109  // EXAMPLE DATA STRUCTURE
   110  //
   111  //    a: #A
   112  //    #A: {
   113  //        #B
   114  //        x: r1
   115  //    }
   116  //    #B: y: r2
   117  //    r1: z: r3
   118  //    r2: 2
   119  //    r3: foo: 2
   120  //
   121  // gets evaluated into:
   122  //
   123  //    V_a: Arcs{
   124  //        x: V_x [ V_def(#A)[ r1 ] ]
   125  //        y: V_y [ V_def(#A)[ V_embed(#B)[ r2 ] ] ]
   126  //    }
   127  //
   128  // When evaluating V_x, its Arcs, in turn become:
   129  //
   130  //    V_x: Arcs{
   131  //        z: V_z [ V_def(#A)[ V_ref(r1)[ r3 ]) ]]
   132  //    }
   133  //
   134  // The V_def(#A) is necessary here to ensure that closedness information can be
   135  // computed, if necessary. The V_ref's, however, are optional, and can be
   136  // omitted if provenance is less important:
   137  //
   138  //    V_x: Arcs{
   139  //        z: V_z [ V_def(#A)[ r3 ]]
   140  //    }
   141  //
   142  // Another possible optimization is to eliminate Vertices if there is only one
   143  // conjunct: the embedding and definition flags in the conjunct can be
   144  // sufficient in that case. The provenance data could potentially be derived
   145  // from the Environment in that case. If an embedding conjunct is itself the
   146  // only conjunct in a list, the embedding bit can be eliminated. So V_y in the
   147  // above example could be reduced to
   148  //
   149  //    V_y [ V_def(#A)[ r2 ] ]
   150  //
   151  
   152  // TODO(perf):
   153  // - the data structures could probably be collapsed with Conjunct. and the
   154  //   Vertex inserted into the Conjuncts could be a special ConjunctGroup.
   155  
   156  type closeContext struct {
   157  	// Used to recursively insert Vertices.
   158  	parent *closeContext
   159  
   160  	// points to the closeContext this closeContext originates when following
   161  	// the reverse or ARC/EVAL dependencies corresponding to parent vertices.
   162  	// This is used to compute the prefix path when resolving a reference.
   163  	origin *closeContext
   164  
   165  	// overlay is used to temporarily link a closeContext to its "overlay" copy,
   166  	// as it is used in a corresponding disjunction.
   167  	overlay *closeContext
   168  	// generation is used to track the current generation of the closeContext
   169  	// in disjunction overlays. This is mostly for debugging.
   170  	generation int
   171  
   172  	dependencies []*ccDep // For testing only. See debug.go
   173  
   174  	// externalDeps lists the closeContexts associated with a root node for
   175  	// which there are outstanding decrements (can only be NOTIFY or ARC). This
   176  	// is used to break counter cycles, if necessary.
   177  	//
   178  	// This is only used for root closedContext and only for debugging.
   179  	// TODO: move to nodeContext.
   180  	externalDeps []ccArcRef
   181  
   182  	// child links to a sequence which additional patterns need to be verified
   183  	// against (&&). If there are more than one, these additional nodes are
   184  	// linked with next. Only closed nodes with patterns are added. Arc sets are
   185  	// already merged during processing.
   186  	// A child is always done. This means it cannot be modified.
   187  	child *closeContext
   188  
   189  	// next holds a linked list of nodes to process.
   190  	// See comments above and see linkPatterns.
   191  	next *closeContext
   192  
   193  	// if conjunctCount is 0, pattern constraints can be merged and the
   194  	// closedness can be checked. To ensure that this is true, there should
   195  	// be an additional increment at the start before any processing is done.
   196  	conjunctCount int
   197  
   198  	// disjunctCount counts the number of disjunctions that contribute to
   199  	// conjunctCount. When a node is unfinished, for instance due to an error,
   200  	// we allow disjunctions to not be decremented. This count is then used
   201  	// to suppress errors about missing decrements.
   202  	disjunctCount int
   203  
   204  	src *Vertex
   205  
   206  	arcType ArcType
   207  
   208  	// isDef is true when isDefOrig is true or when isDef is true for any of its
   209  	// child nodes, recursively.
   210  	isDef bool
   211  
   212  	// isDefOrig indicates whether the closeContext is created as part of a
   213  	// definition. This value propagates to itself and parents through isDef.
   214  	isDefOrig bool
   215  
   216  	// hasEllipsis indicates whether the node contains an ellipsis.
   217  	hasEllipsis bool
   218  
   219  	// hasTop indicates a node has at least one top conjunct.
   220  	hasTop bool
   221  
   222  	// hasNonTop indicates a node has at least one conjunct that is not top.
   223  	hasNonTop bool
   224  
   225  	// isClosedOnce is true if this closeContext is the result of calling the
   226  	// close builtin.
   227  	isClosedOnce bool
   228  
   229  	// isEmbed indicates whether the closeContext is created as part of an
   230  	// embedding.
   231  	isEmbed bool
   232  
   233  	// isClosed is true if a node is a def, it became closed because of a
   234  	// reference or if it is closed by the close builtin.
   235  	//
   236  	// isClosed must only be set to true if all fields and pattern constraints
   237  	// that define the domain of the node have been added.
   238  	isClosed bool
   239  
   240  	// isTotal is true if a node contains an ellipsis and is defined for all
   241  	// values.
   242  	isTotal bool
   243  
   244  	// done is true if all dependencies have been decremented.
   245  	done bool
   246  
   247  	// isDecremented is used to keep track of whether the evaluator decremented
   248  	// a closedContext for the ROOT depKind.
   249  	isDecremented bool
   250  
   251  	// needsCloseInSchedule is non-nil if a closeContext that was created
   252  	// as an arc still needs to be decremented. It points to the creating arc
   253  	// for reporting purposes.
   254  	needsCloseInSchedule *closeContext
   255  
   256  	// parentConjuncts represent the parent of this embedding or definition.
   257  	// Any closeContext is represented by a ConjunctGroup in parent of the
   258  	// expression tree.
   259  	parentConjuncts conjunctGrouper
   260  	// TODO: Only needed if more than one conjuncts.
   261  
   262  	// arcs represents closeContexts for sub fields and notification targets
   263  	// associated with this node that reflect the same point in the expression
   264  	// tree as this closeContext. In both cases the are keyed by Vertex.
   265  	arcs []ccArc
   266  
   267  	// parentIndex is the position in the parent's arcs slice that corresponds
   268  	// to this closeContext. This is currently unused. The intention is to use
   269  	// this to allow groups with single elements (which will be the majority)
   270  	// to be represented in place in the parent.
   271  	parentIndex int
   272  
   273  	group *ConjunctGroup
   274  
   275  	// Patterns contains all patterns of the current closeContext.
   276  	// It is used in the construction of Expr.
   277  	Patterns []Value
   278  
   279  	// Expr contains the Expr that is used for checking whether a Feature
   280  	// is allowed in this context. It is only complete after the full
   281  	// context has been completed, but it can be used for initial checking
   282  	// once isClosed is true.
   283  	Expr Value
   284  }
   285  
   286  // Label is a convenience function to return the label of the associated Vertex.
   287  func (c *closeContext) Label() Feature {
   288  	return c.src.Label
   289  }
   290  
   291  // See also Vertex.updateArcType in composite.go.
   292  func (c *closeContext) updateArcType(t ArcType) {
   293  	if t >= c.arcType {
   294  		return
   295  	}
   296  	if c.arcType == ArcNotPresent {
   297  		return
   298  	}
   299  	c.arcType = t
   300  }
   301  
   302  type ccArc struct {
   303  	kind        depKind
   304  	decremented bool
   305  	key         *closeContext
   306  	cc          *closeContext
   307  }
   308  
   309  // A ccArcRef x refers to the x.src.arcs[x.index].
   310  // We use this instead of pointers, because the address may change when
   311  // growing a slice. We use this instead mechanism instead of a pointers so
   312  // that we do not need to maintain separate free buffers once we use pools of
   313  // closeContext.
   314  type ccArcRef struct {
   315  	src   *closeContext
   316  	index int
   317  }
   318  
   319  type conjunctGrouper interface {
   320  	assignConjunct(ctx *OpContext, root *closeContext, c Conjunct, mode ArcType, check, checkClosed bool) (arc *closeContext, pos int, added bool)
   321  }
   322  
   323  func (n *nodeContext) getArc(f Feature, mode ArcType) (arc *Vertex, isNew bool) {
   324  	// TODO(disjunct,perf): CopyOnRead
   325  	v := n.node
   326  	for _, a := range v.Arcs {
   327  		if a.Label == f {
   328  			if f.IsLet() {
   329  				a.MultiLet = true
   330  				// TODO: add return here?
   331  			}
   332  			a.updateArcType(mode)
   333  			return a, false
   334  		}
   335  	}
   336  
   337  	arc = &Vertex{
   338  		Parent:    v,
   339  		Label:     f,
   340  		ArcType:   mode,
   341  		nonRooted: v.IsDynamic || v.Label.IsLet() || v.nonRooted,
   342  	}
   343  	if n.scheduler.frozen&fieldSetKnown != 0 {
   344  		b := n.ctx.NewErrf("adding field %v not allowed as field set was already referenced", f)
   345  		n.ctx.AddBottom(b)
   346  		// This may panic for list arithmetic. Safer to leave out for now.
   347  		arc.ArcType = ArcNotPresent
   348  	}
   349  	v.Arcs = append(v.Arcs, arc)
   350  	return arc, true
   351  }
   352  
   353  func (v *Vertex) assignConjunct(ctx *OpContext, root *closeContext, c Conjunct, mode ArcType, check, checkClosed bool) (a *closeContext, pos int, added bool) {
   354  	// TODO: consider clearing CloseInfo.cc.
   355  	// c.CloseInfo.cc = nil
   356  
   357  	arc := root.src
   358  	arc.updateArcType(mode) // TODO: probably not necessary: consider removing.
   359  
   360  	pos = len(arc.Conjuncts)
   361  
   362  	added = !check || !arc.hasConjunct(c)
   363  	if added {
   364  		c.CloseInfo.cc = root
   365  		arc.addConjunctUnchecked(c)
   366  	}
   367  
   368  	return root, pos, added
   369  }
   370  
   371  func (cc *closeContext) getKeyedCC(ctx *OpContext, key *closeContext, c CycleInfo, mode ArcType, checkClosed bool) *closeContext {
   372  	for _, a := range cc.arcs {
   373  		if a.key == key {
   374  			a.cc.updateArcType(mode)
   375  			return a.cc
   376  		}
   377  	}
   378  
   379  	group := &ConjunctGroup{}
   380  
   381  	if cc.parentConjuncts == cc {
   382  		panic("parent is self")
   383  	}
   384  
   385  	parent, pos, _ := cc.parentConjuncts.assignConjunct(ctx, key, Conjunct{
   386  		CloseInfo: CloseInfo{
   387  			FromDef:   cc.isDef,
   388  			FromEmbed: cc.isEmbed,
   389  			CycleInfo: c,
   390  		},
   391  		x: group,
   392  	}, mode, false, checkClosed)
   393  
   394  	arc := &closeContext{
   395  		origin:          cc.origin,
   396  		generation:      cc.generation,
   397  		parent:          parent,
   398  		parentConjuncts: parent,
   399  		parentIndex:     pos,
   400  
   401  		src:     key.src,
   402  		arcType: mode,
   403  		group:   group,
   404  
   405  		isDef:                cc.isDef,
   406  		isDefOrig:            cc.isDefOrig,
   407  		isEmbed:              cc.isEmbed,
   408  		needsCloseInSchedule: cc,
   409  	}
   410  
   411  	arc.parent.incDependent(ctx, PARENT, arc)
   412  
   413  	// If the parent, w.r.t. the subfield relation was already processed,
   414  	// there is no need to register the notification.
   415  	arc.incDependent(ctx, EVAL, cc) // matched in REF(decrement:nodeDone)
   416  
   417  	// A let field never depends on its parent. So it is okay to filter here.
   418  	if !arc.Label().IsLet() {
   419  		// prevent a dependency on self.
   420  		if key.src != cc.src {
   421  			cc.addDependency(ctx, ARC, key, arc, key)
   422  		}
   423  	}
   424  
   425  	v := key.src
   426  	if checkClosed && v.Parent != nil && v.Parent.state != nil {
   427  		v.Parent.state.checkArc(cc, v)
   428  	}
   429  
   430  	return arc
   431  }
   432  
   433  func (cc *closeContext) linkNotify(ctx *OpContext, dst *Vertex, key *closeContext, c CycleInfo) bool {
   434  	for _, a := range cc.arcs {
   435  		if a.key == key {
   436  			return false
   437  		}
   438  	}
   439  
   440  	cc.addDependency(ctx, NOTIFY, key, key, dst.cc)
   441  	return true
   442  }
   443  
   444  func (cc *closeContext) assignConjunct(ctx *OpContext, root *closeContext, c Conjunct, mode ArcType, check, checkClosed bool) (arc *closeContext, pos int, added bool) {
   445  	arc = cc.getKeyedCC(ctx, root, c.CloseInfo.CycleInfo, mode, checkClosed)
   446  
   447  	c.CloseInfo.cc = nil
   448  
   449  	var group ConjunctGroup
   450  	if arc.group != nil {
   451  		group = *arc.group
   452  	}
   453  	pos = len(group)
   454  
   455  	added = !check || !hasConjunct(group, c)
   456  	if added {
   457  		c.CloseInfo.cc = arc
   458  
   459  		if c.CloseInfo.cc.src != arc.src {
   460  			panic("Inconsistent src")
   461  		}
   462  
   463  		group = append(group, c)
   464  		if arc.group == nil {
   465  			arc.group = &group
   466  		} else {
   467  			*arc.group = group
   468  		}
   469  	}
   470  
   471  	return arc, pos, added
   472  }
   473  
   474  // spawnCloseContext wraps the closeContext in c with a new one and returns
   475  // this new context along with an updated CloseInfo. The new values reflect
   476  // that the set of fields represented by c are now, for instance, enclosed in
   477  // an embedding or a definition.
   478  //
   479  // This call is used when preparing ADT values for evaluation.
   480  func (c CloseInfo) spawnCloseContext(ctx *OpContext, t closeNodeType) (CloseInfo, *closeContext) {
   481  	cc := c.cc
   482  	if cc == nil {
   483  		panic("nil closeContext")
   484  	}
   485  
   486  	c.cc = &closeContext{
   487  		generation:      cc.generation,
   488  		parent:          cc,
   489  		src:             cc.src,
   490  		parentConjuncts: cc,
   491  	}
   492  
   493  	// By definition, a spawned closeContext is its own root.
   494  	c.cc.origin = c.cc
   495  
   496  	cc.incDependent(ctx, PARENT, c.cc) // REF(decrement: spawn)
   497  
   498  	switch t {
   499  	case closeDef:
   500  		c.cc.isDef = true
   501  		c.cc.isDefOrig = true
   502  	case closeEmbed:
   503  		c.cc.isEmbed = true
   504  	}
   505  
   506  	return c, c.cc
   507  }
   508  
   509  // addDependency adds a dependent arc to c. If child is an arc, child.src == key
   510  func (c *closeContext) addDependency(ctx *OpContext, kind depKind, key, child, root *closeContext) {
   511  	// NOTE: do not increment
   512  	// - either root closeContext or otherwise resulting from sub closeContext
   513  	//   all conjuncts will be added now, notified, or scheduled as task.
   514  
   515  	child.incDependent(ctx, kind, c) // matched in decDependent REF(arcs)
   516  
   517  	for _, a := range c.arcs {
   518  		if a.key == key {
   519  			panic("addArc: Label already exists")
   520  		}
   521  	}
   522  
   523  	// TODO: this tests seems sensible, but panics. Investigate what could
   524  	// trigger this.
   525  	// if child.src.Parent != c.src {
   526  	// 	panic("addArc: inconsistent parent")
   527  	// }
   528  	if child.src.cc != root.src.cc {
   529  		panic("addArc: inconsistent root")
   530  	}
   531  	c.arcs = append(c.arcs, ccArc{
   532  		kind: kind,
   533  		key:  key,
   534  		cc:   child,
   535  	})
   536  	root.externalDeps = append(root.externalDeps, ccArcRef{
   537  		src:   c,
   538  		index: len(c.arcs) - 1,
   539  	})
   540  }
   541  
   542  // incDependent needs to be called for any conjunct or child closeContext
   543  // scheduled for c that is queued for later processing and not scheduled
   544  // immediately.
   545  func (c *closeContext) incDependent(ctx *OpContext, kind depKind, dependant *closeContext) (debug *ccDep) {
   546  	if c.src == nil {
   547  		panic("incDependent: unexpected nil src")
   548  	}
   549  	if dependant != nil && c.generation != dependant.generation {
   550  		// TODO: enable this check.
   551  
   552  		// panic(fmt.Sprintf("incDependent: inconsistent generation: %d %d", c.generation, dependant.generation))
   553  	}
   554  	debug = c.addDependent(ctx, kind, dependant)
   555  
   556  	if c.done {
   557  		openDebugGraph(ctx, c.src, "incDependent: already checked")
   558  
   559  		panic(fmt.Sprintf("incDependent: already closed: %p", c))
   560  	}
   561  
   562  	c.conjunctCount++
   563  	return debug
   564  }
   565  
   566  // decDependent needs to be called for any conjunct or child closeContext for
   567  // which a corresponding incDependent was called after it has been successfully
   568  // processed.
   569  func (c *closeContext) decDependent(ctx *OpContext, kind depKind, dependant *closeContext) {
   570  	v := c.src
   571  
   572  	c.matchDecrement(ctx, v, kind, dependant)
   573  
   574  	if c.conjunctCount == 0 {
   575  		panic(fmt.Sprintf("negative reference counter %d %p", c.conjunctCount, c))
   576  	}
   577  
   578  	c.conjunctCount--
   579  	if c.conjunctCount > 0 {
   580  		return
   581  	}
   582  
   583  	c.done = true
   584  
   585  	p := c.parent
   586  
   587  	if c.isDef && !c.hasEllipsis && (!c.hasTop || c.hasNonTop) {
   588  		c.isClosed = true
   589  		if p != nil {
   590  			p.isDef = true
   591  		}
   592  	}
   593  
   594  	if c.isClosedOnce {
   595  		c.isClosed = true
   596  		if p != nil {
   597  			p.isClosedOnce = true
   598  		}
   599  	}
   600  
   601  	for i, a := range c.arcs {
   602  		cc := a.cc
   603  		if a.decremented {
   604  			continue
   605  		}
   606  		c.arcs[i].decremented = true
   607  		cc.decDependent(ctx, a.kind, c) // REF(arcs)
   608  	}
   609  
   610  	c.finalizePattern()
   611  
   612  	if p == nil {
   613  		// Root pattern, set allowed patterns.
   614  		if pcs := v.PatternConstraints; pcs != nil {
   615  			if pcs.Allowed != nil {
   616  				// This can happen for lists.
   617  				// TODO: unify the values.
   618  				// panic("unexpected allowed set")
   619  			}
   620  			pcs.Allowed = c.Expr
   621  			return
   622  		}
   623  		return
   624  	}
   625  
   626  	if c.hasEllipsis {
   627  		p.hasEllipsis = true
   628  	}
   629  	if c.hasTop {
   630  		p.hasTop = true
   631  	}
   632  	if c.hasNonTop {
   633  		p.hasNonTop = true
   634  	}
   635  
   636  	switch {
   637  	case c.isTotal:
   638  		if !p.isClosed {
   639  			p.isTotal = true
   640  		}
   641  	case !c.isEmbed && c.isClosed:
   642  		// Merge the two closeContexts and ensure that the patterns and fields
   643  		// are mutually compatible according to the closedness rules.
   644  		injectClosed(ctx, c, p)
   645  		p.Expr = mergeConjunctions(p.Expr, c.Expr)
   646  	default:
   647  		// Do not check closedness of fields for embeddings.
   648  		// The pattern constraints of the embedding still need to be added
   649  		// to the current context.
   650  		p.linkPatterns(c)
   651  	}
   652  
   653  	p.decDependent(ctx, PARENT, c) // REF(decrement: spawn)
   654  
   655  	// If we have started decrementing a child closeContext, the parent started
   656  	// as well. If it is still marked as needing an EVAL decrement, which can
   657  	// happen if processing started before the node was added, it is safe to
   658  	// decrement it now. In this case the NOTIFY and ARC dependencies will keep
   659  	// the nodes alive until they can be completed.
   660  	if dep := p.needsCloseInSchedule; dep != nil {
   661  		p.needsCloseInSchedule = nil
   662  		p.decDependent(ctx, EVAL, dep)
   663  	}
   664  }
   665  
   666  // incDisjunct increases disjunction-related counters. We require kind to be
   667  // passed explicitly so that we can easily find the points where certain kinds
   668  // are used.
   669  func (c *closeContext) incDisjunct(ctx *OpContext, kind depKind) {
   670  	if kind != DISJUNCT {
   671  		panic("unexpected kind")
   672  	}
   673  	c.incDependent(ctx, DISJUNCT, nil)
   674  
   675  	// TODO: the counters are only used in debug mode and we could skip this
   676  	// if debug is disabled.
   677  	for ; c != nil; c = c.parent {
   678  		c.disjunctCount++
   679  	}
   680  }
   681  
   682  // decDisjunct decreases disjunction-related counters. We require kind to be
   683  // passed explicitly so that we can easily find the points where certain kinds
   684  // are used.
   685  func (c *closeContext) decDisjunct(ctx *OpContext, kind depKind) {
   686  	if kind != DISJUNCT {
   687  		panic("unexpected kind")
   688  	}
   689  	c.decDependent(ctx, DISJUNCT, nil)
   690  
   691  	// TODO: the counters are only used in debug mode and we could skip this
   692  	// if debug is disabled.
   693  	for ; c != nil; c = c.parent {
   694  		c.disjunctCount++
   695  	}
   696  }
   697  
   698  // linkPatterns merges the patterns of child into c, if needed.
   699  func (c *closeContext) linkPatterns(child *closeContext) {
   700  	if len(child.Patterns) > 0 {
   701  		child.next = c.child
   702  		c.child = child
   703  	}
   704  }
   705  
   706  // checkArc validates that the node corresponding to cc allows a field with
   707  // label v.Label.
   708  func (n *nodeContext) checkArc(cc *closeContext, v *Vertex) *Vertex {
   709  	n.assertInitialized()
   710  
   711  	f := v.Label
   712  	ctx := n.ctx
   713  
   714  	if f.IsHidden() || f.IsLet() {
   715  		return v
   716  	}
   717  
   718  	if cc.isClosed && !matchPattern(ctx, cc.Expr, f) {
   719  		ctx.notAllowedError(n.node, v)
   720  	}
   721  	if n.scheduler.frozen&fieldSetKnown != 0 {
   722  		for _, a := range n.node.Arcs {
   723  			if a.Label == f {
   724  				return v
   725  			}
   726  		}
   727  		var b *Bottom
   728  		// TODO: include cycle data and improve error message.
   729  		if f.IsInt() {
   730  			b = ctx.NewErrf(
   731  				"element at index %v not allowed by earlier comprehension or reference cycle", f)
   732  		} else {
   733  			b = ctx.NewErrf(
   734  				"field %v not allowed by earlier comprehension or reference cycle", f)
   735  		}
   736  		v.SetValue(ctx, b)
   737  	}
   738  
   739  	return v
   740  }
   741  
   742  // insertConjunct inserts conjunct c into cc.
   743  func (cc *closeContext) insertConjunct(ctx *OpContext, key *closeContext, c Conjunct, id CloseInfo, mode ArcType, check, checkClosed bool) (arc *closeContext, added bool) {
   744  	arc, _, added = cc.assignConjunct(ctx, key, c, mode, check, checkClosed)
   745  	if key.src != arc.src {
   746  		panic("inconsistent src")
   747  	}
   748  
   749  	if !added {
   750  		return
   751  	}
   752  
   753  	n := key.src.getBareState(ctx)
   754  	if n == nil {
   755  		// already done
   756  		return
   757  	}
   758  
   759  	if key.src.isInProgress() {
   760  		c.CloseInfo.cc = nil
   761  		id.cc = arc
   762  		n.scheduleConjunct(c, id)
   763  	}
   764  
   765  	for _, rec := range n.notify {
   766  		if mode == ArcPending {
   767  			panic("unexpected pending arc")
   768  		}
   769  		// TODO: we should probably only notify a conjunct once the root of the
   770  		// conjunct group is completed. This will make it easier to "stitch" the
   771  		// conjunct trees together, as its correctness will be guaranteed.
   772  		cc.insertConjunct(ctx, rec.cc, c, id, mode, check, checkClosed)
   773  	}
   774  
   775  	return
   776  }
   777  
   778  func (n *nodeContext) insertArc(f Feature, mode ArcType, c Conjunct, id CloseInfo, check bool) *Vertex {
   779  	v, _ := n.insertArcCC(f, mode, c, id, check)
   780  	return v
   781  }
   782  
   783  // insertArc inserts conjunct c into n. If check is true it will not add c if it
   784  // was already added.
   785  // Returns the arc of n.node with label f.
   786  func (n *nodeContext) insertArcCC(f Feature, mode ArcType, c Conjunct, id CloseInfo, check bool) (*Vertex, *closeContext) {
   787  	n.assertInitialized()
   788  
   789  	if n == nil {
   790  		panic("nil nodeContext")
   791  	}
   792  	if n.node == nil {
   793  		panic("nil node")
   794  	}
   795  	cc := id.cc
   796  	if cc == nil {
   797  		panic("nil closeContext")
   798  	}
   799  
   800  	v, insertedArc := n.getArc(f, mode)
   801  
   802  	defer n.ctx.PopArc(n.ctx.PushArc(v))
   803  
   804  	// TODO: this block is not strictly needed. Removing it slightly changes the
   805  	// paths at which errors are reported, arguably, but not clearly, for the
   806  	// better. Investigate this once the new evaluator is done.
   807  	if v.ArcType == ArcNotPresent {
   808  		// It was already determined before that this arc may not be present.
   809  		// This case can only manifest itself if we have a cycle.
   810  		n.node.reportFieldCycleError(n.ctx, pos(c.x), f)
   811  		return v, nil
   812  	}
   813  
   814  	if v.cc == nil {
   815  		v.cc = v.rootCloseContext(n.ctx)
   816  		v.cc.generation = n.node.cc.generation
   817  	}
   818  
   819  	arc, added := cc.insertConjunct(n.ctx, v.cc, c, id, mode, check, true)
   820  	if !added {
   821  		return v, arc
   822  	}
   823  
   824  	if !insertedArc {
   825  		return v, arc
   826  	}
   827  
   828  	// Match and insert patterns.
   829  	if pcs := n.node.PatternConstraints; pcs != nil {
   830  		for _, pc := range pcs.Pairs {
   831  			if matchPattern(n.ctx, pc.Pattern, f) {
   832  				for _, c := range pc.Constraint.Conjuncts {
   833  					// TODO: consider using the root cc, but probably does not
   834  					// matter.
   835  					// This is necessary if we defunct tasks, but otherwise not.
   836  					// It breaks the CloseContext tests, though.
   837  					// c.CloseInfo.cc = id.cc
   838  					n.addConstraint(v, mode, c, check)
   839  				}
   840  			}
   841  		}
   842  	}
   843  
   844  	return v, arc
   845  }
   846  
   847  // addConstraint adds a constraint to arc of n.
   848  //
   849  // In order to resolve LabelReferences, it is not always possible to walk up
   850  // the parent Vertex chain to determan the label, because a label reference
   851  // may point past a point of referral. For instance,
   852  //
   853  //	test: [ID=_]: name: ID
   854  //	test: A: {}
   855  //	B: test.A & {}  // B.name should be "A", not "B".
   856  //
   857  // The arc must be the node arc to which the conjunct is added.
   858  func (n *nodeContext) addConstraint(arc *Vertex, mode ArcType, c Conjunct, check bool) {
   859  	n.assertInitialized()
   860  
   861  	// TODO(perf): avoid cloning the Environment, if:
   862  	// - the pattern constraint has no LabelReference
   863  	//   (require compile-time support)
   864  	// - there are no references in the conjunct pointing to this node.
   865  	// - consider adding this value to the Conjunct struct
   866  	f := arc.Label
   867  	bulkEnv := *c.Env
   868  	bulkEnv.DynamicLabel = f
   869  	c.Env = &bulkEnv
   870  
   871  	// TODO(constraintNode): this should ideally be
   872  	//    cc := id.cc
   873  	// or
   874  	//    cc := c.CloseInfo.cc.src.cc
   875  	//
   876  	// Where id is the closeContext corresponding to the field, or the root
   877  	// context. But it is a bit hard to figure out how to account for this, as
   878  	// either this information is not available or the root context results in
   879  	// errors for the other use of addConstraint. For this reason, we keep
   880  	// things symmetric for now and will keep things as is, just avoiding the
   881  	// closedness check.
   882  	cc := c.CloseInfo.cc
   883  
   884  	arc, _ = n.getArc(f, mode)
   885  
   886  	root := arc.rootCloseContext(n.ctx)
   887  	cc.insertConjunct(n.ctx, root, c, c.CloseInfo, mode, check, false)
   888  }
   889  
   890  func (n *nodeContext) insertPattern(pattern Value, c Conjunct) {
   891  	n.assertInitialized()
   892  
   893  	ctx := n.ctx
   894  	cc := c.CloseInfo.cc
   895  
   896  	// Collect patterns in root vertex. This allows comparing disjuncts for
   897  	// equality as well as inserting new arcs down the line as they are
   898  	// inserted.
   899  	if n.insertConstraint(pattern, c) {
   900  		// Match against full set of arcs from root, but insert in current vertex.
   901  		// Hypothesis: this may not be necessary. Maybe for closedness.
   902  		// TODO: may need to replicate the closedContext for patterns.
   903  		// Also: Conjuncts for matching other arcs in this node may be different
   904  		// for matching arcs using v.foo?, if we need to ensure that conjuncts
   905  		// from arcs and patterns are grouped under the same vertex.
   906  		// TODO: verify. See test Pattern 1b
   907  		for _, a := range n.node.Arcs {
   908  			if matchPattern(n.ctx, pattern, a.Label) {
   909  				// TODO: is it necessary to check for uniqueness here?
   910  				n.addConstraint(a, a.ArcType, c, true)
   911  			}
   912  		}
   913  	}
   914  
   915  	if cc.isTotal {
   916  		return
   917  	}
   918  	if isTotal(pattern) {
   919  		cc.isTotal = true
   920  		cc.Patterns = cc.Patterns[:0]
   921  		return
   922  	}
   923  
   924  	// insert pattern in current set.
   925  	// TODO: normalize patterns
   926  	// TODO: do we only need to do this for closed contexts?
   927  	for _, pc := range cc.Patterns {
   928  		if Equal(ctx, pc, pattern, 0) {
   929  			return
   930  		}
   931  	}
   932  	cc.Patterns = append(cc.Patterns, pattern)
   933  }
   934  
   935  // isTotal reports whether pattern value p represents a full domain, that is,
   936  // whether it is of type BasicType or Top.
   937  func isTotal(p Value) bool {
   938  	switch p.(type) {
   939  	case *BasicType:
   940  		return true
   941  	case *Top:
   942  		return true
   943  	}
   944  	return false
   945  }
   946  
   947  // injectClosed updates dst so that it only allows fields allowed by closed.
   948  //
   949  // It first ensures that the fields contained in dst are allowed by the fields
   950  // and patterns defined in closed. It reports an error in the nodeContext if
   951  // this is not the case.
   952  func injectClosed(ctx *OpContext, closed, dst *closeContext) {
   953  	// TODO: check that fields are not void arcs.
   954  outer:
   955  	for _, a := range dst.arcs {
   956  		if a.kind != ARC {
   957  			continue
   958  		}
   959  		ca := a.cc
   960  		f := ca.Label()
   961  		switch ca.src.ArcType {
   962  		case ArcMember, ArcRequired:
   963  		case ArcOptional, ArcNotPresent:
   964  			// Without this continue, an evaluation error may be propagated to
   965  			// parent nodes that are otherwise allowed.
   966  			continue
   967  		case ArcPending:
   968  			// TODO: Need to evaluate?
   969  		default:
   970  			panic("unreachable")
   971  		}
   972  		// TODO: disallow new definitions in closed structs.
   973  		if f.IsHidden() || f.IsLet() || f.IsDef() {
   974  			continue
   975  		}
   976  		for _, b := range closed.arcs {
   977  			cb := b.cc
   978  			// TODO: we could potentially remove the check  for ArcPending if we
   979  			// explicitly set the arcType to ArcNonPresent when a comprehension
   980  			// yields no results.
   981  			if cb.arcType == ArcNotPresent || cb.arcType == ArcPending {
   982  				continue
   983  			}
   984  			if f == cb.Label() {
   985  				continue outer
   986  			}
   987  		}
   988  		if !matchPattern(ctx, closed.Expr, ca.Label()) {
   989  			ctx.notAllowedError(closed.src, ca.src)
   990  			continue
   991  		}
   992  	}
   993  
   994  	if !dst.isClosed {
   995  		// Since dst is not closed, it is safe to take all patterns from
   996  		// closed.
   997  		// This is only necessary for passing up patterns into embeddings. For
   998  		// (the conjunction of) definitions the construction is handled
   999  		// elsewhere.
  1000  		// TODO(perf): reclaim slice memory
  1001  		dst.Patterns = closed.Patterns
  1002  
  1003  		dst.isClosed = true
  1004  	}
  1005  }
  1006  
  1007  func (ctx *OpContext) addPositions(c Conjunct) {
  1008  	if x, ok := c.x.(*ConjunctGroup); ok {
  1009  		for _, c := range *x {
  1010  			ctx.addPositions(c)
  1011  		}
  1012  	}
  1013  	if pos := c.Field(); pos != nil {
  1014  		ctx.AddPosition(pos)
  1015  	}
  1016  }
  1017  
  1018  // notAllowedError reports a field not allowed error in n and sets the value
  1019  // for arc f to that error.
  1020  func (ctx *OpContext) notAllowedError(v, arc *Vertex) {
  1021  	defer ctx.PopArc(ctx.PushArc(arc))
  1022  
  1023  	defer ctx.ReleasePositions(ctx.MarkPositions())
  1024  
  1025  	for _, c := range arc.Conjuncts {
  1026  		ctx.addPositions(c)
  1027  	}
  1028  	// TODO(0.7): Find another way to get this provenance information. Not
  1029  	// currently stored in new evaluator.
  1030  	// for _, s := range x.Structs {
  1031  	//  s.AddPositions(ctx)
  1032  	// }
  1033  
  1034  	// TODO: use the arcType from the closeContext.
  1035  	if arc.ArcType == ArcPending {
  1036  		// arc.ArcType = ArcNotPresent
  1037  		// We do not know yet whether the arc will be present or not. Checking
  1038  		// this will be deferred until this is known, after the comprehension
  1039  		// has been evaluated.
  1040  		return
  1041  	}
  1042  	// TODO: setting arc instead of n.node eliminates subfields. This may be
  1043  	// desirable or not, but it differs, at least from <=v0.6 behavior.
  1044  	arc.SetValue(ctx, ctx.NewErrf("field not allowed"))
  1045  	if arc.state != nil {
  1046  		arc.state.kind = 0
  1047  	}
  1048  
  1049  	// TODO: remove? We are now setting it on both fields, which seems to be
  1050  	// necessary for now. But we should remove this as it often results in
  1051  	// a duplicate error.
  1052  	// v.SetValue(ctx, ctx.NewErrf("field not allowed"))
  1053  
  1054  	// TODO: create a special kind of error that gets the positions
  1055  	// of the relevant locations upon request from the arc.
  1056  }
  1057  
  1058  // mergeConjunctions combines two values into one. It never modifies an
  1059  // existing conjunction.
  1060  func mergeConjunctions(a, b Value) Value {
  1061  	if a == nil {
  1062  		return b
  1063  	}
  1064  	if b == nil {
  1065  		return a
  1066  	}
  1067  	ca, _ := a.(*Conjunction)
  1068  	cb, _ := b.(*Conjunction)
  1069  	n := 2
  1070  	if ca != nil {
  1071  		n += len(ca.Values) - 1
  1072  	}
  1073  	if cb != nil {
  1074  		n += len(cb.Values) - 1
  1075  	}
  1076  	vs := make([]Value, 0, n)
  1077  	if ca != nil {
  1078  		vs = append(vs, ca.Values...)
  1079  	} else {
  1080  		vs = append(vs, a)
  1081  	}
  1082  	if cb != nil {
  1083  		vs = append(vs, cb.Values...)
  1084  	} else {
  1085  		vs = append(vs, b)
  1086  	}
  1087  	// TODO: potentially order conjuncts to make matching more likely.
  1088  	return &Conjunction{Values: vs}
  1089  }
  1090  
  1091  // finalizePattern updates c.Expr to a CUE Value representing all fields allowed
  1092  // by the pattern constraints of c. If this context or any of its direct
  1093  // children is closed, the result will be a conjunction of all these closed
  1094  // values. Otherwise it will be a disjunction of all its children. A nil value
  1095  // represents all values.
  1096  func (c *closeContext) finalizePattern() {
  1097  	switch {
  1098  	case c.Expr != nil: // Patterns and expression are already set.
  1099  		if !c.isClosed {
  1100  			panic("c.Expr set unexpectedly")
  1101  		}
  1102  		return
  1103  	case c.isTotal: // All values are allowed always.
  1104  		return
  1105  	}
  1106  
  1107  	// As this context is not closed, the pattern is somewhat meaningless.
  1108  	// It may still be useful for analysis.
  1109  	or := c.Patterns
  1110  
  1111  	for cc := c.child; cc != nil; cc = cc.next {
  1112  		if cc.isTotal {
  1113  			return
  1114  		}
  1115  		// Could be closed, in which case it must also be an embedding.
  1116  
  1117  		// TODO: simplify the values.
  1118  		switch x := cc.Expr.(type) {
  1119  		case nil:
  1120  		case *Disjunction:
  1121  			or = append(or, x.Values...)
  1122  		default:
  1123  			or = append(or, x)
  1124  		}
  1125  	}
  1126  
  1127  	switch len(or) {
  1128  	case 0:
  1129  	case 1:
  1130  		c.Expr = or[0]
  1131  	default:
  1132  		// TODO: potentially order conjuncts to make matching more likely.
  1133  		c.Expr = &Disjunction{Values: or}
  1134  	}
  1135  }