cuelang.org/go@v0.10.1/internal/core/adt/fields.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 21 // This file holds the logic for the insertion of fields and pattern 22 // constraints, including tracking closedness. 23 // 24 // 25 // DESIGN GOALS 26 // 27 // Key to performance is to fail early during evaluation. This is especially 28 // true for disjunctions. In CUE evaluation, conjuncts may be evaluated in a 29 // fairly arbitrary order. We want to retain this flexibility while also failing 30 // on disallowed fields as soon as we have enough data to tell for certain. 31 // 32 // Keeping track of which fields are allowed means keeping provenance data on 33 // whether certain conjuncts originate from embeddings or definitions, as well 34 // as how they group together with other conjuncts. These data structures should 35 // allow for a "mark and unwind" approach to allow for backtracking when 36 // computing disjunctions. 37 // 38 // References to the same CUE value may be added as conjuncts through various 39 // paths. For instance, a reference to a definition may be added directly, or 40 // through embedding. How they are added affects which set of fields are 41 // allowed. This can make the removal of duplicate conjuncts hard. A solution 42 // should make it straightforward to deduplicate conjuncts if they have the same 43 // impact on field inclusion. 44 // 45 // All conjuncts associated with field constraints, including optional fields 46 // and pattern constraints, should be collated, deduplicated, and evaluated as 47 // if they were regular fields. This allows comparisons between values to be 48 // meaningful and helps to filter disjuncts. 49 // 50 // The provenance data generated by this algorithm should ideally be easily 51 // usable in external APIs. 52 // 53 // 54 // DATA STRUCTURES 55 // 56 // Conjuncts 57 // 58 // To keep track of conjunct provenance, each conjunct has a few flags that 59 // indicates whether it originates from 60 // - an embedding 61 // - a definition 62 // - a reference (optional and unimplemented) 63 // 64 // Conjuncts with the same origin are represented as a single Conjunct in the 65 // Vertex, where this conjunct is a list of these conjuncts. In other words, the 66 // conjuncts of a Vertex are really a forest (group of trees) of conjuncts that, 67 // recursively, reflect the provenance of the conjuncts contained within it. 68 // 69 // The current implementation uses a Vertex for listing conjuncts with the same 70 // origin. This Vertex is marked as "Dynamic", as it does not have a CUE path 71 // that leads to them. 72 // 73 // 74 // Constraints 75 // 76 // Vertex values separately keep track of pattern constraints. These consist of 77 // a list of patterns with associated conjuncts, and a CUE expression that 78 // represents the set of allowed fields. This information is mostly for equality 79 // checking: by the time this data is produced, conjuncts associated with 80 // patterns are already inserted into the computed subfields. 81 // 82 // Note that this representation assumes that patterns are always accrued 83 // cumulatively: a field that is allowed will accrue the conjuncts of any 84 // matched pattern, even if it originates from an embedding that itself does not 85 // allow this field. 86 // 87 // 88 // ALGORITHM 89 // 90 // When processing the conjuncts of a Vertex, subfields are tracked per 91 // "grouping" (the list of conjuncts of the same origin). Each grouping keeps a 92 // counter of the number of unprocessed conjuncts and subgroups associated with 93 // it. Field inclusion (closedness) can be computed as soon as all subconjuncts 94 // and subgroups are processed. 95 // 96 // Conjuncts of subfields are inserted in such a way that they reflect the same 97 // grouping as the parent Vertex, plus any grouping that may be added by the 98 // subfield itself. 99 // 100 // It would be possible, though, to collapse certain (combinations of) groups 101 // that contain only a single conjunct. This can limit the size of such conjunct 102 // trees. 103 // 104 // As conjuncts are added within their grouping context, it is possible to 105 // uniquely identify conjuncts only by Vertex and expression pointer, 106 // disregarding the Environment. 107 // 108 // 109 // EXAMPLE DATA STRUCTURE 110 // 111 // a: #A 112 // #A: { 113 // #B 114 // x: r1 115 // } 116 // #B: y: r2 117 // r1: z: r3 118 // r2: 2 119 // r3: foo: 2 120 // 121 // gets evaluated into: 122 // 123 // V_a: Arcs{ 124 // x: V_x [ V_def(#A)[ r1 ] ] 125 // y: V_y [ V_def(#A)[ V_embed(#B)[ r2 ] ] ] 126 // } 127 // 128 // When evaluating V_x, its Arcs, in turn become: 129 // 130 // V_x: Arcs{ 131 // z: V_z [ V_def(#A)[ V_ref(r1)[ r3 ]) ]] 132 // } 133 // 134 // The V_def(#A) is necessary here to ensure that closedness information can be 135 // computed, if necessary. The V_ref's, however, are optional, and can be 136 // omitted if provenance is less important: 137 // 138 // V_x: Arcs{ 139 // z: V_z [ V_def(#A)[ r3 ]] 140 // } 141 // 142 // Another possible optimization is to eliminate Vertices if there is only one 143 // conjunct: the embedding and definition flags in the conjunct can be 144 // sufficient in that case. The provenance data could potentially be derived 145 // from the Environment in that case. If an embedding conjunct is itself the 146 // only conjunct in a list, the embedding bit can be eliminated. So V_y in the 147 // above example could be reduced to 148 // 149 // V_y [ V_def(#A)[ r2 ] ] 150 // 151 152 // TODO(perf): 153 // - the data structures could probably be collapsed with Conjunct. and the 154 // Vertex inserted into the Conjuncts could be a special ConjunctGroup. 155 156 type closeContext struct { 157 // Used to recursively insert Vertices. 158 parent *closeContext 159 160 // points to the closeContext this closeContext originates when following 161 // the reverse or ARC/EVAL dependencies corresponding to parent vertices. 162 // This is used to compute the prefix path when resolving a reference. 163 origin *closeContext 164 165 // overlay is used to temporarily link a closeContext to its "overlay" copy, 166 // as it is used in a corresponding disjunction. 167 overlay *closeContext 168 // generation is used to track the current generation of the closeContext 169 // in disjunction overlays. This is mostly for debugging. 170 generation int 171 172 dependencies []*ccDep // For testing only. See debug.go 173 174 // externalDeps lists the closeContexts associated with a root node for 175 // which there are outstanding decrements (can only be NOTIFY or ARC). This 176 // is used to break counter cycles, if necessary. 177 // 178 // This is only used for root closedContext and only for debugging. 179 // TODO: move to nodeContext. 180 externalDeps []ccArcRef 181 182 // child links to a sequence which additional patterns need to be verified 183 // against (&&). If there are more than one, these additional nodes are 184 // linked with next. Only closed nodes with patterns are added. Arc sets are 185 // already merged during processing. 186 // A child is always done. This means it cannot be modified. 187 child *closeContext 188 189 // next holds a linked list of nodes to process. 190 // See comments above and see linkPatterns. 191 next *closeContext 192 193 // if conjunctCount is 0, pattern constraints can be merged and the 194 // closedness can be checked. To ensure that this is true, there should 195 // be an additional increment at the start before any processing is done. 196 conjunctCount int 197 198 // disjunctCount counts the number of disjunctions that contribute to 199 // conjunctCount. When a node is unfinished, for instance due to an error, 200 // we allow disjunctions to not be decremented. This count is then used 201 // to suppress errors about missing decrements. 202 disjunctCount int 203 204 src *Vertex 205 206 arcType ArcType 207 208 // isDef is true when isDefOrig is true or when isDef is true for any of its 209 // child nodes, recursively. 210 isDef bool 211 212 // isDefOrig indicates whether the closeContext is created as part of a 213 // definition. This value propagates to itself and parents through isDef. 214 isDefOrig bool 215 216 // hasEllipsis indicates whether the node contains an ellipsis. 217 hasEllipsis bool 218 219 // hasTop indicates a node has at least one top conjunct. 220 hasTop bool 221 222 // hasNonTop indicates a node has at least one conjunct that is not top. 223 hasNonTop bool 224 225 // isClosedOnce is true if this closeContext is the result of calling the 226 // close builtin. 227 isClosedOnce bool 228 229 // isEmbed indicates whether the closeContext is created as part of an 230 // embedding. 231 isEmbed bool 232 233 // isClosed is true if a node is a def, it became closed because of a 234 // reference or if it is closed by the close builtin. 235 // 236 // isClosed must only be set to true if all fields and pattern constraints 237 // that define the domain of the node have been added. 238 isClosed bool 239 240 // isTotal is true if a node contains an ellipsis and is defined for all 241 // values. 242 isTotal bool 243 244 // done is true if all dependencies have been decremented. 245 done bool 246 247 // isDecremented is used to keep track of whether the evaluator decremented 248 // a closedContext for the ROOT depKind. 249 isDecremented bool 250 251 // needsCloseInSchedule is non-nil if a closeContext that was created 252 // as an arc still needs to be decremented. It points to the creating arc 253 // for reporting purposes. 254 needsCloseInSchedule *closeContext 255 256 // parentConjuncts represent the parent of this embedding or definition. 257 // Any closeContext is represented by a ConjunctGroup in parent of the 258 // expression tree. 259 parentConjuncts conjunctGrouper 260 // TODO: Only needed if more than one conjuncts. 261 262 // arcs represents closeContexts for sub fields and notification targets 263 // associated with this node that reflect the same point in the expression 264 // tree as this closeContext. In both cases the are keyed by Vertex. 265 arcs []ccArc 266 267 // parentIndex is the position in the parent's arcs slice that corresponds 268 // to this closeContext. This is currently unused. The intention is to use 269 // this to allow groups with single elements (which will be the majority) 270 // to be represented in place in the parent. 271 parentIndex int 272 273 group *ConjunctGroup 274 275 // Patterns contains all patterns of the current closeContext. 276 // It is used in the construction of Expr. 277 Patterns []Value 278 279 // Expr contains the Expr that is used for checking whether a Feature 280 // is allowed in this context. It is only complete after the full 281 // context has been completed, but it can be used for initial checking 282 // once isClosed is true. 283 Expr Value 284 } 285 286 // Label is a convenience function to return the label of the associated Vertex. 287 func (c *closeContext) Label() Feature { 288 return c.src.Label 289 } 290 291 // See also Vertex.updateArcType in composite.go. 292 func (c *closeContext) updateArcType(t ArcType) { 293 if t >= c.arcType { 294 return 295 } 296 if c.arcType == ArcNotPresent { 297 return 298 } 299 c.arcType = t 300 } 301 302 type ccArc struct { 303 kind depKind 304 decremented bool 305 key *closeContext 306 cc *closeContext 307 } 308 309 // A ccArcRef x refers to the x.src.arcs[x.index]. 310 // We use this instead of pointers, because the address may change when 311 // growing a slice. We use this instead mechanism instead of a pointers so 312 // that we do not need to maintain separate free buffers once we use pools of 313 // closeContext. 314 type ccArcRef struct { 315 src *closeContext 316 index int 317 } 318 319 type conjunctGrouper interface { 320 assignConjunct(ctx *OpContext, root *closeContext, c Conjunct, mode ArcType, check, checkClosed bool) (arc *closeContext, pos int, added bool) 321 } 322 323 func (n *nodeContext) getArc(f Feature, mode ArcType) (arc *Vertex, isNew bool) { 324 // TODO(disjunct,perf): CopyOnRead 325 v := n.node 326 for _, a := range v.Arcs { 327 if a.Label == f { 328 if f.IsLet() { 329 a.MultiLet = true 330 // TODO: add return here? 331 } 332 a.updateArcType(mode) 333 return a, false 334 } 335 } 336 337 arc = &Vertex{ 338 Parent: v, 339 Label: f, 340 ArcType: mode, 341 nonRooted: v.IsDynamic || v.Label.IsLet() || v.nonRooted, 342 } 343 if n.scheduler.frozen&fieldSetKnown != 0 { 344 b := n.ctx.NewErrf("adding field %v not allowed as field set was already referenced", f) 345 n.ctx.AddBottom(b) 346 // This may panic for list arithmetic. Safer to leave out for now. 347 arc.ArcType = ArcNotPresent 348 } 349 v.Arcs = append(v.Arcs, arc) 350 return arc, true 351 } 352 353 func (v *Vertex) assignConjunct(ctx *OpContext, root *closeContext, c Conjunct, mode ArcType, check, checkClosed bool) (a *closeContext, pos int, added bool) { 354 // TODO: consider clearing CloseInfo.cc. 355 // c.CloseInfo.cc = nil 356 357 arc := root.src 358 arc.updateArcType(mode) // TODO: probably not necessary: consider removing. 359 360 pos = len(arc.Conjuncts) 361 362 added = !check || !arc.hasConjunct(c) 363 if added { 364 c.CloseInfo.cc = root 365 arc.addConjunctUnchecked(c) 366 } 367 368 return root, pos, added 369 } 370 371 func (cc *closeContext) getKeyedCC(ctx *OpContext, key *closeContext, c CycleInfo, mode ArcType, checkClosed bool) *closeContext { 372 for _, a := range cc.arcs { 373 if a.key == key { 374 a.cc.updateArcType(mode) 375 return a.cc 376 } 377 } 378 379 group := &ConjunctGroup{} 380 381 if cc.parentConjuncts == cc { 382 panic("parent is self") 383 } 384 385 parent, pos, _ := cc.parentConjuncts.assignConjunct(ctx, key, Conjunct{ 386 CloseInfo: CloseInfo{ 387 FromDef: cc.isDef, 388 FromEmbed: cc.isEmbed, 389 CycleInfo: c, 390 }, 391 x: group, 392 }, mode, false, checkClosed) 393 394 arc := &closeContext{ 395 origin: cc.origin, 396 generation: cc.generation, 397 parent: parent, 398 parentConjuncts: parent, 399 parentIndex: pos, 400 401 src: key.src, 402 arcType: mode, 403 group: group, 404 405 isDef: cc.isDef, 406 isDefOrig: cc.isDefOrig, 407 isEmbed: cc.isEmbed, 408 needsCloseInSchedule: cc, 409 } 410 411 arc.parent.incDependent(ctx, PARENT, arc) 412 413 // If the parent, w.r.t. the subfield relation was already processed, 414 // there is no need to register the notification. 415 arc.incDependent(ctx, EVAL, cc) // matched in REF(decrement:nodeDone) 416 417 // A let field never depends on its parent. So it is okay to filter here. 418 if !arc.Label().IsLet() { 419 // prevent a dependency on self. 420 if key.src != cc.src { 421 cc.addDependency(ctx, ARC, key, arc, key) 422 } 423 } 424 425 v := key.src 426 if checkClosed && v.Parent != nil && v.Parent.state != nil { 427 v.Parent.state.checkArc(cc, v) 428 } 429 430 return arc 431 } 432 433 func (cc *closeContext) linkNotify(ctx *OpContext, dst *Vertex, key *closeContext, c CycleInfo) bool { 434 for _, a := range cc.arcs { 435 if a.key == key { 436 return false 437 } 438 } 439 440 cc.addDependency(ctx, NOTIFY, key, key, dst.cc) 441 return true 442 } 443 444 func (cc *closeContext) assignConjunct(ctx *OpContext, root *closeContext, c Conjunct, mode ArcType, check, checkClosed bool) (arc *closeContext, pos int, added bool) { 445 arc = cc.getKeyedCC(ctx, root, c.CloseInfo.CycleInfo, mode, checkClosed) 446 447 c.CloseInfo.cc = nil 448 449 var group ConjunctGroup 450 if arc.group != nil { 451 group = *arc.group 452 } 453 pos = len(group) 454 455 added = !check || !hasConjunct(group, c) 456 if added { 457 c.CloseInfo.cc = arc 458 459 if c.CloseInfo.cc.src != arc.src { 460 panic("Inconsistent src") 461 } 462 463 group = append(group, c) 464 if arc.group == nil { 465 arc.group = &group 466 } else { 467 *arc.group = group 468 } 469 } 470 471 return arc, pos, added 472 } 473 474 // spawnCloseContext wraps the closeContext in c with a new one and returns 475 // this new context along with an updated CloseInfo. The new values reflect 476 // that the set of fields represented by c are now, for instance, enclosed in 477 // an embedding or a definition. 478 // 479 // This call is used when preparing ADT values for evaluation. 480 func (c CloseInfo) spawnCloseContext(ctx *OpContext, t closeNodeType) (CloseInfo, *closeContext) { 481 cc := c.cc 482 if cc == nil { 483 panic("nil closeContext") 484 } 485 486 c.cc = &closeContext{ 487 generation: cc.generation, 488 parent: cc, 489 src: cc.src, 490 parentConjuncts: cc, 491 } 492 493 // By definition, a spawned closeContext is its own root. 494 c.cc.origin = c.cc 495 496 cc.incDependent(ctx, PARENT, c.cc) // REF(decrement: spawn) 497 498 switch t { 499 case closeDef: 500 c.cc.isDef = true 501 c.cc.isDefOrig = true 502 case closeEmbed: 503 c.cc.isEmbed = true 504 } 505 506 return c, c.cc 507 } 508 509 // addDependency adds a dependent arc to c. If child is an arc, child.src == key 510 func (c *closeContext) addDependency(ctx *OpContext, kind depKind, key, child, root *closeContext) { 511 // NOTE: do not increment 512 // - either root closeContext or otherwise resulting from sub closeContext 513 // all conjuncts will be added now, notified, or scheduled as task. 514 515 child.incDependent(ctx, kind, c) // matched in decDependent REF(arcs) 516 517 for _, a := range c.arcs { 518 if a.key == key { 519 panic("addArc: Label already exists") 520 } 521 } 522 523 // TODO: this tests seems sensible, but panics. Investigate what could 524 // trigger this. 525 // if child.src.Parent != c.src { 526 // panic("addArc: inconsistent parent") 527 // } 528 if child.src.cc != root.src.cc { 529 panic("addArc: inconsistent root") 530 } 531 c.arcs = append(c.arcs, ccArc{ 532 kind: kind, 533 key: key, 534 cc: child, 535 }) 536 root.externalDeps = append(root.externalDeps, ccArcRef{ 537 src: c, 538 index: len(c.arcs) - 1, 539 }) 540 } 541 542 // incDependent needs to be called for any conjunct or child closeContext 543 // scheduled for c that is queued for later processing and not scheduled 544 // immediately. 545 func (c *closeContext) incDependent(ctx *OpContext, kind depKind, dependant *closeContext) (debug *ccDep) { 546 if c.src == nil { 547 panic("incDependent: unexpected nil src") 548 } 549 if dependant != nil && c.generation != dependant.generation { 550 // TODO: enable this check. 551 552 // panic(fmt.Sprintf("incDependent: inconsistent generation: %d %d", c.generation, dependant.generation)) 553 } 554 debug = c.addDependent(ctx, kind, dependant) 555 556 if c.done { 557 openDebugGraph(ctx, c.src, "incDependent: already checked") 558 559 panic(fmt.Sprintf("incDependent: already closed: %p", c)) 560 } 561 562 c.conjunctCount++ 563 return debug 564 } 565 566 // decDependent needs to be called for any conjunct or child closeContext for 567 // which a corresponding incDependent was called after it has been successfully 568 // processed. 569 func (c *closeContext) decDependent(ctx *OpContext, kind depKind, dependant *closeContext) { 570 v := c.src 571 572 c.matchDecrement(ctx, v, kind, dependant) 573 574 if c.conjunctCount == 0 { 575 panic(fmt.Sprintf("negative reference counter %d %p", c.conjunctCount, c)) 576 } 577 578 c.conjunctCount-- 579 if c.conjunctCount > 0 { 580 return 581 } 582 583 c.done = true 584 585 p := c.parent 586 587 if c.isDef && !c.hasEllipsis && (!c.hasTop || c.hasNonTop) { 588 c.isClosed = true 589 if p != nil { 590 p.isDef = true 591 } 592 } 593 594 if c.isClosedOnce { 595 c.isClosed = true 596 if p != nil { 597 p.isClosedOnce = true 598 } 599 } 600 601 for i, a := range c.arcs { 602 cc := a.cc 603 if a.decremented { 604 continue 605 } 606 c.arcs[i].decremented = true 607 cc.decDependent(ctx, a.kind, c) // REF(arcs) 608 } 609 610 c.finalizePattern() 611 612 if p == nil { 613 // Root pattern, set allowed patterns. 614 if pcs := v.PatternConstraints; pcs != nil { 615 if pcs.Allowed != nil { 616 // This can happen for lists. 617 // TODO: unify the values. 618 // panic("unexpected allowed set") 619 } 620 pcs.Allowed = c.Expr 621 return 622 } 623 return 624 } 625 626 if c.hasEllipsis { 627 p.hasEllipsis = true 628 } 629 if c.hasTop { 630 p.hasTop = true 631 } 632 if c.hasNonTop { 633 p.hasNonTop = true 634 } 635 636 switch { 637 case c.isTotal: 638 if !p.isClosed { 639 p.isTotal = true 640 } 641 case !c.isEmbed && c.isClosed: 642 // Merge the two closeContexts and ensure that the patterns and fields 643 // are mutually compatible according to the closedness rules. 644 injectClosed(ctx, c, p) 645 p.Expr = mergeConjunctions(p.Expr, c.Expr) 646 default: 647 // Do not check closedness of fields for embeddings. 648 // The pattern constraints of the embedding still need to be added 649 // to the current context. 650 p.linkPatterns(c) 651 } 652 653 p.decDependent(ctx, PARENT, c) // REF(decrement: spawn) 654 655 // If we have started decrementing a child closeContext, the parent started 656 // as well. If it is still marked as needing an EVAL decrement, which can 657 // happen if processing started before the node was added, it is safe to 658 // decrement it now. In this case the NOTIFY and ARC dependencies will keep 659 // the nodes alive until they can be completed. 660 if dep := p.needsCloseInSchedule; dep != nil { 661 p.needsCloseInSchedule = nil 662 p.decDependent(ctx, EVAL, dep) 663 } 664 } 665 666 // incDisjunct increases disjunction-related counters. We require kind to be 667 // passed explicitly so that we can easily find the points where certain kinds 668 // are used. 669 func (c *closeContext) incDisjunct(ctx *OpContext, kind depKind) { 670 if kind != DISJUNCT { 671 panic("unexpected kind") 672 } 673 c.incDependent(ctx, DISJUNCT, nil) 674 675 // TODO: the counters are only used in debug mode and we could skip this 676 // if debug is disabled. 677 for ; c != nil; c = c.parent { 678 c.disjunctCount++ 679 } 680 } 681 682 // decDisjunct decreases disjunction-related counters. We require kind to be 683 // passed explicitly so that we can easily find the points where certain kinds 684 // are used. 685 func (c *closeContext) decDisjunct(ctx *OpContext, kind depKind) { 686 if kind != DISJUNCT { 687 panic("unexpected kind") 688 } 689 c.decDependent(ctx, DISJUNCT, nil) 690 691 // TODO: the counters are only used in debug mode and we could skip this 692 // if debug is disabled. 693 for ; c != nil; c = c.parent { 694 c.disjunctCount++ 695 } 696 } 697 698 // linkPatterns merges the patterns of child into c, if needed. 699 func (c *closeContext) linkPatterns(child *closeContext) { 700 if len(child.Patterns) > 0 { 701 child.next = c.child 702 c.child = child 703 } 704 } 705 706 // checkArc validates that the node corresponding to cc allows a field with 707 // label v.Label. 708 func (n *nodeContext) checkArc(cc *closeContext, v *Vertex) *Vertex { 709 n.assertInitialized() 710 711 f := v.Label 712 ctx := n.ctx 713 714 if f.IsHidden() || f.IsLet() { 715 return v 716 } 717 718 if cc.isClosed && !matchPattern(ctx, cc.Expr, f) { 719 ctx.notAllowedError(n.node, v) 720 } 721 if n.scheduler.frozen&fieldSetKnown != 0 { 722 for _, a := range n.node.Arcs { 723 if a.Label == f { 724 return v 725 } 726 } 727 var b *Bottom 728 // TODO: include cycle data and improve error message. 729 if f.IsInt() { 730 b = ctx.NewErrf( 731 "element at index %v not allowed by earlier comprehension or reference cycle", f) 732 } else { 733 b = ctx.NewErrf( 734 "field %v not allowed by earlier comprehension or reference cycle", f) 735 } 736 v.SetValue(ctx, b) 737 } 738 739 return v 740 } 741 742 // insertConjunct inserts conjunct c into cc. 743 func (cc *closeContext) insertConjunct(ctx *OpContext, key *closeContext, c Conjunct, id CloseInfo, mode ArcType, check, checkClosed bool) (arc *closeContext, added bool) { 744 arc, _, added = cc.assignConjunct(ctx, key, c, mode, check, checkClosed) 745 if key.src != arc.src { 746 panic("inconsistent src") 747 } 748 749 if !added { 750 return 751 } 752 753 n := key.src.getBareState(ctx) 754 if n == nil { 755 // already done 756 return 757 } 758 759 if key.src.isInProgress() { 760 c.CloseInfo.cc = nil 761 id.cc = arc 762 n.scheduleConjunct(c, id) 763 } 764 765 for _, rec := range n.notify { 766 if mode == ArcPending { 767 panic("unexpected pending arc") 768 } 769 // TODO: we should probably only notify a conjunct once the root of the 770 // conjunct group is completed. This will make it easier to "stitch" the 771 // conjunct trees together, as its correctness will be guaranteed. 772 cc.insertConjunct(ctx, rec.cc, c, id, mode, check, checkClosed) 773 } 774 775 return 776 } 777 778 func (n *nodeContext) insertArc(f Feature, mode ArcType, c Conjunct, id CloseInfo, check bool) *Vertex { 779 v, _ := n.insertArcCC(f, mode, c, id, check) 780 return v 781 } 782 783 // insertArc inserts conjunct c into n. If check is true it will not add c if it 784 // was already added. 785 // Returns the arc of n.node with label f. 786 func (n *nodeContext) insertArcCC(f Feature, mode ArcType, c Conjunct, id CloseInfo, check bool) (*Vertex, *closeContext) { 787 n.assertInitialized() 788 789 if n == nil { 790 panic("nil nodeContext") 791 } 792 if n.node == nil { 793 panic("nil node") 794 } 795 cc := id.cc 796 if cc == nil { 797 panic("nil closeContext") 798 } 799 800 v, insertedArc := n.getArc(f, mode) 801 802 defer n.ctx.PopArc(n.ctx.PushArc(v)) 803 804 // TODO: this block is not strictly needed. Removing it slightly changes the 805 // paths at which errors are reported, arguably, but not clearly, for the 806 // better. Investigate this once the new evaluator is done. 807 if v.ArcType == ArcNotPresent { 808 // It was already determined before that this arc may not be present. 809 // This case can only manifest itself if we have a cycle. 810 n.node.reportFieldCycleError(n.ctx, pos(c.x), f) 811 return v, nil 812 } 813 814 if v.cc == nil { 815 v.cc = v.rootCloseContext(n.ctx) 816 v.cc.generation = n.node.cc.generation 817 } 818 819 arc, added := cc.insertConjunct(n.ctx, v.cc, c, id, mode, check, true) 820 if !added { 821 return v, arc 822 } 823 824 if !insertedArc { 825 return v, arc 826 } 827 828 // Match and insert patterns. 829 if pcs := n.node.PatternConstraints; pcs != nil { 830 for _, pc := range pcs.Pairs { 831 if matchPattern(n.ctx, pc.Pattern, f) { 832 for _, c := range pc.Constraint.Conjuncts { 833 // TODO: consider using the root cc, but probably does not 834 // matter. 835 // This is necessary if we defunct tasks, but otherwise not. 836 // It breaks the CloseContext tests, though. 837 // c.CloseInfo.cc = id.cc 838 n.addConstraint(v, mode, c, check) 839 } 840 } 841 } 842 } 843 844 return v, arc 845 } 846 847 // addConstraint adds a constraint to arc of n. 848 // 849 // In order to resolve LabelReferences, it is not always possible to walk up 850 // the parent Vertex chain to determan the label, because a label reference 851 // may point past a point of referral. For instance, 852 // 853 // test: [ID=_]: name: ID 854 // test: A: {} 855 // B: test.A & {} // B.name should be "A", not "B". 856 // 857 // The arc must be the node arc to which the conjunct is added. 858 func (n *nodeContext) addConstraint(arc *Vertex, mode ArcType, c Conjunct, check bool) { 859 n.assertInitialized() 860 861 // TODO(perf): avoid cloning the Environment, if: 862 // - the pattern constraint has no LabelReference 863 // (require compile-time support) 864 // - there are no references in the conjunct pointing to this node. 865 // - consider adding this value to the Conjunct struct 866 f := arc.Label 867 bulkEnv := *c.Env 868 bulkEnv.DynamicLabel = f 869 c.Env = &bulkEnv 870 871 // TODO(constraintNode): this should ideally be 872 // cc := id.cc 873 // or 874 // cc := c.CloseInfo.cc.src.cc 875 // 876 // Where id is the closeContext corresponding to the field, or the root 877 // context. But it is a bit hard to figure out how to account for this, as 878 // either this information is not available or the root context results in 879 // errors for the other use of addConstraint. For this reason, we keep 880 // things symmetric for now and will keep things as is, just avoiding the 881 // closedness check. 882 cc := c.CloseInfo.cc 883 884 arc, _ = n.getArc(f, mode) 885 886 root := arc.rootCloseContext(n.ctx) 887 cc.insertConjunct(n.ctx, root, c, c.CloseInfo, mode, check, false) 888 } 889 890 func (n *nodeContext) insertPattern(pattern Value, c Conjunct) { 891 n.assertInitialized() 892 893 ctx := n.ctx 894 cc := c.CloseInfo.cc 895 896 // Collect patterns in root vertex. This allows comparing disjuncts for 897 // equality as well as inserting new arcs down the line as they are 898 // inserted. 899 if n.insertConstraint(pattern, c) { 900 // Match against full set of arcs from root, but insert in current vertex. 901 // Hypothesis: this may not be necessary. Maybe for closedness. 902 // TODO: may need to replicate the closedContext for patterns. 903 // Also: Conjuncts for matching other arcs in this node may be different 904 // for matching arcs using v.foo?, if we need to ensure that conjuncts 905 // from arcs and patterns are grouped under the same vertex. 906 // TODO: verify. See test Pattern 1b 907 for _, a := range n.node.Arcs { 908 if matchPattern(n.ctx, pattern, a.Label) { 909 // TODO: is it necessary to check for uniqueness here? 910 n.addConstraint(a, a.ArcType, c, true) 911 } 912 } 913 } 914 915 if cc.isTotal { 916 return 917 } 918 if isTotal(pattern) { 919 cc.isTotal = true 920 cc.Patterns = cc.Patterns[:0] 921 return 922 } 923 924 // insert pattern in current set. 925 // TODO: normalize patterns 926 // TODO: do we only need to do this for closed contexts? 927 for _, pc := range cc.Patterns { 928 if Equal(ctx, pc, pattern, 0) { 929 return 930 } 931 } 932 cc.Patterns = append(cc.Patterns, pattern) 933 } 934 935 // isTotal reports whether pattern value p represents a full domain, that is, 936 // whether it is of type BasicType or Top. 937 func isTotal(p Value) bool { 938 switch p.(type) { 939 case *BasicType: 940 return true 941 case *Top: 942 return true 943 } 944 return false 945 } 946 947 // injectClosed updates dst so that it only allows fields allowed by closed. 948 // 949 // It first ensures that the fields contained in dst are allowed by the fields 950 // and patterns defined in closed. It reports an error in the nodeContext if 951 // this is not the case. 952 func injectClosed(ctx *OpContext, closed, dst *closeContext) { 953 // TODO: check that fields are not void arcs. 954 outer: 955 for _, a := range dst.arcs { 956 if a.kind != ARC { 957 continue 958 } 959 ca := a.cc 960 f := ca.Label() 961 switch ca.src.ArcType { 962 case ArcMember, ArcRequired: 963 case ArcOptional, ArcNotPresent: 964 // Without this continue, an evaluation error may be propagated to 965 // parent nodes that are otherwise allowed. 966 continue 967 case ArcPending: 968 // TODO: Need to evaluate? 969 default: 970 panic("unreachable") 971 } 972 // TODO: disallow new definitions in closed structs. 973 if f.IsHidden() || f.IsLet() || f.IsDef() { 974 continue 975 } 976 for _, b := range closed.arcs { 977 cb := b.cc 978 // TODO: we could potentially remove the check for ArcPending if we 979 // explicitly set the arcType to ArcNonPresent when a comprehension 980 // yields no results. 981 if cb.arcType == ArcNotPresent || cb.arcType == ArcPending { 982 continue 983 } 984 if f == cb.Label() { 985 continue outer 986 } 987 } 988 if !matchPattern(ctx, closed.Expr, ca.Label()) { 989 ctx.notAllowedError(closed.src, ca.src) 990 continue 991 } 992 } 993 994 if !dst.isClosed { 995 // Since dst is not closed, it is safe to take all patterns from 996 // closed. 997 // This is only necessary for passing up patterns into embeddings. For 998 // (the conjunction of) definitions the construction is handled 999 // elsewhere. 1000 // TODO(perf): reclaim slice memory 1001 dst.Patterns = closed.Patterns 1002 1003 dst.isClosed = true 1004 } 1005 } 1006 1007 func (ctx *OpContext) addPositions(c Conjunct) { 1008 if x, ok := c.x.(*ConjunctGroup); ok { 1009 for _, c := range *x { 1010 ctx.addPositions(c) 1011 } 1012 } 1013 if pos := c.Field(); pos != nil { 1014 ctx.AddPosition(pos) 1015 } 1016 } 1017 1018 // notAllowedError reports a field not allowed error in n and sets the value 1019 // for arc f to that error. 1020 func (ctx *OpContext) notAllowedError(v, arc *Vertex) { 1021 defer ctx.PopArc(ctx.PushArc(arc)) 1022 1023 defer ctx.ReleasePositions(ctx.MarkPositions()) 1024 1025 for _, c := range arc.Conjuncts { 1026 ctx.addPositions(c) 1027 } 1028 // TODO(0.7): Find another way to get this provenance information. Not 1029 // currently stored in new evaluator. 1030 // for _, s := range x.Structs { 1031 // s.AddPositions(ctx) 1032 // } 1033 1034 // TODO: use the arcType from the closeContext. 1035 if arc.ArcType == ArcPending { 1036 // arc.ArcType = ArcNotPresent 1037 // We do not know yet whether the arc will be present or not. Checking 1038 // this will be deferred until this is known, after the comprehension 1039 // has been evaluated. 1040 return 1041 } 1042 // TODO: setting arc instead of n.node eliminates subfields. This may be 1043 // desirable or not, but it differs, at least from <=v0.6 behavior. 1044 arc.SetValue(ctx, ctx.NewErrf("field not allowed")) 1045 if arc.state != nil { 1046 arc.state.kind = 0 1047 } 1048 1049 // TODO: remove? We are now setting it on both fields, which seems to be 1050 // necessary for now. But we should remove this as it often results in 1051 // a duplicate error. 1052 // v.SetValue(ctx, ctx.NewErrf("field not allowed")) 1053 1054 // TODO: create a special kind of error that gets the positions 1055 // of the relevant locations upon request from the arc. 1056 } 1057 1058 // mergeConjunctions combines two values into one. It never modifies an 1059 // existing conjunction. 1060 func mergeConjunctions(a, b Value) Value { 1061 if a == nil { 1062 return b 1063 } 1064 if b == nil { 1065 return a 1066 } 1067 ca, _ := a.(*Conjunction) 1068 cb, _ := b.(*Conjunction) 1069 n := 2 1070 if ca != nil { 1071 n += len(ca.Values) - 1 1072 } 1073 if cb != nil { 1074 n += len(cb.Values) - 1 1075 } 1076 vs := make([]Value, 0, n) 1077 if ca != nil { 1078 vs = append(vs, ca.Values...) 1079 } else { 1080 vs = append(vs, a) 1081 } 1082 if cb != nil { 1083 vs = append(vs, cb.Values...) 1084 } else { 1085 vs = append(vs, b) 1086 } 1087 // TODO: potentially order conjuncts to make matching more likely. 1088 return &Conjunction{Values: vs} 1089 } 1090 1091 // finalizePattern updates c.Expr to a CUE Value representing all fields allowed 1092 // by the pattern constraints of c. If this context or any of its direct 1093 // children is closed, the result will be a conjunction of all these closed 1094 // values. Otherwise it will be a disjunction of all its children. A nil value 1095 // represents all values. 1096 func (c *closeContext) finalizePattern() { 1097 switch { 1098 case c.Expr != nil: // Patterns and expression are already set. 1099 if !c.isClosed { 1100 panic("c.Expr set unexpectedly") 1101 } 1102 return 1103 case c.isTotal: // All values are allowed always. 1104 return 1105 } 1106 1107 // As this context is not closed, the pattern is somewhat meaningless. 1108 // It may still be useful for analysis. 1109 or := c.Patterns 1110 1111 for cc := c.child; cc != nil; cc = cc.next { 1112 if cc.isTotal { 1113 return 1114 } 1115 // Could be closed, in which case it must also be an embedding. 1116 1117 // TODO: simplify the values. 1118 switch x := cc.Expr.(type) { 1119 case nil: 1120 case *Disjunction: 1121 or = append(or, x.Values...) 1122 default: 1123 or = append(or, x) 1124 } 1125 } 1126 1127 switch len(or) { 1128 case 0: 1129 case 1: 1130 c.Expr = or[0] 1131 default: 1132 // TODO: potentially order conjuncts to make matching more likely. 1133 c.Expr = &Disjunction{Values: or} 1134 } 1135 }