github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/sql/sem/tree/constant.go (about) 1 // Copyright 2016 The Cockroach Authors. 2 // 3 // Use of this software is governed by the Business Source License 4 // included in the file licenses/BSL.txt. 5 // 6 // As of the Change Date specified in that file, in accordance with 7 // the Business Source License, use of this software will be governed 8 // by the Apache License, Version 2.0, included in the file 9 // licenses/APL.txt. 10 11 package tree 12 13 import ( 14 "go/constant" 15 "go/token" 16 "math" 17 "strings" 18 19 "github.com/cockroachdb/cockroach/pkg/sql/lex" 20 "github.com/cockroachdb/cockroach/pkg/sql/pgwire/pgcode" 21 "github.com/cockroachdb/cockroach/pkg/sql/pgwire/pgerror" 22 "github.com/cockroachdb/cockroach/pkg/sql/types" 23 "github.com/cockroachdb/errors" 24 "github.com/lib/pq/oid" 25 ) 26 27 // Constant is an constant literal expression which may be resolved to more than one type. 28 type Constant interface { 29 Expr 30 // AvailableTypes returns the ordered set of types that the Constant is able to 31 // be resolved into. The order of the type slice provides a notion of precedence, 32 // with the first element in the ordering being the Constant's "natural type". 33 AvailableTypes() []*types.T 34 // DesirableTypes returns the ordered set of types that the constant would 35 // prefer to be resolved into. As in AvailableTypes, the order of the returned 36 // type slice provides a notion of precedence, with the first element in the 37 // ordering being the Constant's "natural type." The function is meant to be 38 // differentiated from AvailableTypes in that it will exclude certain types 39 // that are possible, but not desirable. 40 // 41 // An example of this is a floating point numeric constant without a value 42 // past the decimal point. It is possible to resolve this constant as a 43 // decimal, but it is not desirable. 44 DesirableTypes() []*types.T 45 // ResolveAsType resolves the Constant as the Datum type specified, or returns an 46 // error if the Constant could not be resolved as that type. The method should only 47 // be passed a type returned from AvailableTypes and should never be called more than 48 // once for a given Constant. 49 ResolveAsType(*SemaContext, *types.T) (Datum, error) 50 } 51 52 var _ Constant = &NumVal{} 53 var _ Constant = &StrVal{} 54 55 func isConstant(expr Expr) bool { 56 _, ok := expr.(Constant) 57 return ok 58 } 59 60 func typeCheckConstant( 61 c Constant, semaCtx *SemaContext, desired *types.T, 62 ) (ret TypedExpr, err error) { 63 avail := c.AvailableTypes() 64 if desired.Family() != types.AnyFamily { 65 for _, typ := range avail { 66 if desired.Equivalent(typ) { 67 return c.ResolveAsType(semaCtx, desired) 68 } 69 } 70 } 71 72 // If a numeric constant will be promoted to a DECIMAL because it was out 73 // of range of an INT, but an INT is desired, throw an error here so that 74 // the error message specifically mentions the overflow. 75 if desired.Family() == types.IntFamily { 76 if n, ok := c.(*NumVal); ok { 77 _, err := n.AsInt64() 78 switch { 79 case errors.Is(err, errConstOutOfRange64): 80 return nil, err 81 case errors.Is(err, errConstNotInt): 82 default: 83 return nil, errors.NewAssertionErrorWithWrappedErrf(err, "unexpected error") 84 } 85 } 86 } 87 88 natural := avail[0] 89 return c.ResolveAsType(semaCtx, natural) 90 } 91 92 func naturalConstantType(c Constant) *types.T { 93 return c.AvailableTypes()[0] 94 } 95 96 // canConstantBecome returns whether the provided Constant can become resolved 97 // as the provided type. 98 func canConstantBecome(c Constant, typ *types.T) bool { 99 avail := c.AvailableTypes() 100 for _, availTyp := range avail { 101 if availTyp.Equivalent(typ) { 102 return true 103 } 104 } 105 return false 106 } 107 108 // NumVal represents a constant numeric value. 109 type NumVal struct { 110 // value is the constant number, without any sign information. 111 value constant.Value 112 // negative is the sign bit to add to any interpretation of the 113 // value or origString fields. 114 negative bool 115 // origString is the "original" string representation (before 116 // folding). This should remain sign-less. 117 origString string 118 119 // The following fields are used to avoid allocating Datums on type resolution. 120 resInt DInt 121 resFloat DFloat 122 resDecimal DDecimal 123 } 124 125 var _ Constant = &NumVal{} 126 127 // NewNumVal constructs a new NumVal instance. This is used during parsing and 128 // in tests. 129 func NewNumVal(value constant.Value, origString string, negative bool) *NumVal { 130 return &NumVal{value: value, origString: origString, negative: negative} 131 } 132 133 // Kind implements the constant.Value interface. 134 func (expr *NumVal) Kind() constant.Kind { 135 return expr.value.Kind() 136 } 137 138 // ExactString implements the constant.Value interface. 139 func (expr *NumVal) ExactString() string { 140 return expr.value.ExactString() 141 } 142 143 // OrigString returns the origString field. 144 func (expr *NumVal) OrigString() string { 145 return expr.origString 146 } 147 148 // SetNegative sets the negative field to true. The parser calls this when it 149 // identifies a negative constant. 150 func (expr *NumVal) SetNegative() { 151 expr.negative = true 152 } 153 154 // Negate sets the negative field to the opposite of its current value. The 155 // parser calls this to simplify unary negation expressions. 156 func (expr *NumVal) Negate() { 157 expr.negative = !expr.negative 158 } 159 160 // Format implements the NodeFormatter interface. 161 func (expr *NumVal) Format(ctx *FmtCtx) { 162 s := expr.origString 163 if s == "" { 164 s = expr.value.String() 165 } 166 if expr.negative { 167 ctx.WriteByte('-') 168 } 169 ctx.WriteString(s) 170 } 171 172 // canBeInt64 checks if it's possible for the value to become an int64: 173 // 1 = yes 174 // 1.0 = yes 175 // 1.1 = no 176 // 123...overflow...456 = no 177 func (expr *NumVal) canBeInt64() bool { 178 _, err := expr.AsInt64() 179 return err == nil 180 } 181 182 // ShouldBeInt64 checks if the value naturally is an int64: 183 // 1 = yes 184 // 1.0 = no 185 // 1.1 = no 186 // 123...overflow...456 = no 187 func (expr *NumVal) ShouldBeInt64() bool { 188 return expr.Kind() == constant.Int && expr.canBeInt64() 189 } 190 191 // These errors are statically allocated, because they are returned in the 192 // common path of AsInt64. 193 var errConstNotInt = pgerror.New(pgcode.NumericValueOutOfRange, "cannot represent numeric constant as an int") 194 var errConstOutOfRange64 = pgerror.New(pgcode.NumericValueOutOfRange, "numeric constant out of int64 range") 195 var errConstOutOfRange32 = pgerror.New(pgcode.NumericValueOutOfRange, "numeric constant out of int32 range") 196 197 // AsInt64 returns the value as a 64-bit integer if possible, or returns an 198 // error if not possible. The method will set expr.resInt to the value of 199 // this int64 if it is successful, avoiding the need to call the method again. 200 func (expr *NumVal) AsInt64() (int64, error) { 201 intVal, ok := expr.AsConstantInt() 202 if !ok { 203 return 0, errConstNotInt 204 } 205 i, exact := constant.Int64Val(intVal) 206 if !exact { 207 return 0, errConstOutOfRange64 208 } 209 expr.resInt = DInt(i) 210 return i, nil 211 } 212 213 // AsInt32 returns the value as 32-bit integer if possible, or returns 214 // an error if not possible. The method will set expr.resInt to the 215 // value of this int32 if it is successful, avoiding the need to call 216 // the method again. 217 func (expr *NumVal) AsInt32() (int32, error) { 218 intVal, ok := expr.AsConstantInt() 219 if !ok { 220 return 0, errConstNotInt 221 } 222 i, exact := constant.Int64Val(intVal) 223 if !exact { 224 return 0, errConstOutOfRange32 225 } 226 if i > math.MaxInt32 || i < math.MinInt32 { 227 return 0, errConstOutOfRange32 228 } 229 expr.resInt = DInt(i) 230 return int32(i), nil 231 } 232 233 // AsConstantValue returns the value as a constant numerical value, with the proper sign 234 // as given by expr.negative. 235 func (expr *NumVal) AsConstantValue() constant.Value { 236 v := expr.value 237 if expr.negative { 238 v = constant.UnaryOp(token.SUB, v, 0) 239 } 240 return v 241 } 242 243 // AsConstantInt returns the value as an constant.Int if possible, along 244 // with a flag indicating whether the conversion was possible. 245 // The result contains the proper sign as per expr.negative. 246 func (expr *NumVal) AsConstantInt() (constant.Value, bool) { 247 v := expr.AsConstantValue() 248 intVal := constant.ToInt(v) 249 if intVal.Kind() == constant.Int { 250 return intVal, true 251 } 252 return nil, false 253 } 254 255 var ( 256 intLikeTypes = []*types.T{types.Int, types.Oid} 257 decimalLikeTypes = []*types.T{types.Decimal, types.Float} 258 259 // NumValAvailInteger is the set of available integer types. 260 NumValAvailInteger = append(intLikeTypes, decimalLikeTypes...) 261 // NumValAvailDecimalNoFraction is the set of available integral numeric types. 262 NumValAvailDecimalNoFraction = append(decimalLikeTypes, intLikeTypes...) 263 // NumValAvailDecimalWithFraction is the set of available fractional numeric types. 264 NumValAvailDecimalWithFraction = decimalLikeTypes 265 ) 266 267 // AvailableTypes implements the Constant interface. 268 func (expr *NumVal) AvailableTypes() []*types.T { 269 switch { 270 case expr.canBeInt64(): 271 if expr.Kind() == constant.Int { 272 return NumValAvailInteger 273 } 274 return NumValAvailDecimalNoFraction 275 default: 276 return NumValAvailDecimalWithFraction 277 } 278 } 279 280 // DesirableTypes implements the Constant interface. 281 func (expr *NumVal) DesirableTypes() []*types.T { 282 if expr.ShouldBeInt64() { 283 return NumValAvailInteger 284 } 285 return NumValAvailDecimalWithFraction 286 } 287 288 // ResolveAsType implements the Constant interface. 289 func (expr *NumVal) ResolveAsType(ctx *SemaContext, typ *types.T) (Datum, error) { 290 switch typ.Family() { 291 case types.IntFamily: 292 // We may have already set expr.resInt in AsInt64. 293 if expr.resInt == 0 { 294 if _, err := expr.AsInt64(); err != nil { 295 return nil, err 296 } 297 } 298 return &expr.resInt, nil 299 case types.FloatFamily: 300 f, _ := constant.Float64Val(expr.value) 301 if expr.negative { 302 f = -f 303 } 304 expr.resFloat = DFloat(f) 305 return &expr.resFloat, nil 306 case types.DecimalFamily: 307 dd := &expr.resDecimal 308 s := expr.origString 309 if s == "" { 310 // TODO(nvanbenschoten): We should propagate width through constant folding so that we 311 // can control precision on folded values as well. 312 s = expr.ExactString() 313 } 314 if idx := strings.IndexRune(s, '/'); idx != -1 { 315 // Handle constant.ratVal, which will return a rational string 316 // like 6/7. If only we could call big.Rat.FloatString() on it... 317 num, den := s[:idx], s[idx+1:] 318 if err := dd.SetString(num); err != nil { 319 return nil, pgerror.Wrapf(err, pgcode.Syntax, 320 "could not evaluate numerator of %v as Datum type DDecimal from string %q", 321 expr, num) 322 } 323 // TODO(nvanbenschoten): Should we try to avoid this allocation? 324 denDec, err := ParseDDecimal(den) 325 if err != nil { 326 return nil, pgerror.Wrapf(err, pgcode.Syntax, 327 "could not evaluate denominator %v as Datum type DDecimal from string %q", 328 expr, den) 329 } 330 if cond, err := DecimalCtx.Quo(&dd.Decimal, &dd.Decimal, &denDec.Decimal); err != nil { 331 if cond.DivisionByZero() { 332 return nil, ErrDivByZero 333 } 334 return nil, err 335 } 336 } else { 337 if err := dd.SetString(s); err != nil { 338 return nil, pgerror.Wrapf(err, pgcode.Syntax, 339 "could not evaluate %v as Datum type DDecimal from string %q", expr, s) 340 } 341 } 342 if !dd.IsZero() { 343 // Negative zero does not exist for DECIMAL, in that case we ignore the 344 // sign. Otherwise XOR the signs of the expr and the decimal value 345 // contained in the expr, since the negative may have been folded into the 346 // inner decimal. 347 dd.Negative = dd.Negative != expr.negative 348 } 349 return dd, nil 350 case types.OidFamily: 351 d, err := expr.ResolveAsType(ctx, types.Int) 352 if err != nil { 353 return nil, err 354 } 355 oid := NewDOid(*d.(*DInt)) 356 oid.semanticType = typ 357 return oid, nil 358 default: 359 return nil, errors.AssertionFailedf("could not resolve %T %v into a %T", expr, expr, typ) 360 } 361 } 362 363 func intersectTypeSlices(xs, ys []*types.T) (out []*types.T) { 364 for _, x := range xs { 365 for _, y := range ys { 366 if x == y { 367 out = append(out, x) 368 } 369 } 370 } 371 return out 372 } 373 374 // commonConstantType returns the most constrained type which is mutually 375 // resolvable between a set of provided constants. 376 // 377 // The function takes a slice of Exprs and indexes, but expects all the indexed 378 // Exprs to wrap a Constant. The reason it does no take a slice of Constants 379 // instead is to avoid forcing callers to allocate separate slices of Constant. 380 func commonConstantType(vals []Expr, idxs []int) (*types.T, bool) { 381 var candidates []*types.T 382 383 for _, i := range idxs { 384 availableTypes := vals[i].(Constant).DesirableTypes() 385 if candidates == nil { 386 candidates = availableTypes 387 } else { 388 candidates = intersectTypeSlices(candidates, availableTypes) 389 } 390 } 391 392 if len(candidates) > 0 { 393 return candidates[0], true 394 } 395 return nil, false 396 } 397 398 // StrVal represents a constant string value. 399 type StrVal struct { 400 // We could embed a constant.Value here (like NumVal) and use the stringVal implementation, 401 // but that would have extra overhead without much of a benefit. However, it would make 402 // constant folding (below) a little more straightforward. 403 s string 404 405 // scannedAsBytes is true iff the input syntax was using b'...' or 406 // x'....'. If false, the string is guaranteed to be a valid UTF-8 407 // sequence. 408 scannedAsBytes bool 409 410 // The following fields are used to avoid allocating Datums on type resolution. 411 resString DString 412 resBytes DBytes 413 } 414 415 // NewStrVal constructs a StrVal instance. This is used during 416 // parsing when interpreting a token of type SCONST, i.e. *not* using 417 // the b'...' or x'...' syntax. 418 func NewStrVal(s string) *StrVal { 419 return &StrVal{s: s} 420 } 421 422 // NewBytesStrVal constructs a StrVal instance suitable as byte array. 423 // This is used during parsing when interpreting a token of type BCONST, 424 // i.e. using the b'...' or x'...' syntax. 425 func NewBytesStrVal(s string) *StrVal { 426 return &StrVal{s: s, scannedAsBytes: true} 427 } 428 429 // RawString retrieves the underlying string of the StrVal. 430 func (expr *StrVal) RawString() string { 431 return expr.s 432 } 433 434 // Format implements the NodeFormatter interface. 435 func (expr *StrVal) Format(ctx *FmtCtx) { 436 buf, f := &ctx.Buffer, ctx.flags 437 if expr.scannedAsBytes { 438 lex.EncodeSQLBytes(buf, expr.s) 439 } else { 440 lex.EncodeSQLStringWithFlags(buf, expr.s, f.EncodeFlags()) 441 } 442 } 443 444 var ( 445 // StrValAvailAllParsable is the set of parsable string types. 446 StrValAvailAllParsable = []*types.T{ 447 // Note: String is deliberately first, to make sure that "string" is the 448 // default type that raw strings get parsed into, without any casts or type 449 // assertions. 450 types.String, 451 types.Bytes, 452 types.Bool, 453 types.Int, 454 types.Float, 455 types.Decimal, 456 types.Date, 457 types.StringArray, 458 types.IntArray, 459 types.Geography, 460 types.Geometry, 461 types.DecimalArray, 462 types.Time, 463 types.TimeTZ, 464 types.Timestamp, 465 types.TimestampTZ, 466 types.Interval, 467 types.Uuid, 468 types.INet, 469 types.Jsonb, 470 types.VarBit, 471 types.AnyEnum, 472 } 473 // StrValAvailBytes is the set of types convertible to byte array. 474 StrValAvailBytes = []*types.T{types.Bytes, types.Uuid, types.String, types.AnyEnum} 475 ) 476 477 // AvailableTypes implements the Constant interface. 478 // 479 // To fully take advantage of literal type inference, this method would 480 // determine exactly which types are available for a given string. This would 481 // entail attempting to parse the literal string as a date, a timestamp, an 482 // interval, etc. and having more fine-grained results than StrValAvailAllParsable. 483 // However, this is not feasible in practice because of the associated parsing 484 // overhead. 485 // 486 // Conservative approaches like checking the string's length have been investigated 487 // to reduce ambiguity and improve type inference in some cases. When doing so, the 488 // length of the string literal was compared against all valid date and timestamp 489 // formats to quickly gain limited insight into whether parsing the string as the 490 // respective datum types could succeed. The hope was to eliminate impossibilities 491 // and constrain the returned type sets as much as possible. Unfortunately, two issues 492 // were found with this approach: 493 // - date and timestamp formats do not always imply a fixed-length valid input. For 494 // instance, timestamp formats that take fractional seconds can successfully parse 495 // inputs of varied length. 496 // - the set of date and timestamp formats are not disjoint, which means that ambiguity 497 // can not be eliminated when inferring the type of string literals that use these 498 // shared formats. 499 // While these limitations still permitted improved type inference in many cases, they 500 // resulted in behavior that was ultimately incomplete, resulted in unpredictable levels 501 // of inference, and occasionally failed to eliminate ambiguity. Further heuristics could 502 // have been applied to improve the accuracy of the inference, like checking that all 503 // or some characters were digits, but it would not have circumvented the fundamental 504 // issues here. Fully parsing the literal into each type would be the only way to 505 // concretely avoid the issue of unpredictable inference behavior. 506 func (expr *StrVal) AvailableTypes() []*types.T { 507 if expr.scannedAsBytes { 508 return StrValAvailBytes 509 } 510 return StrValAvailAllParsable 511 } 512 513 // DesirableTypes implements the Constant interface. 514 func (expr *StrVal) DesirableTypes() []*types.T { 515 return expr.AvailableTypes() 516 } 517 518 // ResolveAsType implements the Constant interface. 519 func (expr *StrVal) ResolveAsType(ctx *SemaContext, typ *types.T) (Datum, error) { 520 if expr.scannedAsBytes { 521 // We're looking at typing a byte literal constant into some value type. 522 switch typ.Family() { 523 case types.BytesFamily: 524 expr.resBytes = DBytes(expr.s) 525 return &expr.resBytes, nil 526 case types.EnumFamily: 527 return MakeDEnumFromPhysicalRepresentation(typ, []byte(expr.s)) 528 case types.UuidFamily: 529 return ParseDUuidFromBytes([]byte(expr.s)) 530 case types.StringFamily: 531 expr.resString = DString(expr.s) 532 return &expr.resString, nil 533 } 534 return nil, errors.AssertionFailedf("attempt to type byte array literal to %T", typ) 535 } 536 537 // Typing a string literal constant into some value type. 538 switch typ.Family() { 539 case types.StringFamily: 540 if typ.Oid() == oid.T_name { 541 expr.resString = DString(expr.s) 542 return NewDNameFromDString(&expr.resString), nil 543 } 544 expr.resString = DString(expr.s) 545 return &expr.resString, nil 546 case types.BytesFamily: 547 return ParseDByte(expr.s) 548 } 549 550 datum, err := ParseAndRequireString(typ, expr.s, ctx) 551 return datum, err 552 } 553 554 type constantFolderVisitor struct{} 555 556 var _ Visitor = constantFolderVisitor{} 557 558 func (constantFolderVisitor) VisitPre(expr Expr) (recurse bool, newExpr Expr) { 559 return true, expr 560 } 561 562 var unaryOpToToken = map[UnaryOperator]token.Token{ 563 UnaryMinus: token.SUB, 564 } 565 var unaryOpToTokenIntOnly = map[UnaryOperator]token.Token{ 566 UnaryComplement: token.XOR, 567 } 568 var binaryOpToToken = map[BinaryOperator]token.Token{ 569 Plus: token.ADD, 570 Minus: token.SUB, 571 Mult: token.MUL, 572 Div: token.QUO, 573 } 574 var binaryOpToTokenIntOnly = map[BinaryOperator]token.Token{ 575 FloorDiv: token.QUO_ASSIGN, 576 Mod: token.REM, 577 Bitand: token.AND, 578 Bitor: token.OR, 579 Bitxor: token.XOR, 580 } 581 var comparisonOpToToken = map[ComparisonOperator]token.Token{ 582 EQ: token.EQL, 583 NE: token.NEQ, 584 LT: token.LSS, 585 LE: token.LEQ, 586 GT: token.GTR, 587 GE: token.GEQ, 588 } 589 590 func (constantFolderVisitor) VisitPost(expr Expr) (retExpr Expr) { 591 defer func() { 592 // go/constant operations can panic for a number of reasons (like division 593 // by zero), but it's difficult to preemptively detect when they will. It's 594 // safest to just recover here without folding the expression and let 595 // normalization or evaluation deal with error handling. 596 if r := recover(); r != nil { 597 retExpr = expr 598 } 599 }() 600 switch t := expr.(type) { 601 case *ParenExpr: 602 switch cv := t.Expr.(type) { 603 case *NumVal, *StrVal: 604 return cv 605 } 606 case *UnaryExpr: 607 switch cv := t.Expr.(type) { 608 case *NumVal: 609 if tok, ok := unaryOpToToken[t.Operator]; ok { 610 return &NumVal{value: constant.UnaryOp(tok, cv.AsConstantValue(), 0)} 611 } 612 if token, ok := unaryOpToTokenIntOnly[t.Operator]; ok { 613 if intVal, ok := cv.AsConstantInt(); ok { 614 return &NumVal{value: constant.UnaryOp(token, intVal, 0)} 615 } 616 } 617 } 618 case *BinaryExpr: 619 switch l := t.Left.(type) { 620 case *NumVal: 621 if r, ok := t.Right.(*NumVal); ok { 622 if token, ok := binaryOpToToken[t.Operator]; ok { 623 return &NumVal{value: constant.BinaryOp(l.AsConstantValue(), token, r.AsConstantValue())} 624 } 625 if token, ok := binaryOpToTokenIntOnly[t.Operator]; ok { 626 if lInt, ok := l.AsConstantInt(); ok { 627 if rInt, ok := r.AsConstantInt(); ok { 628 return &NumVal{value: constant.BinaryOp(lInt, token, rInt)} 629 } 630 } 631 } 632 // Explicitly ignore shift operators so the expression is evaluated as a 633 // non-const. This is because 1 << 63 as a 64-bit int (which is a negative 634 // number due to 2s complement) is different than 1 << 63 as constant, 635 // which is positive. 636 } 637 case *StrVal: 638 if r, ok := t.Right.(*StrVal); ok { 639 switch t.Operator { 640 case Concat: 641 // When folding string-like constants, if either was a byte 642 // array literal, the result is also a byte literal. 643 return &StrVal{s: l.s + r.s, scannedAsBytes: l.scannedAsBytes || r.scannedAsBytes} 644 } 645 } 646 } 647 case *ComparisonExpr: 648 switch l := t.Left.(type) { 649 case *NumVal: 650 if r, ok := t.Right.(*NumVal); ok { 651 if token, ok := comparisonOpToToken[t.Operator]; ok { 652 return MakeDBool(DBool(constant.Compare(l.AsConstantValue(), token, r.AsConstantValue()))) 653 } 654 } 655 case *StrVal: 656 // ComparisonExpr folding for String-like constants is not significantly different 657 // from constant evalutation during normalization (because both should be exact, 658 // unlike numeric comparisons). Still, folding these comparisons when possible here 659 // can reduce the amount of work performed during type checking, can reduce necessary 660 // allocations, and maintains symmetry with numeric constants. 661 if r, ok := t.Right.(*StrVal); ok { 662 switch t.Operator { 663 case EQ: 664 return MakeDBool(DBool(l.s == r.s)) 665 case NE: 666 return MakeDBool(DBool(l.s != r.s)) 667 case LT: 668 return MakeDBool(DBool(l.s < r.s)) 669 case LE: 670 return MakeDBool(DBool(l.s <= r.s)) 671 case GT: 672 return MakeDBool(DBool(l.s > r.s)) 673 case GE: 674 return MakeDBool(DBool(l.s >= r.s)) 675 } 676 } 677 } 678 } 679 return expr 680 } 681 682 // FoldConstantLiterals folds all constant literals using exact arithmetic. 683 // 684 // TODO(nvanbenschoten): Can this visitor be preallocated (like normalizeVisitor)? 685 // TODO(nvanbenschoten): Investigate normalizing associative operations to group 686 // constants together and permit further numeric constant folding. 687 func FoldConstantLiterals(expr Expr) (Expr, error) { 688 v := constantFolderVisitor{} 689 expr, _ = WalkExpr(v, expr) 690 return expr, nil 691 }