cuelang.org/go@v0.10.1/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, 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 := ctx.evalState(x, state) 89 t.node.insertValueConjunct(t.env, v, t.id) 90 } 91 92 func processResolver(ctx *OpContext, t *task, mode runMode) { 93 r := t.x.(Resolver) 94 95 // TODO(perf): if we are resolving a value where we know a scalar value can 96 // be conclusive, we could avoid triggering evaluating disjunctions. This 97 // would be a pretty significant rework, though. 98 99 arc := r.resolve(ctx, oldOnly(0)) 100 if arc == nil { 101 // TODO: yield instead? 102 return 103 } 104 arc = arc.DerefNonDisjunct() 105 106 ctx.Logf(t.node.node, "RESOLVED %v to %v %v", r, arc.Label, fmt.Sprintf("%p", arc)) 107 // TODO: consider moving after markCycle or removing. 108 d := arc.DerefDisjunct() 109 110 // A reference that points to itself indicates equality. In that case 111 // we are done computing and we can return the arc as is. 112 ci, skip := t.node.markCycle(d, t.env, r, t.id) 113 if skip { 114 return 115 } 116 117 if t.defunct { 118 return 119 } 120 121 c := MakeConjunct(t.env, t.x, ci) 122 t.node.scheduleVertexConjuncts(c, arc, ci) 123 } 124 125 func processDynamic(ctx *OpContext, t *task, mode runMode) { 126 n := t.node 127 128 field := t.x.(*DynamicField) 129 130 v := ctx.scalarValue(t, field.Key) 131 if v == nil { 132 return 133 } 134 135 if v.Concreteness() != Concrete { 136 n.addBottom(&Bottom{ 137 Code: IncompleteError, 138 Err: ctx.NewPosf(pos(field.Key), 139 "key value of dynamic field must be concrete, found %v", v), 140 }) 141 return 142 } 143 144 f := ctx.Label(field.Key, v) 145 // TODO: remove this restriction. 146 if f.IsInt() { 147 n.addErr(ctx.NewPosf(pos(field.Key), "integer fields not supported")) 148 return 149 } 150 151 c := MakeConjunct(t.env, field, t.id) 152 c.CloseInfo.cc = nil 153 n.insertArc(f, field.ArcType, c, t.id, true) 154 } 155 156 func processPatternConstraint(ctx *OpContext, t *task, mode runMode) { 157 n := t.node 158 159 field := t.x.(*BulkOptionalField) 160 161 // Note that the result may be a disjunction. Be sure to not take the 162 // default value as we want to retain the options of the disjunction. 163 v := ctx.evalState(field.Filter, require(0, scalarKnown)) 164 if v == nil { 165 return 166 } 167 168 n.insertPattern(v, MakeConjunct(t.env, t.x, t.id)) 169 } 170 171 func processComprehension(ctx *OpContext, t *task, mode runMode) { 172 n := t.node 173 174 y := &envYield{ 175 envComprehension: t.comp, 176 leaf: t.leaf, 177 env: t.env, 178 id: t.id, 179 expr: t.x, 180 } 181 182 err := n.processComprehension(y, 0) 183 t.err = CombineErrors(nil, t.err, err) 184 t.comp.vertex.state.addBottom(err) 185 } 186 187 func processDisjunctions(c *OpContext, t *task, mode runMode) { 188 n := t.node 189 err := n.processDisjunctions() 190 t.err = CombineErrors(nil, t.err, err) 191 } 192 193 func processFinalizeDisjunctions(c *OpContext, t *task, mode runMode) { 194 n := t.node 195 n.finalizeDisjunctions() 196 } 197 198 func processListLit(c *OpContext, t *task, mode runMode) { 199 n := t.node 200 201 l := t.x.(*ListLit) 202 203 n.updateCyclicStatus(t.id) 204 205 var ellipsis Node 206 207 index := int64(0) 208 hasComprehension := false 209 for j, elem := range l.Elems { 210 // TODO: Terminate early in case of runaway comprehension. 211 212 switch x := elem.(type) { 213 case *Comprehension: 214 err := c.yield(nil, t.env, x, 0, func(e *Environment) { 215 label, err := MakeLabel(x.Source(), index, IntLabel) 216 n.addErr(err) 217 index++ 218 c := MakeConjunct(e, x.Value, t.id) 219 n.insertArc(label, ArcMember, c, t.id, true) 220 }) 221 hasComprehension = true 222 if err != nil { 223 n.addBottom(err) 224 return 225 } 226 227 case *Ellipsis: 228 if j != len(l.Elems)-1 { 229 n.addErr(c.Newf("ellipsis must be last element in list")) 230 return 231 } 232 233 elem := x.Value 234 if elem == nil { 235 elem = &Top{} 236 } 237 238 c := MakeConjunct(t.env, elem, t.id) 239 pat := &BoundValue{ 240 Op: GreaterEqualOp, 241 Value: n.ctx.NewInt64(index, x), 242 } 243 n.insertPattern(pat, c) 244 ellipsis = x 245 246 default: 247 label, err := MakeLabel(x.Source(), index, IntLabel) 248 n.addErr(err) 249 index++ 250 c := MakeConjunct(t.env, x, t.id) 251 n.insertArc(label, ArcMember, c, t.id, true) 252 } 253 254 if max := n.maxListLen; n.listIsClosed && int(index) > max { 255 n.invalidListLength(max, len(l.Elems), n.maxNode, l) 256 return 257 } 258 } 259 260 isClosed := ellipsis == nil 261 262 switch max := n.maxListLen; { 263 case int(index) < max: 264 if isClosed { 265 n.invalidListLength(int(index), max, l, n.maxNode) 266 return 267 } 268 269 case int(index) > max, 270 isClosed && !n.listIsClosed, 271 (isClosed == n.listIsClosed) && !hasComprehension: 272 n.maxListLen = int(index) 273 n.maxNode = l 274 n.listIsClosed = isClosed 275 } 276 277 n.updateListType(l, t.id, isClosed, ellipsis) 278 } 279 280 func processListVertex(c *OpContext, t *task, mode runMode) { 281 n := t.node 282 283 l := t.x.(*Vertex) 284 285 elems := l.Elems() 286 isClosed := l.IsClosedList() 287 288 // TODO: Share with code above. 289 switch max := n.maxListLen; { 290 case len(elems) < max: 291 if isClosed { 292 n.invalidListLength(len(elems), max, l, n.maxNode) 293 return 294 } 295 296 case len(elems) > max: 297 if n.listIsClosed { 298 n.invalidListLength(max, len(elems), n.maxNode, l) 299 return 300 } 301 n.listIsClosed = isClosed 302 n.maxListLen = len(elems) 303 n.maxNode = l 304 305 case isClosed: 306 n.listIsClosed = true 307 n.maxNode = l 308 } 309 310 for _, a := range elems { 311 if a.Conjuncts == nil { 312 c := MakeRootConjunct(nil, a) 313 n.insertArc(a.Label, ArcMember, c, CloseInfo{}, true) 314 continue 315 } 316 for _, c := range a.Conjuncts { 317 c.CloseInfo.cc = t.id.cc 318 n.insertArc(a.Label, ArcMember, c, t.id, true) 319 } 320 } 321 322 n.updateListType(l, t.id, isClosed, nil) 323 } 324 325 func (n *nodeContext) updateListType(list Expr, id CloseInfo, isClosed bool, ellipsis Node) { 326 if n.kind == 0 { 327 n.node.updateStatus(finalized) // TODO(neweval): remove once transitioned. 328 return 329 } 330 m, ok := n.node.BaseValue.(*ListMarker) 331 if !ok { 332 m = &ListMarker{ 333 IsOpen: true, 334 } 335 n.node.setValue(n.ctx, conjuncts, m) 336 } 337 m.IsOpen = m.IsOpen && !isClosed 338 339 if ellipsis != nil { 340 if src, _ := ellipsis.Source().(ast.Expr); src != nil { 341 if m.Src == nil { 342 m.Src = src 343 } else { 344 m.Src = ast.NewBinExpr(token.AND, m.Src, src) 345 } 346 } 347 } 348 349 if n.kind != ListKind { 350 n.updateNodeType(ListKind, list, id) 351 } 352 }