1// Copyright 2013 The Go Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style 3// license that can be found in the LICENSE file. 4 5package constant 6 7import ( 8 "fmt" 9 "go/token" 10 "math" 11 "math/big" 12 "strings" 13 "testing" 14) 15 16var intTests = []string{ 17 // 0-octals 18 `0_123 = 0123`, 19 `0123_456 = 0123456`, 20 21 // decimals 22 `1_234 = 1234`, 23 `1_234_567 = 1234567`, 24 25 // hexadecimals 26 `0X_0 = 0`, 27 `0X_1234 = 0x1234`, 28 `0X_CAFE_f00d = 0xcafef00d`, 29 30 // octals 31 `0o0 = 0`, 32 `0o1234 = 01234`, 33 `0o01234567 = 01234567`, 34 35 `0O0 = 0`, 36 `0O1234 = 01234`, 37 `0O01234567 = 01234567`, 38 39 `0o_0 = 0`, 40 `0o_1234 = 01234`, 41 `0o0123_4567 = 01234567`, 42 43 `0O_0 = 0`, 44 `0O_1234 = 01234`, 45 `0O0123_4567 = 01234567`, 46 47 // binaries 48 `0b0 = 0`, 49 `0b1011 = 0xb`, 50 `0b00101101 = 0x2d`, 51 52 `0B0 = 0`, 53 `0B1011 = 0xb`, 54 `0B00101101 = 0x2d`, 55 56 `0b_0 = 0`, 57 `0b10_11 = 0xb`, 58 `0b_0010_1101 = 0x2d`, 59} 60 61// The RHS operand may be a floating-point quotient n/d of two integer values n and d. 62var floatTests = []string{ 63 // decimal floats 64 `1_2_3. = 123.`, 65 `0_123. = 123.`, 66 67 `0_0e0 = 0.`, 68 `1_2_3e0 = 123.`, 69 `0_123e0 = 123.`, 70 71 `0e-0_0 = 0.`, 72 `1_2_3E+0 = 123.`, 73 `0123E1_2_3 = 123e123`, 74 75 `0.e+1 = 0.`, 76 `123.E-1_0 = 123e-10`, 77 `01_23.e123 = 123e123`, 78 79 `.0e-1 = .0`, 80 `.123E+10 = .123e10`, 81 `.0123E123 = .0123e123`, 82 83 `1_2_3.123 = 123.123`, 84 `0123.01_23 = 123.0123`, 85 86 `1e-1000000000 = 0`, 87 `1e+1000000000 = ?`, 88 `6e5518446744 = ?`, 89 `-6e5518446744 = ?`, 90 91 // hexadecimal floats 92 `0x0.p+0 = 0.`, 93 `0Xdeadcafe.p-10 = 0xdeadcafe/1024`, 94 `0x1234.P84 = 0x1234000000000000000000000`, 95 96 `0x.1p-0 = 1/16`, 97 `0X.deadcafep4 = 0xdeadcafe/0x10000000`, 98 `0x.1234P+12 = 0x1234/0x10`, 99 100 `0x0p0 = 0.`, 101 `0Xdeadcafep+1 = 0x1bd5b95fc`, 102 `0x1234P-10 = 0x1234/1024`, 103 104 `0x0.0p0 = 0.`, 105 `0Xdead.cafep+1 = 0x1bd5b95fc/0x10000`, 106 `0x12.34P-10 = 0x1234/0x40000`, 107 108 `0Xdead_cafep+1 = 0xdeadcafep+1`, 109 `0x_1234P-10 = 0x1234p-10`, 110 111 `0X_dead_cafe.p-10 = 0xdeadcafe.p-10`, 112 `0x12_34.P1_2_3 = 0x1234.p123`, 113} 114 115var imagTests = []string{ 116 `1_234i = 1234i`, 117 `1_234_567i = 1234567i`, 118 119 `0.i = 0i`, 120 `123.i = 123i`, 121 `0123.i = 123i`, 122 123 `0.e+1i = 0i`, 124 `123.E-1_0i = 123e-10i`, 125 `01_23.e123i = 123e123i`, 126 127 `1e-1000000000i = 0i`, 128 `1e+1000000000i = ?`, 129 `6e5518446744i = ?`, 130 `-6e5518446744i = ?`, 131} 132 133func testNumbers(t *testing.T, kind token.Token, tests []string) { 134 for _, test := range tests { 135 a := strings.Split(test, " = ") 136 if len(a) != 2 { 137 t.Errorf("invalid test case: %s", test) 138 continue 139 } 140 141 x := MakeFromLiteral(a[0], kind, 0) 142 var y Value 143 if a[1] == "?" { 144 y = MakeUnknown() 145 } else { 146 if ns, ds, ok := strings.Cut(a[1], "/"); ok && kind == token.FLOAT { 147 n := MakeFromLiteral(ns, token.INT, 0) 148 d := MakeFromLiteral(ds, token.INT, 0) 149 y = BinaryOp(n, token.QUO, d) 150 } else { 151 y = MakeFromLiteral(a[1], kind, 0) 152 } 153 if y.Kind() == Unknown { 154 panic(fmt.Sprintf("invalid test case: %s %d", test, y.Kind())) 155 } 156 } 157 158 xk := x.Kind() 159 yk := y.Kind() 160 if xk != yk { 161 t.Errorf("%s: got kind %d != %d", test, xk, yk) 162 continue 163 } 164 165 if yk == Unknown { 166 continue 167 } 168 169 if !Compare(x, token.EQL, y) { 170 t.Errorf("%s: %s != %s", test, x, y) 171 } 172 } 173} 174 175// TestNumbers verifies that differently written literals 176// representing the same number do have the same value. 177func TestNumbers(t *testing.T) { 178 testNumbers(t, token.INT, intTests) 179 testNumbers(t, token.FLOAT, floatTests) 180 testNumbers(t, token.IMAG, imagTests) 181} 182 183var opTests = []string{ 184 // unary operations 185 `+ 0 = 0`, 186 `+ ? = ?`, 187 `- 1 = -1`, 188 `- ? = ?`, 189 `^ 0 = -1`, 190 `^ ? = ?`, 191 192 `! true = false`, 193 `! false = true`, 194 `! ? = ?`, 195 196 // etc. 197 198 // binary operations 199 `"" + "" = ""`, 200 `"foo" + "" = "foo"`, 201 `"" + "bar" = "bar"`, 202 `"foo" + "bar" = "foobar"`, 203 204 `0 + 0 = 0`, 205 `0 + 0.1 = 0.1`, 206 `0 + 0.1i = 0.1i`, 207 `0.1 + 0.9 = 1`, 208 `1e100 + 1e100 = 2e100`, 209 `? + 0 = ?`, 210 `0 + ? = ?`, 211 212 `0 - 0 = 0`, 213 `0 - 0.1 = -0.1`, 214 `0 - 0.1i = -0.1i`, 215 `1e100 - 1e100 = 0`, 216 `? - 0 = ?`, 217 `0 - ? = ?`, 218 219 `0 * 0 = 0`, 220 `1 * 0.1 = 0.1`, 221 `1 * 0.1i = 0.1i`, 222 `1i * 1i = -1`, 223 `? * 0 = ?`, 224 `0 * ? = ?`, 225 `0 * 1e+1000000000 = ?`, 226 227 `0 / 0 = "division_by_zero"`, 228 `10 / 2 = 5`, 229 `5 / 3 = 5/3`, 230 `5i / 3i = 5/3`, 231 `? / 0 = ?`, 232 `0 / ? = ?`, 233 `0 * 1e+1000000000i = ?`, 234 235 `0 % 0 = "runtime_error:_integer_divide_by_zero"`, // TODO(gri) should be the same as for / 236 `10 % 3 = 1`, 237 `? % 0 = ?`, 238 `0 % ? = ?`, 239 240 `0 & 0 = 0`, 241 `12345 & 0 = 0`, 242 `0xff & 0xf = 0xf`, 243 `? & 0 = ?`, 244 `0 & ? = ?`, 245 246 `0 | 0 = 0`, 247 `12345 | 0 = 12345`, 248 `0xb | 0xa0 = 0xab`, 249 `? | 0 = ?`, 250 `0 | ? = ?`, 251 252 `0 ^ 0 = 0`, 253 `1 ^ -1 = -2`, 254 `? ^ 0 = ?`, 255 `0 ^ ? = ?`, 256 257 `0 &^ 0 = 0`, 258 `0xf &^ 1 = 0xe`, 259 `1 &^ 0xf = 0`, 260 // etc. 261 262 // shifts 263 `0 << 0 = 0`, 264 `1 << 10 = 1024`, 265 `0 >> 0 = 0`, 266 `1024 >> 10 == 1`, 267 `? << 0 == ?`, 268 `? >> 10 == ?`, 269 // etc. 270 271 // comparisons 272 `false == false = true`, 273 `false == true = false`, 274 `true == false = false`, 275 `true == true = true`, 276 277 `false != false = false`, 278 `false != true = true`, 279 `true != false = true`, 280 `true != true = false`, 281 282 `"foo" == "bar" = false`, 283 `"foo" != "bar" = true`, 284 `"foo" < "bar" = false`, 285 `"foo" <= "bar" = false`, 286 `"foo" > "bar" = true`, 287 `"foo" >= "bar" = true`, 288 289 `0 == 0 = true`, 290 `0 != 0 = false`, 291 `0 < 10 = true`, 292 `10 <= 10 = true`, 293 `0 > 10 = false`, 294 `10 >= 10 = true`, 295 296 `1/123456789 == 1/123456789 == true`, 297 `1/123456789 != 1/123456789 == false`, 298 `1/123456789 < 1/123456788 == true`, 299 `1/123456788 <= 1/123456789 == false`, 300 `0.11 > 0.11 = false`, 301 `0.11 >= 0.11 = true`, 302 303 `? == 0 = false`, 304 `? != 0 = false`, 305 `? < 10 = false`, 306 `? <= 10 = false`, 307 `? > 10 = false`, 308 `? >= 10 = false`, 309 310 `0 == ? = false`, 311 `0 != ? = false`, 312 `0 < ? = false`, 313 `10 <= ? = false`, 314 `0 > ? = false`, 315 `10 >= ? = false`, 316 317 // etc. 318} 319 320func TestOps(t *testing.T) { 321 for _, test := range opTests { 322 a := strings.Split(test, " ") 323 i := 0 // operator index 324 325 var x, x0 Value 326 switch len(a) { 327 case 4: 328 // unary operation 329 case 5: 330 // binary operation 331 x, x0 = val(a[0]), val(a[0]) 332 i = 1 333 default: 334 t.Errorf("invalid test case: %s", test) 335 continue 336 } 337 338 op, ok := optab[a[i]] 339 if !ok { 340 panic("missing optab entry for " + a[i]) 341 } 342 343 y, y0 := val(a[i+1]), val(a[i+1]) 344 345 got := doOp(x, op, y) 346 want := val(a[i+3]) 347 if !eql(got, want) { 348 t.Errorf("%s: got %s; want %s", test, got, want) 349 continue 350 } 351 352 if x0 != nil && !eql(x, x0) { 353 t.Errorf("%s: x changed to %s", test, x) 354 continue 355 } 356 357 if !eql(y, y0) { 358 t.Errorf("%s: y changed to %s", test, y) 359 continue 360 } 361 } 362} 363 364func eql(x, y Value) bool { 365 _, ux := x.(unknownVal) 366 _, uy := y.(unknownVal) 367 if ux || uy { 368 return ux == uy 369 } 370 return Compare(x, token.EQL, y) 371} 372 373// ---------------------------------------------------------------------------- 374// String tests 375 376var xxx = strings.Repeat("x", 68) 377var issue14262 = `"بموجب الشروط التالية نسب المصنف — يجب عليك أن تنسب العمل بالطريقة التي تحددها المؤلف أو المرخص (ولكن ليس بأي حال من الأحوال أن توحي وتقترح بتحول أو استخدامك للعمل). المشاركة على قدم المساواة — إذا كنت يعدل ، والتغيير ، أو الاستفادة من هذا العمل ، قد ينتج عن توزيع العمل إلا في ظل تشابه او تطابق فى واحد لهذا الترخيص."` 378 379var stringTests = []struct { 380 input, short, exact string 381}{ 382 // Unknown 383 {"", "unknown", "unknown"}, 384 {"0x", "unknown", "unknown"}, 385 {"'", "unknown", "unknown"}, 386 {"1f0", "unknown", "unknown"}, 387 {"unknown", "unknown", "unknown"}, 388 389 // Bool 390 {"true", "true", "true"}, 391 {"false", "false", "false"}, 392 393 // String 394 {`""`, `""`, `""`}, 395 {`"foo"`, `"foo"`, `"foo"`}, 396 {`"` + xxx + `xx"`, `"` + xxx + `xx"`, `"` + xxx + `xx"`}, 397 {`"` + xxx + `xxx"`, `"` + xxx + `...`, `"` + xxx + `xxx"`}, 398 {`"` + xxx + xxx + `xxx"`, `"` + xxx + `...`, `"` + xxx + xxx + `xxx"`}, 399 {issue14262, `"بموجب الشروط التالية نسب المصنف — يجب عليك أن تنسب العمل بالطريقة ال...`, issue14262}, 400 401 // Int 402 {"0", "0", "0"}, 403 {"-1", "-1", "-1"}, 404 {"12345", "12345", "12345"}, 405 {"-12345678901234567890", "-12345678901234567890", "-12345678901234567890"}, 406 {"12345678901234567890", "12345678901234567890", "12345678901234567890"}, 407 408 // Float 409 {"0.", "0", "0"}, 410 {"-0.0", "0", "0"}, 411 {"10.0", "10", "10"}, 412 {"2.1", "2.1", "21/10"}, 413 {"-2.1", "-2.1", "-21/10"}, 414 {"1e9999", "1e+9999", "0x.f8d4a9da224650a8cb2959e10d985ad92adbd44c62917e608b1f24c0e1b76b6f61edffeb15c135a4b601637315f7662f325f82325422b244286a07663c9415d2p+33216"}, 415 {"1e-9999", "1e-9999", "0x.83b01ba6d8c0425eec1b21e96f7742d63c2653ed0a024cf8a2f9686df578d7b07d7a83d84df6a2ec70a921d1f6cd5574893a7eda4d28ee719e13a5dce2700759p-33215"}, 416 {"2.71828182845904523536028747135266249775724709369995957496696763", "2.71828", "271828182845904523536028747135266249775724709369995957496696763/100000000000000000000000000000000000000000000000000000000000000"}, 417 {"0e9999999999", "0", "0"}, // issue #16176 418 {"-6e-1886451601", "0", "0"}, // issue #20228 419 420 // Complex 421 {"0i", "(0 + 0i)", "(0 + 0i)"}, 422 {"-0i", "(0 + 0i)", "(0 + 0i)"}, 423 {"10i", "(0 + 10i)", "(0 + 10i)"}, 424 {"-10i", "(0 + -10i)", "(0 + -10i)"}, 425 {"1e9999i", "(0 + 1e+9999i)", "(0 + 0x.f8d4a9da224650a8cb2959e10d985ad92adbd44c62917e608b1f24c0e1b76b6f61edffeb15c135a4b601637315f7662f325f82325422b244286a07663c9415d2p+33216i)"}, 426} 427 428func TestString(t *testing.T) { 429 for _, test := range stringTests { 430 x := val(test.input) 431 if got := x.String(); got != test.short { 432 t.Errorf("%s: got %q; want %q as short string", test.input, got, test.short) 433 } 434 if got := x.ExactString(); got != test.exact { 435 t.Errorf("%s: got %q; want %q as exact string", test.input, got, test.exact) 436 } 437 } 438} 439 440// ---------------------------------------------------------------------------- 441// Support functions 442 443func val(lit string) Value { 444 if len(lit) == 0 { 445 return MakeUnknown() 446 } 447 448 switch lit { 449 case "?": 450 return MakeUnknown() 451 case "true": 452 return MakeBool(true) 453 case "false": 454 return MakeBool(false) 455 } 456 457 if as, bs, ok := strings.Cut(lit, "/"); ok { 458 // assume fraction 459 a := MakeFromLiteral(as, token.INT, 0) 460 b := MakeFromLiteral(bs, token.INT, 0) 461 return BinaryOp(a, token.QUO, b) 462 } 463 464 tok := token.INT 465 switch first, last := lit[0], lit[len(lit)-1]; { 466 case first == '"' || first == '`': 467 tok = token.STRING 468 lit = strings.ReplaceAll(lit, "_", " ") 469 case first == '\'': 470 tok = token.CHAR 471 case last == 'i': 472 tok = token.IMAG 473 default: 474 if !strings.HasPrefix(lit, "0x") && strings.ContainsAny(lit, "./Ee") { 475 tok = token.FLOAT 476 } 477 } 478 479 return MakeFromLiteral(lit, tok, 0) 480} 481 482var optab = map[string]token.Token{ 483 "!": token.NOT, 484 485 "+": token.ADD, 486 "-": token.SUB, 487 "*": token.MUL, 488 "/": token.QUO, 489 "%": token.REM, 490 491 "<<": token.SHL, 492 ">>": token.SHR, 493 494 "&": token.AND, 495 "|": token.OR, 496 "^": token.XOR, 497 "&^": token.AND_NOT, 498 499 "==": token.EQL, 500 "!=": token.NEQ, 501 "<": token.LSS, 502 "<=": token.LEQ, 503 ">": token.GTR, 504 ">=": token.GEQ, 505} 506 507func panicHandler(v *Value) { 508 switch p := recover().(type) { 509 case nil: 510 // nothing to do 511 case string: 512 *v = MakeString(p) 513 case error: 514 *v = MakeString(p.Error()) 515 default: 516 panic(p) 517 } 518} 519 520func doOp(x Value, op token.Token, y Value) (z Value) { 521 defer panicHandler(&z) 522 523 if x == nil { 524 return UnaryOp(op, y, 0) 525 } 526 527 switch op { 528 case token.EQL, token.NEQ, token.LSS, token.LEQ, token.GTR, token.GEQ: 529 return MakeBool(Compare(x, op, y)) 530 case token.SHL, token.SHR: 531 s, _ := Int64Val(y) 532 return Shift(x, op, uint(s)) 533 default: 534 return BinaryOp(x, op, y) 535 } 536} 537 538// ---------------------------------------------------------------------------- 539// Other tests 540 541var fracTests = []string{ 542 "0", 543 "1", 544 "-1", 545 "1.2", 546 "-0.991", 547 "2.718281828", 548 "3.14159265358979323e-10", 549 "1e100", 550 "1e1000", 551} 552 553func TestFractions(t *testing.T) { 554 for _, test := range fracTests { 555 x := val(test) 556 // We don't check the actual numerator and denominator because they 557 // are unlikely to be 100% correct due to floatVal rounding errors. 558 // Instead, we compute the fraction again and compare the rounded 559 // result. 560 q := BinaryOp(Num(x), token.QUO, Denom(x)) 561 got := q.String() 562 want := x.String() 563 if got != want { 564 t.Errorf("%s: got quotient %s, want %s", x, got, want) 565 } 566 } 567} 568 569var bytesTests = []string{ 570 "0", 571 "1", 572 "123456789", 573 "123456789012345678901234567890123456789012345678901234567890", 574} 575 576func TestBytes(t *testing.T) { 577 for _, test := range bytesTests { 578 x := val(test) 579 bytes := Bytes(x) 580 581 // special case 0 582 if Sign(x) == 0 && len(bytes) != 0 { 583 t.Errorf("%s: got %v; want empty byte slice", test, bytes) 584 } 585 586 if n := len(bytes); n > 0 && bytes[n-1] == 0 { 587 t.Errorf("%s: got %v; want no leading 0 byte", test, bytes) 588 } 589 590 if got := MakeFromBytes(bytes); !eql(got, x) { 591 t.Errorf("%s: got %s; want %s (bytes = %v)", test, got, x, bytes) 592 } 593 } 594} 595 596func TestUnknown(t *testing.T) { 597 u := MakeUnknown() 598 var values = []Value{ 599 u, 600 MakeBool(false), // token.ADD ok below, operation is never considered 601 MakeString(""), 602 MakeInt64(1), 603 MakeFromLiteral("''", token.CHAR, 0), 604 MakeFromLiteral("-1234567890123456789012345678901234567890", token.INT, 0), 605 MakeFloat64(1.2), 606 MakeImag(MakeFloat64(1.2)), 607 } 608 for _, val := range values { 609 x, y := val, u 610 for i := range [2]int{} { 611 if i == 1 { 612 x, y = y, x 613 } 614 if got := BinaryOp(x, token.ADD, y); got.Kind() != Unknown { 615 t.Errorf("%s + %s: got %s; want %s", x, y, got, u) 616 } 617 if got := Compare(x, token.EQL, y); got { 618 t.Errorf("%s == %s: got true; want false", x, y) 619 } 620 } 621 } 622} 623 624func TestMakeFloat64(t *testing.T) { 625 var zero float64 626 for _, arg := range []float64{ 627 -math.MaxFloat32, 628 -10, 629 -0.5, 630 -zero, 631 zero, 632 1, 633 10, 634 123456789.87654321e-23, 635 1e10, 636 math.MaxFloat64, 637 } { 638 val := MakeFloat64(arg) 639 if val.Kind() != Float { 640 t.Errorf("%v: got kind = %d; want %d", arg, val.Kind(), Float) 641 } 642 643 // -0.0 is mapped to 0.0 644 got, exact := Float64Val(val) 645 if !exact || math.Float64bits(got) != math.Float64bits(arg+0) { 646 t.Errorf("%v: got %v (exact = %v)", arg, got, exact) 647 } 648 } 649 650 // infinity 651 for sign := range []int{-1, 1} { 652 arg := math.Inf(sign) 653 val := MakeFloat64(arg) 654 if val.Kind() != Unknown { 655 t.Errorf("%v: got kind = %d; want %d", arg, val.Kind(), Unknown) 656 } 657 } 658} 659 660type makeTestCase struct { 661 kind Kind 662 arg, want any 663} 664 665func dup(k Kind, x any) makeTestCase { return makeTestCase{k, x, x} } 666 667func TestMake(t *testing.T) { 668 for _, test := range []makeTestCase{ 669 {Bool, false, false}, 670 {String, "hello", "hello"}, 671 672 {Int, int64(1), int64(1)}, 673 {Int, big.NewInt(10), int64(10)}, 674 {Int, new(big.Int).Lsh(big.NewInt(1), 62), int64(1 << 62)}, 675 dup(Int, new(big.Int).Lsh(big.NewInt(1), 63)), 676 677 {Float, big.NewFloat(0), floatVal0.val}, 678 dup(Float, big.NewFloat(2.0)), 679 dup(Float, big.NewRat(1, 3)), 680 } { 681 val := Make(test.arg) 682 got := Val(val) 683 if val.Kind() != test.kind || got != test.want { 684 t.Errorf("got %v (%T, kind = %d); want %v (%T, kind = %d)", 685 got, got, val.Kind(), test.want, test.want, test.kind) 686 } 687 } 688} 689 690func BenchmarkStringAdd(b *testing.B) { 691 for size := 1; size <= 65536; size *= 4 { 692 b.Run(fmt.Sprint(size), func(b *testing.B) { 693 b.ReportAllocs() 694 n := int64(0) 695 for i := 0; i < b.N; i++ { 696 x := MakeString(strings.Repeat("x", 100)) 697 y := x 698 for j := 0; j < size-1; j++ { 699 y = BinaryOp(y, token.ADD, x) 700 } 701 n += int64(len(StringVal(y))) 702 } 703 if n != int64(b.N)*int64(size)*100 { 704 b.Fatalf("bad string %d != %d", n, int64(b.N)*int64(size)*100) 705 } 706 }) 707 } 708} 709 710var bitLenTests = []struct { 711 val int64 712 want int 713}{ 714 {0, 0}, 715 {1, 1}, 716 {-16, 5}, 717 {1 << 61, 62}, 718 {1 << 62, 63}, 719 {-1 << 62, 63}, 720 {-1 << 63, 64}, 721} 722 723func TestBitLen(t *testing.T) { 724 for _, test := range bitLenTests { 725 if got := BitLen(MakeInt64(test.val)); got != test.want { 726 t.Errorf("%v: got %v, want %v", test.val, got, test.want) 727 } 728 } 729} 730