cuelang.org/go@v0.10.1/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/ast"
    21  )
    22  
    23  // This file contains functionality for processing conjuncts to insert the
    24  // corresponding values in the Vertex.
    25  //
    26  // Conjuncts are divided into two classes:
    27  // - literal values that need no evaluation: these are inserted directly into
    28  //   the Vertex.
    29  // - field or value expressions that need to be evaluated: these are inserted
    30  //   as a task into the Vertex' associated scheduler for later evaluation.
    31  //   The implementation of these tasks can be found in tasks.go.
    32  //
    33  // The main entrypoint is scheduleConjunct.
    34  
    35  // scheduleConjunct splits c into parts to be incrementally processed and queues
    36  // these parts up for processing. it will itself not cause recursive processing.
    37  func (n *nodeContext) scheduleConjunct(c Conjunct, id CloseInfo) {
    38  	n.assertInitialized()
    39  
    40  	// Explanation of switch statement:
    41  	//
    42  	// A Conjunct can be a leaf or, through a ConjunctGroup, a tree. The tree
    43  	// reflects the history of how the conjunct was inserted in terms of
    44  	// definitions and embeddings. This, in turn, is used to compute closedness.
    45  	//
    46  	// Once all conjuncts for a Vertex have been collected, this tree contains
    47  	// all the information needed to trace its histroy: if a Vertex is
    48  	// referenced in an expression, this tree can be used to insert the
    49  	// conjuncts keeping closedness in mind.
    50  	//
    51  	// In the collection phase, however, this is not sufficient. CUE computes
    52  	// conjuncts "out of band". This means that conjuncts accumulate in
    53  	// different parts of the tree in an indeterminate order. closeContext is
    54  	// used to account for this.
    55  	//
    56  	// Basically, if the closeContext associated with c belongs to n, we take
    57  	// it that the conjunct needs to be inserted at the point in the tree
    58  	// associated by this closeContext. If, on the other hand, the closeContext
    59  	// is not defined or does not belong to this node, we take this conjunct
    60  	// is inserted by means of a reference. In this case we assume that the
    61  	// computation of the tree has completed and the tree can be used to reflect
    62  	// the closedness structure.
    63  	//
    64  	// TODO: once the evaluator is done and all tests pass, consider having
    65  	// two different entry points to account for these cases.
    66  	switch cc := c.CloseInfo.cc; {
    67  	case cc == nil || cc.src != n.node:
    68  		// In this case, a Conjunct is inserted from another Arc. If the
    69  		// conjunct represents an embedding or definition, we need to create a
    70  		// new closeContext to represent this.
    71  		if id.cc == nil {
    72  			id.cc = n.node.rootCloseContext(n.ctx)
    73  		}
    74  		if id.cc == cc {
    75  			panic("inconsistent state: same closeContext")
    76  		}
    77  		var t closeNodeType
    78  		if c.CloseInfo.FromDef {
    79  			t |= closeDef
    80  		}
    81  		if c.CloseInfo.FromEmbed {
    82  			t |= closeEmbed
    83  		}
    84  		if t != 0 {
    85  			id, _ = id.spawnCloseContext(n.ctx, t)
    86  		}
    87  		if !id.cc.done {
    88  			id.cc.incDependent(n.ctx, DEFER, nil)
    89  			defer id.cc.decDependent(n.ctx, DEFER, nil)
    90  		}
    91  
    92  		if id.cc.src != n.node {
    93  			panic("inconsistent state: nodes differ")
    94  		}
    95  	default:
    96  
    97  		// In this case, the conjunct is inserted as the result of an expansion
    98  		// of a conjunct in place, not a reference. In this case, we must use
    99  		// the cached closeContext.
   100  		id.cc = cc
   101  
   102  		// Note this subtlety: we MUST take the cycle info from c when this is
   103  		// an in place evaluated node, otherwise we must take that of id.
   104  		id.CycleInfo = c.CloseInfo.CycleInfo
   105  	}
   106  
   107  	if id.cc.needsCloseInSchedule != nil {
   108  		dep := id.cc.needsCloseInSchedule
   109  		id.cc.needsCloseInSchedule = nil
   110  		defer id.cc.decDependent(n.ctx, EVAL, dep)
   111  	}
   112  
   113  	env := c.Env
   114  
   115  	if id.cc.isDef {
   116  		n.node.Closed = true
   117  	}
   118  
   119  	switch x := c.Elem().(type) {
   120  	case *ConjunctGroup:
   121  		for _, c := range *x {
   122  			// TODO(perf): can be one loop
   123  
   124  			cc := c.CloseInfo.cc
   125  			if cc.src == n.node && cc.needsCloseInSchedule != nil {
   126  				// We need to handle this specifically within the ConjunctGroup
   127  				// loop, because multiple conjuncts may be using the same root
   128  				// closeContext. This can be merged once Vertex.Conjuncts is an
   129  				// interface, requiring any list to be a root conjunct.
   130  
   131  				dep := cc.needsCloseInSchedule
   132  				cc.needsCloseInSchedule = nil
   133  				defer cc.decDependent(n.ctx, EVAL, dep)
   134  			}
   135  		}
   136  		for _, c := range *x {
   137  			n.scheduleConjunct(c, id)
   138  		}
   139  
   140  	case *Vertex:
   141  		// TODO: move this logic to scheduleVertexConjuncts or at least ensure
   142  		// that we can also share data Vertices?
   143  		if x.IsData() {
   144  			n.unshare()
   145  			n.insertValueConjunct(env, x, id)
   146  		} else {
   147  			n.scheduleVertexConjuncts(c, x, id)
   148  		}
   149  
   150  	case Value:
   151  		// TODO: perhaps some values could be shared.
   152  		n.unshare()
   153  		n.insertValueConjunct(env, x, id)
   154  
   155  	case *BinaryExpr:
   156  		// NOTE: do not unshare: a conjunction could still allow structure
   157  		// sharing, such as in the case of `ref & ref`.
   158  		if x.Op == AndOp {
   159  			n.scheduleConjunct(MakeConjunct(env, x.X, id), id)
   160  			n.scheduleConjunct(MakeConjunct(env, x.Y, id), id)
   161  			return
   162  		}
   163  
   164  		n.unshare()
   165  		// Even though disjunctions and conjunctions are excluded, the result
   166  		// must may still be list in the case of list arithmetic. This could
   167  		// be a scalar value only once this is no longer supported.
   168  		n.scheduleTask(handleExpr, env, x, id)
   169  
   170  	case *StructLit:
   171  		n.unshare()
   172  		n.scheduleStruct(env, x, id)
   173  
   174  	case *ListLit:
   175  		n.unshare()
   176  
   177  		// At this point we known we have at least an empty list.
   178  		n.updateCyclicStatus(id)
   179  
   180  		env := &Environment{
   181  			Up:     env,
   182  			Vertex: n.node,
   183  		}
   184  		n.scheduleTask(handleListLit, env, x, id)
   185  
   186  	case *DisjunctionExpr:
   187  		n.unshare()
   188  
   189  		// TODO(perf): reuse envDisjunct values so that we can also reuse the
   190  		// disjunct slice.
   191  		d := envDisjunct{
   192  			env:     env,
   193  			cloneID: id,
   194  			src:     x,
   195  			expr:    x,
   196  		}
   197  		for _, dv := range x.Values {
   198  			d.disjuncts = append(d.disjuncts, disjunct{
   199  				expr:      dv.Val,
   200  				isDefault: dv.Default,
   201  				mode:      mode(x.HasDefaults, dv.Default),
   202  			})
   203  		}
   204  		n.scheduleDisjunction(d)
   205  
   206  	case *Comprehension:
   207  		// always a partial comprehension.
   208  		n.insertComprehension(env, x, id)
   209  
   210  	case Resolver:
   211  		n.scheduleTask(handleResolver, env, x, id)
   212  
   213  	case Evaluator:
   214  		n.unshare()
   215  		// Interpolation, UnaryExpr, CallExpr
   216  		n.scheduleTask(handleExpr, env, x, id)
   217  
   218  	default:
   219  		panic("unreachable")
   220  	}
   221  
   222  	n.ctx.stats.Conjuncts++
   223  }
   224  
   225  // scheduleStruct records all elements of this conjunct in the structure and
   226  // then processes it. If an element needs to be inserted for evaluation,
   227  // it may be scheduled.
   228  func (n *nodeContext) scheduleStruct(env *Environment,
   229  	s *StructLit,
   230  	ci CloseInfo) {
   231  	n.updateCyclicStatus(ci)
   232  
   233  	// NOTE: This is a crucial point in the code:
   234  	// Unification dereferencing happens here. The child nodes are set to
   235  	// an Environment linked to the current node. Together with the De Bruijn
   236  	// indices, this determines to which Vertex a reference resolves.
   237  
   238  	childEnv := &Environment{
   239  		Up:     env,
   240  		Vertex: n.node,
   241  	}
   242  
   243  	hasEmbed := false
   244  	hasEllipsis := false
   245  
   246  	// TODO: do we still need this?
   247  	// shouldClose := ci.cc.isDef || ci.cc.isClosedOnce
   248  
   249  	s.Init(n.ctx)
   250  
   251  	// TODO: do we still need to AddStruct and do we still need to Disable?
   252  	parent := n.node.AddStruct(s, childEnv, ci)
   253  	parent.Disable = true // disable until processing is done.
   254  	ci.IsClosed = false
   255  
   256  	// TODO: precompile
   257  loop1:
   258  	for _, d := range s.Decls {
   259  		switch d.(type) {
   260  		case *Ellipsis:
   261  			hasEllipsis = true
   262  			break loop1
   263  		}
   264  	}
   265  
   266  	// TODO(perf): precompile whether struct has embedding.
   267  loop2:
   268  	for _, d := range s.Decls {
   269  		switch d.(type) {
   270  		case *Comprehension, Expr:
   271  			// No need to increment and decrement, as there will be at least
   272  			// one entry.
   273  			if _, ok := s.Src.(*ast.File); !ok {
   274  				// If this is not a file, the struct indicates the scope/
   275  				// boundary at which closedness should apply. This is not true
   276  				// for files.
   277  				// TODO: set this as a flag in StructLit so as to not have to
   278  				// do the somewhat dangerous cast here.
   279  				ci, _ = ci.spawnCloseContext(n.ctx, 0)
   280  			}
   281  			// Note: adding a count is not needed here, as there will be an
   282  			// embed spawn below.
   283  			hasEmbed = true
   284  			break loop2
   285  		}
   286  	}
   287  
   288  	// First add fixed fields and schedule expressions.
   289  	for _, d := range s.Decls {
   290  		switch x := d.(type) {
   291  		case *Field:
   292  			if x.Label.IsString() && x.ArcType == ArcMember {
   293  				n.aStruct = s
   294  				n.aStructID = ci
   295  			}
   296  			fc := MakeConjunct(childEnv, x, ci)
   297  			// fc.CloseInfo.cc = nil // TODO: should we add this?
   298  			n.insertArc(x.Label, x.ArcType, fc, ci, true)
   299  
   300  		case *LetField:
   301  			lc := MakeConjunct(childEnv, x, ci)
   302  			n.insertArc(x.Label, ArcMember, lc, ci, true)
   303  
   304  		case *Comprehension:
   305  			ci, cc := ci.spawnCloseContext(n.ctx, closeEmbed)
   306  			cc.incDependent(n.ctx, DEFER, nil)
   307  			defer cc.decDependent(n.ctx, DEFER, nil)
   308  			n.insertComprehension(childEnv, x, ci)
   309  			hasEmbed = true
   310  
   311  		case *Ellipsis:
   312  			// Can be added unconditionally to patterns.
   313  			ci.cc.isDef = false
   314  			ci.cc.isClosed = false
   315  			ci.cc.isDefOrig = false
   316  
   317  		case *DynamicField:
   318  			if x.ArcType == ArcMember {
   319  				n.aStruct = s
   320  				n.aStructID = ci
   321  			}
   322  			n.scheduleTask(handleDynamic, childEnv, x, ci)
   323  
   324  		case *BulkOptionalField:
   325  
   326  			// All do not depend on each other, so can be added at once.
   327  			n.scheduleTask(handlePatternConstraint, childEnv, x, ci)
   328  
   329  		case Expr:
   330  			// TODO: perhaps special case scalar Values to avoid creating embedding.
   331  			ci, cc := ci.spawnCloseContext(n.ctx, closeEmbed)
   332  
   333  			// TODO: do we need to increment here?
   334  			cc.incDependent(n.ctx, DEFER, nil) // decrement deferred below
   335  			defer cc.decDependent(n.ctx, DEFER, nil)
   336  
   337  			ec := MakeConjunct(childEnv, x, ci)
   338  			n.scheduleConjunct(ec, ci)
   339  			hasEmbed = true
   340  		}
   341  	}
   342  	if hasEllipsis {
   343  		ci.cc.hasEllipsis = true
   344  	}
   345  	if !hasEmbed {
   346  		n.aStruct = s
   347  		n.aStructID = ci
   348  		ci.cc.hasNonTop = true
   349  	}
   350  
   351  	// TODO: probably no longer necessary.
   352  	parent.Disable = false
   353  }
   354  
   355  // scheduleVertexConjuncts injects the conjuncst of src n. If src was not fully
   356  // evaluated, it subscribes dst for future updates.
   357  func (n *nodeContext) scheduleVertexConjuncts(c Conjunct, arc *Vertex, closeInfo CloseInfo) {
   358  	// disjunctions, we need to dereference he underlying node.
   359  	if deref(n.node) == deref(arc) {
   360  		return
   361  	}
   362  
   363  	if n.shareIfPossible(c, arc, closeInfo) {
   364  		arc.getState(n.ctx)
   365  		return
   366  	}
   367  
   368  	// We need to ensure that each arc is only unified once (or at least) a
   369  	// bounded time, witch each conjunct. Comprehensions, for instance, may
   370  	// distribute a value across many values that get unified back into the
   371  	// same value. If such a value is a disjunction, than a disjunction of N
   372  	// disjuncts will result in a factor N more unifications for each
   373  	// occurrence of such value, resulting in exponential running time. This
   374  	// is especially common values that are used as a type.
   375  	//
   376  	// However, unification is idempotent, so each such conjunct only needs
   377  	// to be unified once. This cache checks for this and prevents an
   378  	// exponential blowup in such case.
   379  	//
   380  	// TODO(perf): this cache ensures the conjuncts of an arc at most once
   381  	// per ID. However, we really need to add the conjuncts of an arc only
   382  	// once total, and then add the close information once per close ID
   383  	// (pointer can probably be shared). Aside from being more performant,
   384  	// this is probably the best way to guarantee that conjunctions are
   385  	// linear in this case.
   386  
   387  	ciKey := closeInfo
   388  	ciKey.Refs = nil
   389  	ciKey.Inline = false
   390  	key := arcKey{arc, ciKey}
   391  	for _, k := range n.arcMap {
   392  		if key == k {
   393  			return
   394  		}
   395  	}
   396  	n.arcMap = append(n.arcMap, key)
   397  
   398  	if IsDef(c.Expr()) {
   399  		// TODO: or should we always insert the wrapper (for errors)?
   400  		ci, dc := closeInfo.spawnCloseContext(n.ctx, closeDef)
   401  		closeInfo = ci
   402  
   403  		dc.incDependent(n.ctx, DEFER, nil) // decrement deferred below
   404  		defer dc.decDependent(n.ctx, DEFER, nil)
   405  	}
   406  
   407  	if !n.node.nonRooted || n.node.IsDynamic {
   408  		if state := arc.getBareState(n.ctx); state != nil {
   409  			state.addNotify2(n.node, closeInfo)
   410  		}
   411  	}
   412  
   413  	// TODO(perf): buffer or use stack.
   414  	var a []*closeContext
   415  	a = appendPrefix(a, closeInfo.cc)
   416  
   417  	// Use explicit index in case Conjuncts grows during iteration.
   418  	for i := 0; i < len(arc.Conjuncts); i++ {
   419  		c := arc.Conjuncts[i]
   420  		n.insertAndSkipConjuncts(a, c, closeInfo)
   421  	}
   422  
   423  	if state := arc.getBareState(n.ctx); state != nil {
   424  		n.toComplete = true
   425  	}
   426  }
   427  
   428  // appendPrefix records the closeContext from the root of the current node to
   429  // cc by walking up the parent chain and storing the results ancestor first.
   430  // This is used to split conjunct trees into a forest of little trees.
   431  func appendPrefix(a []*closeContext, cc *closeContext) []*closeContext {
   432  	if cc.parent != nil {
   433  		a = appendPrefix(a, cc.parent)
   434  	}
   435  	a = append(a, cc)
   436  	return a
   437  }
   438  
   439  // insertAndSkipConjuncts analyzes the conjunct tree represented by c and splits
   440  // it into branches from the point where it deviates from the conjunct branch
   441  // represented by skip.
   442  //
   443  // TODO(evalv3): Consider this example:
   444  //
   445  //	#A: {
   446  //		b: {} // error only reported here.
   447  //		c: b & {
   448  //			// error (g not allowed) not reported here, as it would be okay if b
   449  //			// were valid. Instead, it is reported at b only. This is conform
   450  //			// the spec.
   451  //			d: 1
   452  //		}
   453  //	}
   454  //	x: #A
   455  //	x: b: g: 1
   456  //
   457  // We could also report an error at g by tracing if a conjunct crosses a isDef
   458  // boundary between the root of c and the cc of the conjunct.
   459  // Not doing so might have an effect on the outcome of disjunctions. This may be
   460  // okay (ideally closedness is not modal), but something to consider. For now,
   461  // we should probably copy whatever v2 was doing.
   462  func (n *nodeContext) insertAndSkipConjuncts(skip []*closeContext, c Conjunct, id CloseInfo) {
   463  	if c.CloseInfo.cc == nil {
   464  		n.scheduleConjunct(c, id)
   465  		return
   466  	}
   467  
   468  	root := c.CloseInfo.cc.origin
   469  
   470  	// TODO(perf): closeContexts should be exact prefixes. So instead of
   471  	// searching the list, we could test them incrementally. This seems more
   472  	// robust for now as the data structure might slightly change and cause
   473  	// disalignment.
   474  	for _, s := range skip {
   475  		if root == s.origin {
   476  			switch x := c.Elem().(type) {
   477  			case *ConjunctGroup:
   478  				for _, c := range *x {
   479  					n.insertAndSkipConjuncts(skip, c, id)
   480  				}
   481  
   482  			default:
   483  				// TODO: do leaf conjuncts that match need different treatment
   484  				// from those that don't? Right now, we treat them the same.
   485  				n.scheduleConjunct(c, id)
   486  			}
   487  			return
   488  		}
   489  	}
   490  
   491  	n.scheduleConjunct(c, id)
   492  }
   493  
   494  func (n *nodeContext) addNotify2(v *Vertex, c CloseInfo) []receiver {
   495  	// No need to do the notification mechanism if we are already complete.
   496  	old := n.notify
   497  	switch {
   498  	case n.node.isFinal():
   499  		return old
   500  	case !n.node.isInProgress():
   501  	case n.meets(allAncestorsProcessed):
   502  		return old
   503  	}
   504  
   505  	// Create a "root" closeContext to reflect the entry point of the
   506  	// reference into n.node relative to cc within v. After that, we can use
   507  	// assignConjunct to add new conjuncts.
   508  
   509  	// TODO: dedup: only add if t does not already exist. First check if this
   510  	// is even possible by adding a panic.
   511  	root := n.node.rootCloseContext(n.ctx)
   512  	if root.isDecremented {
   513  		return old
   514  	}
   515  
   516  	for _, r := range n.notify {
   517  		if r.v == v && r.cc == c.cc {
   518  			return old
   519  		}
   520  	}
   521  
   522  	cc := c.cc
   523  
   524  	if root.linkNotify(n.ctx, v, cc, c.CycleInfo) {
   525  		n.notify = append(n.notify, receiver{v, cc})
   526  	}
   527  
   528  	return old
   529  }
   530  
   531  // Literal conjuncts
   532  
   533  func (n *nodeContext) insertValueConjunct(env *Environment, v Value, id CloseInfo) {
   534  	n.updateCyclicStatus(id)
   535  
   536  	ctx := n.ctx
   537  
   538  	switch x := v.(type) {
   539  	case *Vertex:
   540  		if m, ok := x.BaseValue.(*StructMarker); ok {
   541  			n.aStruct = x
   542  			n.aStructID = id
   543  			if m.NeedClose {
   544  				// TODO: In the new evaluator this is used to mark a struct
   545  				// as closed in the debug output. Once the old evaluator is
   546  				// gone, we could simplify this.
   547  				id.IsClosed = true
   548  				if ctx.isDevVersion() {
   549  					var cc *closeContext
   550  					id, cc = id.spawnCloseContext(n.ctx, 0)
   551  					cc.isClosedOnce = true
   552  				}
   553  			}
   554  		}
   555  
   556  		if !x.IsData() {
   557  			c := MakeConjunct(env, x, id)
   558  			n.scheduleVertexConjuncts(c, x, id)
   559  			return
   560  		}
   561  
   562  		// TODO: evaluate value?
   563  		switch v := x.BaseValue.(type) {
   564  		default:
   565  			panic(fmt.Sprintf("invalid type %T", x.BaseValue))
   566  
   567  		case *ListMarker:
   568  			n.updateCyclicStatus(id)
   569  
   570  			// TODO: arguably we know now that the type _must_ be a list.
   571  			n.scheduleTask(handleListVertex, env, x, id)
   572  
   573  			return
   574  
   575  		case *StructMarker:
   576  			for _, a := range x.Arcs {
   577  				if a.ArcType != ArcMember {
   578  					continue
   579  				}
   580  				// TODO(errors): report error when this is a regular field.
   581  				c := MakeConjunct(nil, a, id)
   582  				n.insertArc(a.Label, a.ArcType, c, id, true)
   583  			}
   584  
   585  		case Value:
   586  			n.insertValueConjunct(env, v, id)
   587  		}
   588  
   589  		return
   590  
   591  	case *Bottom:
   592  		id.cc.hasNonTop = true
   593  		n.addBottom(x)
   594  		return
   595  
   596  	case *Builtin:
   597  		id.cc.hasNonTop = true
   598  		if v := x.BareValidator(); v != nil {
   599  			n.insertValueConjunct(env, v, id)
   600  			return
   601  		}
   602  	}
   603  
   604  	if !n.updateNodeType(v.Kind(), v, id) {
   605  		return
   606  	}
   607  
   608  	switch x := v.(type) {
   609  	case *Disjunction:
   610  		// TODO(perf): reuse envDisjunct values so that we can also reuse the
   611  		// disjunct slice.
   612  		d := envDisjunct{
   613  			env:     env,
   614  			cloneID: id,
   615  			src:     x,
   616  			value:   x,
   617  		}
   618  		for i, dv := range x.Values {
   619  			d.disjuncts = append(d.disjuncts, disjunct{
   620  				expr:      dv,
   621  				isDefault: i < x.NumDefaults,
   622  				mode:      mode(x.HasDefaults, i < x.NumDefaults),
   623  			})
   624  		}
   625  		n.scheduleDisjunction(d)
   626  
   627  	case *Conjunction:
   628  		// TODO: consider sharing: conjunct could be `ref & ref`, for instance,
   629  		// in which case ref could still be shared.
   630  
   631  		for _, x := range x.Values {
   632  			n.insertValueConjunct(env, x, id)
   633  		}
   634  
   635  	case *Top:
   636  		n.hasTop = true
   637  		id.cc.hasTop = true
   638  
   639  	case *BasicType:
   640  		id.cc.hasNonTop = true
   641  
   642  	case *BoundValue:
   643  		id.cc.hasNonTop = true
   644  		switch x.Op {
   645  		case LessThanOp, LessEqualOp:
   646  			if y := n.upperBound; y != nil {
   647  				n.upperBound = nil
   648  				v := SimplifyBounds(ctx, n.kind, x, y)
   649  				if err := valueError(v); err != nil {
   650  					err.AddPosition(v)
   651  					err.AddPosition(n.upperBound)
   652  					err.AddClosedPositions(id)
   653  				}
   654  				n.insertValueConjunct(env, v, id)
   655  				return
   656  			}
   657  			n.upperBound = x
   658  
   659  		case GreaterThanOp, GreaterEqualOp:
   660  			if y := n.lowerBound; y != nil {
   661  				n.lowerBound = nil
   662  				v := SimplifyBounds(ctx, n.kind, x, y)
   663  				if err := valueError(v); err != nil {
   664  					err.AddPosition(v)
   665  					err.AddPosition(n.lowerBound)
   666  					err.AddClosedPositions(id)
   667  				}
   668  				n.insertValueConjunct(env, v, id)
   669  				return
   670  			}
   671  			n.lowerBound = x
   672  
   673  		case EqualOp, NotEqualOp, MatchOp, NotMatchOp:
   674  			// This check serves as simplifier, but also to remove duplicates.
   675  			k := 0
   676  			match := false
   677  			for _, c := range n.checks {
   678  				if y, ok := c.(*BoundValue); ok {
   679  					switch z := SimplifyBounds(ctx, n.kind, x, y); {
   680  					case z == y:
   681  						match = true
   682  					case z == x:
   683  						continue
   684  					}
   685  				}
   686  				n.checks[k] = c
   687  				k++
   688  			}
   689  			n.checks = n.checks[:k]
   690  			if !match {
   691  				n.checks = append(n.checks, x)
   692  			}
   693  			return
   694  		}
   695  
   696  	case Validator:
   697  		// This check serves as simplifier, but also to remove duplicates.
   698  		for i, y := range n.checks {
   699  			if b := SimplifyValidator(ctx, x, y); b != nil {
   700  				n.checks[i] = b
   701  				return
   702  			}
   703  		}
   704  		kind := x.Kind()
   705  		n.updateNodeType(kind, x, id)
   706  		// A validator that is inserted in a closeContext should behave like top
   707  		// in the sense that the closeContext should not be closed if no other
   708  		// value is present that would erase top (cc.hasNonTop): if a field is
   709  		// only associated with a validator, we leave it to the validator to
   710  		// decide what fields are allowed.
   711  		if kind&(ListKind|StructKind) != 0 {
   712  			id.cc.hasTop = true
   713  		}
   714  		n.checks = append(n.checks, x)
   715  
   716  	case *Vertex:
   717  	// handled above.
   718  
   719  	case Value: // *NullLit, *BoolLit, *NumLit, *StringLit, *BytesLit, *Builtin
   720  		if y := n.scalar; y != nil {
   721  			if b, ok := BinOp(ctx, EqualOp, x, y).(*Bool); !ok || !b.B {
   722  				n.reportConflict(x, y, x.Kind(), y.Kind(), n.scalarID, id)
   723  			}
   724  			break
   725  		}
   726  		n.scalar = x
   727  		n.scalarID = id
   728  		n.signal(scalarKnown)
   729  
   730  	default:
   731  		panic(fmt.Sprintf("unknown value type %T", x))
   732  	}
   733  
   734  	if n.lowerBound != nil && n.upperBound != nil {
   735  		if u := SimplifyBounds(ctx, n.kind, n.lowerBound, n.upperBound); u != nil {
   736  			if err := valueError(u); err != nil {
   737  				err.AddPosition(n.lowerBound)
   738  				err.AddPosition(n.upperBound)
   739  				err.AddClosedPositions(id)
   740  			}
   741  			n.lowerBound = nil
   742  			n.upperBound = nil
   743  			n.insertValueConjunct(env, u, id)
   744  		}
   745  	}
   746  }