cuelang.org/go@v0.10.1/internal/golangorgx/tools/refactor/inline/inline.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 import ( 8 "bytes" 9 "fmt" 10 "go/ast" 11 "go/constant" 12 "go/format" 13 "go/parser" 14 "go/token" 15 "go/types" 16 pathpkg "path" 17 "reflect" 18 "strconv" 19 "strings" 20 21 internalastutil "cuelang.org/go/internal/golangorgx/tools/astutil" 22 "cuelang.org/go/internal/golangorgx/tools/typeparams" 23 "golang.org/x/tools/go/ast/astutil" 24 "golang.org/x/tools/go/types/typeutil" 25 "golang.org/x/tools/imports" 26 ) 27 28 // A Caller describes the function call and its enclosing context. 29 // 30 // The client is responsible for populating this struct and passing it to Inline. 31 type Caller struct { 32 Fset *token.FileSet 33 Types *types.Package 34 Info *types.Info 35 File *ast.File 36 Call *ast.CallExpr 37 Content []byte // source of file containing 38 39 path []ast.Node // path from call to root of file syntax tree 40 enclosingFunc *ast.FuncDecl // top-level function/method enclosing the call, if any 41 } 42 43 // Inline inlines the called function (callee) into the function call (caller) 44 // and returns the updated, formatted content of the caller source file. 45 // 46 // Inline does not mutate any public fields of Caller or Callee. 47 // 48 // The log records the decision-making process. 49 // 50 // TODO(adonovan): provide an API for clients that want structured 51 // output: a list of import additions and deletions plus one or more 52 // localized diffs (or even AST transformations, though ownership and 53 // mutation are tricky) near the call site. 54 func Inline(logf func(string, ...any), caller *Caller, callee *Callee) ([]byte, error) { 55 logf("inline %s @ %v", 56 debugFormatNode(caller.Fset, caller.Call), 57 caller.Fset.PositionFor(caller.Call.Lparen, false)) 58 59 if !consistentOffsets(caller) { 60 return nil, fmt.Errorf("internal error: caller syntax positions are inconsistent with file content (did you forget to use FileSet.PositionFor when computing the file name?)") 61 } 62 63 // TODO(adonovan): use go1.21's ast.IsGenerated. 64 // Break the string literal so we can use inlining in this file. :) 65 if bytes.Contains(caller.Content, []byte("// Code generated by "+"cmd/cgo; DO NOT EDIT.")) { 66 return nil, fmt.Errorf("cannot inline calls from files that import \"C\"") 67 } 68 69 res, err := inline(logf, caller, &callee.impl) 70 if err != nil { 71 return nil, err 72 } 73 74 // Replace the call (or some node that encloses it) by new syntax. 75 assert(res.old != nil, "old is nil") 76 assert(res.new != nil, "new is nil") 77 78 // A single return operand inlined to a unary 79 // expression context may need parens. Otherwise: 80 // func two() int { return 1+1 } 81 // print(-two()) => print(-1+1) // oops! 82 // 83 // Usually it is not necessary to insert ParenExprs 84 // as the formatter is smart enough to insert them as 85 // needed by the context. But the res.{old,new} 86 // substitution is done by formatting res.new in isolation 87 // and then splicing its text over res.old, so the 88 // formatter doesn't see the parent node and cannot do 89 // the right thing. (One solution would be to always 90 // format the enclosing node of old, but that requires 91 // non-lossy comment handling, #20744.) 92 // 93 // So, we must analyze the call's context 94 // to see whether ambiguity is possible. 95 // For example, if the context is x[y:z], then 96 // the x subtree is subject to precedence ambiguity 97 // (replacing x by p+q would give p+q[y:z] which is wrong) 98 // but the y and z subtrees are safe. 99 if needsParens(caller.path, res.old, res.new) { 100 res.new = &ast.ParenExpr{X: res.new.(ast.Expr)} 101 } 102 103 // Some reduction strategies return a new block holding the 104 // callee's statements. The block's braces may be elided when 105 // there is no conflict between names declared in the block 106 // with those declared by the parent block, and no risk of 107 // a caller's goto jumping forward across a declaration. 108 // 109 // This elision is only safe when the ExprStmt is beneath a 110 // BlockStmt, CaseClause.Body, or CommClause.Body; 111 // (see "statement theory"). 112 elideBraces := false 113 if newBlock, ok := res.new.(*ast.BlockStmt); ok { 114 i := nodeIndex(caller.path, res.old) 115 parent := caller.path[i+1] 116 var body []ast.Stmt 117 switch parent := parent.(type) { 118 case *ast.BlockStmt: 119 body = parent.List 120 case *ast.CommClause: 121 body = parent.Body 122 case *ast.CaseClause: 123 body = parent.Body 124 } 125 if body != nil { 126 callerNames := declares(body) 127 128 // If BlockStmt is a function body, 129 // include its receiver, params, and results. 130 addFieldNames := func(fields *ast.FieldList) { 131 if fields != nil { 132 for _, field := range fields.List { 133 for _, id := range field.Names { 134 callerNames[id.Name] = true 135 } 136 } 137 } 138 } 139 switch f := caller.path[i+2].(type) { 140 case *ast.FuncDecl: 141 addFieldNames(f.Recv) 142 addFieldNames(f.Type.Params) 143 addFieldNames(f.Type.Results) 144 case *ast.FuncLit: 145 addFieldNames(f.Type.Params) 146 addFieldNames(f.Type.Results) 147 } 148 149 if len(callerLabels(caller.path)) > 0 { 150 // TODO(adonovan): be more precise and reject 151 // only forward gotos across the inlined block. 152 logf("keeping block braces: caller uses control labels") 153 } else if intersects(declares(newBlock.List), callerNames) { 154 logf("keeping block braces: avoids name conflict") 155 } else { 156 elideBraces = true 157 } 158 } 159 } 160 161 // Don't call replaceNode(caller.File, res.old, res.new) 162 // as it mutates the caller's syntax tree. 163 // Instead, splice the file, replacing the extent of the "old" 164 // node by a formatting of the "new" node, and re-parse. 165 // We'll fix up the imports on this new tree, and format again. 166 var f *ast.File 167 { 168 start := offsetOf(caller.Fset, res.old.Pos()) 169 end := offsetOf(caller.Fset, res.old.End()) 170 var out bytes.Buffer 171 out.Write(caller.Content[:start]) 172 // TODO(adonovan): might it make more sense to use 173 // callee.Fset when formatting res.new? 174 // The new tree is a mix of (cloned) caller nodes for 175 // the argument expressions and callee nodes for the 176 // function body. In essence the question is: which 177 // is more likely to have comments? 178 // Usually the callee body will be larger and more 179 // statement-heavy than the the arguments, but a 180 // strategy may widen the scope of the replacement 181 // (res.old) from CallExpr to, say, its enclosing 182 // block, so the caller nodes dominate. 183 // Precise comment handling would make this a 184 // non-issue. Formatting wouldn't really need a 185 // FileSet at all. 186 if elideBraces { 187 for i, stmt := range res.new.(*ast.BlockStmt).List { 188 if i > 0 { 189 out.WriteByte('\n') 190 } 191 if err := format.Node(&out, caller.Fset, stmt); err != nil { 192 return nil, err 193 } 194 } 195 } else { 196 if err := format.Node(&out, caller.Fset, res.new); err != nil { 197 return nil, err 198 } 199 } 200 out.Write(caller.Content[end:]) 201 const mode = parser.ParseComments | parser.SkipObjectResolution | parser.AllErrors 202 f, err = parser.ParseFile(caller.Fset, "callee.go", &out, mode) 203 if err != nil { 204 // Something has gone very wrong. 205 logf("failed to parse <<%s>>", &out) // debugging 206 return nil, err 207 } 208 } 209 210 // Add new imports. 211 // 212 // Insert new imports after last existing import, 213 // to avoid migration of pre-import comments. 214 // The imports will be organized below. 215 if len(res.newImports) > 0 { 216 var importDecl *ast.GenDecl 217 if len(f.Imports) > 0 { 218 // Append specs to existing import decl 219 importDecl = f.Decls[0].(*ast.GenDecl) 220 } else { 221 // Insert new import decl. 222 importDecl = &ast.GenDecl{Tok: token.IMPORT} 223 f.Decls = prepend[ast.Decl](importDecl, f.Decls...) 224 } 225 for _, imp := range res.newImports { 226 // Check that the new imports are accessible. 227 path, _ := strconv.Unquote(imp.spec.Path.Value) 228 if !canImport(caller.Types.Path(), path) { 229 return nil, fmt.Errorf("can't inline function %v as its body refers to inaccessible package %q", callee, path) 230 } 231 importDecl.Specs = append(importDecl.Specs, imp.spec) 232 } 233 } 234 235 var out bytes.Buffer 236 if err := format.Node(&out, caller.Fset, f); err != nil { 237 return nil, err 238 } 239 newSrc := out.Bytes() 240 241 // Remove imports that are no longer referenced. 242 // 243 // It ought to be possible to compute the set of PkgNames used 244 // by the "old" code, compute the free identifiers of the 245 // "new" code using a syntax-only (no go/types) algorithm, and 246 // see if the reduction in the number of uses of any PkgName 247 // equals the number of times it appears in caller.Info.Uses, 248 // indicating that it is no longer referenced by res.new. 249 // 250 // However, the notorious ambiguity of resolving T{F: 0} makes this 251 // unreliable: without types, we can't tell whether F refers to 252 // a field of struct T, or a package-level const/var of a 253 // dot-imported (!) package. 254 // 255 // So, for now, we run imports.Process, which is 256 // unsatisfactory as it has to run the go command, and it 257 // looks at the user's module cache state--unnecessarily, 258 // since this step cannot add new imports. 259 // 260 // TODO(adonovan): replace with a simpler implementation since 261 // all the necessary imports are present but merely untidy. 262 // That will be faster, and also less prone to nondeterminism 263 // if there are bugs in our logic for import maintenance. 264 // 265 // However, cuelang.org/go/internal/golangorgx/tools/imports.ApplyFixes is 266 // too simple as it requires the caller to have figured out 267 // all the logical edits. In our case, we know all the new 268 // imports that are needed (see newImports), each of which can 269 // be specified as: 270 // 271 // &imports.ImportFix{ 272 // StmtInfo: imports.ImportInfo{path, name, 273 // IdentName: name, 274 // FixType: imports.AddImport, 275 // } 276 // 277 // but we don't know which imports are made redundant by the 278 // inlining itself. For example, inlining a call to 279 // fmt.Println may make the "fmt" import redundant. 280 // 281 // Also, both imports.Process and internal/imports.ApplyFixes 282 // reformat the entire file, which is not ideal for clients 283 // such as gopls. (That said, the point of a canonical format 284 // is arguably that any tool can reformat as needed without 285 // this being inconvenient.) 286 // 287 // We could invoke imports.Process and parse its result, 288 // compare against the original AST, compute a list of import 289 // fixes, and return that too. 290 291 // Recompute imports only if there were existing ones. 292 if len(f.Imports) > 0 { 293 formatted, err := imports.Process("output", newSrc, nil) 294 if err != nil { 295 logf("cannot reformat: %v <<%s>>", err, &out) 296 return nil, err // cannot reformat (a bug?) 297 } 298 newSrc = formatted 299 } 300 return newSrc, nil 301 } 302 303 type newImport struct { 304 pkgName string 305 spec *ast.ImportSpec 306 } 307 308 type result struct { 309 newImports []newImport 310 old, new ast.Node // e.g. replace call expr by callee function body expression 311 } 312 313 // inline returns a pair of an old node (the call, or something 314 // enclosing it) and a new node (its replacement, which may be a 315 // combination of caller, callee, and new nodes), along with the set 316 // of new imports needed. 317 // 318 // TODO(adonovan): rethink the 'result' interface. The assumption of a 319 // one-to-one replacement seems fragile. One can easily imagine the 320 // transformation replacing the call and adding new variable 321 // declarations, for example, or replacing a call statement by zero or 322 // many statements.) 323 // 324 // TODO(adonovan): in earlier drafts, the transformation was expressed 325 // by splicing substrings of the two source files because syntax 326 // trees don't preserve comments faithfully (see #20744), but such 327 // transformations don't compose. The current implementation is 328 // tree-based but is very lossy wrt comments. It would make a good 329 // candidate for evaluating an alternative fully self-contained tree 330 // representation, such as any proposed solution to #20744, or even 331 // dst or some private fork of go/ast.) 332 func inline(logf func(string, ...any), caller *Caller, callee *gobCallee) (*result, error) { 333 checkInfoFields(caller.Info) 334 335 // Inlining of dynamic calls is not currently supported, 336 // even for local closure calls. (This would be a lot of work.) 337 calleeSymbol := typeutil.StaticCallee(caller.Info, caller.Call) 338 if calleeSymbol == nil { 339 // e.g. interface method 340 return nil, fmt.Errorf("cannot inline: not a static function call") 341 } 342 343 // Reject cross-package inlining if callee has 344 // free references to unexported symbols. 345 samePkg := caller.Types.Path() == callee.PkgPath 346 if !samePkg && len(callee.Unexported) > 0 { 347 return nil, fmt.Errorf("cannot inline call to %s because body refers to non-exported %s", 348 callee.Name, callee.Unexported[0]) 349 } 350 351 // -- analyze callee's free references in caller context -- 352 353 // Compute syntax path enclosing Call, innermost first (Path[0]=Call), 354 // and outermost enclosing function, if any. 355 caller.path, _ = astutil.PathEnclosingInterval(caller.File, caller.Call.Pos(), caller.Call.End()) 356 for _, n := range caller.path { 357 if decl, ok := n.(*ast.FuncDecl); ok { 358 caller.enclosingFunc = decl 359 break 360 } 361 } 362 363 // If call is within a function, analyze all its 364 // local vars for the "single assignment" property. 365 // (Taking the address &v counts as a potential assignment.) 366 var assign1 func(v *types.Var) bool // reports whether v a single-assignment local var 367 { 368 updatedLocals := make(map[*types.Var]bool) 369 if caller.enclosingFunc != nil { 370 escape(caller.Info, caller.enclosingFunc, func(v *types.Var, _ bool) { 371 updatedLocals[v] = true 372 }) 373 logf("multiple-assignment vars: %v", updatedLocals) 374 } 375 assign1 = func(v *types.Var) bool { return !updatedLocals[v] } 376 } 377 378 // import map, initially populated with caller imports. 379 // 380 // For simplicity we ignore existing dot imports, so that a 381 // qualified identifier (QI) in the callee is always 382 // represented by a QI in the caller, allowing us to treat a 383 // QI like a selection on a package name. 384 importMap := make(map[string][]string) // maps package path to local name(s) 385 for _, imp := range caller.File.Imports { 386 if pkgname, ok := importedPkgName(caller.Info, imp); ok && 387 pkgname.Name() != "." && 388 pkgname.Name() != "_" { 389 path := pkgname.Imported().Path() 390 importMap[path] = append(importMap[path], pkgname.Name()) 391 } 392 } 393 394 // localImportName returns the local name for a given imported package path. 395 var newImports []newImport 396 localImportName := func(obj *object) string { 397 // Does an import exist? 398 for _, name := range importMap[obj.PkgPath] { 399 // Check that either the import preexisted, 400 // or that it was newly added (no PkgName) but is not shadowed, 401 // either in the callee (shadows) or caller (caller.lookup). 402 if !obj.Shadow[name] { 403 found := caller.lookup(name) 404 if is[*types.PkgName](found) || found == nil { 405 return name 406 } 407 } 408 } 409 410 newlyAdded := func(name string) bool { 411 for _, new := range newImports { 412 if new.pkgName == name { 413 return true 414 } 415 } 416 return false 417 } 418 419 // import added by callee 420 // 421 // Choose local PkgName based on last segment of 422 // package path plus, if needed, a numeric suffix to 423 // ensure uniqueness. 424 // 425 // "init" is not a legal PkgName. 426 // 427 // TODO(rfindley): is it worth preserving local package names for callee 428 // imports? Are they likely to be better or worse than the name we choose 429 // here? 430 base := obj.PkgName 431 name := base 432 for n := 0; obj.Shadow[name] || caller.lookup(name) != nil || newlyAdded(name) || name == "init"; n++ { 433 name = fmt.Sprintf("%s%d", base, n) 434 } 435 436 logf("adding import %s %q", name, obj.PkgPath) 437 spec := &ast.ImportSpec{ 438 Path: &ast.BasicLit{ 439 Kind: token.STRING, 440 Value: strconv.Quote(obj.PkgPath), 441 }, 442 } 443 // Use explicit pkgname (out of necessity) when it differs from the declared name, 444 // or (for good style) when it differs from base(pkgpath). 445 if name != obj.PkgName || name != pathpkg.Base(obj.PkgPath) { 446 spec.Name = makeIdent(name) 447 } 448 newImports = append(newImports, newImport{ 449 pkgName: name, 450 spec: spec, 451 }) 452 importMap[obj.PkgPath] = append(importMap[obj.PkgPath], name) 453 return name 454 } 455 456 // Compute the renaming of the callee's free identifiers. 457 objRenames := make([]ast.Expr, len(callee.FreeObjs)) // nil => no change 458 for i, obj := range callee.FreeObjs { 459 // obj is a free object of the callee. 460 // 461 // Possible cases are: 462 // - builtin function, type, or value (e.g. nil, zero) 463 // => check not shadowed in caller. 464 // - package-level var/func/const/types 465 // => same package: check not shadowed in caller. 466 // => otherwise: import other package, form a qualified identifier. 467 // (Unexported cross-package references were rejected already.) 468 // - type parameter 469 // => not yet supported 470 // - pkgname 471 // => import other package and use its local name. 472 // 473 // There can be no free references to labels, fields, or methods. 474 475 // Note that we must consider potential shadowing both 476 // at the caller side (caller.lookup) and, when 477 // choosing new PkgNames, within the callee (obj.shadow). 478 479 var newName ast.Expr 480 if obj.Kind == "pkgname" { 481 // Use locally appropriate import, creating as needed. 482 newName = makeIdent(localImportName(&obj)) // imported package 483 } else if !obj.ValidPos { 484 // Built-in function, type, or value (e.g. nil, zero): 485 // check not shadowed at caller. 486 found := caller.lookup(obj.Name) // always finds something 487 if found.Pos().IsValid() { 488 return nil, fmt.Errorf("cannot inline, because the callee refers to built-in %q, which in the caller is shadowed by a %s (declared at line %d)", 489 obj.Name, objectKind(found), 490 caller.Fset.PositionFor(found.Pos(), false).Line) 491 } 492 493 } else { 494 // Must be reference to package-level var/func/const/type, 495 // since type parameters are not yet supported. 496 qualify := false 497 if obj.PkgPath == callee.PkgPath { 498 // reference within callee package 499 if samePkg { 500 // Caller and callee are in same package. 501 // Check caller has not shadowed the decl. 502 // 503 // This may fail if the callee is "fake", such as for signature 504 // refactoring where the callee is modified to be a trivial wrapper 505 // around the refactored signature. 506 found := caller.lookup(obj.Name) 507 if found != nil && !isPkgLevel(found) { 508 return nil, fmt.Errorf("cannot inline, because the callee refers to %s %q, which in the caller is shadowed by a %s (declared at line %d)", 509 obj.Kind, obj.Name, 510 objectKind(found), 511 caller.Fset.PositionFor(found.Pos(), false).Line) 512 } 513 } else { 514 // Cross-package reference. 515 qualify = true 516 } 517 } else { 518 // Reference to a package-level declaration 519 // in another package, without a qualified identifier: 520 // it must be a dot import. 521 qualify = true 522 } 523 524 // Form a qualified identifier, pkg.Name. 525 if qualify { 526 pkgName := localImportName(&obj) 527 newName = &ast.SelectorExpr{ 528 X: makeIdent(pkgName), 529 Sel: makeIdent(obj.Name), 530 } 531 } 532 } 533 objRenames[i] = newName 534 } 535 536 res := &result{ 537 newImports: newImports, 538 } 539 540 // Parse callee function declaration. 541 calleeFset, calleeDecl, err := parseCompact(callee.Content) 542 if err != nil { 543 return nil, err // "can't happen" 544 } 545 546 // replaceCalleeID replaces an identifier in the callee. 547 // The replacement tree must not belong to the caller; use cloneNode as needed. 548 replaceCalleeID := func(offset int, repl ast.Expr) { 549 id := findIdent(calleeDecl, calleeDecl.Pos()+token.Pos(offset)) 550 logf("- replace id %q @ #%d to %q", id.Name, offset, debugFormatNode(calleeFset, repl)) 551 replaceNode(calleeDecl, id, repl) 552 } 553 554 // Generate replacements for each free identifier. 555 // (The same tree may be spliced in multiple times, resulting in a DAG.) 556 for _, ref := range callee.FreeRefs { 557 if repl := objRenames[ref.Object]; repl != nil { 558 replaceCalleeID(ref.Offset, repl) 559 } 560 } 561 562 // Gather the effective call arguments, including the receiver. 563 // Later, elements will be eliminated (=> nil) by parameter substitution. 564 args, err := arguments(caller, calleeDecl, assign1) 565 if err != nil { 566 return nil, err // e.g. implicit field selection cannot be made explicit 567 } 568 569 // Gather effective parameter tuple, including the receiver if any. 570 // Simplify variadic parameters to slices (in all cases but one). 571 var params []*parameter // including receiver; nil => parameter substituted 572 { 573 sig := calleeSymbol.Type().(*types.Signature) 574 if sig.Recv() != nil { 575 params = append(params, ¶meter{ 576 obj: sig.Recv(), 577 fieldType: calleeDecl.Recv.List[0].Type, 578 info: callee.Params[0], 579 }) 580 } 581 582 // Flatten the list of syntactic types. 583 var types []ast.Expr 584 for _, field := range calleeDecl.Type.Params.List { 585 if field.Names == nil { 586 types = append(types, field.Type) 587 } else { 588 for range field.Names { 589 types = append(types, field.Type) 590 } 591 } 592 } 593 594 for i := 0; i < sig.Params().Len(); i++ { 595 params = append(params, ¶meter{ 596 obj: sig.Params().At(i), 597 fieldType: types[i], 598 info: callee.Params[len(params)], 599 }) 600 } 601 602 // Variadic function? 603 // 604 // There are three possible types of call: 605 // - ordinary f(a1, ..., aN) 606 // - ellipsis f(a1, ..., slice...) 607 // - spread f(recv?, g()) where g() is a tuple. 608 // The first two are desugared to non-variadic calls 609 // with an ordinary slice parameter; 610 // the third is tricky and cannot be reduced, and (if 611 // a receiver is present) cannot even be literalized. 612 // Fortunately it is vanishingly rare. 613 // 614 // TODO(adonovan): extract this to a function. 615 if sig.Variadic() { 616 lastParam := last(params) 617 if len(args) > 0 && last(args).spread { 618 // spread call to variadic: tricky 619 lastParam.variadic = true 620 } else { 621 // ordinary/ellipsis call to variadic 622 623 // simplify decl: func(T...) -> func([]T) 624 lastParamField := last(calleeDecl.Type.Params.List) 625 lastParamField.Type = &ast.ArrayType{ 626 Elt: lastParamField.Type.(*ast.Ellipsis).Elt, 627 } 628 629 if caller.Call.Ellipsis.IsValid() { 630 // ellipsis call: f(slice...) -> f(slice) 631 // nop 632 } else { 633 // ordinary call: f(a1, ... aN) -> f([]T{a1, ..., aN}) 634 n := len(params) - 1 635 ordinary, extra := args[:n], args[n:] 636 var elts []ast.Expr 637 pure, effects := true, false 638 for _, arg := range extra { 639 elts = append(elts, arg.expr) 640 pure = pure && arg.pure 641 effects = effects || arg.effects 642 } 643 args = append(ordinary, &argument{ 644 expr: &ast.CompositeLit{ 645 Type: lastParamField.Type, 646 Elts: elts, 647 }, 648 typ: lastParam.obj.Type(), 649 constant: nil, 650 pure: pure, 651 effects: effects, 652 duplicable: false, 653 freevars: nil, // not needed 654 }) 655 } 656 } 657 } 658 } 659 660 // Log effective arguments. 661 for i, arg := range args { 662 logf("arg #%d: %s pure=%t effects=%t duplicable=%t free=%v type=%v", 663 i, debugFormatNode(caller.Fset, arg.expr), 664 arg.pure, arg.effects, arg.duplicable, arg.freevars, arg.typ) 665 } 666 667 // Note: computation below should be expressed in terms of 668 // the args and params slices, not the raw material. 669 670 // Perform parameter substitution. 671 // May eliminate some elements of params/args. 672 substitute(logf, caller, params, args, callee.Effects, callee.Falcon, replaceCalleeID) 673 674 // Update the callee's signature syntax. 675 updateCalleeParams(calleeDecl, params) 676 677 // Create a var (param = arg; ...) decl for use by some strategies. 678 bindingDeclStmt := createBindingDecl(logf, caller, args, calleeDecl, callee.Results) 679 680 var remainingArgs []ast.Expr 681 for _, arg := range args { 682 if arg != nil { 683 remainingArgs = append(remainingArgs, arg.expr) 684 } 685 } 686 687 // -- let the inlining strategies begin -- 688 // 689 // When we commit to a strategy, we log a message of the form: 690 // 691 // "strategy: reduce expr-context call to { return expr }" 692 // 693 // This is a terse way of saying: 694 // 695 // we plan to reduce a call 696 // that appears in expression context 697 // to a function whose body is of the form { return expr } 698 699 // TODO(adonovan): split this huge function into a sequence of 700 // function calls with an error sentinel that means "try the 701 // next strategy", and make sure each strategy writes to the 702 // log the reason it didn't match. 703 704 // Special case: eliminate a call to a function whose body is empty. 705 // (=> callee has no results and caller is a statement.) 706 // 707 // func f(params) {} 708 // f(args) 709 // => _, _ = args 710 // 711 if len(calleeDecl.Body.List) == 0 { 712 logf("strategy: reduce call to empty body") 713 714 // Evaluate the arguments for effects and delete the call entirely. 715 stmt := callStmt(caller.path, false) // cannot fail 716 res.old = stmt 717 if nargs := len(remainingArgs); nargs > 0 { 718 // Emit "_, _ = args" to discard results. 719 720 // TODO(adonovan): if args is the []T{a1, ..., an} 721 // literal synthesized during variadic simplification, 722 // consider unwrapping it to its (pure) elements. 723 // Perhaps there's no harm doing this for any slice literal. 724 725 // Make correction for spread calls 726 // f(g()) or recv.f(g()) where g() is a tuple. 727 if last := last(args); last != nil && last.spread { 728 nspread := last.typ.(*types.Tuple).Len() 729 if len(args) > 1 { // [recv, g()] 730 // A single AssignStmt cannot discard both, so use a 2-spec var decl. 731 res.new = &ast.GenDecl{ 732 Tok: token.VAR, 733 Specs: []ast.Spec{ 734 &ast.ValueSpec{ 735 Names: []*ast.Ident{makeIdent("_")}, 736 Values: []ast.Expr{args[0].expr}, 737 }, 738 &ast.ValueSpec{ 739 Names: blanks[*ast.Ident](nspread), 740 Values: []ast.Expr{args[1].expr}, 741 }, 742 }, 743 } 744 return res, nil 745 } 746 747 // Sole argument is spread call. 748 nargs = nspread 749 } 750 751 res.new = &ast.AssignStmt{ 752 Lhs: blanks[ast.Expr](nargs), 753 Tok: token.ASSIGN, 754 Rhs: remainingArgs, 755 } 756 757 } else { 758 // No remaining arguments: delete call statement entirely 759 res.new = &ast.EmptyStmt{} 760 } 761 return res, nil 762 } 763 764 // If all parameters have been substituted and no result 765 // variable is referenced, we don't need a binding decl. 766 // This may enable better reduction strategies. 767 allResultsUnreferenced := forall(callee.Results, func(i int, r *paramInfo) bool { return len(r.Refs) == 0 }) 768 needBindingDecl := !allResultsUnreferenced || 769 exists(params, func(i int, p *parameter) bool { return p != nil }) 770 771 // The two strategies below overlap for a tail call of {return exprs}: 772 // The expr-context reduction is nice because it keeps the 773 // caller's return stmt and merely switches its operand, 774 // without introducing a new block, but it doesn't work with 775 // implicit return conversions. 776 // 777 // TODO(adonovan): unify these cases more cleanly, allowing return- 778 // operand replacement and implicit conversions, by adding 779 // conversions around each return operand (if not a spread return). 780 781 // Special case: call to { return exprs }. 782 // 783 // Reduces to: 784 // { var (bindings); _, _ = exprs } 785 // or _, _ = exprs 786 // or expr 787 // 788 // If: 789 // - the body is just "return expr" with trivial implicit conversions, 790 // or the caller's return type matches the callee's, 791 // - all parameters and result vars can be eliminated 792 // or replaced by a binding decl, 793 // then the call expression can be replaced by the 794 // callee's body expression, suitably substituted. 795 if len(calleeDecl.Body.List) == 1 && 796 is[*ast.ReturnStmt](calleeDecl.Body.List[0]) && 797 len(calleeDecl.Body.List[0].(*ast.ReturnStmt).Results) > 0 { // not a bare return 798 results := calleeDecl.Body.List[0].(*ast.ReturnStmt).Results 799 800 context := callContext(caller.path) 801 802 // statement context 803 if stmt, ok := context.(*ast.ExprStmt); ok && 804 (!needBindingDecl || bindingDeclStmt != nil) { 805 logf("strategy: reduce stmt-context call to { return exprs }") 806 clearPositions(calleeDecl.Body) 807 808 if callee.ValidForCallStmt { 809 logf("callee body is valid as statement") 810 // Inv: len(results) == 1 811 if !needBindingDecl { 812 // Reduces to: expr 813 res.old = caller.Call 814 res.new = results[0] 815 } else { 816 // Reduces to: { var (bindings); expr } 817 res.old = stmt 818 res.new = &ast.BlockStmt{ 819 List: []ast.Stmt{ 820 bindingDeclStmt, 821 &ast.ExprStmt{X: results[0]}, 822 }, 823 } 824 } 825 } else { 826 logf("callee body is not valid as statement") 827 // The call is a standalone statement, but the 828 // callee body is not suitable as a standalone statement 829 // (f() or <-ch), explicitly discard the results: 830 // Reduces to: _, _ = exprs 831 discard := &ast.AssignStmt{ 832 Lhs: blanks[ast.Expr](callee.NumResults), 833 Tok: token.ASSIGN, 834 Rhs: results, 835 } 836 res.old = stmt 837 if !needBindingDecl { 838 // Reduces to: _, _ = exprs 839 res.new = discard 840 } else { 841 // Reduces to: { var (bindings); _, _ = exprs } 842 res.new = &ast.BlockStmt{ 843 List: []ast.Stmt{ 844 bindingDeclStmt, 845 discard, 846 }, 847 } 848 } 849 } 850 return res, nil 851 } 852 853 // expression context 854 if !needBindingDecl { 855 clearPositions(calleeDecl.Body) 856 857 if callee.NumResults == 1 { 858 logf("strategy: reduce expr-context call to { return expr }") 859 // (includes some simple tail-calls) 860 861 // Make implicit return conversion explicit. 862 if callee.TrivialReturns < callee.TotalReturns { 863 results[0] = convert(calleeDecl.Type.Results.List[0].Type, results[0]) 864 } 865 866 res.old = caller.Call 867 res.new = results[0] 868 return res, nil 869 870 } else if callee.TrivialReturns == callee.TotalReturns { 871 logf("strategy: reduce spread-context call to { return expr }") 872 // There is no general way to reify conversions in a spread 873 // return, hence the requirement above. 874 // 875 // TODO(adonovan): allow this reduction when no 876 // conversion is required by the context. 877 878 // The call returns multiple results but is 879 // not a standalone call statement. It must 880 // be the RHS of a spread assignment: 881 // var x, y = f() 882 // x, y := f() 883 // x, y = f() 884 // or the sole argument to a spread call: 885 // printf(f()) 886 // or spread return statement: 887 // return f() 888 res.old = context 889 switch context := context.(type) { 890 case *ast.AssignStmt: 891 // Inv: the call must be in Rhs[0], not Lhs. 892 assign := shallowCopy(context) 893 assign.Rhs = results 894 res.new = assign 895 case *ast.ValueSpec: 896 // Inv: the call must be in Values[0], not Names. 897 spec := shallowCopy(context) 898 spec.Values = results 899 res.new = spec 900 case *ast.CallExpr: 901 // Inv: the call must be in Args[0], not Fun. 902 call := shallowCopy(context) 903 call.Args = results 904 res.new = call 905 case *ast.ReturnStmt: 906 // Inv: the call must be Results[0]. 907 ret := shallowCopy(context) 908 ret.Results = results 909 res.new = ret 910 default: 911 return nil, fmt.Errorf("internal error: unexpected context %T for spread call", context) 912 } 913 return res, nil 914 } 915 } 916 } 917 918 // Special case: tail-call. 919 // 920 // Inlining: 921 // return f(args) 922 // where: 923 // func f(params) (results) { body } 924 // reduces to: 925 // { var (bindings); body } 926 // { body } 927 // so long as: 928 // - all parameters can be eliminated or replaced by a binding decl, 929 // - call is a tail-call; 930 // - all returns in body have trivial result conversions, 931 // or the caller's return type matches the callee's, 932 // - there is no label conflict; 933 // - no result variable is referenced by name, 934 // or implicitly by a bare return. 935 // 936 // The body may use defer, arbitrary control flow, and 937 // multiple returns. 938 // 939 // TODO(adonovan): add a strategy for a 'void tail 940 // call', i.e. a call statement prior to an (explicit 941 // or implicit) return. 942 if ret, ok := callContext(caller.path).(*ast.ReturnStmt); ok && 943 len(ret.Results) == 1 && 944 tailCallSafeReturn(caller, calleeSymbol, callee) && 945 !callee.HasBareReturn && 946 (!needBindingDecl || bindingDeclStmt != nil) && 947 !hasLabelConflict(caller.path, callee.Labels) && 948 allResultsUnreferenced { 949 logf("strategy: reduce tail-call") 950 body := calleeDecl.Body 951 clearPositions(body) 952 if needBindingDecl { 953 body.List = prepend(bindingDeclStmt, body.List...) 954 } 955 res.old = ret 956 res.new = body 957 return res, nil 958 } 959 960 // Special case: call to void function 961 // 962 // Inlining: 963 // f(args) 964 // where: 965 // func f(params) { stmts } 966 // reduces to: 967 // { var (bindings); stmts } 968 // { stmts } 969 // so long as: 970 // - callee is a void function (no returns) 971 // - callee does not use defer 972 // - there is no label conflict between caller and callee 973 // - all parameters and result vars can be eliminated 974 // or replaced by a binding decl, 975 // - caller ExprStmt is in unrestricted statement context. 976 if stmt := callStmt(caller.path, true); stmt != nil && 977 (!needBindingDecl || bindingDeclStmt != nil) && 978 !callee.HasDefer && 979 !hasLabelConflict(caller.path, callee.Labels) && 980 callee.TotalReturns == 0 { 981 logf("strategy: reduce stmt-context call to { stmts }") 982 body := calleeDecl.Body 983 var repl ast.Stmt = body 984 clearPositions(repl) 985 if needBindingDecl { 986 body.List = prepend(bindingDeclStmt, body.List...) 987 } 988 res.old = stmt 989 res.new = repl 990 return res, nil 991 } 992 993 // TODO(adonovan): parameterless call to { stmts; return expr } 994 // from one of these contexts: 995 // x, y = f() 996 // x, y := f() 997 // var x, y = f() 998 // => 999 // var (x T1, y T2); { stmts; x, y = expr } 1000 // 1001 // Because the params are no longer declared simultaneously 1002 // we need to check that (for example) x ∉ freevars(T2), 1003 // in addition to the usual checks for arg/result conversions, 1004 // complex control, etc. 1005 // Also test cases where expr is an n-ary call (spread returns). 1006 1007 // Literalization isn't quite infallible. 1008 // Consider a spread call to a method in which 1009 // no parameters are eliminated, e.g. 1010 // new(T).f(g()) 1011 // where 1012 // func (recv *T) f(x, y int) { body } 1013 // func g() (int, int) 1014 // This would be literalized to: 1015 // func (recv *T, x, y int) { body }(new(T), g()), 1016 // which is not a valid argument list because g() must appear alone. 1017 // Reject this case for now. 1018 if len(args) == 2 && args[0] != nil && args[1] != nil && is[*types.Tuple](args[1].typ) { 1019 return nil, fmt.Errorf("can't yet inline spread call to method") 1020 } 1021 1022 // Infallible general case: literalization. 1023 // 1024 // func(params) { body }(args) 1025 // 1026 logf("strategy: literalization") 1027 funcLit := &ast.FuncLit{ 1028 Type: calleeDecl.Type, 1029 Body: calleeDecl.Body, 1030 } 1031 1032 // Literalization can still make use of a binding 1033 // decl as it gives a more natural reading order: 1034 // 1035 // func() { var params = args; body }() 1036 // 1037 // TODO(adonovan): relax the allResultsUnreferenced requirement 1038 // by adding a parameter-only (no named results) binding decl. 1039 if bindingDeclStmt != nil && allResultsUnreferenced { 1040 funcLit.Type.Params.List = nil 1041 remainingArgs = nil 1042 funcLit.Body.List = prepend(bindingDeclStmt, funcLit.Body.List...) 1043 } 1044 1045 // Emit a new call to a function literal in place of 1046 // the callee name, with appropriate replacements. 1047 newCall := &ast.CallExpr{ 1048 Fun: funcLit, 1049 Ellipsis: token.NoPos, // f(slice...) is always simplified 1050 Args: remainingArgs, 1051 } 1052 clearPositions(newCall.Fun) 1053 res.old = caller.Call 1054 res.new = newCall 1055 return res, nil 1056 } 1057 1058 type argument struct { 1059 expr ast.Expr 1060 typ types.Type // may be tuple for sole non-receiver arg in spread call 1061 constant constant.Value // value of argument if constant 1062 spread bool // final arg is call() assigned to multiple params 1063 pure bool // expr is pure (doesn't read variables) 1064 effects bool // expr has effects (updates variables) 1065 duplicable bool // expr may be duplicated 1066 freevars map[string]bool // free names of expr 1067 substitutable bool // is candidate for substitution 1068 } 1069 1070 // arguments returns the effective arguments of the call. 1071 // 1072 // If the receiver argument and parameter have 1073 // different pointerness, make the "&" or "*" explicit. 1074 // 1075 // Also, if x.f() is shorthand for promoted method x.y.f(), 1076 // make the .y explicit in T.f(x.y, ...). 1077 // 1078 // Beware that: 1079 // 1080 // - a method can only be called through a selection, but only 1081 // the first of these two forms needs special treatment: 1082 // 1083 // expr.f(args) -> ([&*]expr, args) MethodVal 1084 // T.f(recv, args) -> ( expr, args) MethodExpr 1085 // 1086 // - the presence of a value in receiver-position in the call 1087 // is a property of the caller, not the callee. A method 1088 // (calleeDecl.Recv != nil) may be called like an ordinary 1089 // function. 1090 // 1091 // - the types.Signatures seen by the caller (from 1092 // StaticCallee) and by the callee (from decl type) 1093 // differ in this case. 1094 // 1095 // In a spread call f(g()), the sole ordinary argument g(), 1096 // always last in args, has a tuple type. 1097 // 1098 // We compute type-based predicates like pure, duplicable, 1099 // freevars, etc, now, before we start modifying syntax. 1100 func arguments(caller *Caller, calleeDecl *ast.FuncDecl, assign1 func(*types.Var) bool) ([]*argument, error) { 1101 var args []*argument 1102 1103 callArgs := caller.Call.Args 1104 if calleeDecl.Recv != nil { 1105 sel := astutil.Unparen(caller.Call.Fun).(*ast.SelectorExpr) 1106 seln := caller.Info.Selections[sel] 1107 var recvArg ast.Expr 1108 switch seln.Kind() { 1109 case types.MethodVal: // recv.f(callArgs) 1110 recvArg = sel.X 1111 case types.MethodExpr: // T.f(recv, callArgs) 1112 recvArg = callArgs[0] 1113 callArgs = callArgs[1:] 1114 } 1115 if recvArg != nil { 1116 // Compute all the type-based predicates now, 1117 // before we start meddling with the syntax; 1118 // the meddling will update them. 1119 arg := &argument{ 1120 expr: recvArg, 1121 typ: caller.Info.TypeOf(recvArg), 1122 constant: caller.Info.Types[recvArg].Value, 1123 pure: pure(caller.Info, assign1, recvArg), 1124 effects: effects(caller.Info, recvArg), 1125 duplicable: duplicable(caller.Info, recvArg), 1126 freevars: freeVars(caller.Info, recvArg), 1127 } 1128 recvArg = nil // prevent accidental use 1129 1130 // Move receiver argument recv.f(args) to argument list f(&recv, args). 1131 args = append(args, arg) 1132 1133 // Make field selections explicit (recv.f -> recv.y.f), 1134 // updating arg.{expr,typ}. 1135 indices := seln.Index() 1136 for _, index := range indices[:len(indices)-1] { 1137 t := deref(arg.typ) 1138 fld := typeparams.CoreType(t).(*types.Struct).Field(index) 1139 if fld.Pkg() != caller.Types && !fld.Exported() { 1140 return nil, fmt.Errorf("in %s, implicit reference to unexported field .%s cannot be made explicit", 1141 debugFormatNode(caller.Fset, caller.Call.Fun), 1142 fld.Name()) 1143 } 1144 if isPointer(arg.typ) { 1145 arg.pure = false // implicit *ptr operation => impure 1146 } 1147 arg.expr = &ast.SelectorExpr{ 1148 X: arg.expr, 1149 Sel: makeIdent(fld.Name()), 1150 } 1151 arg.typ = fld.Type() 1152 arg.duplicable = false 1153 } 1154 1155 // Make * or & explicit. 1156 argIsPtr := isPointer(arg.typ) 1157 paramIsPtr := isPointer(seln.Obj().Type().Underlying().(*types.Signature).Recv().Type()) 1158 if !argIsPtr && paramIsPtr { 1159 // &recv 1160 arg.expr = &ast.UnaryExpr{Op: token.AND, X: arg.expr} 1161 arg.typ = types.NewPointer(arg.typ) 1162 } else if argIsPtr && !paramIsPtr { 1163 // *recv 1164 arg.expr = &ast.StarExpr{X: arg.expr} 1165 arg.typ = deref(arg.typ) 1166 arg.duplicable = false 1167 arg.pure = false 1168 } 1169 } 1170 } 1171 for _, expr := range callArgs { 1172 tv := caller.Info.Types[expr] 1173 args = append(args, &argument{ 1174 expr: expr, 1175 typ: tv.Type, 1176 constant: tv.Value, 1177 spread: is[*types.Tuple](tv.Type), // => last 1178 pure: pure(caller.Info, assign1, expr), 1179 effects: effects(caller.Info, expr), 1180 duplicable: duplicable(caller.Info, expr), 1181 freevars: freeVars(caller.Info, expr), 1182 }) 1183 } 1184 1185 // Re-typecheck each constant argument expression in a neutral context. 1186 // 1187 // In a call such as func(int16){}(1), the type checker infers 1188 // the type "int16", not "untyped int", for the argument 1, 1189 // because it has incorporated information from the left-hand 1190 // side of the assignment implicit in parameter passing, but 1191 // of course in a different context, the expression 1 may have 1192 // a different type. 1193 // 1194 // So, we must use CheckExpr to recompute the type of the 1195 // argument in a neutral context to find its inherent type. 1196 // (This is arguably a bug in go/types, but I'm pretty certain 1197 // I requested it be this way long ago... -adonovan) 1198 // 1199 // This is only needed for constants. Other implicit 1200 // assignment conversions, such as unnamed-to-named struct or 1201 // chan to <-chan, do not result in the type-checker imposing 1202 // the LHS type on the RHS value. 1203 for _, arg := range args { 1204 if arg.constant == nil { 1205 continue 1206 } 1207 info := &types.Info{Types: make(map[ast.Expr]types.TypeAndValue)} 1208 if err := types.CheckExpr(caller.Fset, caller.Types, caller.Call.Pos(), arg.expr, info); err != nil { 1209 return nil, err 1210 } 1211 arg.typ = info.TypeOf(arg.expr) 1212 } 1213 1214 return args, nil 1215 } 1216 1217 type parameter struct { 1218 obj *types.Var // parameter var from caller's signature 1219 fieldType ast.Expr // syntax of type, from calleeDecl.Type.{Recv,Params} 1220 info *paramInfo // information from AnalyzeCallee 1221 variadic bool // (final) parameter is unsimplified ...T 1222 } 1223 1224 // substitute implements parameter elimination by substitution. 1225 // 1226 // It considers each parameter and its corresponding argument in turn 1227 // and evaluate these conditions: 1228 // 1229 // - the parameter is neither address-taken nor assigned; 1230 // - the argument is pure; 1231 // - if the parameter refcount is zero, the argument must 1232 // not contain the last use of a local var; 1233 // - if the parameter refcount is > 1, the argument must be duplicable; 1234 // - the argument (or types.Default(argument) if it's untyped) has 1235 // the same type as the parameter. 1236 // 1237 // If all conditions are met then the parameter can be substituted and 1238 // each reference to it replaced by the argument. In that case, the 1239 // replaceCalleeID function is called for each reference to the 1240 // parameter, and is provided with its relative offset and replacement 1241 // expression (argument), and the corresponding elements of params and 1242 // args are replaced by nil. 1243 func substitute(logf func(string, ...any), caller *Caller, params []*parameter, args []*argument, effects []int, falcon falconResult, replaceCalleeID func(offset int, repl ast.Expr)) { 1244 // Inv: 1245 // in calls to variadic, len(args) >= len(params)-1 1246 // in spread calls to non-variadic, len(args) < len(params) 1247 // in spread calls to variadic, len(args) <= len(params) 1248 // (In spread calls len(args) = 1, or 2 if call has receiver.) 1249 // Non-spread variadics have been simplified away already, 1250 // so the args[i] lookup is safe if we stop after the spread arg. 1251 next: 1252 for i, param := range params { 1253 arg := args[i] 1254 // Check argument against parameter. 1255 // 1256 // Beware: don't use types.Info on arg since 1257 // the syntax may be synthetic (not created by parser) 1258 // and thus lacking positions and types; 1259 // do it earlier (see pure/duplicable/freevars). 1260 1261 if arg.spread { 1262 // spread => last argument, but not always last parameter 1263 logf("keeping param %q and following ones: argument %s is spread", 1264 param.info.Name, debugFormatNode(caller.Fset, arg.expr)) 1265 return // give up 1266 } 1267 assert(!param.variadic, "unsimplified variadic parameter") 1268 if param.info.Escapes { 1269 logf("keeping param %q: escapes from callee", param.info.Name) 1270 continue 1271 } 1272 if param.info.Assigned { 1273 logf("keeping param %q: assigned by callee", param.info.Name) 1274 continue // callee needs the parameter variable 1275 } 1276 if len(param.info.Refs) > 1 && !arg.duplicable { 1277 logf("keeping param %q: argument is not duplicable", param.info.Name) 1278 continue // incorrect or poor style to duplicate an expression 1279 } 1280 if len(param.info.Refs) == 0 { 1281 if arg.effects { 1282 logf("keeping param %q: though unreferenced, it has effects", param.info.Name) 1283 continue 1284 } 1285 1286 // If the caller is within a function body, 1287 // eliminating an unreferenced parameter might 1288 // remove the last reference to a caller local var. 1289 if caller.enclosingFunc != nil { 1290 for free := range arg.freevars { 1291 // TODO(rfindley): we can get this 100% right by looking for 1292 // references among other arguments which have non-zero references 1293 // within the callee. 1294 if v, ok := caller.lookup(free).(*types.Var); ok && within(v.Pos(), caller.enclosingFunc.Body) && !isUsedOutsideCall(caller, v) { 1295 logf("keeping param %q: arg contains perhaps the last reference to caller local %v @ %v", 1296 param.info.Name, v, caller.Fset.PositionFor(v.Pos(), false)) 1297 continue next 1298 } 1299 } 1300 } 1301 } 1302 1303 // Check for shadowing. 1304 // 1305 // Consider inlining a call f(z, 1) to 1306 // func f(x, y int) int { z := y; return x + y + z }: 1307 // we can't replace x in the body by z (or any 1308 // expression that has z as a free identifier) 1309 // because there's an intervening declaration of z 1310 // that would shadow the caller's one. 1311 for free := range arg.freevars { 1312 if param.info.Shadow[free] { 1313 logf("keeping param %q: cannot replace with argument as it has free ref to %s that is shadowed", param.info.Name, free) 1314 continue next // shadowing conflict 1315 } 1316 } 1317 1318 arg.substitutable = true // may be substituted, if effects permit 1319 } 1320 1321 // Reject constant arguments as substitution candidates 1322 // if they cause violation of falcon constraints. 1323 checkFalconConstraints(logf, params, args, falcon) 1324 1325 // As a final step, introduce bindings to resolve any 1326 // evaluation order hazards. This must be done last, as 1327 // additional subsequent bindings could introduce new hazards. 1328 resolveEffects(logf, args, effects) 1329 1330 // The remaining candidates are safe to substitute. 1331 for i, param := range params { 1332 if arg := args[i]; arg.substitutable { 1333 1334 // Wrap the argument in an explicit conversion if 1335 // substitution might materially change its type. 1336 // (We already did the necessary shadowing check 1337 // on the parameter type syntax.) 1338 // 1339 // This is only needed for substituted arguments. All 1340 // other arguments are given explicit types in either 1341 // a binding decl or when using the literalization 1342 // strategy. 1343 if len(param.info.Refs) > 0 && !trivialConversion(args[i].typ, params[i].obj) { 1344 arg.expr = convert(params[i].fieldType, arg.expr) 1345 logf("param %q: adding explicit %s -> %s conversion around argument", 1346 param.info.Name, args[i].typ, params[i].obj.Type()) 1347 } 1348 1349 // It is safe to substitute param and replace it with arg. 1350 // The formatter introduces parens as needed for precedence. 1351 // 1352 // Because arg.expr belongs to the caller, 1353 // we clone it before splicing it into the callee tree. 1354 logf("replacing parameter %q by argument %q", 1355 param.info.Name, debugFormatNode(caller.Fset, arg.expr)) 1356 for _, ref := range param.info.Refs { 1357 replaceCalleeID(ref, internalastutil.CloneNode(arg.expr).(ast.Expr)) 1358 } 1359 params[i] = nil // substituted 1360 args[i] = nil // substituted 1361 } 1362 } 1363 } 1364 1365 // isUsedOutsideCall reports whether v is used outside of caller.Call, within 1366 // the body of caller.enclosingFunc. 1367 func isUsedOutsideCall(caller *Caller, v *types.Var) bool { 1368 used := false 1369 ast.Inspect(caller.enclosingFunc.Body, func(n ast.Node) bool { 1370 if n == caller.Call { 1371 return false 1372 } 1373 switch n := n.(type) { 1374 case *ast.Ident: 1375 if use := caller.Info.Uses[n]; use == v { 1376 used = true 1377 } 1378 case *ast.FuncType: 1379 // All params are used. 1380 for _, fld := range n.Params.List { 1381 for _, n := range fld.Names { 1382 if def := caller.Info.Defs[n]; def == v { 1383 used = true 1384 } 1385 } 1386 } 1387 } 1388 return !used // keep going until we find a use 1389 }) 1390 return used 1391 } 1392 1393 // checkFalconConstraints checks whether constant arguments 1394 // are safe to substitute (e.g. s[i] -> ""[0] is not safe.) 1395 // 1396 // Any failed constraint causes us to reject all constant arguments as 1397 // substitution candidates (by clearing args[i].substitution=false). 1398 // 1399 // TODO(adonovan): we could obtain a finer result rejecting only the 1400 // freevars of each failed constraint, and processing constraints in 1401 // order of increasing arity, but failures are quite rare. 1402 func checkFalconConstraints(logf func(string, ...any), params []*parameter, args []*argument, falcon falconResult) { 1403 // Create a dummy package, as this is the only 1404 // way to create an environment for CheckExpr. 1405 pkg := types.NewPackage("falcon", "falcon") 1406 1407 // Declare types used by constraints. 1408 for _, typ := range falcon.Types { 1409 logf("falcon env: type %s %s", typ.Name, types.Typ[typ.Kind]) 1410 pkg.Scope().Insert(types.NewTypeName(token.NoPos, pkg, typ.Name, types.Typ[typ.Kind])) 1411 } 1412 1413 // Declared constants and variables for for parameters. 1414 nconst := 0 1415 for i, param := range params { 1416 name := param.info.Name 1417 if name == "" { 1418 continue // unreferenced 1419 } 1420 arg := args[i] 1421 if arg.constant != nil && arg.substitutable && param.info.FalconType != "" { 1422 t := pkg.Scope().Lookup(param.info.FalconType).Type() 1423 pkg.Scope().Insert(types.NewConst(token.NoPos, pkg, name, t, arg.constant)) 1424 logf("falcon env: const %s %s = %v", name, param.info.FalconType, arg.constant) 1425 nconst++ 1426 } else { 1427 pkg.Scope().Insert(types.NewVar(token.NoPos, pkg, name, arg.typ)) 1428 logf("falcon env: var %s %s", name, arg.typ) 1429 } 1430 } 1431 if nconst == 0 { 1432 return // nothing to do 1433 } 1434 1435 // Parse and evaluate the constraints in the environment. 1436 fset := token.NewFileSet() 1437 for _, falcon := range falcon.Constraints { 1438 expr, err := parser.ParseExprFrom(fset, "falcon", falcon, 0) 1439 if err != nil { 1440 panic(fmt.Sprintf("failed to parse falcon constraint %s: %v", falcon, err)) 1441 } 1442 if err := types.CheckExpr(fset, pkg, token.NoPos, expr, nil); err != nil { 1443 logf("falcon: constraint %s violated: %v", falcon, err) 1444 for j, arg := range args { 1445 if arg.constant != nil && arg.substitutable { 1446 logf("keeping param %q due falcon violation", params[j].info.Name) 1447 arg.substitutable = false 1448 } 1449 } 1450 break 1451 } 1452 logf("falcon: constraint %s satisfied", falcon) 1453 } 1454 } 1455 1456 // resolveEffects marks arguments as non-substitutable to resolve 1457 // hazards resulting from the callee evaluation order described by the 1458 // effects list. 1459 // 1460 // To do this, each argument is categorized as a read (R), write (W), 1461 // or pure. A hazard occurs when the order of evaluation of a W 1462 // changes with respect to any R or W. Pure arguments can be 1463 // effectively ignored, as they can be safely evaluated in any order. 1464 // 1465 // The callee effects list contains the index of each parameter in the 1466 // order it is first evaluated during execution of the callee. In 1467 // addition, the two special values R∞ and W∞ indicate the relative 1468 // position of the callee's first non-parameter read and its first 1469 // effects (or other unknown behavior). 1470 // For example, the list [0 2 1 R∞ 3 W∞] for func(a, b, c, d) 1471 // indicates that the callee referenced parameters a, c, and b, 1472 // followed by an arbitrary read, then parameter d, and finally 1473 // unknown behavior. 1474 // 1475 // When an argument is marked as not substitutable, we say that it is 1476 // 'bound', in the sense that its evaluation occurs in a binding decl 1477 // or literalized call. Such bindings always occur in the original 1478 // callee parameter order. 1479 // 1480 // In this context, "resolving hazards" means binding arguments so 1481 // that they are evaluated in a valid, hazard-free order. A trivial 1482 // solution to this problem would be to bind all arguments, but of 1483 // course that's not useful. The goal is to bind as few arguments as 1484 // possible. 1485 // 1486 // The algorithm proceeds by inspecting arguments in reverse parameter 1487 // order (right to left), preserving the invariant that every 1488 // higher-ordered argument is either already substituted or does not 1489 // need to be substituted. At each iteration, if there is an 1490 // evaluation hazard in the callee effects relative to the current 1491 // argument, the argument must be bound. Subsequently, if the argument 1492 // is bound for any reason, each lower-ordered argument must also be 1493 // bound if either the argument or lower-order argument is a 1494 // W---otherwise the binding itself would introduce a hazard. 1495 // 1496 // Thus, after each iteration, there are no hazards relative to the 1497 // current argument. Subsequent iterations cannot introduce hazards 1498 // with that argument because they can result only in additional 1499 // binding of lower-ordered arguments. 1500 func resolveEffects(logf func(string, ...any), args []*argument, effects []int) { 1501 effectStr := func(effects bool, idx int) string { 1502 i := fmt.Sprint(idx) 1503 if idx == len(args) { 1504 i = "∞" 1505 } 1506 return string("RW"[btoi(effects)]) + i 1507 } 1508 for i := len(args) - 1; i >= 0; i-- { 1509 argi := args[i] 1510 if argi.substitutable && !argi.pure { 1511 // i is not bound: check whether it must be bound due to hazards. 1512 idx := index(effects, i) 1513 if idx >= 0 { 1514 for _, j := range effects[:idx] { 1515 var ( 1516 ji int // effective param index 1517 jw bool // j is a write 1518 ) 1519 if j == winf || j == rinf { 1520 jw = j == winf 1521 ji = len(args) 1522 } else { 1523 jw = args[j].effects 1524 ji = j 1525 } 1526 if ji > i && (jw || argi.effects) { // out of order evaluation 1527 logf("binding argument %s: preceded by %s", 1528 effectStr(argi.effects, i), effectStr(jw, ji)) 1529 argi.substitutable = false 1530 break 1531 } 1532 } 1533 } 1534 } 1535 if !argi.substitutable { 1536 for j := 0; j < i; j++ { 1537 argj := args[j] 1538 if argj.pure { 1539 continue 1540 } 1541 if (argi.effects || argj.effects) && argj.substitutable { 1542 logf("binding argument %s: %s is bound", 1543 effectStr(argj.effects, j), effectStr(argi.effects, i)) 1544 argj.substitutable = false 1545 } 1546 } 1547 } 1548 } 1549 } 1550 1551 // updateCalleeParams updates the calleeDecl syntax to remove 1552 // substituted parameters and move the receiver (if any) to the head 1553 // of the ordinary parameters. 1554 func updateCalleeParams(calleeDecl *ast.FuncDecl, params []*parameter) { 1555 // The logic is fiddly because of the three forms of ast.Field: 1556 // 1557 // func(int), func(x int), func(x, y int) 1558 // 1559 // Also, ensure that all remaining parameters are named 1560 // to avoid a mix of named/unnamed when joining (recv, params...). 1561 // func (T) f(int, bool) -> (_ T, _ int, _ bool) 1562 // (Strictly, we need do this only for methods and only when 1563 // the namednesses of Recv and Params differ; that might be tidier.) 1564 1565 paramIdx := 0 // index in original parameter list (incl. receiver) 1566 var newParams []*ast.Field 1567 filterParams := func(field *ast.Field) { 1568 var names []*ast.Ident 1569 if field.Names == nil { 1570 // Unnamed parameter field (e.g. func f(int) 1571 if params[paramIdx] != nil { 1572 // Give it an explicit name "_" since we will 1573 // make the receiver (if any) a regular parameter 1574 // and one cannot mix named and unnamed parameters. 1575 names = append(names, makeIdent("_")) 1576 } 1577 paramIdx++ 1578 } else { 1579 // Named parameter field e.g. func f(x, y int) 1580 // Remove substituted parameters in place. 1581 // If all were substituted, delete field. 1582 for _, id := range field.Names { 1583 if pinfo := params[paramIdx]; pinfo != nil { 1584 // Rename unreferenced parameters with "_". 1585 // This is crucial for binding decls, since 1586 // unlike parameters, they are subject to 1587 // "unreferenced var" checks. 1588 if len(pinfo.info.Refs) == 0 { 1589 id = makeIdent("_") 1590 } 1591 names = append(names, id) 1592 } 1593 paramIdx++ 1594 } 1595 } 1596 if names != nil { 1597 newParams = append(newParams, &ast.Field{ 1598 Names: names, 1599 Type: field.Type, 1600 }) 1601 } 1602 } 1603 if calleeDecl.Recv != nil { 1604 filterParams(calleeDecl.Recv.List[0]) 1605 calleeDecl.Recv = nil 1606 } 1607 for _, field := range calleeDecl.Type.Params.List { 1608 filterParams(field) 1609 } 1610 calleeDecl.Type.Params.List = newParams 1611 } 1612 1613 // createBindingDecl constructs a "binding decl" that implements 1614 // parameter assignment and declares any named result variables 1615 // referenced by the callee. It returns nil if there were no 1616 // unsubstituted parameters. 1617 // 1618 // It may not always be possible to create the decl (e.g. due to 1619 // shadowing), in which case it also returns nil; but if it succeeds, 1620 // the declaration may be used by reduction strategies to relax the 1621 // requirement that all parameters have been substituted. 1622 // 1623 // For example, a call: 1624 // 1625 // f(a0, a1, a2) 1626 // 1627 // where: 1628 // 1629 // func f(p0, p1 T0, p2 T1) { body } 1630 // 1631 // reduces to: 1632 // 1633 // { 1634 // var ( 1635 // p0, p1 T0 = a0, a1 1636 // p2 T1 = a2 1637 // ) 1638 // body 1639 // } 1640 // 1641 // so long as p0, p1 ∉ freevars(T1) or freevars(a2), and so on, 1642 // because each spec is statically resolved in sequence and 1643 // dynamically assigned in sequence. By contrast, all 1644 // parameters are resolved simultaneously and assigned 1645 // simultaneously. 1646 // 1647 // The pX names should already be blank ("_") if the parameter 1648 // is unreferenced; this avoids "unreferenced local var" checks. 1649 // 1650 // Strategies may impose additional checks on return 1651 // conversions, labels, defer, etc. 1652 func createBindingDecl(logf func(string, ...any), caller *Caller, args []*argument, calleeDecl *ast.FuncDecl, results []*paramInfo) ast.Stmt { 1653 // Spread calls are tricky as they may not align with the 1654 // parameters' field groupings nor types. 1655 // For example, given 1656 // func g() (int, string) 1657 // the call 1658 // f(g()) 1659 // is legal with these decls of f: 1660 // func f(int, string) 1661 // func f(x, y any) 1662 // func f(x, y ...any) 1663 // TODO(adonovan): support binding decls for spread calls by 1664 // splitting parameter groupings as needed. 1665 if lastArg := last(args); lastArg != nil && lastArg.spread { 1666 logf("binding decls not yet supported for spread calls") 1667 return nil 1668 } 1669 1670 var ( 1671 specs []ast.Spec 1672 shadowed = make(map[string]bool) // names defined by previous specs 1673 ) 1674 // shadow reports whether any name referenced by spec is 1675 // shadowed by a name declared by a previous spec (since, 1676 // unlike parameters, each spec of a var decl is within the 1677 // scope of the previous specs). 1678 shadow := func(spec *ast.ValueSpec) bool { 1679 // Compute union of free names of type and values 1680 // and detect shadowing. Values is the arguments 1681 // (caller syntax), so we can use type info. 1682 // But Type is the untyped callee syntax, 1683 // so we have to use a syntax-only algorithm. 1684 free := make(map[string]bool) 1685 for _, value := range spec.Values { 1686 for name := range freeVars(caller.Info, value) { 1687 free[name] = true 1688 } 1689 } 1690 freeishNames(free, spec.Type) 1691 for name := range free { 1692 if shadowed[name] { 1693 logf("binding decl would shadow free name %q", name) 1694 return true 1695 } 1696 } 1697 for _, id := range spec.Names { 1698 if id.Name != "_" { 1699 shadowed[id.Name] = true 1700 } 1701 } 1702 return false 1703 } 1704 1705 // parameters 1706 // 1707 // Bind parameters that were not eliminated through 1708 // substitution. (Non-nil arguments correspond to the 1709 // remaining parameters in calleeDecl.) 1710 var values []ast.Expr 1711 for _, arg := range args { 1712 if arg != nil { 1713 values = append(values, arg.expr) 1714 } 1715 } 1716 for _, field := range calleeDecl.Type.Params.List { 1717 // Each field (param group) becomes a ValueSpec. 1718 spec := &ast.ValueSpec{ 1719 Names: field.Names, 1720 Type: field.Type, 1721 Values: values[:len(field.Names)], 1722 } 1723 values = values[len(field.Names):] 1724 if shadow(spec) { 1725 return nil 1726 } 1727 specs = append(specs, spec) 1728 } 1729 assert(len(values) == 0, "args/params mismatch") 1730 1731 // results 1732 // 1733 // Add specs to declare any named result 1734 // variables that are referenced by the body. 1735 if calleeDecl.Type.Results != nil { 1736 resultIdx := 0 1737 for _, field := range calleeDecl.Type.Results.List { 1738 if field.Names == nil { 1739 resultIdx++ 1740 continue // unnamed field 1741 } 1742 var names []*ast.Ident 1743 for _, id := range field.Names { 1744 if len(results[resultIdx].Refs) > 0 { 1745 names = append(names, id) 1746 } 1747 resultIdx++ 1748 } 1749 if len(names) > 0 { 1750 spec := &ast.ValueSpec{ 1751 Names: names, 1752 Type: field.Type, 1753 } 1754 if shadow(spec) { 1755 return nil 1756 } 1757 specs = append(specs, spec) 1758 } 1759 } 1760 } 1761 1762 if len(specs) == 0 { 1763 logf("binding decl not needed: all parameters substituted") 1764 return nil 1765 } 1766 1767 stmt := &ast.DeclStmt{ 1768 Decl: &ast.GenDecl{ 1769 Tok: token.VAR, 1770 Specs: specs, 1771 }, 1772 } 1773 logf("binding decl: %s", debugFormatNode(caller.Fset, stmt)) 1774 return stmt 1775 } 1776 1777 // lookup does a symbol lookup in the lexical environment of the caller. 1778 func (caller *Caller) lookup(name string) types.Object { 1779 pos := caller.Call.Pos() 1780 for _, n := range caller.path { 1781 if scope := scopeFor(caller.Info, n); scope != nil { 1782 if _, obj := scope.LookupParent(name, pos); obj != nil { 1783 return obj 1784 } 1785 } 1786 } 1787 return nil 1788 } 1789 1790 func scopeFor(info *types.Info, n ast.Node) *types.Scope { 1791 // The function body scope (containing not just params) 1792 // is associated with the function's type, not body. 1793 switch fn := n.(type) { 1794 case *ast.FuncDecl: 1795 n = fn.Type 1796 case *ast.FuncLit: 1797 n = fn.Type 1798 } 1799 return info.Scopes[n] 1800 } 1801 1802 // -- predicates over expressions -- 1803 1804 // freeVars returns the names of all free identifiers of e: 1805 // those lexically referenced by it but not defined within it. 1806 // (Fields and methods are not included.) 1807 func freeVars(info *types.Info, e ast.Expr) map[string]bool { 1808 free := make(map[string]bool) 1809 ast.Inspect(e, func(n ast.Node) bool { 1810 if id, ok := n.(*ast.Ident); ok { 1811 // The isField check is so that we don't treat T{f: 0} as a ref to f. 1812 if obj, ok := info.Uses[id]; ok && !within(obj.Pos(), e) && !isField(obj) { 1813 free[obj.Name()] = true 1814 } 1815 } 1816 return true 1817 }) 1818 return free 1819 } 1820 1821 // freeishNames computes an over-approximation to the free names 1822 // of the type syntax t, inserting values into the map. 1823 // 1824 // Because we don't have go/types annotations, we can't give an exact 1825 // result in all cases. In particular, an array type [n]T might have a 1826 // size such as unsafe.Sizeof(func() int{stmts...}()) and now the 1827 // precise answer depends upon all the statement syntax too. But that 1828 // never happens in practice. 1829 func freeishNames(free map[string]bool, t ast.Expr) { 1830 var visit func(n ast.Node) bool 1831 visit = func(n ast.Node) bool { 1832 switch n := n.(type) { 1833 case *ast.Ident: 1834 free[n.Name] = true 1835 1836 case *ast.SelectorExpr: 1837 ast.Inspect(n.X, visit) 1838 return false // don't visit .Sel 1839 1840 case *ast.Field: 1841 ast.Inspect(n.Type, visit) 1842 // Don't visit .Names: 1843 // FuncType parameters, interface methods, struct fields 1844 return false 1845 } 1846 return true 1847 } 1848 ast.Inspect(t, visit) 1849 } 1850 1851 // effects reports whether an expression might change the state of the 1852 // program (through function calls and channel receives) and affect 1853 // the evaluation of subsequent expressions. 1854 func effects(info *types.Info, expr ast.Expr) bool { 1855 effects := false 1856 ast.Inspect(expr, func(n ast.Node) bool { 1857 switch n := n.(type) { 1858 case *ast.FuncLit: 1859 return false // prune descent 1860 1861 case *ast.CallExpr: 1862 if info.Types[n.Fun].IsType() { 1863 // A conversion T(x) has only the effect of its operand. 1864 } else if !callsPureBuiltin(info, n) { 1865 // A handful of built-ins have no effect 1866 // beyond those of their arguments. 1867 // All other calls (including append, copy, recover) 1868 // have unknown effects. 1869 // 1870 // As with 'pure', there is room for 1871 // improvement by inspecting the callee. 1872 effects = true 1873 } 1874 1875 case *ast.UnaryExpr: 1876 if n.Op == token.ARROW { // <-ch 1877 effects = true 1878 } 1879 } 1880 return true 1881 }) 1882 return effects 1883 } 1884 1885 // pure reports whether an expression has the same result no matter 1886 // when it is executed relative to other expressions, so it can be 1887 // commuted with any other expression or statement without changing 1888 // its meaning. 1889 // 1890 // An expression is considered impure if it reads the contents of any 1891 // variable, with the exception of "single assignment" local variables 1892 // (as classified by the provided callback), which are never updated 1893 // after their initialization. 1894 // 1895 // Pure does not imply duplicable: for example, new(T) and T{} are 1896 // pure expressions but both return a different value each time they 1897 // are evaluated, so they are not safe to duplicate. 1898 // 1899 // Purity does not imply freedom from run-time panics. We assume that 1900 // target programs do not encounter run-time panics nor depend on them 1901 // for correct operation. 1902 // 1903 // TODO(adonovan): add unit tests of this function. 1904 func pure(info *types.Info, assign1 func(*types.Var) bool, e ast.Expr) bool { 1905 var pure func(e ast.Expr) bool 1906 pure = func(e ast.Expr) bool { 1907 switch e := e.(type) { 1908 case *ast.ParenExpr: 1909 return pure(e.X) 1910 1911 case *ast.Ident: 1912 if v, ok := info.Uses[e].(*types.Var); ok { 1913 // In general variables are impure 1914 // as they may be updated, but 1915 // single-assignment local variables 1916 // never change value. 1917 // 1918 // We assume all package-level variables 1919 // may be updated, but for non-exported 1920 // ones we could do better by analyzing 1921 // the complete package. 1922 return !isPkgLevel(v) && assign1(v) 1923 } 1924 1925 // All other kinds of reference are pure. 1926 return true 1927 1928 case *ast.FuncLit: 1929 // A function literal may allocate a closure that 1930 // references mutable variables, but mutation 1931 // cannot be observed without calling the function, 1932 // and calls are considered impure. 1933 return true 1934 1935 case *ast.BasicLit: 1936 return true 1937 1938 case *ast.UnaryExpr: // + - ! ^ & but not <- 1939 return e.Op != token.ARROW && pure(e.X) 1940 1941 case *ast.BinaryExpr: // arithmetic, shifts, comparisons, &&/|| 1942 return pure(e.X) && pure(e.Y) 1943 1944 case *ast.CallExpr: 1945 // A conversion is as pure as its operand. 1946 if info.Types[e.Fun].IsType() { 1947 return pure(e.Args[0]) 1948 } 1949 1950 // Calls to some built-ins are as pure as their arguments. 1951 if callsPureBuiltin(info, e) { 1952 for _, arg := range e.Args { 1953 if !pure(arg) { 1954 return false 1955 } 1956 } 1957 return true 1958 } 1959 1960 // All other calls are impure, so we can 1961 // reject them without even looking at e.Fun. 1962 // 1963 // More sophisticated analysis could infer purity in 1964 // commonly used functions such as strings.Contains; 1965 // perhaps we could offer the client a hook so that 1966 // go/analysis-based implementation could exploit the 1967 // results of a purity analysis. But that would make 1968 // the inliner's choices harder to explain. 1969 return false 1970 1971 case *ast.CompositeLit: 1972 // T{...} is as pure as its elements. 1973 for _, elt := range e.Elts { 1974 if kv, ok := elt.(*ast.KeyValueExpr); ok { 1975 if !pure(kv.Value) { 1976 return false 1977 } 1978 if id, ok := kv.Key.(*ast.Ident); ok { 1979 if v, ok := info.Uses[id].(*types.Var); ok && v.IsField() { 1980 continue // struct {field: value} 1981 } 1982 } 1983 // map/slice/array {key: value} 1984 if !pure(kv.Key) { 1985 return false 1986 } 1987 1988 } else if !pure(elt) { 1989 return false 1990 } 1991 } 1992 return true 1993 1994 case *ast.SelectorExpr: 1995 if seln, ok := info.Selections[e]; ok { 1996 1997 // See types.SelectionKind for background. 1998 switch seln.Kind() { 1999 case types.MethodExpr: 2000 // A method expression T.f acts like a 2001 // reference to a func decl, so it is pure. 2002 return true 2003 2004 case types.MethodVal, types.FieldVal: 2005 // A field or method selection x.f is pure 2006 // if x is pure and the selection does 2007 // not indirect a pointer. 2008 return !indirectSelection(seln) && pure(e.X) 2009 2010 default: 2011 panic(seln) 2012 } 2013 } else { 2014 // A qualified identifier is 2015 // treated like an unqualified one. 2016 return pure(e.Sel) 2017 } 2018 2019 case *ast.StarExpr: 2020 return false // *ptr depends on the state of the heap 2021 2022 default: 2023 return false 2024 } 2025 } 2026 return pure(e) 2027 } 2028 2029 // callsPureBuiltin reports whether call is a call of a built-in 2030 // function that is a pure computation over its operands (analogous to 2031 // a + operator). Because it does not depend on program state, it may 2032 // be evaluated at any point--though not necessarily at multiple 2033 // points (consider new, make). 2034 func callsPureBuiltin(info *types.Info, call *ast.CallExpr) bool { 2035 if id, ok := astutil.Unparen(call.Fun).(*ast.Ident); ok { 2036 if b, ok := info.ObjectOf(id).(*types.Builtin); ok { 2037 switch b.Name() { 2038 case "len", "cap", "complex", "imag", "real", "make", "new", "max", "min": 2039 return true 2040 } 2041 // Not: append clear close copy delete panic print println recover 2042 } 2043 } 2044 return false 2045 } 2046 2047 // duplicable reports whether it is appropriate for the expression to 2048 // be freely duplicated. 2049 // 2050 // Given the declaration 2051 // 2052 // func f(x T) T { return x + g() + x } 2053 // 2054 // an argument y is considered duplicable if we would wish to see a 2055 // call f(y) simplified to y+g()+y. This is true for identifiers, 2056 // integer literals, unary negation, and selectors x.f where x is not 2057 // a pointer. But we would not wish to duplicate expressions that: 2058 // - have side effects (e.g. nearly all calls), 2059 // - are not referentially transparent (e.g. &T{}, ptr.field, *ptr), or 2060 // - are long (e.g. "huge string literal"). 2061 func duplicable(info *types.Info, e ast.Expr) bool { 2062 switch e := e.(type) { 2063 case *ast.ParenExpr: 2064 return duplicable(info, e.X) 2065 2066 case *ast.Ident: 2067 return true 2068 2069 case *ast.BasicLit: 2070 v := info.Types[e].Value 2071 switch e.Kind { 2072 case token.INT: 2073 return true // any int 2074 case token.STRING: 2075 return consteq(v, kZeroString) // only "" 2076 case token.FLOAT: 2077 return consteq(v, kZeroFloat) || consteq(v, kOneFloat) // only 0.0 or 1.0 2078 } 2079 2080 case *ast.UnaryExpr: // e.g. +1, -1 2081 return (e.Op == token.ADD || e.Op == token.SUB) && duplicable(info, e.X) 2082 2083 case *ast.CompositeLit: 2084 // Empty struct or array literals T{} are duplicable. 2085 // (Non-empty literals are too verbose, and slice/map 2086 // literals allocate indirect variables.) 2087 if len(e.Elts) == 0 { 2088 switch info.TypeOf(e).Underlying().(type) { 2089 case *types.Struct, *types.Array: 2090 return true 2091 } 2092 } 2093 return false 2094 2095 case *ast.CallExpr: 2096 // Don't treat a conversion T(x) as duplicable even 2097 // if x is duplicable because it could duplicate 2098 // allocations. 2099 // 2100 // TODO(adonovan): there are cases to tease apart here: 2101 // duplicating string([]byte) conversions increases 2102 // allocation but doesn't change behavior, but the 2103 // reverse, []byte(string), allocates a distinct array, 2104 // which is observable 2105 return false 2106 2107 case *ast.SelectorExpr: 2108 if seln, ok := info.Selections[e]; ok { 2109 // A field or method selection x.f is referentially 2110 // transparent if it does not indirect a pointer. 2111 return !indirectSelection(seln) 2112 } 2113 // A qualified identifier pkg.Name is referentially transparent. 2114 return true 2115 } 2116 return false 2117 } 2118 2119 func consteq(x, y constant.Value) bool { 2120 return constant.Compare(x, token.EQL, y) 2121 } 2122 2123 var ( 2124 kZeroInt = constant.MakeInt64(0) 2125 kZeroString = constant.MakeString("") 2126 kZeroFloat = constant.MakeFloat64(0.0) 2127 kOneFloat = constant.MakeFloat64(1.0) 2128 ) 2129 2130 // -- inline helpers -- 2131 2132 func assert(cond bool, msg string) { 2133 if !cond { 2134 panic(msg) 2135 } 2136 } 2137 2138 // blanks returns a slice of n > 0 blank identifiers. 2139 func blanks[E ast.Expr](n int) []E { 2140 if n == 0 { 2141 panic("blanks(0)") 2142 } 2143 res := make([]E, n) 2144 for i := range res { 2145 res[i] = ast.Expr(makeIdent("_")).(E) // ugh 2146 } 2147 return res 2148 } 2149 2150 func makeIdent(name string) *ast.Ident { 2151 return &ast.Ident{Name: name} 2152 } 2153 2154 // importedPkgName returns the PkgName object declared by an ImportSpec. 2155 // TODO(adonovan): make this a method of types.Info (#62037). 2156 func importedPkgName(info *types.Info, imp *ast.ImportSpec) (*types.PkgName, bool) { 2157 var obj types.Object 2158 if imp.Name != nil { 2159 obj = info.Defs[imp.Name] 2160 } else { 2161 obj = info.Implicits[imp] 2162 } 2163 pkgname, ok := obj.(*types.PkgName) 2164 return pkgname, ok 2165 } 2166 2167 func isPkgLevel(obj types.Object) bool { 2168 // TODO(adonovan): consider using the simpler obj.Parent() == 2169 // obj.Pkg().Scope() instead. But be sure to test carefully 2170 // with instantiations of generics. 2171 return obj.Pkg().Scope().Lookup(obj.Name()) == obj 2172 } 2173 2174 // callContext returns the node immediately enclosing the call 2175 // (specified as a PathEnclosingInterval), ignoring parens. 2176 func callContext(callPath []ast.Node) ast.Node { 2177 _ = callPath[0].(*ast.CallExpr) // sanity check 2178 for _, n := range callPath[1:] { 2179 if !is[*ast.ParenExpr](n) { 2180 return n 2181 } 2182 } 2183 return nil 2184 } 2185 2186 // hasLabelConflict reports whether the set of labels of the function 2187 // enclosing the call (specified as a PathEnclosingInterval) 2188 // intersects with the set of callee labels. 2189 func hasLabelConflict(callPath []ast.Node, calleeLabels []string) bool { 2190 labels := callerLabels(callPath) 2191 for _, label := range calleeLabels { 2192 if labels[label] { 2193 return true // conflict 2194 } 2195 } 2196 return false 2197 } 2198 2199 // callerLabels returns the set of control labels in the function (if 2200 // any) enclosing the call (specified as a PathEnclosingInterval). 2201 func callerLabels(callPath []ast.Node) map[string]bool { 2202 var callerBody *ast.BlockStmt 2203 switch f := callerFunc(callPath).(type) { 2204 case *ast.FuncDecl: 2205 callerBody = f.Body 2206 case *ast.FuncLit: 2207 callerBody = f.Body 2208 } 2209 var labels map[string]bool 2210 if callerBody != nil { 2211 ast.Inspect(callerBody, func(n ast.Node) bool { 2212 switch n := n.(type) { 2213 case *ast.FuncLit: 2214 return false // prune traversal 2215 case *ast.LabeledStmt: 2216 if labels == nil { 2217 labels = make(map[string]bool) 2218 } 2219 labels[n.Label.Name] = true 2220 } 2221 return true 2222 }) 2223 } 2224 return labels 2225 } 2226 2227 // callerFunc returns the innermost Func{Decl,Lit} node enclosing the 2228 // call (specified as a PathEnclosingInterval). 2229 func callerFunc(callPath []ast.Node) ast.Node { 2230 _ = callPath[0].(*ast.CallExpr) // sanity check 2231 for _, n := range callPath[1:] { 2232 if is[*ast.FuncDecl](n) || is[*ast.FuncLit](n) { 2233 return n 2234 } 2235 } 2236 return nil 2237 } 2238 2239 // callStmt reports whether the function call (specified 2240 // as a PathEnclosingInterval) appears within an ExprStmt, 2241 // and returns it if so. 2242 // 2243 // If unrestricted, callStmt returns nil if the ExprStmt f() appears 2244 // in a restricted context (such as "if f(); cond {") where it cannot 2245 // be replaced by an arbitrary statement. (See "statement theory".) 2246 func callStmt(callPath []ast.Node, unrestricted bool) *ast.ExprStmt { 2247 stmt, ok := callContext(callPath).(*ast.ExprStmt) 2248 if ok && unrestricted { 2249 switch callPath[nodeIndex(callPath, stmt)+1].(type) { 2250 case *ast.LabeledStmt, 2251 *ast.BlockStmt, 2252 *ast.CaseClause, 2253 *ast.CommClause: 2254 // unrestricted 2255 default: 2256 // TODO(adonovan): handle restricted 2257 // XYZStmt.Init contexts (but not ForStmt.Post) 2258 // by creating a block around the if/for/switch: 2259 // "if f(); cond {" -> "{ stmts; if cond {" 2260 2261 return nil // restricted 2262 } 2263 } 2264 return stmt 2265 } 2266 2267 // Statement theory 2268 // 2269 // These are all the places a statement may appear in the AST: 2270 // 2271 // LabeledStmt.Stmt Stmt -- any 2272 // BlockStmt.List []Stmt -- any (but see switch/select) 2273 // IfStmt.Init Stmt? -- simple 2274 // IfStmt.Body BlockStmt 2275 // IfStmt.Else Stmt? -- IfStmt or BlockStmt 2276 // CaseClause.Body []Stmt -- any 2277 // SwitchStmt.Init Stmt? -- simple 2278 // SwitchStmt.Body BlockStmt -- CaseClauses only 2279 // TypeSwitchStmt.Init Stmt? -- simple 2280 // TypeSwitchStmt.Assign Stmt -- AssignStmt(TypeAssertExpr) or ExprStmt(TypeAssertExpr) 2281 // TypeSwitchStmt.Body BlockStmt -- CaseClauses only 2282 // CommClause.Comm Stmt? -- SendStmt or ExprStmt(UnaryExpr) or AssignStmt(UnaryExpr) 2283 // CommClause.Body []Stmt -- any 2284 // SelectStmt.Body BlockStmt -- CommClauses only 2285 // ForStmt.Init Stmt? -- simple 2286 // ForStmt.Post Stmt? -- simple 2287 // ForStmt.Body BlockStmt 2288 // RangeStmt.Body BlockStmt 2289 // 2290 // simple = AssignStmt | SendStmt | IncDecStmt | ExprStmt. 2291 // 2292 // A BlockStmt cannot replace an ExprStmt in 2293 // {If,Switch,TypeSwitch}Stmt.Init or ForStmt.Post. 2294 // That is allowed only within: 2295 // LabeledStmt.Stmt Stmt 2296 // BlockStmt.List []Stmt 2297 // CaseClause.Body []Stmt 2298 // CommClause.Body []Stmt 2299 2300 // replaceNode performs a destructive update of the tree rooted at 2301 // root, replacing each occurrence of "from" with "to". If to is nil and 2302 // the element is within a slice, the slice element is removed. 2303 // 2304 // The root itself cannot be replaced; an attempt will panic. 2305 // 2306 // This function must not be called on the caller's syntax tree. 2307 // 2308 // TODO(adonovan): polish this up and move it to astutil package. 2309 // TODO(adonovan): needs a unit test. 2310 func replaceNode(root ast.Node, from, to ast.Node) { 2311 if from == nil { 2312 panic("from == nil") 2313 } 2314 if reflect.ValueOf(from).IsNil() { 2315 panic(fmt.Sprintf("from == (%T)(nil)", from)) 2316 } 2317 if from == root { 2318 panic("from == root") 2319 } 2320 found := false 2321 var parent reflect.Value // parent variable of interface type, containing a pointer 2322 var visit func(reflect.Value) 2323 visit = func(v reflect.Value) { 2324 switch v.Kind() { 2325 case reflect.Ptr: 2326 if v.Interface() == from { 2327 found = true 2328 2329 // If v is a struct field or array element 2330 // (e.g. Field.Comment or Field.Names[i]) 2331 // then it is addressable (a pointer variable). 2332 // 2333 // But if it was the value an interface 2334 // (e.g. *ast.Ident within ast.Node) 2335 // then it is non-addressable, and we need 2336 // to set the enclosing interface (parent). 2337 if !v.CanAddr() { 2338 v = parent 2339 } 2340 2341 // to=nil => use zero value 2342 var toV reflect.Value 2343 if to != nil { 2344 toV = reflect.ValueOf(to) 2345 } else { 2346 toV = reflect.Zero(v.Type()) // e.g. ast.Expr(nil) 2347 } 2348 v.Set(toV) 2349 2350 } else if !v.IsNil() { 2351 switch v.Interface().(type) { 2352 case *ast.Object, *ast.Scope: 2353 // Skip fields of types potentially involved in cycles. 2354 default: 2355 visit(v.Elem()) 2356 } 2357 } 2358 2359 case reflect.Struct: 2360 for i := 0; i < v.Type().NumField(); i++ { 2361 visit(v.Field(i)) 2362 } 2363 2364 case reflect.Slice: 2365 compact := false 2366 for i := 0; i < v.Len(); i++ { 2367 visit(v.Index(i)) 2368 if v.Index(i).IsNil() { 2369 compact = true 2370 } 2371 } 2372 if compact { 2373 // Elements were deleted. Eliminate nils. 2374 // (Do this is a second pass to avoid 2375 // unnecessary writes in the common case.) 2376 j := 0 2377 for i := 0; i < v.Len(); i++ { 2378 if !v.Index(i).IsNil() { 2379 v.Index(j).Set(v.Index(i)) 2380 j++ 2381 } 2382 } 2383 v.SetLen(j) 2384 } 2385 case reflect.Interface: 2386 parent = v 2387 visit(v.Elem()) 2388 2389 case reflect.Array, reflect.Chan, reflect.Func, reflect.Map, reflect.UnsafePointer: 2390 panic(v) // unreachable in AST 2391 default: 2392 // bool, string, number: nop 2393 } 2394 parent = reflect.Value{} 2395 } 2396 visit(reflect.ValueOf(root)) 2397 if !found { 2398 panic(fmt.Sprintf("%T not found", from)) 2399 } 2400 } 2401 2402 // clearPositions destroys token.Pos information within the tree rooted at root, 2403 // as positions in callee trees may cause caller comments to be emitted prematurely. 2404 // 2405 // In general it isn't safe to clear a valid Pos because some of them 2406 // (e.g. CallExpr.Ellipsis, TypeSpec.Assign) are significant to 2407 // go/printer, so this function sets each non-zero Pos to 1, which 2408 // suffices to avoid advancing the printer's comment cursor. 2409 // 2410 // This function mutates its argument; do not invoke on caller syntax. 2411 // 2412 // TODO(adonovan): remove this horrendous workaround when #20744 is finally fixed. 2413 func clearPositions(root ast.Node) { 2414 posType := reflect.TypeOf(token.NoPos) 2415 ast.Inspect(root, func(n ast.Node) bool { 2416 if n != nil { 2417 v := reflect.ValueOf(n).Elem() // deref the pointer to struct 2418 fields := v.Type().NumField() 2419 for i := 0; i < fields; i++ { 2420 f := v.Field(i) 2421 if f.Type() == posType { 2422 // Clearing Pos arbitrarily is destructive, 2423 // as its presence may be semantically significant 2424 // (e.g. CallExpr.Ellipsis, TypeSpec.Assign) 2425 // or affect formatting preferences (e.g. GenDecl.Lparen). 2426 if f.Interface() != token.NoPos { 2427 f.Set(reflect.ValueOf(token.Pos(1))) 2428 } 2429 } 2430 } 2431 } 2432 return true 2433 }) 2434 } 2435 2436 // findIdent returns the Ident beneath root that has the given pos. 2437 func findIdent(root ast.Node, pos token.Pos) *ast.Ident { 2438 // TODO(adonovan): opt: skip subtrees that don't contain pos. 2439 var found *ast.Ident 2440 ast.Inspect(root, func(n ast.Node) bool { 2441 if found != nil { 2442 return false 2443 } 2444 if id, ok := n.(*ast.Ident); ok { 2445 if id.Pos() == pos { 2446 found = id 2447 } 2448 } 2449 return true 2450 }) 2451 if found == nil { 2452 panic(fmt.Sprintf("findIdent %d not found in %s", 2453 pos, debugFormatNode(token.NewFileSet(), root))) 2454 } 2455 return found 2456 } 2457 2458 func prepend[T any](elem T, slice ...T) []T { 2459 return append([]T{elem}, slice...) 2460 } 2461 2462 // debugFormatNode formats a node or returns a formatting error. 2463 // Its sloppy treatment of errors is appropriate only for logging. 2464 func debugFormatNode(fset *token.FileSet, n ast.Node) string { 2465 var out strings.Builder 2466 if err := format.Node(&out, fset, n); err != nil { 2467 out.WriteString(err.Error()) 2468 } 2469 return out.String() 2470 } 2471 2472 func shallowCopy[T any](ptr *T) *T { 2473 copy := *ptr 2474 return © 2475 } 2476 2477 // ∀ 2478 func forall[T any](list []T, f func(i int, x T) bool) bool { 2479 for i, x := range list { 2480 if !f(i, x) { 2481 return false 2482 } 2483 } 2484 return true 2485 } 2486 2487 // ∃ 2488 func exists[T any](list []T, f func(i int, x T) bool) bool { 2489 for i, x := range list { 2490 if f(i, x) { 2491 return true 2492 } 2493 } 2494 return false 2495 } 2496 2497 // last returns the last element of a slice, or zero if empty. 2498 func last[T any](slice []T) T { 2499 n := len(slice) 2500 if n > 0 { 2501 return slice[n-1] 2502 } 2503 return *new(T) 2504 } 2505 2506 // canImport reports whether one package is allowed to import another. 2507 // 2508 // TODO(adonovan): allow customization of the accessibility relation 2509 // (e.g. for Bazel). 2510 func canImport(from, to string) bool { 2511 // TODO(adonovan): better segment hygiene. 2512 if strings.HasPrefix(to, "internal/") { 2513 // Special case: only std packages may import internal/... 2514 // We can't reliably know whether we're in std, so we 2515 // use a heuristic on the first segment. 2516 first, _, _ := strings.Cut(from, "/") 2517 if strings.Contains(first, ".") { 2518 return false // example.com/foo ∉ std 2519 } 2520 if first == "testdata" { 2521 return false // testdata/foo ∉ std 2522 } 2523 } 2524 if i := strings.LastIndex(to, "/internal/"); i >= 0 { 2525 return strings.HasPrefix(from, to[:i]) 2526 } 2527 return true 2528 } 2529 2530 // consistentOffsets reports whether the portion of caller.Content 2531 // that corresponds to caller.Call can be parsed as a call expression. 2532 // If not, the client has provided inconsistent information, possibly 2533 // because they forgot to ignore line directives when computing the 2534 // filename enclosing the call. 2535 // This is just a heuristic. 2536 func consistentOffsets(caller *Caller) bool { 2537 start := offsetOf(caller.Fset, caller.Call.Pos()) 2538 end := offsetOf(caller.Fset, caller.Call.End()) 2539 if !(0 < start && start < end && end <= len(caller.Content)) { 2540 return false 2541 } 2542 expr, err := parser.ParseExpr(string(caller.Content[start:end])) 2543 if err != nil { 2544 return false 2545 } 2546 return is[*ast.CallExpr](expr) 2547 } 2548 2549 // needsParens reports whether parens are required to avoid ambiguity 2550 // around the new node replacing the specified old node (which is some 2551 // ancestor of the CallExpr identified by its PathEnclosingInterval). 2552 func needsParens(callPath []ast.Node, old, new ast.Node) bool { 2553 // Find enclosing old node and its parent. 2554 i := nodeIndex(callPath, old) 2555 if i == -1 { 2556 panic("not found") 2557 } 2558 2559 // There is no precedence ambiguity when replacing 2560 // (e.g.) a statement enclosing the call. 2561 if !is[ast.Expr](old) { 2562 return false 2563 } 2564 2565 // An expression beneath a non-expression 2566 // has no precedence ambiguity. 2567 parent, ok := callPath[i+1].(ast.Expr) 2568 if !ok { 2569 return false 2570 } 2571 2572 precedence := func(n ast.Node) int { 2573 switch n := n.(type) { 2574 case *ast.UnaryExpr, *ast.StarExpr: 2575 return token.UnaryPrec 2576 case *ast.BinaryExpr: 2577 return n.Op.Precedence() 2578 } 2579 return -1 2580 } 2581 2582 // Parens are not required if the new node 2583 // is not unary or binary. 2584 newprec := precedence(new) 2585 if newprec < 0 { 2586 return false 2587 } 2588 2589 // Parens are required if parent and child are both 2590 // unary or binary and the parent has higher precedence. 2591 if precedence(parent) > newprec { 2592 return true 2593 } 2594 2595 // Was the old node the operand of a postfix operator? 2596 // f().sel 2597 // f()[i:j] 2598 // f()[i] 2599 // f().(T) 2600 // f()(x) 2601 switch parent := parent.(type) { 2602 case *ast.SelectorExpr: 2603 return parent.X == old 2604 case *ast.IndexExpr: 2605 return parent.X == old 2606 case *ast.SliceExpr: 2607 return parent.X == old 2608 case *ast.TypeAssertExpr: 2609 return parent.X == old 2610 case *ast.CallExpr: 2611 return parent.Fun == old 2612 } 2613 return false 2614 } 2615 2616 func nodeIndex(nodes []ast.Node, n ast.Node) int { 2617 // TODO(adonovan): Use index[ast.Node]() in go1.20. 2618 for i, node := range nodes { 2619 if node == n { 2620 return i 2621 } 2622 } 2623 return -1 2624 } 2625 2626 // declares returns the set of lexical names declared by a 2627 // sequence of statements from the same block, excluding sub-blocks. 2628 // (Lexical names do not include control labels.) 2629 func declares(stmts []ast.Stmt) map[string]bool { 2630 names := make(map[string]bool) 2631 for _, stmt := range stmts { 2632 switch stmt := stmt.(type) { 2633 case *ast.DeclStmt: 2634 for _, spec := range stmt.Decl.(*ast.GenDecl).Specs { 2635 switch spec := spec.(type) { 2636 case *ast.ValueSpec: 2637 for _, id := range spec.Names { 2638 names[id.Name] = true 2639 } 2640 case *ast.TypeSpec: 2641 names[spec.Name.Name] = true 2642 } 2643 } 2644 2645 case *ast.AssignStmt: 2646 if stmt.Tok == token.DEFINE { 2647 for _, lhs := range stmt.Lhs { 2648 names[lhs.(*ast.Ident).Name] = true 2649 } 2650 } 2651 } 2652 } 2653 delete(names, "_") 2654 return names 2655 } 2656 2657 // tailCallSafeReturn reports whether the callee's return statements may be safely 2658 // used to return from the function enclosing the caller (which must exist). 2659 func tailCallSafeReturn(caller *Caller, calleeSymbol *types.Func, callee *gobCallee) bool { 2660 // It is safe if all callee returns involve only trivial conversions. 2661 if callee.TrivialReturns == callee.TotalReturns { 2662 return true 2663 } 2664 2665 var callerType types.Type 2666 // Find type of innermost function enclosing call. 2667 // (Beware: Caller.enclosingFunc is the outermost.) 2668 loop: 2669 for _, n := range caller.path { 2670 switch f := n.(type) { 2671 case *ast.FuncDecl: 2672 callerType = caller.Info.ObjectOf(f.Name).Type() 2673 break loop 2674 case *ast.FuncLit: 2675 callerType = caller.Info.TypeOf(f) 2676 break loop 2677 } 2678 } 2679 2680 // Non-trivial return conversions in the callee are permitted 2681 // if the same non-trivial conversion would occur after inlining, 2682 // i.e. if the caller and callee results tuples are identical. 2683 callerResults := callerType.(*types.Signature).Results() 2684 calleeResults := calleeSymbol.Type().(*types.Signature).Results() 2685 return types.Identical(callerResults, calleeResults) 2686 }