cuelang.org/go@v0.13.0/internal/core/adt/conjunct.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/errors" 21 "cuelang.org/go/cue/token" 22 ) 23 24 // This file contains functionality for processing conjuncts to insert the 25 // corresponding values in the Vertex. 26 // 27 // Conjuncts are divided into two classes: 28 // - literal values that need no evaluation: these are inserted directly into 29 // the Vertex. 30 // - field or value expressions that need to be evaluated: these are inserted 31 // as a task into the Vertex' associated scheduler for later evaluation. 32 // The implementation of these tasks can be found in tasks.go. 33 // 34 // The main entrypoint is scheduleConjunct. 35 36 // scheduleConjunct splits c into parts to be incrementally processed and queues 37 // these parts up for processing. it will itself not cause recursive processing. 38 func (n *nodeContext) scheduleConjunct(c Conjunct, id CloseInfo) { 39 n.assertInitialized() 40 41 if c.CloseInfo.FromDef { 42 n.node.ClosedRecursive = true 43 } 44 45 // TODO: consider setting this as a safety measure. 46 // if c.CloseInfo.CycleType > id.CycleType { 47 // id.CycleType = c.CloseInfo.CycleType 48 // } 49 // if c.CloseInfo.IsCyclic { 50 // id.IsCyclic = true 51 // } 52 // default: 53 // Note this subtlety: we MUST take the cycle info from c when this is 54 // an in place evaluated node, otherwise we must take that of id. 55 56 // TODO(evalv3): Why do we no longer need to do this? 57 // id.CycleInfo = c.CloseInfo.CycleInfo 58 59 env := c.Env 60 61 n.markNonCyclic(id) 62 63 switch x := c.Elem().(type) { 64 case *ConjunctGroup: 65 for _, c := range *x { 66 n.scheduleConjunct(c, id) 67 } 68 69 case *Vertex: 70 // TODO: move this logic to scheduleVertexConjuncts or at least ensure 71 // that we can also share data Vertices? 72 if x.IsData() { 73 n.unshare() 74 n.insertValueConjunct(env, x, id) 75 } else { 76 n.scheduleVertexConjuncts(c, x, id) 77 } 78 79 case Value: 80 // TODO: perhaps some values could be shared. 81 n.unshare() 82 n.insertValueConjunct(env, x, id) 83 84 case *BinaryExpr: 85 // NOTE: do not unshare: a conjunction could still allow structure 86 // sharing, such as in the case of `ref & ref`. 87 if x.Op == AndOp { 88 n.scheduleConjunct(MakeConjunct(env, x.X, id), id) 89 n.scheduleConjunct(MakeConjunct(env, x.Y, id), id) 90 return 91 } 92 93 n.unshare() 94 // Even though disjunctions and conjunctions are excluded, the result 95 // must may still be list in the case of list arithmetic. This could 96 // be a scalar value only once this is no longer supported. 97 n.scheduleTask(handleExpr, env, x, id) 98 99 case *StructLit: 100 n.unshare() 101 n.scheduleStruct(env, x, id) 102 103 case *ListLit: 104 n.unshare() 105 106 // At this point we known we have at least an empty list. 107 n.updateCyclicStatusV3(id) 108 109 env := &Environment{ 110 Up: env, 111 Vertex: n.node, 112 } 113 n.updateNodeType(ListKind, x, id) 114 n.scheduleTask(handleListLit, env, x, id) 115 116 case *DisjunctionExpr: 117 n.unshare() 118 id := id 119 id.setOptionalV3(n) 120 121 // TODO(perf): reuse envDisjunct values so that we can also reuse the 122 // disjunct slice. 123 n.ctx.holeID++ 124 d := envDisjunct{ 125 env: env, 126 cloneID: id, 127 holeID: n.ctx.holeID, 128 src: x, 129 expr: x, 130 } 131 for _, dv := range x.Values { 132 d.disjuncts = append(d.disjuncts, disjunct{ 133 expr: dv.Val, 134 isDefault: dv.Default, 135 mode: mode(x.HasDefaults, dv.Default), 136 }) 137 } 138 n.scheduleDisjunction(d) 139 n.updateConjunctInfo(TopKind, id, 0) 140 141 case *Comprehension: 142 // always a partial comprehension. 143 n.insertComprehension(env, x, id) 144 145 case Resolver: 146 n.scheduleTask(handleResolver, env, x, id) 147 148 case Evaluator: 149 n.unshare() 150 151 // Expressions that contain a call may end up in an infinite recursion 152 // here if we do not ensure that there is non-cyclic data to propagate 153 // the evaluation. We therefore postpone expressions until we have 154 // evidence that such non-cyclic conjuncts exist. 155 if id.CycleType == IsCyclic && !n.hasNonCycle && !n.hasNonCyclic { 156 n.hasAncestorCycle = true 157 n.cyclicConjuncts = append(n.cyclicConjuncts, cyclicConjunct{c: c}) 158 return 159 } 160 161 // Interpolation, UnaryExpr, CallExpr 162 n.scheduleTask(handleExpr, env, x, id) 163 164 default: 165 panic("unreachable") 166 } 167 168 n.ctx.stats.Conjuncts++ 169 } 170 171 // scheduleStruct records all elements of this conjunct in the structure and 172 // then processes it. If an element needs to be inserted for evaluation, 173 // it may be scheduled. 174 func (n *nodeContext) scheduleStruct(env *Environment, 175 s *StructLit, 176 ci CloseInfo) { 177 n.updateCyclicStatusV3(ci) 178 n.updateConjunctInfo(StructKind, ci, cHasStruct) 179 180 // NOTE: This is a crucial point in the code: 181 // Unification dereferencing happens here. The child nodes are set to 182 // an Environment linked to the current node. Together with the De Bruijn 183 // indices, this determines to which Vertex a reference resolves. 184 185 childEnv := &Environment{ 186 Up: env, 187 Vertex: n.node, 188 } 189 190 hasEmbed := false 191 hasEllipsis := false 192 193 n.hasStruct = true 194 195 // TODO: do we still need this? 196 // shouldClose := ci.cc.isDef || ci.cc.isClosedOnce 197 198 s.Init(n.ctx) 199 200 // TODO: do we still need to AddStruct and do we still need to Disable? 201 parent := n.node.AddStruct(s, childEnv, ci) 202 parent.Disable = true // disable until processing is done. 203 ci.IsClosed = false 204 205 // TODO(perf): precompile whether struct has embedding. 206 loop1: 207 for _, d := range s.Decls { 208 switch d.(type) { 209 case *Comprehension, Expr: 210 hasEmbed = true 211 break loop1 212 } 213 } 214 215 // TODO: if embed, add an "ignore" field. 216 // When inserting a replace that is a definition, flip the ignore. 217 if hasEmbed { 218 ci = n.splitStruct(s, ci) 219 } 220 221 // First add fixed fields and schedule expressions. 222 for _, d := range s.Decls { 223 switch x := d.(type) { 224 case *Field: 225 if x.Label.IsString() && x.ArcType == ArcMember { 226 n.aStruct = s 227 n.aStructID = ci 228 } 229 ci := n.ctx.subField(ci) 230 if x.ArcType == ArcOptional { 231 ci.setOptionalV3(n) 232 } 233 234 fc := MakeConjunct(childEnv, x, ci) 235 n.insertArc(x.Label, x.ArcType, fc, ci, true) 236 237 case *LetField: 238 ci := n.ctx.subField(ci) 239 lc := MakeConjunct(childEnv, x, ci) 240 n.insertArc(x.Label, ArcMember, lc, ci, true) 241 242 case *Comprehension: 243 ci := n.injectEmbedNode(x, ci) 244 n.insertComprehension(childEnv, x, ci) 245 hasEmbed = true 246 247 case *Ellipsis: 248 // Can be added unconditionally to patterns. 249 hasEllipsis = true 250 251 case *DynamicField: 252 ci := n.ctx.subField(ci) 253 if x.ArcType == ArcMember { 254 n.aStruct = s 255 n.aStructID = ci 256 } 257 n.scheduleTask(handleDynamic, childEnv, x, ci) 258 259 case *BulkOptionalField: 260 ci := n.ctx.subField(ci) 261 ci.setOptionalV3(n) 262 263 // All do not depend on each other, so can be added at once. 264 n.scheduleTask(handlePatternConstraint, childEnv, x, ci) 265 266 case Expr: 267 ci := n.injectEmbedNode(x, ci) 268 ec := MakeConjunct(childEnv, x, ci) 269 n.scheduleConjunct(ec, ci) 270 hasEmbed = true 271 } 272 } 273 if hasEllipsis { 274 n.node.HasEllipsis = true 275 n.updateConjunctInfo(TopKind, ci, cHasEllipsis) 276 } 277 if !hasEmbed { 278 n.aStruct = s 279 n.aStructID = ci 280 } 281 282 // TODO: probably no longer necessary. 283 parent.Disable = false 284 } 285 286 // scheduleVertexConjuncts injects the conjuncst of src n. If src was not fully 287 // evaluated, it subscribes dst for future updates. 288 func (n *nodeContext) scheduleVertexConjuncts(c Conjunct, arc *Vertex, closeInfo CloseInfo) { 289 // We should not "activate" an enclosing struct for typo checking if it is 290 // derived from an embedded, inlined value: 291 // 292 // #Schema: foo: { {embed: embedded: "foo"}.embed } 293 // #Schema: foo: { field: string } 294 // 295 // Even though the embedding is within a schema, it should not treat the 296 // struct as closed if it itself does not refer to a schema, as it may still 297 // be unified with another struct. 298 // 299 // We check this by checking if the result is not marked as Closed. 300 // Alternativley, we could always disable this for inlined structs. 301 // 302 // TODO(#A...): this code could go if we had explicitly opened values. 303 if !arc.ClosedRecursive && 304 !arc.ClosedNonRecursive && 305 closeInfo.enclosingEmbed != 0 { 306 closeInfo.FromDef = false 307 } 308 309 // disjunctions, we need to dereference he underlying node. 310 if deref(n.node) == deref(arc) { 311 if n.isShared { 312 n.addShared(closeInfo) 313 } 314 return 315 } 316 317 if n.shareIfPossible(c, arc, closeInfo) { 318 arc.getState(n.ctx) 319 return 320 } 321 322 // We need to ensure that each arc is only unified once (or at least) a 323 // bounded time, witch each conjunct. Comprehensions, for instance, may 324 // distribute a value across many values that get unified back into the 325 // same value. If such a value is a disjunction, than a disjunction of N 326 // disjuncts will result in a factor N more unifications for each 327 // occurrence of such value, resulting in exponential running time. This 328 // is especially common values that are used as a type. 329 // 330 // However, unification is idempotent, so each such conjunct only needs 331 // to be unified once. This cache checks for this and prevents an 332 // exponential blowup in such case. 333 // 334 // TODO(perf): this cache ensures the conjuncts of an arc at most once 335 // per ID. However, we really need to add the conjuncts of an arc only 336 // once total, and then add the close information once per close ID 337 // (pointer can probably be shared). Aside from being more performant, 338 // this is probably the best way to guarantee that conjunctions are 339 // linear in this case. 340 341 ciKey := closeInfo 342 ciKey.Refs = nil 343 ciKey.Inline = false 344 if n.ctx.isDevVersion() { 345 // No need to key on CloseInfo with evalv3. 346 ciKey = CloseInfo{} 347 } 348 349 // Also check arc.Label: definitions themselves do not have the FromDef to 350 // reflect their closedness. This means that if we are structure sharing, we 351 // may end up with a Vertex that is a definition without the reference 352 // reflecting that. We need to handle this case here. Note that if an 353 // intermediate node refers to a definition, things are evaluated at least 354 // once. 355 switch isDef, _ := IsDef(c.Expr()); { 356 case isDef || arc.Label.IsDef() || closeInfo.TopDef: 357 n.isDef = true 358 // n.node.ClosedRecursive = true // TODO: should we set this here? 359 closeInfo.FromDef = true 360 closeInfo.TopDef = false 361 362 closeInfo = n.addResolver(arc, closeInfo, false) 363 default: 364 closeInfo = n.addResolver(arc, closeInfo, true) 365 } 366 c.CloseInfo.defID = closeInfo.defID 367 c.CloseInfo.outerID = closeInfo.defID 368 369 key := arcKey{arc, ciKey} 370 for _, k := range n.arcMap { 371 if key == k { 372 return 373 } 374 } 375 n.arcMap = append(n.arcMap, key) 376 377 if !n.node.nonRooted || n.node.IsDynamic { 378 if state := arc.getBareState(n.ctx); state != nil { 379 state.addNotify2(n.node, closeInfo) 380 } 381 } 382 383 // Use explicit index in case Conjuncts grows during iteration. 384 for i := 0; i < len(arc.Conjuncts); i++ { 385 c := arc.Conjuncts[i] 386 n.scheduleConjunct(c, closeInfo) 387 } 388 389 if state := arc.getBareState(n.ctx); state != nil { 390 n.toComplete = true 391 } 392 } 393 394 func (n *nodeContext) addNotify2(v *Vertex, c CloseInfo) { 395 // No need to do the notification mechanism if we are already complete. 396 switch { 397 case n.node.isFinal(): 398 return 399 case !n.node.isInProgress(): 400 case n.meets(allAncestorsProcessed): 401 return 402 } 403 404 for _, r := range n.notify { 405 if r.v == v { 406 return 407 } 408 } 409 410 // TODO: it should not be necessary to register for notifications for 411 // let expressions, so we could also filter for !n.node.Label.IsLet(). 412 // However, somehow this appears to result in slightly better error 413 // messages. 414 415 n.notify = append(n.notify, receiver{v}) 416 } 417 418 // Literal conjuncts 419 420 // NoSharingSentinel is a sentinel value that is used to disable sharing of 421 // nodes. We make this an error to make it clear that we discard the value. 422 var NoShareSentinel = &Bottom{ 423 Err: errors.Newf(token.NoPos, "no sharing"), 424 } 425 426 func (n *nodeContext) insertValueConjunct(env *Environment, v Value, id CloseInfo) { 427 ctx := n.ctx 428 429 n.updateConjunctInfo(TopKind, id, 0) 430 431 switch x := v.(type) { 432 case *Vertex: 433 if x.ClosedNonRecursive { 434 n.node.ClosedNonRecursive = true 435 436 // If this is a definition, it will be repeated in the evaluation. 437 if !x.IsFromDisjunction() { 438 id = n.addResolver(x, id, false) 439 } 440 } 441 if _, ok := x.BaseValue.(*StructMarker); ok { 442 n.aStruct = x 443 n.aStructID = id 444 } 445 446 if !x.IsData() { 447 n.updateCyclicStatusV3(id) 448 449 c := MakeConjunct(env, x, id) 450 n.scheduleVertexConjuncts(c, x, id) 451 return 452 } 453 454 // TODO: evaluate value? 455 switch v := x.BaseValue.(type) { 456 default: 457 panic(fmt.Sprintf("invalid type %T", x.BaseValue)) 458 459 case *ListMarker: 460 n.updateCyclicStatusV3(id) 461 462 // TODO: arguably we know now that the type _must_ be a list. 463 n.scheduleTask(handleListVertex, env, x, id) 464 465 return 466 467 case *StructMarker: 468 for _, a := range x.Arcs { 469 if a.ArcType != ArcMember { 470 continue 471 } 472 // TODO(errors): report error when this is a regular field. 473 c := MakeConjunct(nil, a, id) 474 n.insertArc(a.Label, a.ArcType, c, id, true) 475 } 476 n.node.Structs = append(n.node.Structs, x.Structs...) 477 478 case Value: 479 n.insertValueConjunct(env, v, id) 480 } 481 482 return 483 484 case *Bottom: 485 if x == NoShareSentinel { 486 n.unshare() 487 return 488 } 489 n.addBottom(x) 490 return 491 492 case *Builtin: 493 if v := x.BareValidator(); v != nil { 494 n.insertValueConjunct(env, v, id) 495 return 496 } 497 } 498 499 if !n.updateNodeType(v.Kind(), v, id) { 500 return 501 } 502 503 switch x := v.(type) { 504 case *Disjunction: 505 n.updateCyclicStatusV3(id) 506 507 // TODO(perf): reuse envDisjunct values so that we can also reuse the 508 // disjunct slice. 509 id := id 510 id.setOptionalV3(n) 511 512 n.ctx.holeID++ 513 d := envDisjunct{ 514 env: env, 515 cloneID: id, 516 holeID: n.ctx.holeID, 517 src: x, 518 value: x, 519 } 520 for i, dv := range x.Values { 521 d.disjuncts = append(d.disjuncts, disjunct{ 522 expr: dv, 523 isDefault: i < x.NumDefaults, 524 mode: mode(x.HasDefaults, i < x.NumDefaults), 525 }) 526 } 527 n.scheduleDisjunction(d) 528 529 case *Conjunction: 530 // TODO: consider sharing: conjunct could be `ref & ref`, for instance, 531 // in which case ref could still be shared. 532 533 for _, x := range x.Values { 534 n.insertValueConjunct(env, x, id) 535 } 536 537 case *Top: 538 n.updateCyclicStatusV3(id) 539 540 n.hasTop = true 541 n.updateConjunctInfo(TopKind, id, cHasTop) 542 543 case *BasicType: 544 n.updateCyclicStatusV3(id) 545 if x.K != TopKind { 546 n.updateConjunctInfo(TopKind, id, cHasTop) 547 } 548 549 case *BoundValue: 550 n.updateCyclicStatusV3(id) 551 552 switch x.Op { 553 case LessThanOp, LessEqualOp: 554 if y := n.upperBound; y != nil { 555 v := SimplifyBounds(ctx, n.kind, x, y) 556 if err := valueError(v); err != nil { 557 err.AddPosition(v) 558 err.AddPosition(n.upperBound) 559 err.AddClosedPositions(id) 560 } 561 n.upperBound = nil 562 n.insertValueConjunct(env, v, id) 563 return 564 } 565 n.upperBound = x 566 567 case GreaterThanOp, GreaterEqualOp: 568 if y := n.lowerBound; y != nil { 569 v := SimplifyBounds(ctx, n.kind, x, y) 570 if err := valueError(v); err != nil { 571 err.AddPosition(v) 572 err.AddPosition(n.lowerBound) 573 err.AddClosedPositions(id) 574 } 575 n.lowerBound = nil 576 n.insertValueConjunct(env, v, id) 577 return 578 } 579 n.lowerBound = x 580 581 case EqualOp, NotEqualOp, MatchOp, NotMatchOp: 582 // This check serves as simplifier, but also to remove duplicates. 583 k := 0 584 match := false 585 for _, c := range n.checks { 586 if y, ok := c.x.(*BoundValue); ok { 587 switch z := SimplifyBounds(ctx, n.kind, x, y); { 588 case z == y: 589 match = true 590 case z == x: 591 continue 592 } 593 } 594 n.checks[k] = c 595 k++ 596 } 597 n.checks = n.checks[:k] 598 // TODO(perf): do an early check to be able to prune further 599 // processing. 600 if !match { 601 n.checks = append(n.checks, MakeConjunct(env, x, id)) 602 } 603 return 604 } 605 606 case Validator: 607 // This check serves as simplifier, but also to remove duplicates. 608 cx := MakeConjunct(env, x, id) 609 kind := x.Kind() 610 // A validator that is inserted in a closeContext should behave like top 611 // in the sense that the closeContext should not be closed if no other 612 // value is present that would erase top (cc.hasNonTop): if a field is 613 // only associated with a validator, we leave it to the validator to 614 // decide what fields are allowed. 615 if kind&(ListKind|StructKind) != 0 { 616 if b, ok := x.(*BuiltinValidator); ok && b.Builtin.NonConcrete { 617 n.updateConjunctInfo(TopKind, id, cHasOpenValidator|cHasTop) 618 } else { 619 n.updateConjunctInfo(TopKind, id, cHasTop) 620 } 621 } 622 623 for i, y := range n.checks { 624 if b, ok := SimplifyValidator(ctx, cx, y); ok { 625 // It is possible that simplification process triggered further 626 // evaluation, finalizing this node and clearing the checks 627 // slice. In that case it is safe to ignore the result. 628 if len(n.checks) > 0 { 629 n.checks[i] = b 630 } 631 return 632 } 633 } 634 635 n.checks = append(n.checks, cx) 636 637 // We use set the type of the validator argument here to ensure that 638 // validation considers the ultimate value of embedded validators, 639 // rather than assuming that the struct in which an expression is 640 // embedded is always a struct. 641 // TODO(validatorType): get rid of setting n.hasTop here. 642 k := x.Kind() 643 if k == TopKind { 644 n.hasTop = true 645 // TODO: should we set this here? Does not seem necessary. 646 // n.updateConjunctInfo(TopKind, id, cHasTop) 647 } 648 n.updateNodeType(k, x, id) 649 650 case *Vertex: 651 // handled above. 652 653 case Value: // *NullLit, *BoolLit, *NumLit, *StringLit, *BytesLit, *Builtin 654 n.updateCyclicStatusV3(id) 655 656 if y := n.scalar; y != nil { 657 if b, ok := BinOp(ctx, EqualOp, x, y).(*Bool); !ok || !b.B { 658 n.reportConflict(x, y, x.Kind(), y.Kind(), n.scalarID, id) 659 } 660 break 661 } 662 n.scalar = x 663 n.scalarID = id 664 n.signal(scalarKnown) 665 666 default: 667 panic(fmt.Sprintf("unknown value type %T", x)) 668 } 669 670 if n.lowerBound != nil && n.upperBound != nil { 671 if u := SimplifyBounds(ctx, n.kind, n.lowerBound, n.upperBound); u != nil { 672 if err := valueError(u); err != nil { 673 err.AddPosition(n.lowerBound) 674 err.AddPosition(n.upperBound) 675 err.AddClosedPositions(id) 676 } 677 n.lowerBound = nil 678 n.upperBound = nil 679 n.insertValueConjunct(env, u, id) 680 } 681 } 682 }