this repo has no description
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
15package adt
16
17import (
18 "bytes"
19 "strings"
20
21 "github.com/cockroachdb/apd/v3"
22
23 "cuelang.org/go/internal"
24)
25
26// SimplifyBounds collapses bounds if possible. The bound values must be
27// concrete. It returns nil if the bound values cannot be collapsed.
28//
29// k represents additional type constraints, such as `int`.
30func SimplifyBounds(ctx *OpContext, k Kind, x, y *BoundValue) Value {
31 xv := x.Value
32 yv := y.Value
33
34 cmp, xCat := opInfo(x.Op)
35 _, yCat := opInfo(y.Op)
36
37 // k := x.Kind() & y.Kind()
38
39 switch {
40 case xCat == yCat:
41 switch x.Op {
42 // NOTE: EqualOp should not happen, but include it defensively.
43 // Maybe an API would use it, for instance.
44 case EqualOp, NotEqualOp, MatchOp, NotMatchOp:
45 if BinOpBool(ctx, nil, EqualOp, xv, yv) {
46 return x
47 }
48 return nil // keep both bounds
49 }
50
51 // xCat == yCat && x.Op != NotEqualOp
52 // > a & >= b
53 // > a if a >= b
54 // >= b if a < b
55 // > a & > b
56 // > a if a >= b
57 // > b if a < b
58 // >= a & > b
59 // >= a if a > b
60 // > b if a <= b
61 // >= a & >= b
62 // >= a if a > b
63 // >= b if a <= b
64 // inverse is true as well.
65
66 // Tighten bound.
67 if BinOpBool(ctx, nil, cmp, xv, yv) {
68 return x
69 }
70 return y
71
72 case xCat == -yCat && k == StringKind:
73 if xCat == -1 {
74 x, y = y, x
75 xv, yv = yv, xv
76 }
77
78 a, aOK := xv.(*String)
79 b, bOK := yv.(*String)
80
81 if !aOK || !bOK {
82 break
83 }
84
85 switch diff := strings.Compare(a.Str, b.Str); diff {
86 case -1:
87 case 0:
88 if x.Op == GreaterEqualOp && y.Op == LessEqualOp {
89 return nil
90 }
91 fallthrough
92
93 case 1:
94 return ctx.NewErrf("incompatible string bounds %v and %v", y, x)
95 }
96
97 case xCat == -yCat && k == BytesKind:
98 if xCat == -1 {
99 x, y = y, x
100 xv, yv = yv, xv
101 }
102
103 a, aOK := xv.(*Bytes)
104 b, bOK := yv.(*Bytes)
105
106 if !aOK || !bOK {
107 break
108 }
109
110 switch diff := bytes.Compare(a.B, b.B); diff {
111 case -1:
112 case 0:
113 if x.Op == GreaterEqualOp && y.Op == LessEqualOp {
114 return nil
115 }
116 fallthrough
117
118 case 1:
119 return ctx.NewErrf("incompatible bytes bounds %v and %v", y, x)
120 }
121
122 case xCat == -yCat:
123 if xCat == -1 {
124 x, y = y, x
125 xv, yv = yv, xv
126 }
127 a, aOK := xv.(*Num)
128 b, bOK := yv.(*Num)
129 if !aOK || !bOK || a.X.Form != apd.Finite || b.X.Form != apd.Finite {
130 // Nothing to do if either bound is not a finite number.
131 break
132 }
133
134 var d apd.Decimal
135 lo, hi := a.X, b.X
136 if k&FloatKind == 0 {
137 // Readjust bounds for integers if the bounds have decimal places,
138 // which are represented as a negative exponent to multiply with the coefficient.
139 // We reset lo and hi to empty apd.Decimal values to not modify the originals,
140 // given that apd.Decimal contains pointers which are carried with shallow copies.
141 if a.X.Exponent < 0 {
142 lo = apd.Decimal{}
143 if x.Op == GreaterEqualOp {
144 // >=3.4 ==> >=4
145 internal.BaseContext.Ceil(&lo, &a.X)
146 } else {
147 // >3.4 ==> >3
148 internal.BaseContext.Floor(&lo, &a.X)
149 }
150 }
151 if b.X.Exponent < 0 {
152 hi = apd.Decimal{}
153 if y.Op == LessEqualOp {
154 // <=2.3 ==> <= 2
155 internal.BaseContext.Floor(&hi, &b.X)
156 } else {
157 // <2.3 ==> < 3
158 internal.BaseContext.Ceil(&hi, &b.X)
159 }
160 }
161 }
162
163 // Negative or zero minimum with a large positive maximum
164 // common with e.g. int32 being ">=-2147483648 & <=2147483647"
165 // or uint16 being ">=0 & <=65535".
166 //
167 // We detect a large positive maximum by checking that the exponent isn't negative
168 // and that the coefficient has at least two digits, so its value must be at least 10.
169 // When the maximum is at least 10 and the minimum is at most 0,
170 // the subtraction will be positive and larger than 2, so we can avoid all the work below.
171 // This is important given that a subtraction allocates a new number for the result.
172 hiSign, loSign := hi.Sign(), lo.Sign()
173 if hiSign > 0 && loSign <= 0 && hi.Exponent >= 0 && hi.NumDigits() > 1 {
174 break
175 }
176
177 cond, err := internal.BaseContext.Sub(&d, &hi, &lo)
178 if cond.Inexact() || err != nil {
179 break
180 }
181
182 // attempt simplification
183 // numbers
184 // >=a & <=b
185 // a if a == b
186 // _|_ if b < a
187 // >=a & <b
188 // _|_ if b <= a
189 // >a & <=b
190 // _|_ if b <= a
191 // >a & <b
192 // _|_ if b <= a
193
194 // integers
195 // >=a & <=b
196 // a if b-a == 0
197 // _|_ if b < a
198 // >=a & <b
199 // a if b-a == 1
200 // _|_ if b <= a
201 // >a & <=b
202 // b if b-a == 1
203 // _|_ if b <= a
204 // >a & <b
205 // a+1 if b-a == 2
206 // _|_ if b <= a
207
208 if d.Negative {
209 return errIncompatibleBounds(ctx, k, x, y)
210 }
211 switch diff, err := d.Int64(); {
212 case diff == 1:
213 if k&FloatKind == 0 {
214 if x.Op == GreaterEqualOp && y.Op == LessThanOp {
215 return nil
216 }
217 if x.Op == GreaterThanOp && y.Op == LessEqualOp {
218 return nil
219 }
220 if x.Op == GreaterThanOp && y.Op == LessThanOp {
221 return ctx.NewErrf("incompatible integer bounds %v and %v", x, y)
222 }
223 }
224
225 case diff == 2:
226 if k&FloatKind == 0 && x.Op == GreaterThanOp && y.Op == LessThanOp {
227 return nil
228 }
229
230 case diff == 0 && err == nil:
231 if x.Op == GreaterEqualOp && y.Op == LessEqualOp {
232 return nil
233 }
234 return errIncompatibleBounds(ctx, k, x, y)
235 }
236
237 case x.Op == NotEqualOp:
238 if !BinOpBool(ctx, nil, y.Op, xv, yv) {
239 return y
240 }
241
242 case y.Op == NotEqualOp:
243 if !BinOpBool(ctx, nil, x.Op, yv, xv) {
244 return x
245 }
246 }
247 return nil
248}
249
250func errIncompatibleBounds(ctx *OpContext, k Kind, x, y *BoundValue) *Bottom {
251 if k == IntKind {
252 return ctx.NewErrf("incompatible integer bounds %v and %v", y, x)
253 } else {
254 return ctx.NewErrf("incompatible number bounds %v and %v", y, x)
255 }
256}
257
258func opInfo(op Op) (cmp Op, norm int) {
259 switch op {
260 case GreaterThanOp:
261 return GreaterEqualOp, 1
262 case GreaterEqualOp:
263 return GreaterThanOp, 1
264 case LessThanOp:
265 return LessEqualOp, -1
266 case LessEqualOp:
267 return LessThanOp, -1
268 case NotEqualOp:
269 return NotEqualOp, 0
270 case MatchOp:
271 return MatchOp, 2
272 case NotMatchOp:
273 return NotMatchOp, 3
274 }
275 panic("cue: unreachable")
276}
277
278// SimplifyValidator simplifies non-bound validators.
279//
280// Currently this only checks for pure equality. In the future this can be used
281// to simplify certain builtin validators analogously to how we simplify bounds
282// now.
283func SimplifyValidator(ctx *OpContext, v, w Conjunct) (c Conjunct, ok bool) {
284 switch x := v.x.(type) {
285 case *BuiltinValidator:
286 switch y := w.x.(type) {
287 case *BuiltinValidator:
288 if x == y {
289 return v, true
290 }
291 if x.Builtin != y.Builtin || len(x.Args) != len(y.Args) {
292 return c, false
293 }
294 for i, a := range x.Args {
295 b := y.Args[i]
296 if v, ok := a.(*Vertex); ok {
297 v.Finalize(ctx)
298 }
299 if v, ok := b.(*Vertex); ok {
300 v.Finalize(ctx)
301 }
302 if !Equal(ctx, a, b, CheckStructural) {
303 return c, false
304 }
305 }
306 return v, true
307 }
308 }
309 return c, false
310}