cuelang.org/go@v0.13.0/internal/core/adt/states.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  // TODO: clean up following notes:
    18  
    19  // Used in expr.go:
    20  // - ctx.value  (uses: noncrete scalar allowed, concrete scalar, concrete composite)
    21  //   - evalState
    22  // - ctx.node (need to know all fields)
    23  // - ctx.lookup
    24  // - ctx.concrete
    25  //
    26  // - ctx.relLabel
    27  //     OK: always exists
    28  // - ctx.relNode (upcount + unify(partial))
    29  //     OK: node always exists.
    30  //
    31  // - ctx.evalState (in validation of comparison against bottom)
    32  //
    33  // - arc.Finalize (finalized)
    34  // - CompleteArcs (conjuncts)
    35  //
    36  
    37  // lookup in p1
    38  //    - process remaining field todos
    39  
    40  // lookup:
    41  // if node is currently processing, just look up directly and create
    42  // field with notification.
    43  //
    44  // if node has not been processed, process once.
    45  //
    46  // Any dynamic fields should have been triggered by the existence of a new
    47  // arc. This will either cascade the evaluation or not.
    48  
    49  // p1: {
    50  // 	(p1.baz): "bar" // t1
    51  // 	(p1.foo): "baz" // t2
    52  // 	baz: "foo"
    53  // }
    54  //
    55  // <p1, fieldsKnown> -> t1 -> <p1.baz, scalar>
    56  // <p1.foo, scalar> -> t1
    57  // <p1, fieldsKnown> -> t2 -> <p1.foo, scalar>
    58  
    59  // p2: {
    60  // 	(p2[p2.baz]): "bar"
    61  // 	(p2.foo): "baz"
    62  // 	baz: "qux"
    63  // 	qux: "foo"
    64  // }
    65  
    66  // b -> a - > b: detected cycle in b:
    67  //
    68  //		xxx register expression (a-10) being processed as a post constraint.
    69  //		add task to pending.
    70  //		register value as waiting for scalar to be completed later.
    71  //		return with cycle/ in complete error.
    72  //
    73  //	  - in b:
    74  //	    xxx register expression (b+10) as post constraint.
    75  //	    add task to pending
    76  //	    register value as waiting for scalar to be completed later.
    77  //	    5 is processed and set
    78  //	    this completes the task in b
    79  //	    this sets a scalar in b
    80  //	    this completes the expression in a
    81  //
    82  //	    b: a - 10
    83  //	    a: b + 10
    84  //	    a: 5
    85  //
    86  //	    a: a
    87  //	    a: 5
    88  //
    89  
    90  // These are the condition types of the CUE evaluator. A scheduler
    91  // is associated with a single Vertex. So when these states refer to a Vertex,
    92  // it is the Vertex associated with the scheduler.
    93  //
    94  // There are core conditions and condition sets. The core conditions are
    95  // determined during operation as conditions are met. The condition sets are
    96  // used to indicate a set of required or provided conditions.
    97  //
    98  // Core conditions can be signal conditions or counter conditions. A counter
    99  // condition is triggered if all conjuncts that contribute to the computation
   100  // of this condition have been met. A signal condition is triggered as soon as
   101  // evidence is found that this condition is met. Unless otherwise specified,
   102  // conditions are counter conditions.
   103  const (
   104  	// allAncestorsProcessed indicates that all conjuncts that could be added
   105  	// to the Vertex by any of its ancestors have been added. In other words,
   106  	// all ancestors schedulers have reached the state fieldConjunctsKnown.
   107  	//
   108  	// This is a signal condition. It is explicitly set in unify when a
   109  	// parent meets fieldConjunctsKnown|allAncestorsProcessed.
   110  	allAncestorsProcessed condition = 1 << iota
   111  
   112  	// Really: all ancestor subfield tasks processed.
   113  
   114  	// arcTypeKnown means that the ArcType value of a Vertex is fully
   115  	// determined. The ArcType of all fields of a Vertex need to be known
   116  	// before the complete set of fields of this Vertex can be known.
   117  	arcTypeKnown
   118  
   119  	// valueKnown means that it is known what the "type" of the value would be
   120  	// if present.
   121  	valueKnown
   122  
   123  	// scalarKnown indicates that a Vertex has either a concrete scalar value or
   124  	// that it is known that it will never have a scalar value.
   125  	//
   126  	// This is a signal condition that is reached when:
   127  	//    - a node is set to a concrete scalar value
   128  	//    - a node is set to an error
   129  	//    - or if ...state is reached.
   130  	//
   131  	// TODO: rename to something better?
   132  	scalarKnown
   133  
   134  	// listTypeKnown indicates that it is known that lists unified with this
   135  	// Vertex should be interpreted as integer indexed lists, as associative
   136  	// lists, or an error.
   137  	//
   138  	// This is a signal condition that is reached when:
   139  	//    - allFieldsKnown is reached (all expressions have )
   140  	//    - it is unified with an associative list type
   141  	//
   142  	// TODO(assoclist): this is set to 0 below: This mode is only needed for
   143  	// associative lists and is not yet used. We should use this again and fix
   144  	// any performance issues when we implement associative lists.
   145  	// listTypeKnown
   146  
   147  	// fieldConjunctsKnown means that all the conjuncts of all fields are
   148  	// known.
   149  	fieldConjunctsKnown
   150  
   151  	// fieldSetKnown means that all fields of this node are known. This is true
   152  	// if all tasks that can add a field have been processed and if
   153  	// all pending arcs have been resolved.
   154  	fieldSetKnown
   155  
   156  	// // allConjunctsKnown means that all conjuncts have been registered as a
   157  	// // task. allParentsProcessed must be true for this to be true.
   158  	// allConjunctsKnown
   159  
   160  	// allTasksCompleted means that all tasks of a Vertex have been completed
   161  	// with the exception of validation tasks. A Vertex may still not be
   162  	// finalized.
   163  	allTasksCompleted
   164  
   165  	// subFieldsProcessed means that all tasks of a Vertex, including those of
   166  	// its arcs have been completed.
   167  	//
   168  	// This is a signal condition that is met if all arcs have reached the
   169  	// the state finalStateKnown.
   170  	//
   171  	subFieldsProcessed
   172  
   173  	// pendingKnown means that this task is relevant for resolving whether an
   174  	// arc is present or not. This implies actTypeKnown.
   175  	pendingKnown
   176  
   177  	// disjunctionTask indicates that this task is a disjunction. This is
   178  	// used to trigger finalization of disjunctions.
   179  	disjunctionTask
   180  
   181  	leftOfMaxCoreCondition
   182  
   183  	finalStateKnown condition = leftOfMaxCoreCondition - 1
   184  
   185  	preValidation condition = finalStateKnown //&^ validationCompleted
   186  
   187  	conditionsUsingCounters = arcTypeKnown |
   188  		valueKnown |
   189  		fieldConjunctsKnown |
   190  		allTasksCompleted |
   191  		// TODO: not adding this improves error message for issue3691 in
   192  		// eval/comprehensions.txtar. But without this, TestVisit of dep
   193  		// panics. Investigate.
   194  		pendingKnown |
   195  		disjunctionTask
   196  
   197  	// The xConjunct condition sets indicate a conjunct MAY contribute the to
   198  	// final result. For some conjuncts it may not be known what the
   199  	// contribution will be. In such a cases the set that reflects all possible
   200  	// contributions should be used. For instance, an embedded reference may
   201  	// resolve to a scalar or struct.
   202  	//
   203  	// All conjunct states include allTasksCompleted.
   204  
   205  	// a genericConjunct is one for which the contributions to the states
   206  	// are not known in advance. For instance, an embedded reference can be
   207  	// anything. In such case, all conditions are included.
   208  	genericConjunct = allTasksCompleted |
   209  		scalarKnown |
   210  		valueKnown |
   211  		fieldConjunctsKnown
   212  
   213  	// genericDisjunction is used to record processDisjunction tasks.
   214  	genericDisjunction = genericConjunct | disjunctionTask
   215  
   216  	// a fieldConjunct is on that only adds a new field to the struct.
   217  	fieldConjunct = allTasksCompleted |
   218  		fieldConjunctsKnown
   219  
   220  	// a scalarConjunct is one that is guaranteed to result in a scalar or
   221  	// list value.
   222  	scalarConjunct = allTasksCompleted |
   223  		scalarKnown |
   224  		valueKnown |
   225  		disjunctionTask
   226  
   227  	// a scalarValue is one that is guaranteed to result in a scalar.
   228  	// TODO: use more widely instead of scalarKnown.
   229  	scalarValue = scalarKnown | disjunctionTask
   230  
   231  	// needsX condition sets are used to indicate which conditions need to be
   232  	// met.
   233  
   234  	needFieldConjunctsKnown = fieldConjunctsKnown |
   235  		allAncestorsProcessed
   236  
   237  	needFieldSetKnown = fieldSetKnown |
   238  		allAncestorsProcessed
   239  
   240  	needTasksDone = allAncestorsProcessed | allTasksCompleted
   241  
   242  	// concreteKnown means that we know whether a value is concrete or not.
   243  	// At the moment this is equal to 'scalarKnown'.
   244  	concreteKnown = scalarKnown
   245  
   246  	// TODO(assoclist): see comment above.
   247  	listTypeKnown condition = 0
   248  )
   249  
   250  // schedConfig configures a taskContext with the states needed for the
   251  // CUE evaluator. It is used in OpContext.New as a template for creating
   252  // new taskContexts.
   253  var schedConfig = taskContext{
   254  	counterMask: conditionsUsingCounters,
   255  	autoUnblock: listTypeKnown | scalarKnown | arcTypeKnown,
   256  	complete:    stateCompletions,
   257  }
   258  
   259  // stateCompletions indicates the completion of conditions based on the
   260  // completions of other conditions.
   261  func stateCompletions(s *scheduler) condition {
   262  	x := s.completed
   263  	v := s.node.node
   264  	if s.node.ctx.LogEval > 0 {
   265  		s.node.Logf("=== stateCompletions: %v  %v", v.Label, s.completed)
   266  	}
   267  	if x.meets(allAncestorsProcessed) {
   268  		x |= conditionsUsingCounters &^ s.provided
   269  		// If we have a pending or constraint arc, a sub arc may still cause the
   270  		// arc to become a member. For instance, if 'a' is pending in the
   271  		// following
   272  		//   if x != _!_ {
   273  		//       a: b: 1
   274  		//   }
   275  		// it may still become not pending if 'b' becomes a regular arc.
   276  		if s.counters[arcTypeKnown] == 0 && x.meets(subFieldsProcessed) {
   277  			x |= arcTypeKnown
   278  		}
   279  	}
   280  	switch {
   281  	case v.ArcType == ArcMember, v.ArcType == ArcNotPresent:
   282  		x |= arcTypeKnown
   283  	case x&arcTypeKnown != 0 && v.ArcType == ArcPending:
   284  		v.ArcType = ArcNotPresent
   285  	}
   286  
   287  	if x.meets(valueKnown) {
   288  		// NOTE: in this case, scalarKnown is not the same as concreteKnown,
   289  		// especially if this arc is Pending, as it may still become concrete.
   290  		// We probably want to separate this out.
   291  		if v.ArcType == ArcMember || v.ArcType == ArcNotPresent {
   292  			x |= scalarKnown
   293  		}
   294  		x |= listTypeKnown
   295  	}
   296  
   297  	if x.meets(needFieldConjunctsKnown | needTasksDone) {
   298  		switch {
   299  		case x.meets(subFieldsProcessed):
   300  			x |= fieldSetKnown
   301  		default:
   302  			for _, a := range v.Arcs {
   303  				if a.ArcType == ArcPending {
   304  					return x
   305  				}
   306  			}
   307  			x |= fieldSetKnown
   308  		}
   309  	}
   310  	return x
   311  }
   312  
   313  // allChildConjunctsKnown indicates that all conjuncts have been added by
   314  // the parents and every conjunct that may add fields to subfields have been
   315  // processed.
   316  func (v *Vertex) allChildConjunctsKnown() bool {
   317  	if v == nil {
   318  		return true
   319  	}
   320  
   321  	if v.Status() == finalized {
   322  		// This can happen, for instance, if this is called on a parent of a
   323  		// rooted node that is marked as a parent for a dynamic node.
   324  		// In practice this should be handled by the caller, but we add this
   325  		// as an extra safeguard.
   326  		// TODO: remove this check at some point.
   327  		return true
   328  	}
   329  
   330  	return v.state.meets(fieldConjunctsKnown | allAncestorsProcessed)
   331  }
   332  
   333  func (n *nodeContext) scheduleTask(r *runner, env *Environment, x Node, ci CloseInfo) *task {
   334  	t := &task{
   335  		run:  r,
   336  		node: n,
   337  
   338  		env: env,
   339  		id:  ci,
   340  		x:   x,
   341  	}
   342  	n.insertTask(t)
   343  	return t
   344  }