cuelang.org/go@v0.10.1/internal/core/subsume/subsume.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 defines various subsumption relations. 16 package subsume 17 18 import ( 19 "cuelang.org/go/cue/errors" 20 "cuelang.org/go/internal" 21 "cuelang.org/go/internal/core/adt" 22 ) 23 24 // Profile configures the type of subsumption. One should typically use one 25 // of the preconfigured profiles. 26 type Profile struct { 27 // Final indicates subsumption should only consider fields that are relevant 28 // to data mode, and ignore definitions, hidden fields, pattern constraints 29 // and additional constraints. 30 Final bool 31 32 // Defaults indicate that default values should be used for the subsumed 33 // value. 34 Defaults bool 35 36 // LeftDefaults indicates that the default value of the subsuming value 37 // needs to be taken. This is necessary for simplifications like trim 38 // and simplifying disjunctions. 39 LeftDefault bool 40 41 // Ignore optional fields. 42 IgnoreOptional bool 43 44 // IgnoreClosedness ignores closedness of structs and is used for comparing 45 // APIs. 46 IgnoreClosedness bool 47 } 48 49 var Simplify = Profile{ 50 LeftDefault: true, 51 } 52 53 var CUE = Profile{} 54 55 // Final checks subsumption interpreting the subsumed value as data. 56 var Final = Profile{ 57 Final: true, 58 Defaults: true, 59 } 60 61 // FinalOpen exists as an artifact of the old API. One should probably not use 62 // this. 63 var FinalOpen = Profile{ 64 Final: true, 65 Defaults: true, 66 IgnoreClosedness: true, 67 } 68 69 // API is subsumption used for APIs. 70 var API = Profile{ 71 IgnoreClosedness: true, 72 } 73 74 // Value subsumes two values based on their logical (evaluated) values. 75 func Value(ctx *adt.OpContext, a, b adt.Value) errors.Error { 76 return CUE.Value(ctx, a, b) 77 } 78 79 func (p *Profile) Value(ctx *adt.OpContext, a, b adt.Value) errors.Error { 80 s := subsumer{ctx: ctx, Profile: *p} 81 if !s.values(a, b) { 82 return s.getError() 83 } 84 return nil // ignore errors here even if there are some. 85 } 86 87 // Check reports whether b is an instance of a. 88 func (p *Profile) Check(ctx *adt.OpContext, a, b adt.Value) bool { 89 s := subsumer{ctx: ctx, Profile: *p} 90 return s.values(a, b) 91 } 92 93 func isBottom(x adt.Node) bool { 94 b, _ := x.(*adt.Bottom) 95 return b != nil 96 } 97 98 type subsumer struct { 99 ctx *adt.OpContext 100 errs errors.Error 101 102 Profile 103 104 inexact bool // If true, the result could be a false negative. 105 missing adt.Feature 106 gt adt.Value 107 lt adt.Value 108 } 109 110 func (s *subsumer) errf(msg string, args ...interface{}) { 111 b := s.ctx.NewErrf(msg, args...) 112 s.errs = errors.Append(s.errs, b.Err) 113 } 114 115 func unifyValue(c *adt.OpContext, a, b adt.Value) adt.Value { 116 v := &adt.Vertex{} 117 v.AddConjunct(adt.MakeRootConjunct(c.Env(0), a)) 118 v.AddConjunct(adt.MakeRootConjunct(c.Env(0), b)) 119 x, _ := c.Evaluate(c.Env(0), v) 120 return x 121 } 122 123 func (s *subsumer) getError() (err errors.Error) { 124 c := s.ctx 125 // src := binSrc(token.NoPos, opUnify, gt, lt) 126 if s.gt != nil && s.lt != nil { 127 // src := binSrc(token.NoPos, opUnify, s.gt, s.lt) 128 if s.missing != 0 { 129 s.errf("missing field %q", s.missing.SelectorString(c)) 130 } else if b, ok := unifyValue(c, s.gt, s.lt).(*adt.Bottom); !ok { 131 s.errf("value not an instance") 132 } else { 133 s.errs = errors.Append(s.errs, b.Err) 134 } 135 } 136 if s.errs == nil { 137 s.errf("value not an instance") 138 } 139 err = s.errs 140 if s.inexact { 141 err = internal.DecorateError(internal.ErrInexact, err) 142 } 143 return err 144 }