github.com/solo-io/cue@v0.4.7/internal/core/subsume/structural.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 // TODO: structural subsumption has not yet been implemented. 18 19 import "github.com/solo-io/cue/internal/core/adt" 20 21 func (s *subsumer) subsumes(gt, lt adt.Conjunct) bool { 22 if gt == lt { 23 return true 24 } 25 26 // First try evaluating at the value level. 27 x, _ := gt.Expr().(adt.Value) 28 y, _ := lt.Expr().(adt.Value) 29 if x == nil { 30 // Fall back to structural. 31 return s.structural(gt, lt) 32 } 33 if y == nil { 34 return false 35 } 36 37 return s.values(x, y) 38 } 39 40 func (s *subsumer) conjunct(gt, lt adt.Conjunct) bool { 41 return false 42 } 43 44 func (s *subsumer) c(env *adt.Environment, x adt.Expr) adt.Conjunct { 45 return adt.MakeRootConjunct(env, x) 46 } 47 48 func isBottomConjunct(c adt.Conjunct) bool { 49 b, _ := c.Expr().(*adt.Bottom) 50 return b != nil 51 } 52 53 func (s *subsumer) node(env *adt.Environment, up int32) *adt.Vertex { 54 for ; up != 0; up-- { 55 env = env.Up 56 } 57 return env.Vertex 58 } 59 60 func (s *subsumer) structural(a, b adt.Conjunct) bool { 61 if y, ok := b.Expr().(*adt.LetReference); ok { 62 return s.conjunct(a, s.c(b.Env, y.X)) 63 } 64 if isBottomConjunct(b) { 65 return true 66 } 67 68 switch x := a.Expr().(type) { 69 case *adt.DisjunctionExpr: 70 71 case *adt.StructLit: 72 case *adt.ListLit: 73 74 case *adt.FieldReference: 75 if y, ok := b.Expr().(*adt.FieldReference); ok && x.Label == y.Label { 76 if s.node(a.Env, x.UpCount) == s.node(b.Env, y.UpCount) { 77 return true 78 } 79 } 80 81 case *adt.LabelReference: 82 if y, ok := b.Expr().(*adt.LabelReference); ok { 83 if s.node(a.Env, x.UpCount) == s.node(b.Env, y.UpCount) { 84 return true 85 } 86 } 87 88 case *adt.DynamicReference: 89 if y, ok := b.Expr().(*adt.FieldReference); ok { 90 if s.node(a.Env, x.UpCount) == s.node(b.Env, y.UpCount) { 91 return true 92 } 93 } 94 95 case *adt.ImportReference: 96 if y, ok := b.Expr().(*adt.ImportReference); ok && 97 x.ImportPath == y.ImportPath { 98 return true 99 } 100 101 case *adt.LetReference: 102 return s.conjunct(s.c(a.Env, x.X), b) 103 104 case *adt.SelectorExpr: 105 if y, ok := a.Expr().(*adt.SelectorExpr); ok && 106 x.Sel == y.Sel && 107 s.conjunct(s.c(a.Env, x.X), s.c(b.Env, y.X)) { 108 return true 109 } 110 111 case *adt.IndexExpr: 112 if y, ok := b.Expr().(*adt.IndexExpr); ok && 113 s.conjunct(s.c(a.Env, x.X), s.c(b.Env, y.X)) && 114 s.conjunct(s.c(a.Env, x.Index), s.c(b.Env, y.Index)) { 115 return true 116 } 117 118 case *adt.SliceExpr: 119 if r, ok := b.Expr().(*adt.SliceExpr); ok && 120 s.conjunct(s.c(a.Env, x.X), s.c(b.Env, r.X)) && 121 s.conjunct(s.c(a.Env, x.Lo), s.c(b.Env, r.Lo)) && 122 s.conjunct(s.c(a.Env, x.Hi), s.c(b.Env, r.Hi)) { 123 return true 124 } 125 126 case *adt.Interpolation: 127 switch y := b.Expr().(type) { 128 case *adt.String: 129 // Be conservative if not ground. 130 s.inexact = true 131 132 case *adt.Interpolation: 133 // structural equivalence 134 if len(x.Parts) != len(y.Parts) { 135 return false 136 } 137 for i, p := range x.Parts { 138 if !s.conjunct(s.c(a.Env, p), s.c(b.Env, y.Parts[i])) { 139 return false 140 } 141 } 142 return true 143 } 144 145 case *adt.BoundExpr: 146 if y, ok := b.Expr().(*adt.BoundExpr); ok && x.Op == y.Op { 147 return s.conjunct(s.c(a.Env, x.Expr), s.c(b.Env, y.Expr)) 148 } 149 150 case *adt.UnaryExpr: 151 if y, ok := b.Expr().(*adt.UnaryExpr); ok && x.Op == y.Op { 152 return s.conjunct(s.c(a.Env, x.X), s.c(b.Env, y.X)) 153 } 154 155 case *adt.BinaryExpr: 156 if y, ok := b.Expr().(*adt.BinaryExpr); ok && x.Op == y.Op { 157 return s.conjunct(s.c(a.Env, x.X), s.c(b.Env, y.X)) && 158 s.conjunct(s.c(a.Env, x.Y), s.c(b.Env, y.Y)) 159 } 160 161 case *adt.CallExpr: 162 if y, ok := b.Expr().(*adt.CallExpr); ok { 163 if len(x.Args) != len(y.Args) { 164 return false 165 } 166 for i, arg := range x.Args { 167 if !s.conjunct(s.c(a.Env, arg), s.c(b.Env, y.Args[i])) { 168 return false 169 } 170 } 171 return s.conjunct(s.c(a.Env, x.Fun), s.c(b.Env, y.Fun)) 172 } 173 } 174 return false 175 } 176 177 func (s *subsumer) structLit( 178 ea *adt.Environment, sa *adt.StructLit, 179 eb *adt.Environment, sb *adt.StructLit) bool { 180 181 // Create index of instance fields. 182 ca := newCollatedDecls() 183 ca.collate(ea, sa) 184 185 if ca.yielders != nil || ca.dynamic != nil { 186 // TODO: we could do structural comparison of comprehensions 187 // in many cases. For instance, an if clause would subsume 188 // structurally if it subsumes any of the if clauses in sb. 189 s.inexact = true 190 return false 191 } 192 193 cb := newCollatedDecls() 194 cb.collate(eb, sb) 195 196 if ca.hasOptional && !s.IgnoreOptional { 197 // TODO: same argument here as for comprehensions. This could 198 // be made to work. 199 if ca.pattern != nil || ca.dynamic != nil { 200 s.inexact = true 201 return false 202 } 203 204 // for f, b := range cb.fields { 205 // if !b.required || f.IsDef() { 206 // continue 207 // } 208 // name := ctx.LabelStr(b.Label) 209 // arg := &stringLit{x.baseValue, name, nil} 210 // u, _ := x.optionals.constraint(ctx, arg) 211 // if u != nil && !s.subsumes(u, b.v) { 212 // return false 213 // } 214 // } 215 216 } 217 218 return false 219 220 } 221 222 // collatedDecls is used to compute the structural subsumption of two 223 // struct literals. 224 type collatedDecls struct { 225 fields map[adt.Feature]field 226 yielders []adt.Yielder 227 pattern []*adt.BulkOptionalField 228 dynamic []*adt.DynamicField 229 values []adt.Expr 230 additional []*adt.Ellipsis 231 isOpen bool 232 hasOptional bool 233 } 234 235 func newCollatedDecls() *collatedDecls { 236 return &collatedDecls{fields: map[adt.Feature]field{}} 237 } 238 239 type field struct { 240 required bool 241 conjuncts []adt.Conjunct 242 } 243 244 func (c *collatedDecls) collate(env *adt.Environment, s *adt.StructLit) { 245 for _, d := range s.Decls { 246 switch x := d.(type) { 247 case *adt.Field: 248 e := c.fields[x.Label] 249 e.required = true 250 e.conjuncts = append(e.conjuncts, adt.MakeRootConjunct(env, x)) 251 c.fields[x.Label] = e 252 253 case *adt.OptionalField: 254 e := c.fields[x.Label] 255 e.conjuncts = append(e.conjuncts, adt.MakeRootConjunct(env, x)) 256 c.fields[x.Label] = e 257 c.hasOptional = true 258 259 case *adt.BulkOptionalField: 260 c.pattern = append(c.pattern, x) 261 c.hasOptional = true 262 263 case *adt.DynamicField: 264 c.dynamic = append(c.dynamic, x) 265 c.hasOptional = true 266 267 case *adt.Ellipsis: 268 c.isOpen = true 269 c.additional = append(c.additional, x) 270 271 case *adt.ForClause: 272 c.yielders = append(c.yielders, x) 273 274 case *adt.IfClause: 275 c.yielders = append(c.yielders, x) 276 277 case *adt.LetClause: 278 c.yielders = append(c.yielders, x) 279 280 case *adt.ValueClause: 281 } 282 } 283 }