cuelang.org/go@v0.13.0/internal/core/adt/tasks.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/ast" 21 "cuelang.org/go/cue/token" 22 ) 23 24 var ( 25 handleExpr *runner 26 handleResolver *runner 27 handleDynamic *runner 28 handlePatternConstraint *runner 29 handleComprehension *runner 30 handleListLit *runner 31 handleListVertex *runner 32 handleDisjunctions *runner 33 ) 34 35 // Use init to avoid a (spurious?) cyclic dependency in Go. 36 func init() { 37 handleExpr = &runner{ 38 name: "Expr", 39 f: processExpr, 40 completes: genericConjunct, 41 } 42 handleResolver = &runner{ 43 name: "Resolver", 44 f: processResolver, 45 completes: genericConjunct, 46 } 47 handleDynamic = &runner{ 48 name: "Dynamic", 49 f: processDynamic, 50 completes: fieldConjunct, 51 } 52 handlePatternConstraint = &runner{ 53 name: "PatternConstraint", 54 f: processPatternConstraint, 55 completes: allTasksCompleted | fieldConjunctsKnown, 56 } 57 handleComprehension = &runner{ 58 name: "Comprehension", 59 f: processComprehension, 60 completes: valueKnown | allTasksCompleted | fieldConjunctsKnown | pendingKnown, 61 } 62 handleListLit = &runner{ 63 name: "ListLit", 64 f: processListLit, 65 completes: fieldConjunct, 66 needs: listTypeKnown, 67 } 68 handleListVertex = &runner{ 69 name: "ListVertex", 70 f: processListVertex, 71 completes: fieldConjunct, 72 needs: listTypeKnown, 73 } 74 handleDisjunctions = &runner{ 75 name: "Disjunctions", 76 f: processDisjunctions, 77 completes: genericDisjunction, 78 priority: 1, 79 } 80 } 81 82 // This file contains task runners (func(ctx *OpContext, t *task, mode runMode)). 83 84 func processExpr(ctx *OpContext, t *task, mode runMode) { 85 x := t.x.(Expr) 86 87 state := combineMode(concreteKnown, mode) 88 v, ci := ctx.evalStateCI(x, state) 89 if ci.CycleType == IsCyclic && t.node.node.IsPatternConstraint { 90 // This is an optional cycle that we will ignore. 91 return 92 } 93 ci = t.updateCI(ci) 94 t.node.insertValueConjunct(t.env, v, ci) 95 } 96 97 func processResolver(ctx *OpContext, t *task, mode runMode) { 98 r := t.x.(Resolver) 99 100 // TODO(perf): if we are resolving a value where we know a scalar value can 101 // be conclusive, we could avoid triggering evaluating disjunctions. This 102 // would be a pretty significant rework, though. 103 104 arc := r.resolve(ctx, combineMode(fieldSetKnown, mode)) 105 // TODO: ensure that resolve always returns one of these two. 106 if arc == nil || arc == emptyNode { 107 // TODO: yield instead? 108 return 109 } 110 arc = arc.DerefNonDisjunct() 111 112 if ctx.LogEval > 0 { 113 ctx.Logf(t.node.node, "RESOLVED %v to %v %v", r, arc.Label, fmt.Sprintf("%p", arc)) 114 } 115 // TODO: consider moving after markCycle or removing. 116 d := arc.DerefDisjunct() 117 118 ci := t.updateCI(ctx.ci) 119 120 // A reference that points to itself indicates equality. In that case 121 // we are done computing and we can return the arc as is. 122 ci, skip := t.node.detectCycleV3(d, t.env, r, ci) 123 if skip { 124 // Either we have a structure cycle or we are unifying with another 125 // conjunct. In either case, we are no longer structure sharing here. 126 t.node.unshare() 127 return 128 } 129 130 if t.defunct { 131 return 132 } 133 134 // TODO: consider moving this to within if arc.nonRooted below. 135 if b, ok := d.BaseValue.(*Bottom); ok && b.Code == StructuralCycleError { 136 // TODO: ensure better positioning information. 137 ctx.AddBottom(b) 138 return 139 } 140 141 c := MakeConjunct(t.env, t.x, ci) 142 t.node.scheduleVertexConjuncts(c, arc, ci) 143 } 144 145 func processDynamic(ctx *OpContext, t *task, mode runMode) { 146 n := t.node 147 148 field := t.x.(*DynamicField) 149 150 v := ctx.value(field.Key, combineMode(scalarValue, mode)) 151 if v == nil { 152 return 153 } 154 155 if v.Concreteness() != Concrete { 156 n.addBottom(&Bottom{ 157 Code: IncompleteError, 158 Node: n.node, 159 Err: ctx.NewPosf(pos(field.Key), 160 "key value of dynamic field must be concrete, found %v", v), 161 }) 162 return 163 } 164 165 f := ctx.Label(field.Key, v) 166 // TODO: remove this restriction. 167 if f.IsInt() { 168 n.addErr(ctx.NewPosf(pos(field.Key), "integer fields not supported")) 169 return 170 } 171 172 // Do not update the CloseInfo, as we are passing the field value 173 // unevaluated. 174 ci := t.id 175 176 c := MakeConjunct(t.env, field, ci) 177 n.insertArc(f, field.ArcType, c, ci, true) 178 } 179 180 func processPatternConstraint(ctx *OpContext, t *task, mode runMode) { 181 n := t.node 182 183 field := t.x.(*BulkOptionalField) 184 185 // Note that the result may be a disjunction. Be sure to not take the 186 // default value as we want to retain the options of the disjunction. 187 v := ctx.evalState(field.Filter, require(0, scalarValue)) 188 if v == nil { 189 return 190 } 191 192 // Do not update the CloseInfo, as we are passing the constraint value 193 // unevaluated. 194 ci := t.id 195 196 n.insertPattern(v, MakeConjunct(t.env, t.x, ci)) 197 } 198 199 func processComprehension(ctx *OpContext, t *task, mode runMode) { 200 n := t.node 201 202 y := &envYield{ 203 envComprehension: t.comp, 204 leaf: t.leaf, 205 env: t.env, 206 id: t.id, 207 expr: t.x, 208 } 209 210 err := n.processComprehension(y, 0) 211 t.err = CombineErrors(nil, t.err, err) 212 t.comp.vertex.state.addBottom(err) 213 } 214 215 func processDisjunctions(c *OpContext, t *task, mode runMode) { 216 n := t.node 217 err := n.processDisjunctions() 218 t.err = CombineErrors(nil, t.err, err) 219 } 220 221 func processListLit(c *OpContext, t *task, mode runMode) { 222 n := t.node 223 224 l := t.x.(*ListLit) 225 226 n.updateCyclicStatusV3(t.id) 227 228 var ellipsis Node 229 230 index := int64(0) 231 hasComprehension := false 232 for j, elem := range l.Elems { 233 // TODO: Terminate early in case of runaway comprehension. 234 235 switch x := elem.(type) { 236 case *Comprehension: 237 err := c.yield(nil, t.env, x, 0, func(e *Environment) { 238 label, err := MakeLabel(x.Source(), index, IntLabel) 239 n.addErr(err) 240 index++ 241 id := t.id 242 // id.setOptional(t.node) 243 c := MakeConjunct(e, x.Value, id) 244 n.insertArc(label, ArcMember, c, id, true) 245 }) 246 hasComprehension = true 247 if err != nil { 248 n.addBottom(err) 249 return 250 } 251 252 case *Ellipsis: 253 // TODO(openlist): this will work once we have the same closedness 254 // semantics for lists as for structs. 255 // t.id.cc.isTotal = true 256 if j != len(l.Elems)-1 { 257 n.addErr(c.Newf("ellipsis must be last element in list")) 258 return 259 } 260 261 elem := x.Value 262 if elem == nil { 263 elem = &Top{} 264 } 265 266 id := t.id 267 id.setOptionalV3(t.node) 268 269 c := MakeConjunct(t.env, elem, id) 270 pat := &BoundValue{ 271 Op: GreaterEqualOp, 272 Value: n.ctx.NewInt64(index, x), 273 } 274 n.insertPattern(pat, c) 275 ellipsis = x 276 277 default: 278 label, err := MakeLabel(x.Source(), index, IntLabel) 279 n.addErr(err) 280 index++ 281 c := MakeConjunct(t.env, x, t.id) 282 n.insertArc(label, ArcMember, c, t.id, true) 283 } 284 285 if max := n.maxListLen; n.listIsClosed && int(index) > max { 286 n.invalidListLength(max, len(l.Elems), n.maxNode, l) 287 return 288 } 289 } 290 291 isClosed := ellipsis == nil 292 293 switch max := n.maxListLen; { 294 case int(index) < max: 295 if isClosed { 296 n.invalidListLength(int(index), max, l, n.maxNode) 297 return 298 } 299 300 case int(index) > max, 301 isClosed && !n.listIsClosed, 302 (isClosed == n.listIsClosed) && !hasComprehension: 303 n.maxListLen = int(index) 304 n.maxNode = l 305 n.listIsClosed = isClosed 306 } 307 308 n.updateListType(l, t.id, isClosed, ellipsis) 309 } 310 311 func processListVertex(c *OpContext, t *task, mode runMode) { 312 n := t.node 313 314 l := t.x.(*Vertex) 315 316 elems := l.Elems() 317 isClosed := l.IsClosedList() 318 319 // TODO: Share with code above. 320 switch max := n.maxListLen; { 321 case len(elems) < max: 322 if isClosed { 323 n.invalidListLength(len(elems), max, l, n.maxNode) 324 return 325 } 326 327 case len(elems) > max: 328 if n.listIsClosed { 329 n.invalidListLength(max, len(elems), n.maxNode, l) 330 return 331 } 332 n.listIsClosed = isClosed 333 n.maxListLen = len(elems) 334 n.maxNode = l 335 336 case isClosed: 337 n.listIsClosed = true 338 n.maxNode = l 339 } 340 341 for _, a := range elems { 342 if a.Conjuncts == nil { 343 c := MakeRootConjunct(nil, a) 344 n.insertArc(a.Label, ArcMember, c, CloseInfo{}, true) 345 continue 346 } 347 for _, c := range a.Conjuncts { 348 n.insertArc(a.Label, ArcMember, c, t.id, true) 349 } 350 } 351 352 n.updateListType(l, t.id, isClosed, nil) 353 } 354 355 func (n *nodeContext) updateListType(list Expr, id CloseInfo, isClosed bool, ellipsis Node) { 356 if n.kind == 0 { 357 n.node.updateStatus(finalized) // TODO(neweval): remove once transitioned. 358 return 359 } 360 m, ok := n.node.BaseValue.(*ListMarker) 361 if !ok { 362 m = &ListMarker{ 363 IsOpen: true, 364 } 365 n.setBaseValue(m) 366 } 367 m.IsOpen = m.IsOpen && !isClosed 368 369 if ellipsis != nil { 370 if src, _ := ellipsis.Source().(ast.Expr); src != nil { 371 if m.Src == nil { 372 m.Src = src 373 } else { 374 m.Src = ast.NewBinExpr(token.AND, m.Src, src) 375 } 376 } 377 } 378 379 if n.kind != ListKind { 380 n.updateNodeType(ListKind, list, id) 381 } 382 }