cuelang.org/go@v0.10.1/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  	listTypeKnown
   142  
   143  	// fieldConjunctsKnown means that all the conjuncts of all fields are
   144  	// known.
   145  	fieldConjunctsKnown
   146  
   147  	// fieldSetKnown means that all fields of this node are known. This is true
   148  	// if all tasks that can add a field have been processed and if
   149  	// all pending arcs have been resolved.
   150  	fieldSetKnown
   151  
   152  	// // allConjunctsKnown means that all conjuncts have been registered as a
   153  	// // task. allParentsProcessed must be true for this to be true.
   154  	// allConjunctsKnown
   155  
   156  	// allTasksCompleted means that all tasks of a Vertex have been completed
   157  	// with the exception of validation tasks. A Vertex may still not be
   158  	// finalized.
   159  	allTasksCompleted
   160  
   161  	// subFieldsProcessed means that all tasks of a Vertex, including those of
   162  	// its arcs have been completed.
   163  	//
   164  	// This is a signal condition that is met if all arcs have reached the
   165  	// the state finalStateKnown.
   166  	//
   167  	subFieldsProcessed
   168  
   169  	// disjunctionTask indicates that this task is a disjunction. This is
   170  	// used to trigger finalization of disjunctions.
   171  	disjunctionTask
   172  
   173  	leftOfMaxCoreCondition
   174  
   175  	finalStateKnown condition = leftOfMaxCoreCondition - 1
   176  
   177  	preValidation condition = finalStateKnown //&^ validationCompleted
   178  
   179  	conditionsUsingCounters = arcTypeKnown |
   180  		valueKnown |
   181  		fieldConjunctsKnown |
   182  		allTasksCompleted
   183  
   184  	// The xConjunct condition sets indicate a conjunct MAY contribute the to
   185  	// final result. For some conjuncts it may not be known what the
   186  	// contribution will be. In such a cases the set that reflects all possible
   187  	// contributions should be used. For instance, an embedded reference may
   188  	// resolve to a scalar or struct.
   189  	//
   190  	// All conjunct states include allTasksCompleted.
   191  
   192  	// a genericConjunct is one for which the contributions to the states
   193  	// are not known in advance. For instance, an embedded reference can be
   194  	// anything. In such case, all conditions are included.
   195  	genericConjunct = allTasksCompleted |
   196  		scalarKnown |
   197  		valueKnown |
   198  		fieldConjunctsKnown
   199  
   200  	// genericDisjunction is used to record processDisjunction tasks.
   201  	genericDisjunction = genericConjunct | disjunctionTask
   202  
   203  	// a fieldConjunct is on that only adds a new field to the struct.
   204  	fieldConjunct = allTasksCompleted |
   205  		fieldConjunctsKnown
   206  
   207  	// a scalarConjunct is one that is guaranteed to result in a scalar or
   208  	// list value.
   209  	scalarConjunct = allTasksCompleted |
   210  		scalarKnown |
   211  		valueKnown
   212  
   213  	// needsX condition sets are used to indicate which conditions need to be
   214  	// met.
   215  
   216  	needFieldConjunctsKnown = fieldConjunctsKnown |
   217  		allAncestorsProcessed
   218  
   219  	needFieldSetKnown = fieldSetKnown |
   220  		allAncestorsProcessed
   221  
   222  	needTasksDone = allAncestorsProcessed | allTasksCompleted
   223  
   224  	// concreteKnown means that we know whether a value is concrete or not.
   225  	// At the moment this is equal to 'scalarKnown'.
   226  	concreteKnown = scalarKnown
   227  )
   228  
   229  // schedConfig configures a taskContext with the states needed for the
   230  // CUE evaluator. It is used in OpContext.New as a template for creating
   231  // new taskContexts.
   232  var schedConfig = taskContext{
   233  	counterMask: conditionsUsingCounters,
   234  	autoUnblock: listTypeKnown | scalarKnown | arcTypeKnown,
   235  	complete:    stateCompletions,
   236  }
   237  
   238  // stateCompletions indicates the completion of conditions based on the
   239  // completions of other conditions.
   240  func stateCompletions(s *scheduler) condition {
   241  	x := s.completed
   242  	v := s.node.node
   243  	s.node.Logf("=== stateCompletions: %v  %v", v.Label, s.completed)
   244  	if x.meets(allAncestorsProcessed) {
   245  		x |= conditionsUsingCounters &^ s.provided
   246  		// If we have a pending arc, a sub arc may still cause the arc to
   247  		// become not pending. For instance, if 'a' is pending in the following
   248  		//   if x != _!_ {
   249  		//       a: b: 1
   250  		//   }
   251  		// it may still become not pending if 'b' becomes a regular arc.
   252  		if s.counters[arcTypeKnown] == 0 && x.meets(subFieldsProcessed) {
   253  			x |= arcTypeKnown
   254  		}
   255  	}
   256  	switch {
   257  	case v.ArcType == ArcMember, v.ArcType == ArcNotPresent:
   258  		x |= arcTypeKnown
   259  	case x&arcTypeKnown != 0 && v.ArcType == ArcPending:
   260  		v.ArcType = ArcNotPresent
   261  	}
   262  
   263  	if x.meets(valueKnown) {
   264  		// NOTE: in this case, scalarKnown is not the same as concreteKnown,
   265  		// especially if this arc is Pending, as it may still become concrete.
   266  		// We probably want to separate this out.
   267  		if v.ArcType == ArcMember || v.ArcType == ArcNotPresent {
   268  			x |= scalarKnown
   269  		}
   270  		x |= listTypeKnown
   271  	}
   272  
   273  	if x.meets(needFieldConjunctsKnown | needTasksDone) {
   274  		switch {
   275  		case x.meets(subFieldsProcessed):
   276  			x |= fieldSetKnown
   277  		default:
   278  			for _, a := range v.Arcs {
   279  				if a.ArcType == ArcPending {
   280  					return x
   281  				}
   282  			}
   283  			x |= fieldSetKnown
   284  		}
   285  	}
   286  	return x
   287  }
   288  
   289  // allChildConjunctsKnown indicates that all conjuncts have been added by
   290  // the parents and every conjunct that may add fields to subfields have been
   291  // processed.
   292  func (v *Vertex) allChildConjunctsKnown() bool {
   293  	if v == nil {
   294  		return true
   295  	}
   296  
   297  	return v.state.meets(fieldConjunctsKnown | allAncestorsProcessed)
   298  }
   299  
   300  func (n *nodeContext) scheduleTask(r *runner, env *Environment, x Node, ci CloseInfo) *task {
   301  	t := &task{
   302  		run:  r,
   303  		node: n,
   304  
   305  		env: env,
   306  		id:  ci,
   307  		x:   x,
   308  	}
   309  	n.insertTask(t)
   310  	return t
   311  }
   312  
   313  // require ensures that a given condition is met for the given Vertex by
   314  // evaluating it. It yields execution back to the scheduler if it cannot
   315  // be completed at this point.
   316  func (c *OpContext) require(v *Vertex, needs condition) {
   317  	state := v.getState(c)
   318  	if state == nil {
   319  		return
   320  	}
   321  	state.process(needs, yield)
   322  }
   323  
   324  // scalarValue evaluates the given expression and either returns a
   325  // concrete value or schedules the task for later evaluation.
   326  func (ctx *OpContext) scalarValue(t *task, x Expr) Value {
   327  	return ctx.value(x, require(0, scalarKnown))
   328  }