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 }