cuelang.org/go@v0.10.1/internal/core/adt/unify.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  	"cuelang.org/go/cue/token"
    21  )
    22  
    23  // TODO(mpvl): perhaps conjunctsProcessed is a better name for this.
    24  func (v *Vertex) isInitialized() bool {
    25  	return v.status == finalized || (v.state != nil && v.state.isInitialized)
    26  }
    27  
    28  func (n *nodeContext) assertInitialized() {
    29  	if n != nil && n.ctx.isDevVersion() {
    30  		if v := n.node; !v.isInitialized() {
    31  			panic(fmt.Sprintf("vertex %p not initialized", v))
    32  		}
    33  	}
    34  }
    35  
    36  // isInProgress reports whether v is in the midst of being evaluated. This means
    37  // that conjuncts have been scheduled, but that it has not been finalized.
    38  func (v *Vertex) isInProgress() bool {
    39  	return v.status != finalized && v.state != nil && v.state.isInitialized
    40  }
    41  
    42  func (v *Vertex) getBareState(c *OpContext) *nodeContext {
    43  	if v.status == finalized { // TODO: use BaseValue != nil
    44  		return nil
    45  	}
    46  	if v.state == nil {
    47  		v.state = c.newNodeContext(v)
    48  		v.state.initBare()
    49  		v.state.refCount = 1
    50  	}
    51  
    52  	// An additional refCount for the current user.
    53  	v.state.refCount += 1
    54  
    55  	// TODO: see if we can get rid of ref counting after new evaluator is done:
    56  	// the recursive nature of the new evaluator should make this unnecessary.
    57  
    58  	return v.state
    59  }
    60  
    61  func (v *Vertex) getState(c *OpContext) *nodeContext {
    62  	s := v.getBareState(c)
    63  	if s != nil && !s.isInitialized {
    64  		s.scheduleConjuncts()
    65  	}
    66  	return s
    67  }
    68  
    69  // initNode initializes a nodeContext for the evaluation of the given Vertex.
    70  func (n *nodeContext) initBare() {
    71  	v := n.node
    72  	if v.Parent != nil && v.Parent.state != nil {
    73  		v.state.depth = v.Parent.state.depth + 1
    74  		n.blockOn(allAncestorsProcessed)
    75  	}
    76  
    77  	n.blockOn(scalarKnown | listTypeKnown | arcTypeKnown)
    78  
    79  	if v.Label.IsDef() {
    80  		v.Closed = true
    81  	}
    82  
    83  	if v.Parent != nil {
    84  		if v.Parent.Closed {
    85  			v.Closed = true
    86  		}
    87  	}
    88  }
    89  
    90  func (n *nodeContext) scheduleConjuncts() {
    91  	n.isInitialized = true
    92  
    93  	v := n.node
    94  	ctx := n.ctx
    95  
    96  	ctx.stats.Unifications++
    97  
    98  	// Set the cache to a cycle error to ensure a cyclic reference will result
    99  	// in an error if applicable. A cyclic error may be ignored for
   100  	// non-expression references. The cycle error may also be removed as soon
   101  	// as there is evidence what a correct value must be, but before all
   102  	// validation has taken place.
   103  	//
   104  	// TODO(cycle): having a more recursive algorithm would make this
   105  	// special cycle handling unnecessary.
   106  	v.BaseValue = cycle
   107  
   108  	defer ctx.PopArc(ctx.PushArc(v))
   109  
   110  	root := n.node.rootCloseContext(n.ctx)
   111  	root.incDependent(n.ctx, INIT, nil) // decremented below
   112  
   113  	for _, c := range v.Conjuncts {
   114  		ci := c.CloseInfo
   115  		ci.cc = root
   116  		n.scheduleConjunct(c, ci)
   117  	}
   118  
   119  	root.decDependent(ctx, INIT, nil)
   120  }
   121  
   122  func (v *Vertex) unify(c *OpContext, needs condition, mode runMode) bool {
   123  	if c.LogEval > 0 {
   124  		c.nest++
   125  		c.Logf(v, "Unify %v", fmt.Sprintf("%p", v))
   126  		defer func() {
   127  			c.Logf(v, "END Unify")
   128  			c.nest--
   129  		}()
   130  	}
   131  
   132  	if mode == ignore {
   133  		return false
   134  	}
   135  
   136  	n := v.getState(c)
   137  	if n == nil {
   138  		return true // already completed
   139  	}
   140  	defer n.free()
   141  
   142  	defer n.unmarkDepth(n.markDepth())
   143  
   144  	// Typically a node processes all conjuncts before processing its fields.
   145  	// So this condition is very likely to trigger. If for some reason the
   146  	// parent has not been processed yet, we could attempt to process more
   147  	// of its tasks to increase the chances of being able to find the
   148  	// information we are looking for here. For now we just continue as is.
   149  	//
   150  	// For dynamic nodes, the parent only exists to provide a path context.
   151  	//
   152  	// Note that if mode is final, we will guarantee that the conditions for
   153  	// this if clause are met down the line. So we assume this is already the
   154  	// case and set the signal accordingly if so.
   155  	if v.Label.IsLet() || v.IsDynamic || v.Parent.allChildConjunctsKnown() || mode == finalize {
   156  		n.signal(allAncestorsProcessed)
   157  	}
   158  
   159  	nodeOnlyNeeds := needs &^ (subFieldsProcessed)
   160  	n.process(nodeOnlyNeeds, mode)
   161  
   162  	defer c.PopArc(c.PushArc(v))
   163  
   164  	w := v.DerefDisjunct()
   165  	if w != v {
   166  		// Should resolve with dereference.
   167  		v.Closed = w.Closed
   168  		v.status = w.status
   169  		v.ChildErrors = CombineErrors(nil, v.ChildErrors, w.ChildErrors)
   170  		v.Arcs = nil
   171  		return w.state.meets(needs)
   172  	}
   173  	n.updateScalar()
   174  
   175  	if n.aStruct != nil {
   176  		n.updateNodeType(StructKind, n.aStruct, n.aStructID)
   177  	}
   178  
   179  	// First process all but the subfields.
   180  	switch {
   181  	case n.meets(nodeOnlyNeeds):
   182  		// pass through next phase.
   183  	case mode != finalize:
   184  		// TODO: disjunctions may benefit from evaluation as much prematurely
   185  		// as possible, as this increases the chances of premature failure.
   186  		// We should consider doing a recursive "attemptOnly" evaluation here.
   187  		return false
   188  	}
   189  
   190  	if n.isShared {
   191  		if isCyclePlaceholder(n.origBaseValue) {
   192  			n.origBaseValue = nil
   193  		}
   194  	} else if isCyclePlaceholder(n.node.BaseValue) {
   195  		n.node.BaseValue = nil
   196  	}
   197  	if !n.isShared {
   198  		// TODO(sharewithval): allow structure sharing if we only have validator
   199  		// and references.
   200  		// TODO: rewrite to use mode when we get rid of old evaluator.
   201  		state := finalized
   202  		n.validateValue(state)
   203  	}
   204  
   205  	if n.node.Label.IsLet() || n.meets(allAncestorsProcessed) {
   206  		if cc := v.rootCloseContext(n.ctx); !cc.isDecremented { // TODO: use v.cc
   207  			cc.decDependent(c, ROOT, nil) // REF(decrement:nodeDone)
   208  			cc.isDecremented = true
   209  		}
   210  	}
   211  
   212  	// At this point, no more conjuncts will be added, so we could decrement
   213  	// the notification counters.
   214  
   215  	switch {
   216  	case n.completed&subFieldsProcessed != 0:
   217  		// done
   218  
   219  	case needs&subFieldsProcessed != 0:
   220  		if DebugSort > 0 {
   221  			DebugSortArcs(n.ctx, n.node)
   222  		}
   223  
   224  		switch {
   225  		case assertStructuralCycle(n):
   226  		// TODO: consider bailing on error if n.errs != nil.
   227  		case n.completeAllArcs(needs, mode):
   228  		}
   229  
   230  		if mode == finalize {
   231  			n.signal(subFieldsProcessed)
   232  		}
   233  
   234  		if v.BaseValue == nil {
   235  			// TODO: this seems to not be possible. Possibly remove.
   236  			state := finalized
   237  			v.BaseValue = n.getValidators(state)
   238  		}
   239  		if v := n.node.Value(); v != nil && IsConcrete(v) {
   240  			// Ensure that checks are not run again when this value is used
   241  			// in a validator.
   242  			checks := n.checks
   243  			n.checks = n.checks[:0]
   244  			for _, v := range checks {
   245  				// TODO(errors): make Validate return bottom and generate
   246  				// optimized conflict message. Also track and inject IDs
   247  				// to determine origin location.s
   248  				if b := c.Validate(v, n.node); b != nil {
   249  					n.addBottom(b)
   250  				}
   251  			}
   252  		}
   253  
   254  	case needs&fieldSetKnown != 0:
   255  		n.evalArcTypes()
   256  	}
   257  
   258  	if err := n.getErr(); err != nil {
   259  		n.errs = nil
   260  		if b := n.node.Bottom(); b != nil {
   261  			err = CombineErrors(nil, b, err)
   262  		}
   263  		n.node.BaseValue = err
   264  	}
   265  
   266  	if mode == attemptOnly {
   267  		return n.meets(needs)
   268  	}
   269  
   270  	if mask := n.completed & needs; mask != 0 {
   271  		// TODO: phase3: validation
   272  		n.signal(mask)
   273  	}
   274  
   275  	n.finalizeDisjunctions()
   276  
   277  	w = v.DerefValue() // Dereference anything, including shared nodes.
   278  	if w != v {
   279  		// Clear value fields that are now referred to in the dereferenced
   280  		// value (w).
   281  		v.ChildErrors = nil
   282  		v.Arcs = nil
   283  
   284  		result := w.unify(c, needs, mode)
   285  
   286  		// Set control fields that are referenced without dereferencing.
   287  		if w.Closed {
   288  			v.Closed = true
   289  		}
   290  		if w.HasEllipsis {
   291  			v.HasEllipsis = true
   292  		}
   293  		v.status = w.status
   294  
   295  		return result
   296  	}
   297  
   298  	// TODO: adding this is wrong, but it should not cause the snippet below
   299  	// to hang. Investigate.
   300  	// v.Closed = v.cc.isClosed
   301  	//
   302  	// This hangs:
   303  	// issue1940: {
   304  	// 	#T: ["a", #T] | ["c", #T] | ["d", [...#T]]
   305  	// 	#A: t: #T
   306  	// 	#B: x: #A
   307  	// 	#C: #B
   308  	// 	#C: x: #A
   309  	// }
   310  
   311  	// validationCompleted
   312  	if n.completed&(subFieldsProcessed) != 0 {
   313  		n.node.HasEllipsis = n.node.cc.hasEllipsis
   314  
   315  		n.node.updateStatus(finalized)
   316  
   317  		defer n.unmarkOptional(n.markOptional())
   318  
   319  		// The next piece of code addresses the following case.
   320  		// order matters
   321  		// c1: c: [string]: f2
   322  		// f2: c1
   323  		// Also: cycle/issue990
   324  		if pc := n.node.PatternConstraints; pc != nil {
   325  			for _, c := range pc.Pairs {
   326  				c.Constraint.Finalize(n.ctx)
   327  			}
   328  		}
   329  
   330  		if DebugDeps {
   331  			RecordDebugGraph(n.ctx, n.node, "Finalize")
   332  		}
   333  	}
   334  
   335  	return n.meets(needs)
   336  }
   337  
   338  // Once returning, all arcs plus conjuncts that can be known are known.
   339  //
   340  // Proof:
   341  //   - if there is a cycle, all completeNodeConjuncts will be called
   342  //     repeatedly for all nodes in this cycle, and all tasks on the cycle
   343  //     will have run at least once.
   344  //   - any tasks that were blocking on values on this circle to be completed
   345  //     will thus have to be completed at some point in time if they can.
   346  //   - any tasks that were blocking on values outside of this ring will have
   347  //     initiated its own execution, which is either not cyclic, and thus
   348  //     completes, or is on a different cycle, in which case it completes as
   349  //     well.
   350  //
   351  // Goal:
   352  // - complete notifications
   353  // - decrement reference counts for root and notify.
   354  // NOT:
   355  // - complete value. That is reserved for Unify.
   356  func (n *nodeContext) completeNodeTasks(mode runMode) {
   357  	n.assertInitialized()
   358  
   359  	if n.isCompleting > 0 {
   360  		return
   361  	}
   362  	n.isCompleting++
   363  	defer func() {
   364  		n.isCompleting--
   365  	}()
   366  
   367  	v := n.node
   368  	c := n.ctx
   369  
   370  	if n.ctx.LogEval > 0 {
   371  		c.nest++
   372  		defer func() {
   373  			c.nest--
   374  		}()
   375  	}
   376  
   377  	if p := v.Parent; p != nil && p.state != nil {
   378  		if !v.IsDynamic && n.completed&allAncestorsProcessed == 0 {
   379  			p.state.completeNodeTasks(mode)
   380  		}
   381  	}
   382  
   383  	if v.IsDynamic || v.Parent.allChildConjunctsKnown() {
   384  		n.signal(allAncestorsProcessed)
   385  	}
   386  
   387  	if len(n.scheduler.tasks) != n.scheduler.taskPos {
   388  		// TODO: do we need any more requirements here?
   389  		const needs = valueKnown | fieldConjunctsKnown
   390  
   391  		n.process(needs, mode)
   392  		n.updateScalar()
   393  	}
   394  
   395  	// Check:
   396  	// - parents (done)
   397  	// - incoming notifications
   398  	// - pending arcs (or incoming COMPS)
   399  	// TODO: replace with something more principled that does not piggyback on
   400  	// debug information.
   401  	for _, r := range v.cc.externalDeps {
   402  		src := r.src
   403  		a := &src.arcs[r.index]
   404  		if a.decremented || a.kind != NOTIFY {
   405  			continue
   406  		}
   407  		if n := src.src.getState(n.ctx); n != nil {
   408  			n.completeNodeTasks(mode)
   409  		}
   410  	}
   411  
   412  	// As long as ancestors are not processed, it is still possible for
   413  	// conjuncts to be inserted. Until that time, it is not okay to decrement
   414  	// theroot. It is not necessary to wait on tasks to complete, though,
   415  	// as pending tasks will have their own dependencies on root, meaning it
   416  	// is safe to decrement here.
   417  	if !n.meets(allAncestorsProcessed) && !n.node.Label.IsLet() && mode != finalize {
   418  		return
   419  	}
   420  
   421  	// At this point, no more conjuncts will be added, so we could decrement
   422  	// the notification counters.
   423  
   424  	if cc := v.rootCloseContext(n.ctx); !cc.isDecremented { // TODO: use v.cc
   425  		cc.isDecremented = true
   426  
   427  		cc.decDependent(n.ctx, ROOT, nil) // REF(decrement:nodeDone)
   428  	}
   429  
   430  	return
   431  }
   432  
   433  func (n *nodeContext) updateScalar() {
   434  	// Set BaseValue to scalar, but only if it was not set before. Most notably,
   435  	// errors should not be discarded.
   436  	if n.scalar != nil && (!n.node.IsErr() || isCyclePlaceholder(n.node.BaseValue)) {
   437  		n.node.BaseValue = n.scalar
   438  		n.signal(scalarKnown)
   439  	}
   440  }
   441  
   442  func (n *nodeContext) completeAllArcs(needs condition, mode runMode) bool {
   443  	if n.node.status == evaluatingArcs {
   444  		// NOTE: this was an "incomplete" error pre v0.6. If this is a problem
   445  		// we could make this a CycleError. Technically, this may be correct,
   446  		// as it is possible to make the values exactly as the inserted
   447  		// values. It seems more user friendly to just disallow this, though.
   448  		// TODO: make uniform error messages
   449  		// see compbottom2.cue:
   450  		n.ctx.addErrf(CycleError, pos(n.node), "mutual dependency")
   451  		n.node.IsCyclic = true
   452  		// Consider using this, although not all
   453  		// mutual dependencies are irrecoverable.
   454  		// n.reportCycleError()
   455  	}
   456  
   457  	// TODO: remove the use of updateStatus as a cycle detection mechanism.
   458  	n.node.updateStatus(evaluatingArcs)
   459  
   460  	if n.underlying != nil {
   461  		// References within the disjunct may end up referencing the layer that
   462  		// this node overlays. Also for these nodes we want to be able to detect
   463  		// structural cycles early. For this reason, we also set the
   464  		// evaluatingArcs status in the underlying layer.
   465  		//
   466  		// TODO: for now, this seems not necessary. Moreover, this will cause
   467  		// benchmarks/cycle to display a spurious structural cycle. But it
   468  		// shortens some of the structural cycle depths. So consider using this.
   469  		//
   470  		// status := n.underlying.status
   471  		// n.underlying.updateStatus(evaluatingArcs) defer func() {
   472  		// n.underlying.status = status }()
   473  	}
   474  
   475  	// TODO: this should only be done if n is not currently running tasks.
   476  	// Investigate how to work around this.
   477  	n.completeNodeTasks(finalize)
   478  
   479  	for _, r := range n.node.cc.externalDeps {
   480  		src := r.src
   481  		a := &src.arcs[r.index]
   482  		if a.decremented {
   483  			continue
   484  		}
   485  		a.decremented = true
   486  
   487  		// FIXME: we should be careful to not evaluate parent nodes if we
   488  		// are inside a disjunction, or at least ensure that there are no
   489  		// disjunction values leaked into non-disjunction nodes through
   490  		// evaluating externalDeps.
   491  		src.src.unify(n.ctx, needTasksDone, attemptOnly)
   492  		a.cc.decDependent(n.ctx, a.kind, src) // REF(arcs)
   493  	}
   494  
   495  	n.incDepth()
   496  
   497  	// XXX(0.7): only set success if needs complete arcs.
   498  	success := true
   499  	// Visit arcs recursively to validate and compute error.
   500  	for n.arcPos < len(n.node.Arcs) {
   501  		a := n.node.Arcs[n.arcPos]
   502  		n.arcPos++
   503  
   504  		if !a.unify(n.ctx, needs, mode) {
   505  			success = false
   506  		}
   507  
   508  		// At this point we need to ensure that all notification cycles
   509  		// for Arc a have been processed.
   510  
   511  		if a.ArcType == ArcPending {
   512  			// TODO: cancel tasks?
   513  			// TODO: is this ever run? Investigate once new evaluator work is
   514  			// complete.
   515  			a.ArcType = ArcNotPresent
   516  			continue
   517  		}
   518  
   519  		// Errors are allowed in let fields. Handle errors and failure to
   520  		// complete accordingly.
   521  		if !a.Label.IsLet() && a.ArcType <= ArcRequired {
   522  			a := a.DerefValue()
   523  			if err := a.Bottom(); err != nil {
   524  				n.node.AddChildError(err)
   525  			}
   526  			success = true // other arcs are irrelevant
   527  		}
   528  
   529  		// TODO: harmonize this error with "cannot combine"
   530  		switch {
   531  		case a.ArcType > ArcRequired, !a.Label.IsString():
   532  		case n.kind&StructKind == 0:
   533  			if !n.node.IsErr() {
   534  				n.reportFieldMismatch(pos(a.Value()), nil, a.Label, n.node.Value())
   535  			}
   536  			// case !wasVoid:
   537  			// case n.kind == TopKind:
   538  			// 	// Theoretically it may be possible that a "void" arc references
   539  			// 	// this top value where it really should have been a struct. One
   540  			// 	// way to solve this is to have two passes over the arcs, where
   541  			// 	// the first pass additionally analyzes whether comprehensions
   542  			// 	// will yield values and "un-voids" an arc ahead of the rest.
   543  			// 	//
   544  			// 	// At this moment, though, I fail to see a possibility to create
   545  			// 	// faulty CUE using this mechanism, though. At most error
   546  			// 	// messages are a bit unintuitive. This may change once we have
   547  			// 	// functionality to reflect on types.
   548  			// 	if _, ok := n.node.BaseValue.(*Bottom); !ok {
   549  			// 		n.node.BaseValue = &StructMarker{}
   550  			// 		n.kind = StructKind
   551  			// 	}
   552  		}
   553  	}
   554  
   555  	n.decDepth()
   556  
   557  	k := 0
   558  	for _, a := range n.node.Arcs {
   559  		if a.ArcType != ArcNotPresent {
   560  			n.node.Arcs[k] = a
   561  			k++
   562  		}
   563  	}
   564  	n.node.Arcs = n.node.Arcs[:k]
   565  
   566  	// This should be called after all arcs have been processed, because
   567  	// whether sharing is possible or not may depend on how arcs with type
   568  	// ArcPending will resolve.
   569  	n.finalizeSharing()
   570  
   571  	// Strip struct literals that were not initialized and are not part
   572  	// of the output.
   573  	//
   574  	// TODO(perf): we could keep track if any such structs exist and only
   575  	// do this removal if there is a change of shrinking the list.
   576  	k = 0
   577  	for _, s := range n.node.Structs {
   578  		if s.initialized {
   579  			n.node.Structs[k] = s
   580  			k++
   581  		}
   582  	}
   583  	n.node.Structs = n.node.Structs[:k]
   584  
   585  	// TODO: This seems to be necessary, but enables structural cycles.
   586  	// Evaluator whether we still need this.
   587  	//
   588  	// pc := n.node.PatternConstraints
   589  	// if pc == nil {
   590  	// 	return success
   591  	// }
   592  	// for _, c := range pc.Pairs {
   593  	// 	c.Constraint.Finalize(n.ctx)
   594  	// }
   595  
   596  	return success
   597  }
   598  
   599  func (n *nodeContext) evalArcTypes() {
   600  	for _, a := range n.node.Arcs {
   601  		if a.ArcType != ArcPending {
   602  			continue
   603  		}
   604  		a.unify(n.ctx, arcTypeKnown, yield)
   605  		// Ensure the arc is processed up to the desired level
   606  		if a.ArcType == ArcPending {
   607  			// TODO: cancel tasks?
   608  			a.ArcType = ArcNotPresent
   609  		}
   610  	}
   611  }
   612  
   613  func (v *Vertex) lookup(c *OpContext, pos token.Pos, f Feature, flags combinedFlags) *Vertex {
   614  	task := c.current()
   615  	needs := flags.conditions()
   616  	runMode := flags.runMode()
   617  
   618  	v = v.DerefValue()
   619  
   620  	c.Logf(c.vertex, "LOOKUP %v", f)
   621  
   622  	state := v.getState(c)
   623  	if state != nil {
   624  		// If the scheduler associated with this vertex was already running,
   625  		// it means we have encountered a cycle. In that case, we allow to
   626  		// proceed with partial data, in which case a "pending" arc will be
   627  		// created to be completed later.
   628  
   629  		// Report error for now.
   630  		if state.hasErr() {
   631  			c.AddBottom(state.getErr())
   632  		}
   633  		// TODO: ideally this should not be run at this point. Consider under
   634  		// which circumstances this is still necessary, and at least ensure
   635  		// this will not be run if node v currently has a running task.
   636  		state.completeNodeTasks(attemptOnly)
   637  	}
   638  
   639  	// TODO: remove because unnecessary?
   640  	if task != nil && task.state != taskRUNNING {
   641  		return nil // abort, task is blocked or terminated in a cycle.
   642  	}
   643  
   644  	// TODO: verify lookup types.
   645  
   646  	arc := v.LookupRaw(f)
   647  	// We leave further dereferencing to the caller, but we do dereference for
   648  	// the remainder of this function to be able to check the status.
   649  	arcReturn := arc
   650  	if arc != nil {
   651  		arc = arc.DerefNonRooted()
   652  		// TODO(perf): NonRooted is the minimum, but consider doing more.
   653  		// arc = arc.DerefValue()
   654  	}
   655  
   656  	// TODO: clean up this logic:
   657  	// - signal arcTypeKnown when ArcMember or ArcNotPresent is set,
   658  	//   similarly to scalarKnown.
   659  	// - make it clear we want to yield if it is now known if a field exists.
   660  
   661  	var arcState *nodeContext
   662  	switch {
   663  	case arc != nil:
   664  		if arc.ArcType == ArcMember {
   665  			return arcReturn
   666  		}
   667  		arcState = arc.getState(c)
   668  
   669  	case state == nil || state.meets(needTasksDone):
   670  		// This arc cannot exist.
   671  		v.reportFieldIndexError(c, pos, f)
   672  		return nil
   673  
   674  	default:
   675  		arc = &Vertex{Parent: state.node, Label: f, ArcType: ArcPending}
   676  		v.Arcs = append(v.Arcs, arc)
   677  		arcState = arc.getState(c) // TODO: consider using getBareState.
   678  	}
   679  
   680  	if arcState != nil && (!arcState.meets(needTasksDone) || !arcState.meets(arcTypeKnown)) {
   681  		needs |= arcTypeKnown
   682  		// If this arc is not ArcMember, which it is not at this point,
   683  		// any pending arcs could influence the field set.
   684  		for _, a := range arc.Arcs {
   685  			if a.ArcType == ArcPending {
   686  				needs |= fieldSetKnown
   687  				break
   688  			}
   689  		}
   690  		arcState.completeNodeTasks(attemptOnly)
   691  
   692  		// Child nodes, if pending and derived from a comprehension, may
   693  		// still cause this arc to become not pending.
   694  		if arc.ArcType != ArcMember {
   695  			for _, a := range arcState.node.Arcs {
   696  				if a.ArcType == ArcPending {
   697  					a.unify(c, arcTypeKnown, runMode)
   698  				}
   699  			}
   700  		}
   701  
   702  		switch runMode {
   703  		case ignore, attemptOnly:
   704  			// TODO: should we avoid notifying ArcPending vertices here?
   705  			if task != nil {
   706  				arcState.addNotify2(task.node.node, task.id)
   707  			}
   708  			return arcReturn
   709  
   710  		case yield:
   711  			arcState.process(needs, yield)
   712  			// continue processing, as successful processing may still result
   713  			// in an invalid field.
   714  
   715  		case finalize:
   716  			// TODO: should we try to use finalize? Using it results in errors and this works. It would be more principled, though.
   717  			arcState.process(needs, yield)
   718  		}
   719  	}
   720  
   721  	switch arc.ArcType {
   722  	case ArcMember:
   723  		return arcReturn
   724  
   725  	case ArcOptional, ArcRequired:
   726  		label := f.SelectorString(c.Runtime)
   727  		b := &Bottom{
   728  			Code: IncompleteError,
   729  			Err: c.NewPosf(pos,
   730  				"cannot reference optional field: %s", label),
   731  		}
   732  		c.AddBottom(b)
   733  		// TODO: yield failure
   734  		return nil
   735  
   736  	case ArcNotPresent:
   737  		v.reportFieldIndexError(c, pos, f)
   738  		return nil
   739  
   740  	case ArcPending:
   741  		// should not happen.
   742  		panic("unreachable")
   743  	}
   744  
   745  	v.reportFieldIndexError(c, pos, f)
   746  	return nil
   747  }
   748  
   749  // accept reports whether the given feature is allowed by the pattern
   750  // constraints.
   751  func (v *Vertex) accept(ctx *OpContext, f Feature) bool {
   752  	// TODO: this is already handled by callers at the moment, but it may be
   753  	// better design to move this here.
   754  	// if v.LookupRaw(f) != nil {
   755  	// 	return true, true
   756  	// }
   757  
   758  	v = v.DerefValue()
   759  
   760  	pc := v.PatternConstraints
   761  	if pc == nil {
   762  		return false
   763  	}
   764  
   765  	return matchPattern(ctx, pc.Allowed, f)
   766  }