github.com/solo-io/cue@v0.4.7/internal/core/subsume/vertex.go (about)

     1  // Copyright 2020 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 subsume
    16  
    17  import (
    18  	"fmt"
    19  
    20  	"github.com/solo-io/cue/internal/core/adt"
    21  	"github.com/solo-io/cue/internal/core/export"
    22  )
    23  
    24  // Notes:
    25  // - Can optional fields of y can always be ignored here? Maybe not in the
    26  //   schema case.
    27  // - Definitions of y can be ignored in data mode.
    28  //
    29  // TODO(perf): use merge sort where possible.
    30  func (s *subsumer) vertices(x, y *adt.Vertex) bool {
    31  	if x == y {
    32  		return true
    33  	}
    34  
    35  	if s.Defaults {
    36  		y = y.Default()
    37  	}
    38  
    39  	if b, _ := y.BaseValue.(*adt.Bottom); b != nil {
    40  		// If the value is incomplete, the error is not final. So either check
    41  		// structural equivalence or return an error.
    42  		return !b.IsIncomplete()
    43  	}
    44  
    45  	ctx := s.ctx
    46  
    47  	final := y.IsData() || s.Final
    48  
    49  	switch v := x.BaseValue.(type) {
    50  	case *adt.Bottom:
    51  		return false
    52  
    53  	case *adt.ListMarker:
    54  		if !y.IsList() {
    55  			s.errf("list does not subsume %s (type %s)", y, y.Kind())
    56  			return false
    57  		}
    58  		if !s.listVertices(x, y) {
    59  			return false
    60  		}
    61  		// TODO: allow other arcs alongside list arc.
    62  		return true
    63  
    64  	case *adt.StructMarker:
    65  		_, ok := y.BaseValue.(*adt.StructMarker)
    66  		if !ok {
    67  			return false
    68  		}
    69  
    70  	case adt.Value:
    71  		if !s.values(v, y.Value()) {
    72  			return false
    73  		}
    74  
    75  		// Embedded scalars could still have arcs.
    76  		if final {
    77  			return true
    78  		}
    79  
    80  	default:
    81  		panic(fmt.Sprintf("unexpected type %T", v))
    82  	}
    83  
    84  	xClosed := x.IsClosedStruct() && !s.IgnoreClosedness
    85  	// TODO: this should not close for taking defaults. Do a more principled
    86  	// makeover of this package before making it public, though.
    87  	yClosed := s.Final || s.Defaults ||
    88  		(y.IsClosedStruct() && !s.IgnoreClosedness)
    89  
    90  	if xClosed && !yClosed && !final {
    91  		return false
    92  	}
    93  
    94  	types := x.OptionalTypes()
    95  	if !final && !s.IgnoreOptional && types&(adt.HasPattern|adt.HasAdditional) != 0 {
    96  		// TODO: there are many cases where pattern constraints can be checked.
    97  		s.inexact = true
    98  		return false
    99  	}
   100  
   101  	// All arcs in x must exist in y and its values must subsume.
   102  	xFeatures := export.VertexFeatures(x)
   103  	for _, f := range xFeatures {
   104  		if s.Final && !f.IsRegular() {
   105  			continue
   106  		}
   107  
   108  		a := x.Lookup(f)
   109  		aOpt := false
   110  		if a == nil {
   111  			// x.f is optional
   112  			if s.IgnoreOptional {
   113  				continue
   114  			}
   115  
   116  			a = &adt.Vertex{Label: f}
   117  			x.MatchAndInsert(ctx, a)
   118  			a.Finalize(ctx)
   119  
   120  			// If field a is optional and has value top, neither the
   121  			// omission of the field nor the field defined with any value
   122  			// may cause unification to fail.
   123  			if a.Kind() == adt.TopKind {
   124  				continue
   125  			}
   126  
   127  			aOpt = true
   128  		}
   129  
   130  		b := y.Lookup(f)
   131  		if b == nil {
   132  			// y.f is optional
   133  			if !aOpt {
   134  				s.errf("required field is optional in subsumed value: %s", f)
   135  				return false
   136  			}
   137  
   138  			// If f is undefined for y and if y is closed, the field is
   139  			// implicitly defined as _|_ and thus subsumed. Technically, this is
   140  			// even true if a is not optional, but in that case it means that y
   141  			// is invalid, so return false regardless
   142  			if !y.Accept(ctx, f) || y.IsData() || s.Final {
   143  				continue
   144  			}
   145  
   146  			b = &adt.Vertex{Label: f}
   147  			y.MatchAndInsert(ctx, b)
   148  			b.Finalize(ctx)
   149  		}
   150  
   151  		if s.values(a, b) {
   152  			continue
   153  		}
   154  
   155  		s.missing = f
   156  		s.gt = a
   157  		s.lt = y
   158  
   159  		s.errf("field %s not present in %s", f, y)
   160  		return false
   161  	}
   162  
   163  	if xClosed && !yClosed && !s.Final {
   164  		s.errf("closed struct does not subsume open struct")
   165  		return false
   166  	}
   167  
   168  	yFeatures := export.VertexFeatures(y)
   169  outer:
   170  	for _, f := range yFeatures {
   171  		if s.Final && !f.IsRegular() {
   172  			continue
   173  		}
   174  
   175  		for _, g := range xFeatures {
   176  			if g == f {
   177  				// already validated
   178  				continue outer
   179  			}
   180  		}
   181  
   182  		b := y.Lookup(f)
   183  		if b == nil {
   184  			if s.IgnoreOptional || s.Final {
   185  				continue
   186  			}
   187  
   188  			b = &adt.Vertex{Label: f}
   189  			y.MatchAndInsert(ctx, b)
   190  		}
   191  
   192  		if !x.Accept(ctx, f) {
   193  			if s.Profile.IgnoreClosedness {
   194  				continue
   195  			}
   196  			s.errf("field not allowed in closed struct: %s", f)
   197  			return false
   198  		}
   199  
   200  		a := &adt.Vertex{Label: f}
   201  		x.MatchAndInsert(ctx, a)
   202  		if len(a.Conjuncts) == 0 {
   203  			// It is accepted and has no further constraints, so all good.
   204  			continue
   205  		}
   206  
   207  		a.Finalize(ctx)
   208  		b.Finalize(ctx)
   209  
   210  		if !s.vertices(a, b) {
   211  			return false
   212  		}
   213  	}
   214  
   215  	return true
   216  }
   217  
   218  func (s *subsumer) listVertices(x, y *adt.Vertex) bool {
   219  	ctx := s.ctx
   220  
   221  	if !y.IsData() && x.IsClosedList() && !y.IsClosedList() {
   222  		return false
   223  	}
   224  
   225  	xElems := x.Elems()
   226  	yElems := y.Elems()
   227  
   228  	switch {
   229  	case len(xElems) == len(yElems):
   230  	case len(xElems) > len(yElems):
   231  		return false
   232  	case x.IsClosedList():
   233  		return false
   234  	default:
   235  		a := &adt.Vertex{Label: 0}
   236  		x.MatchAndInsert(ctx, a)
   237  		a.Finalize(ctx)
   238  
   239  		// x must be open
   240  		for _, b := range yElems[len(xElems):] {
   241  			if !s.vertices(a, b) {
   242  				return false
   243  			}
   244  		}
   245  
   246  		if !y.IsClosedList() {
   247  			b := &adt.Vertex{Label: adt.InvalidLabel}
   248  			y.MatchAndInsert(ctx, b)
   249  			b.Finalize(ctx)
   250  		}
   251  	}
   252  
   253  	for i, a := range xElems {
   254  		if !s.vertices(a, yElems[i]) {
   255  			return false
   256  		}
   257  	}
   258  
   259  	return true
   260  }