cuelang.org/go@v0.10.1/internal/golangorgx/tools/refactor/inline/falcon.go (about) 1 // Copyright 2023 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 5 package inline 6 7 // This file defines the callee side of the "fallible constant" analysis. 8 9 import ( 10 "fmt" 11 "go/ast" 12 "go/constant" 13 "go/format" 14 "go/token" 15 "go/types" 16 "strconv" 17 "strings" 18 19 "cuelang.org/go/internal/golangorgx/tools/aliases" 20 "cuelang.org/go/internal/golangorgx/tools/typeparams" 21 "golang.org/x/tools/go/types/typeutil" 22 ) 23 24 // falconResult is the result of the analysis of the callee. 25 type falconResult struct { 26 Types []falconType // types for falcon constraint environment 27 Constraints []string // constraints (Go expressions) on values of fallible constants 28 } 29 30 // A falconType specifies the name and underlying type of a synthetic 31 // defined type for use in falcon constraints. 32 // 33 // Unique types from callee code are bijectively mapped onto falcon 34 // types so that constraints are independent of callee type 35 // information but preserve type equivalence classes. 36 // 37 // Fresh names are deliberately obscure to avoid shadowing even if a 38 // callee parameter has a nanme like "int" or "any". 39 type falconType struct { 40 Name string 41 Kind types.BasicKind // string/number/bool 42 } 43 44 // falcon identifies "fallible constant" expressions, which are 45 // expressions that may fail to compile if one or more of their 46 // operands is changed from non-constant to constant. 47 // 48 // Consider: 49 // 50 // func sub(s string, i, j int) string { return s[i:j] } 51 // 52 // If parameters are replaced by constants, the compiler is 53 // required to perform these additional checks: 54 // 55 // - if i is constant, 0 <= i. 56 // - if s and i are constant, i <= len(s). 57 // - ditto for j. 58 // - if i and j are constant, i <= j. 59 // 60 // s[i:j] is thus a "fallible constant" expression dependent on {s, i, 61 // j}. Each falcon creates a set of conditional constraints across one 62 // or more parameter variables. 63 // 64 // - When inlining a call such as sub("abc", -1, 2), the parameter i 65 // cannot be eliminated by substitution as its argument value is 66 // negative. 67 // 68 // - When inlining sub("", 2, 1), all three parameters cannot be be 69 // simultaneously eliminated by substitution without violating i 70 // <= len(s) and j <= len(s), but the parameters i and j could be 71 // safely eliminated without s. 72 // 73 // Parameters that cannot be eliminated must remain non-constant, 74 // either in the form of a binding declaration: 75 // 76 // { var i int = -1; return "abc"[i:2] } 77 // 78 // or a parameter of a literalization: 79 // 80 // func (i int) string { return "abc"[i:2] }(-1) 81 // 82 // These example expressions are obviously doomed to fail at run 83 // time, but in realistic cases such expressions are dominated by 84 // appropriate conditions that make them reachable only when safe: 85 // 86 // if 0 <= i && i <= j && j <= len(s) { _ = s[i:j] } 87 // 88 // (In principle a more sophisticated inliner could entirely eliminate 89 // such unreachable blocks based on the condition being always-false 90 // for the given parameter substitution, but this is tricky to do safely 91 // because the type-checker considers only a single configuration. 92 // Consider: if runtime.GOOS == "linux" { ... }.) 93 // 94 // We believe this is an exhaustive list of "fallible constant" operations: 95 // 96 // - switch z { case x: case y } // duplicate case values 97 // - s[i], s[i:j], s[i:j:k] // index out of bounds (0 <= i <= j <= k <= len(s)) 98 // - T{x: 0} // index out of bounds, duplicate index 99 // - x/y, x%y, x/=y, x%=y // integer division by zero; minint/-1 overflow 100 // - x+y, x-y, x*y // arithmetic overflow 101 // - x<<y // shift out of range 102 // - -x // negation of minint 103 // - T(x) // value out of range 104 // 105 // The fundamental reason for this elaborate algorithm is that the 106 // "separate analysis" of callee and caller, as required when running 107 // in an environment such as unitchecker, means that there is no way 108 // for us to simply invoke the type checker on the combination of 109 // caller and callee code, as by the time we analyze the caller, we no 110 // longer have access to type information for the callee (and, in 111 // particular, any of its direct dependencies that are not direct 112 // dependencies of the caller). So, in effect, we are forced to map 113 // the problem in a neutral (callee-type-independent) constraint 114 // system that can be verified later. 115 func falcon(logf func(string, ...any), fset *token.FileSet, params map[*types.Var]*paramInfo, info *types.Info, decl *ast.FuncDecl) falconResult { 116 117 st := &falconState{ 118 logf: logf, 119 fset: fset, 120 params: params, 121 info: info, 122 decl: decl, 123 } 124 125 // type mapping 126 st.int = st.typename(types.Typ[types.Int]) 127 st.any = "interface{}" // don't use "any" as it may be shadowed 128 for obj, info := range st.params { 129 if isBasic(obj.Type(), types.IsConstType) { 130 info.FalconType = st.typename(obj.Type()) 131 } 132 } 133 134 st.stmt(st.decl.Body) 135 136 return st.result 137 } 138 139 type falconState struct { 140 // inputs 141 logf func(string, ...any) 142 fset *token.FileSet 143 params map[*types.Var]*paramInfo 144 info *types.Info 145 decl *ast.FuncDecl 146 147 // working state 148 int string 149 any string 150 typenames typeutil.Map 151 152 result falconResult 153 } 154 155 // typename returns the name in the falcon constraint system 156 // of a given string/number/bool type t. Falcon types are 157 // specified directly in go/types data structures rather than 158 // by name, avoiding potential shadowing conflicts with 159 // confusing parameter names such as "int". 160 // 161 // Also, each distinct type (as determined by types.Identical) 162 // is mapped to a fresh type in the falcon system so that we 163 // can map the types in the callee code into a neutral form 164 // that does not depend on imports, allowing us to detect 165 // potential conflicts such as 166 // 167 // map[any]{T1(1): 0, T2(1): 0} 168 // 169 // where T1=T2. 170 func (st *falconState) typename(t types.Type) string { 171 name, ok := st.typenames.At(t).(string) 172 if !ok { 173 basic := t.Underlying().(*types.Basic) 174 175 // That dot ۰ is an Arabic zero numeral U+06F0. 176 // It is very unlikely to appear in a real program. 177 // TODO(adonovan): use a non-heuristic solution. 178 name = fmt.Sprintf("%s۰%d", basic, st.typenames.Len()) 179 st.typenames.Set(t, name) 180 st.logf("falcon: emit type %s %s // %q", name, basic, t) 181 st.result.Types = append(st.result.Types, falconType{ 182 Name: name, 183 Kind: basic.Kind(), 184 }) 185 } 186 return name 187 } 188 189 // -- constraint emission -- 190 191 // emit emits a Go expression that must have a legal type. 192 // In effect, we let the go/types constant folding algorithm 193 // do most of the heavy lifting (though it may be hard to 194 // believe from the complexity of this algorithm!). 195 func (st *falconState) emit(constraint ast.Expr) { 196 var out strings.Builder 197 if err := format.Node(&out, st.fset, constraint); err != nil { 198 panic(err) // can't happen 199 } 200 syntax := out.String() 201 st.logf("falcon: emit constraint %s", syntax) 202 st.result.Constraints = append(st.result.Constraints, syntax) 203 } 204 205 // emitNonNegative emits an []T{}[index] constraint, 206 // which ensures index is non-negative if constant. 207 func (st *falconState) emitNonNegative(index ast.Expr) { 208 st.emit(&ast.IndexExpr{ 209 X: &ast.CompositeLit{ 210 Type: &ast.ArrayType{ 211 Elt: makeIdent(st.int), 212 }, 213 }, 214 Index: index, 215 }) 216 } 217 218 // emitMonotonic emits an []T{}[i:j] constraint, 219 // which ensures i <= j if both are constant. 220 func (st *falconState) emitMonotonic(i, j ast.Expr) { 221 st.emit(&ast.SliceExpr{ 222 X: &ast.CompositeLit{ 223 Type: &ast.ArrayType{ 224 Elt: makeIdent(st.int), 225 }, 226 }, 227 Low: i, 228 High: j, 229 }) 230 } 231 232 // emitUnique emits a T{elem1: 0, ... elemN: 0} constraint, 233 // which ensures that all constant elems are unique. 234 // T may be a map, slice, or array depending 235 // on the desired check semantics. 236 func (st *falconState) emitUnique(typ ast.Expr, elems []ast.Expr) { 237 if len(elems) > 1 { 238 var elts []ast.Expr 239 for _, elem := range elems { 240 elts = append(elts, &ast.KeyValueExpr{ 241 Key: elem, 242 Value: makeIntLit(0), 243 }) 244 } 245 st.emit(&ast.CompositeLit{ 246 Type: typ, 247 Elts: elts, 248 }) 249 } 250 } 251 252 // -- traversal -- 253 254 // The traversal functions scan the callee body for expressions that 255 // are not constant but would become constant if the parameter vars 256 // were redeclared as constants, and emits for each one a constraint 257 // (a Go expression) with the property that it will not type-check 258 // (using types.CheckExpr) if the particular argument values are 259 // unsuitable. 260 // 261 // These constraints are checked by Inline with the actual 262 // constant argument values. Violations cause it to reject 263 // parameters as candidates for substitution. 264 265 func (st *falconState) stmt(s ast.Stmt) { 266 ast.Inspect(s, func(n ast.Node) bool { 267 switch n := n.(type) { 268 case ast.Expr: 269 _ = st.expr(n) 270 return false // skip usual traversal 271 272 case *ast.AssignStmt: 273 switch n.Tok { 274 case token.QUO_ASSIGN, token.REM_ASSIGN: 275 // x /= y 276 // Possible "integer division by zero" 277 // Emit constraint: 1/y. 278 _ = st.expr(n.Lhs[0]) 279 kY := st.expr(n.Rhs[0]) 280 if kY, ok := kY.(ast.Expr); ok { 281 op := token.QUO 282 if n.Tok == token.REM_ASSIGN { 283 op = token.REM 284 } 285 st.emit(&ast.BinaryExpr{ 286 Op: op, 287 X: makeIntLit(1), 288 Y: kY, 289 }) 290 } 291 return false // skip usual traversal 292 } 293 294 case *ast.SwitchStmt: 295 if n.Init != nil { 296 st.stmt(n.Init) 297 } 298 tBool := types.Type(types.Typ[types.Bool]) 299 tagType := tBool // default: true 300 if n.Tag != nil { 301 st.expr(n.Tag) 302 tagType = st.info.TypeOf(n.Tag) 303 } 304 305 // Possible "duplicate case value". 306 // Emit constraint map[T]int{v1: 0, ..., vN:0} 307 // to ensure all maybe-constant case values are unique 308 // (unless switch tag is boolean, which is relaxed). 309 var unique []ast.Expr 310 for _, clause := range n.Body.List { 311 clause := clause.(*ast.CaseClause) 312 for _, caseval := range clause.List { 313 if k := st.expr(caseval); k != nil { 314 unique = append(unique, st.toExpr(k)) 315 } 316 } 317 for _, stmt := range clause.Body { 318 st.stmt(stmt) 319 } 320 } 321 if unique != nil && !types.Identical(tagType.Underlying(), tBool) { 322 tname := st.any 323 if !types.IsInterface(tagType) { 324 tname = st.typename(tagType) 325 } 326 t := &ast.MapType{ 327 Key: makeIdent(tname), 328 Value: makeIdent(st.int), 329 } 330 st.emitUnique(t, unique) 331 } 332 } 333 return true 334 }) 335 } 336 337 // fieldTypes visits the .Type of each field in the list. 338 func (st *falconState) fieldTypes(fields *ast.FieldList) { 339 if fields != nil { 340 for _, field := range fields.List { 341 _ = st.expr(field.Type) 342 } 343 } 344 } 345 346 // expr visits the expression (or type) and returns a 347 // non-nil result if the expression is constant or would 348 // become constant if all suitable function parameters were 349 // redeclared as constants. 350 // 351 // If the expression is constant, st.expr returns its type 352 // and value (types.TypeAndValue). If the expression would 353 // become constant, st.expr returns an ast.Expr tree whose 354 // leaves are literals and parameter references, and whose 355 // interior nodes are operations that may become constant, 356 // such as -x, x+y, f(x), and T(x). We call these would-be 357 // constant expressions "fallible constants", since they may 358 // fail to type-check for some values of x, i, and j. (We 359 // refer to the non-nil cases collectively as "maybe 360 // constant", and the nil case as "definitely non-constant".) 361 // 362 // As a side effect, st.expr emits constraints for each 363 // fallible constant expression; this is its main purpose. 364 // 365 // Consequently, st.expr must visit the entire subtree so 366 // that all necessary constraints are emitted. It may not 367 // short-circuit the traversal when it encounters a constant 368 // subexpression as constants may contain arbitrary other 369 // syntax that may impose constraints. Consider (as always) 370 // this contrived but legal example of a type parameter (!) 371 // that contains statement syntax: 372 // 373 // func f[T [unsafe.Sizeof(func() { stmts })]int]() 374 // 375 // There is no need to emit constraints for (e.g.) s[i] when s 376 // and i are already constants, because we know the expression 377 // is sound, but it is sometimes easier to emit these 378 // redundant constraints than to avoid them. 379 func (st *falconState) expr(e ast.Expr) (res any) { // = types.TypeAndValue | ast.Expr 380 tv := st.info.Types[e] 381 if tv.Value != nil { 382 // A constant value overrides any other result. 383 defer func() { res = tv }() 384 } 385 386 switch e := e.(type) { 387 case *ast.Ident: 388 if v, ok := st.info.Uses[e].(*types.Var); ok { 389 if _, ok := st.params[v]; ok && isBasic(v.Type(), types.IsConstType) { 390 return e // reference to constable parameter 391 } 392 } 393 // (References to *types.Const are handled by the defer.) 394 395 case *ast.BasicLit: 396 // constant 397 398 case *ast.ParenExpr: 399 return st.expr(e.X) 400 401 case *ast.FuncLit: 402 _ = st.expr(e.Type) 403 st.stmt(e.Body) 404 // definitely non-constant 405 406 case *ast.CompositeLit: 407 // T{k: v, ...}, where T ∈ {array,*array,slice,map}, 408 // imposes a constraint that all constant k are 409 // distinct and, for arrays [n]T, within range 0-n. 410 // 411 // Types matter, not just values. For example, 412 // an interface-keyed map may contain keys 413 // that are numerically equal so long as they 414 // are of distinct types. For example: 415 // 416 // type myint int 417 // map[any]bool{1: true, 1: true} // error: duplicate key 418 // map[any]bool{1: true, int16(1): true} // ok 419 // map[any]bool{1: true, myint(1): true} // ok 420 // 421 // This can be asserted by emitting a 422 // constraint of the form T{k1: 0, ..., kN: 0}. 423 if e.Type != nil { 424 _ = st.expr(e.Type) 425 } 426 t := deref(typeparams.CoreType(deref(tv.Type))) 427 var uniques []ast.Expr 428 for _, elt := range e.Elts { 429 if kv, ok := elt.(*ast.KeyValueExpr); ok { 430 if !is[*types.Struct](t) { 431 if k := st.expr(kv.Key); k != nil { 432 uniques = append(uniques, st.toExpr(k)) 433 } 434 } 435 _ = st.expr(kv.Value) 436 } else { 437 _ = st.expr(elt) 438 } 439 } 440 if uniques != nil { 441 // Inv: not a struct. 442 443 // The type T in constraint T{...} depends on the CompLit: 444 // - for a basic-keyed map, use map[K]int; 445 // - for an interface-keyed map, use map[any]int; 446 // - for a slice, use []int; 447 // - for an array or *array, use [n]int. 448 // The last two entail progressively stronger index checks. 449 var ct ast.Expr // type syntax for constraint 450 switch t := aliases.Unalias(t).(type) { 451 case *types.Map: 452 if types.IsInterface(t.Key()) { 453 ct = &ast.MapType{ 454 Key: makeIdent(st.any), 455 Value: makeIdent(st.int), 456 } 457 } else { 458 ct = &ast.MapType{ 459 Key: makeIdent(st.typename(t.Key())), 460 Value: makeIdent(st.int), 461 } 462 } 463 case *types.Array: // or *array 464 ct = &ast.ArrayType{ 465 Len: makeIntLit(t.Len()), 466 Elt: makeIdent(st.int), 467 } 468 default: 469 panic(t) 470 } 471 st.emitUnique(ct, uniques) 472 } 473 // definitely non-constant 474 475 case *ast.SelectorExpr: 476 _ = st.expr(e.X) 477 _ = st.expr(e.Sel) 478 // The defer is sufficient to handle 479 // qualified identifiers (pkg.Const). 480 // All other cases are definitely non-constant. 481 482 case *ast.IndexExpr: 483 if tv.IsType() { 484 // type C[T] 485 _ = st.expr(e.X) 486 _ = st.expr(e.Index) 487 } else { 488 // term x[i] 489 // 490 // Constraints (if x is slice/string/array/*array, not map): 491 // - i >= 0 492 // if i is a fallible constant 493 // - i < len(x) 494 // if x is array/*array and 495 // i is a fallible constant; 496 // or if s is a string and both i, 497 // s are maybe-constants, 498 // but not both are constants. 499 kX := st.expr(e.X) 500 kI := st.expr(e.Index) 501 if kI != nil && !is[*types.Map](st.info.TypeOf(e.X).Underlying()) { 502 if kI, ok := kI.(ast.Expr); ok { 503 st.emitNonNegative(kI) 504 } 505 // Emit constraint to check indices against known length. 506 // TODO(adonovan): factor with SliceExpr logic. 507 var x ast.Expr 508 if kX != nil { 509 // string 510 x = st.toExpr(kX) 511 } else if arr, ok := deref(st.info.TypeOf(e.X).Underlying()).(*types.Array); ok { 512 // array, *array 513 x = &ast.CompositeLit{ 514 Type: &ast.ArrayType{ 515 Len: makeIntLit(arr.Len()), 516 Elt: makeIdent(st.int), 517 }, 518 } 519 } 520 if x != nil { 521 st.emit(&ast.IndexExpr{ 522 X: x, 523 Index: st.toExpr(kI), 524 }) 525 } 526 } 527 } 528 // definitely non-constant 529 530 case *ast.SliceExpr: 531 // x[low:high:max] 532 // 533 // Emit non-negative constraints for each index, 534 // plus low <= high <= max <= len(x) 535 // for each pair that are maybe-constant 536 // but not definitely constant. 537 538 kX := st.expr(e.X) 539 var kLow, kHigh, kMax any 540 if e.Low != nil { 541 kLow = st.expr(e.Low) 542 if kLow != nil { 543 if kLow, ok := kLow.(ast.Expr); ok { 544 st.emitNonNegative(kLow) 545 } 546 } 547 } 548 if e.High != nil { 549 kHigh = st.expr(e.High) 550 if kHigh != nil { 551 if kHigh, ok := kHigh.(ast.Expr); ok { 552 st.emitNonNegative(kHigh) 553 } 554 if kLow != nil { 555 st.emitMonotonic(st.toExpr(kLow), st.toExpr(kHigh)) 556 } 557 } 558 } 559 if e.Max != nil { 560 kMax = st.expr(e.Max) 561 if kMax != nil { 562 if kMax, ok := kMax.(ast.Expr); ok { 563 st.emitNonNegative(kMax) 564 } 565 if kHigh != nil { 566 st.emitMonotonic(st.toExpr(kHigh), st.toExpr(kMax)) 567 } 568 } 569 } 570 571 // Emit constraint to check indices against known length. 572 var x ast.Expr 573 if kX != nil { 574 // string 575 x = st.toExpr(kX) 576 } else if arr, ok := deref(st.info.TypeOf(e.X).Underlying()).(*types.Array); ok { 577 // array, *array 578 x = &ast.CompositeLit{ 579 Type: &ast.ArrayType{ 580 Len: makeIntLit(arr.Len()), 581 Elt: makeIdent(st.int), 582 }, 583 } 584 } 585 if x != nil { 586 // Avoid slice[::max] if kHigh is nonconstant (nil). 587 high, max := st.toExpr(kHigh), st.toExpr(kMax) 588 if high == nil { 589 high = max // => slice[:max:max] 590 } 591 st.emit(&ast.SliceExpr{ 592 X: x, 593 Low: st.toExpr(kLow), 594 High: high, 595 Max: max, 596 }) 597 } 598 // definitely non-constant 599 600 case *ast.TypeAssertExpr: 601 _ = st.expr(e.X) 602 if e.Type != nil { 603 _ = st.expr(e.Type) 604 } 605 606 case *ast.CallExpr: 607 _ = st.expr(e.Fun) 608 if tv, ok := st.info.Types[e.Fun]; ok && tv.IsType() { 609 // conversion T(x) 610 // 611 // Possible "value out of range". 612 kX := st.expr(e.Args[0]) 613 if kX != nil && isBasic(tv.Type, types.IsConstType) { 614 conv := convert(makeIdent(st.typename(tv.Type)), st.toExpr(kX)) 615 if is[ast.Expr](kX) { 616 st.emit(conv) 617 } 618 return conv 619 } 620 return nil // definitely non-constant 621 } 622 623 // call f(x) 624 625 all := true // all args are possibly-constant 626 kArgs := make([]ast.Expr, len(e.Args)) 627 for i, arg := range e.Args { 628 if kArg := st.expr(arg); kArg != nil { 629 kArgs[i] = st.toExpr(kArg) 630 } else { 631 all = false 632 } 633 } 634 635 // Calls to built-ins with fallibly constant arguments 636 // may become constant. All other calls are either 637 // constant or non-constant 638 if id, ok := e.Fun.(*ast.Ident); ok && all && tv.Value == nil { 639 if builtin, ok := st.info.Uses[id].(*types.Builtin); ok { 640 switch builtin.Name() { 641 case "len", "imag", "real", "complex", "min", "max": 642 return &ast.CallExpr{ 643 Fun: id, 644 Args: kArgs, 645 Ellipsis: e.Ellipsis, 646 } 647 } 648 } 649 } 650 651 case *ast.StarExpr: // *T, *ptr 652 _ = st.expr(e.X) 653 654 case *ast.UnaryExpr: 655 // + - ! ^ & <- ~ 656 // 657 // Possible "negation of minint". 658 // Emit constraint: -x 659 kX := st.expr(e.X) 660 if kX != nil && !is[types.TypeAndValue](kX) { 661 if e.Op == token.SUB { 662 st.emit(&ast.UnaryExpr{ 663 Op: e.Op, 664 X: st.toExpr(kX), 665 }) 666 } 667 668 return &ast.UnaryExpr{ 669 Op: e.Op, 670 X: st.toExpr(kX), 671 } 672 } 673 674 case *ast.BinaryExpr: 675 kX := st.expr(e.X) 676 kY := st.expr(e.Y) 677 switch e.Op { 678 case token.QUO, token.REM: 679 // x/y, x%y 680 // 681 // Possible "integer division by zero" or 682 // "minint / -1" overflow. 683 // Emit constraint: x/y or 1/y 684 if kY != nil { 685 if kX == nil { 686 kX = makeIntLit(1) 687 } 688 st.emit(&ast.BinaryExpr{ 689 Op: e.Op, 690 X: st.toExpr(kX), 691 Y: st.toExpr(kY), 692 }) 693 } 694 695 case token.ADD, token.SUB, token.MUL: 696 // x+y, x-y, x*y 697 // 698 // Possible "arithmetic overflow". 699 // Emit constraint: x+y 700 if kX != nil && kY != nil { 701 st.emit(&ast.BinaryExpr{ 702 Op: e.Op, 703 X: st.toExpr(kX), 704 Y: st.toExpr(kY), 705 }) 706 } 707 708 case token.SHL, token.SHR: 709 // x << y, x >> y 710 // 711 // Possible "constant shift too large". 712 // Either operand may be too large individually, 713 // and they may be too large together. 714 // Emit constraint: 715 // x << y (if both maybe-constant) 716 // x << 0 (if y is non-constant) 717 // 1 << y (if x is non-constant) 718 if kX != nil || kY != nil { 719 x := st.toExpr(kX) 720 if x == nil { 721 x = makeIntLit(1) 722 } 723 y := st.toExpr(kY) 724 if y == nil { 725 y = makeIntLit(0) 726 } 727 st.emit(&ast.BinaryExpr{ 728 Op: e.Op, 729 X: x, 730 Y: y, 731 }) 732 } 733 734 case token.LSS, token.GTR, token.EQL, token.NEQ, token.LEQ, token.GEQ: 735 // < > == != <= <= 736 // 737 // A "x cmp y" expression with constant operands x, y is 738 // itself constant, but I can't see how a constant bool 739 // could be fallible: the compiler doesn't reject duplicate 740 // boolean cases in a switch, presumably because boolean 741 // switches are less like n-way branches and more like 742 // sequential if-else chains with possibly overlapping 743 // conditions; and there is (sadly) no way to convert a 744 // boolean constant to an int constant. 745 } 746 if kX != nil && kY != nil { 747 return &ast.BinaryExpr{ 748 Op: e.Op, 749 X: st.toExpr(kX), 750 Y: st.toExpr(kY), 751 } 752 } 753 754 // types 755 // 756 // We need to visit types (and even type parameters) 757 // in order to reach all the places where things could go wrong: 758 // 759 // const ( 760 // s = "" 761 // i = 0 762 // ) 763 // type C[T [unsafe.Sizeof(func() { _ = s[i] })]int] bool 764 765 case *ast.IndexListExpr: 766 _ = st.expr(e.X) 767 for _, expr := range e.Indices { 768 _ = st.expr(expr) 769 } 770 771 case *ast.Ellipsis: 772 if e.Elt != nil { 773 _ = st.expr(e.Elt) 774 } 775 776 case *ast.ArrayType: 777 if e.Len != nil { 778 _ = st.expr(e.Len) 779 } 780 _ = st.expr(e.Elt) 781 782 case *ast.StructType: 783 st.fieldTypes(e.Fields) 784 785 case *ast.FuncType: 786 st.fieldTypes(e.TypeParams) 787 st.fieldTypes(e.Params) 788 st.fieldTypes(e.Results) 789 790 case *ast.InterfaceType: 791 st.fieldTypes(e.Methods) 792 793 case *ast.MapType: 794 _ = st.expr(e.Key) 795 _ = st.expr(e.Value) 796 797 case *ast.ChanType: 798 _ = st.expr(e.Value) 799 } 800 return 801 } 802 803 // toExpr converts the result of visitExpr to a falcon expression. 804 // (We don't do this in visitExpr as we first need to discriminate 805 // constants from maybe-constants.) 806 func (st *falconState) toExpr(x any) ast.Expr { 807 switch x := x.(type) { 808 case nil: 809 return nil 810 811 case types.TypeAndValue: 812 lit := makeLiteral(x.Value) 813 if !isBasic(x.Type, types.IsUntyped) { 814 // convert to "typed" type 815 lit = &ast.CallExpr{ 816 Fun: makeIdent(st.typename(x.Type)), 817 Args: []ast.Expr{lit}, 818 } 819 } 820 return lit 821 822 case ast.Expr: 823 return x 824 825 default: 826 panic(x) 827 } 828 } 829 830 func makeLiteral(v constant.Value) ast.Expr { 831 switch v.Kind() { 832 case constant.Bool: 833 // Rather than refer to the true or false built-ins, 834 // which could be shadowed by poorly chosen parameter 835 // names, we use 0 == 0 for true and 0 != 0 for false. 836 op := token.EQL 837 if !constant.BoolVal(v) { 838 op = token.NEQ 839 } 840 return &ast.BinaryExpr{ 841 Op: op, 842 X: makeIntLit(0), 843 Y: makeIntLit(0), 844 } 845 846 case constant.String: 847 return &ast.BasicLit{ 848 Kind: token.STRING, 849 Value: v.ExactString(), 850 } 851 852 case constant.Int: 853 return &ast.BasicLit{ 854 Kind: token.INT, 855 Value: v.ExactString(), 856 } 857 858 case constant.Float: 859 return &ast.BasicLit{ 860 Kind: token.FLOAT, 861 Value: v.ExactString(), 862 } 863 864 case constant.Complex: 865 // The components could be float or int. 866 y := makeLiteral(constant.Imag(v)) 867 y.(*ast.BasicLit).Value += "i" // ugh 868 if re := constant.Real(v); !consteq(re, kZeroInt) { 869 // complex: x + yi 870 y = &ast.BinaryExpr{ 871 Op: token.ADD, 872 X: makeLiteral(re), 873 Y: y, 874 } 875 } 876 return y 877 878 default: 879 panic(v.Kind()) 880 } 881 } 882 883 func makeIntLit(x int64) *ast.BasicLit { 884 return &ast.BasicLit{ 885 Kind: token.INT, 886 Value: strconv.FormatInt(x, 10), 887 } 888 } 889 890 func isBasic(t types.Type, info types.BasicInfo) bool { 891 basic, ok := t.Underlying().(*types.Basic) 892 return ok && basic.Info()&info != 0 893 }