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 }