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  }