blob: 45cce11a9e202eda89d4e6e13afaecbdbb74feb0 [file] [log] [blame]
Alan Donovanf0961292023-09-20 14:01:07 -04001// 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
5package inline
6
7import (
8 "fmt"
9 "go/ast"
10 "go/token"
11 "go/types"
12)
13
14// escape implements a simple "address-taken" escape analysis. It
15// calls f for each local variable that appears on the left side of an
16// assignment (escapes=false) or has its address taken (escapes=true).
17// The initialization of a variable by its declaration does not count
18// as an assignment.
19func escape(info *types.Info, root ast.Node, f func(v *types.Var, escapes bool)) {
20
21 // lvalue is called for each address-taken expression or LHS of assignment.
22 // Supported forms are: x, (x), x[i], x.f, *x, T{}.
23 var lvalue func(e ast.Expr, escapes bool)
24 lvalue = func(e ast.Expr, escapes bool) {
25 switch e := e.(type) {
26 case *ast.Ident:
27 if v, ok := info.Uses[e].(*types.Var); ok {
28 if !isPkgLevel(v) {
29 f(v, escapes)
30 }
31 }
32 case *ast.ParenExpr:
33 lvalue(e.X, escapes)
34 case *ast.IndexExpr:
35 // TODO(adonovan): support generics without assuming e.X has a core type.
36 // Consider:
37 //
38 // func Index[T interface{ [3]int | []int }](t T, i int) *int {
39 // return &t[i]
40 // }
41 //
42 // We must traverse the normal terms and check
43 // whether any of them is an array.
Alan Donovan454be602024-10-02 16:37:06 -040044 //
45 // We assume TypeOf returns non-nil.
Alan Donovanf0961292023-09-20 14:01:07 -040046 if _, ok := info.TypeOf(e.X).Underlying().(*types.Array); ok {
47 lvalue(e.X, escapes) // &a[i] on array
48 }
49 case *ast.SelectorExpr:
Alan Donovan454be602024-10-02 16:37:06 -040050 // We assume TypeOf returns non-nil.
Alan Donovanf0961292023-09-20 14:01:07 -040051 if _, ok := info.TypeOf(e.X).Underlying().(*types.Struct); ok {
52 lvalue(e.X, escapes) // &s.f on struct
53 }
54 case *ast.StarExpr:
55 // *ptr indirects an existing pointer
56 case *ast.CompositeLit:
57 // &T{...} creates a new variable
58 default:
59 panic(fmt.Sprintf("&x on %T", e)) // unreachable in well-typed code
60 }
61 }
62
Alan Donovan313150a2023-09-25 17:46:25 -040063 // Search function body for operations &x, x.f(), x++, and x = y
Alan Donovanf0961292023-09-20 14:01:07 -040064 // where x is a parameter. Each of these treats x as an address.
65 ast.Inspect(root, func(n ast.Node) bool {
66 switch n := n.(type) {
67 case *ast.UnaryExpr:
68 if n.Op == token.AND {
69 lvalue(n.X, true) // &x
70 }
71
72 case *ast.CallExpr:
73 // implicit &x in method call x.f(),
74 // where x has type T and method is (*T).f
75 if sel, ok := n.Fun.(*ast.SelectorExpr); ok {
76 if seln, ok := info.Selections[sel]; ok &&
77 seln.Kind() == types.MethodVal &&
Alan Donovand64ed6a2024-02-06 15:57:13 -050078 isPointer(seln.Obj().Type().Underlying().(*types.Signature).Recv().Type()) {
Alan Donovan187911b2023-10-05 15:04:05 -040079 tArg, indirect := effectiveReceiver(seln)
80 if !indirect && !isPointer(tArg) {
81 lvalue(sel.X, true) // &x.f
82 }
Alan Donovanf0961292023-09-20 14:01:07 -040083 }
84 }
85
86 case *ast.AssignStmt:
87 for _, lhs := range n.Lhs {
88 if id, ok := lhs.(*ast.Ident); ok &&
89 info.Defs[id] != nil &&
90 n.Tok == token.DEFINE {
91 // declaration: doesn't count
92 } else {
93 lvalue(lhs, false)
94 }
95 }
Alan Donovan313150a2023-09-25 17:46:25 -040096
97 case *ast.IncDecStmt:
98 lvalue(n.X, false)
Alan Donovanf0961292023-09-20 14:01:07 -040099 }
100 return true
101 })
102}