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  }