cuelang.org/go@v0.10.1/internal/golangorgx/tools/refactor/inline/calleefx.go (about) 1 // Copyright 2023 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package inline 6 7 // This file defines the analysis of callee effects. 8 9 import ( 10 "go/ast" 11 "go/token" 12 "go/types" 13 ) 14 15 const ( 16 rinf = -1 // R∞: arbitrary read from memory 17 winf = -2 // W∞: arbitrary write to memory (or unknown control) 18 ) 19 20 // calleefx returns a list of parameter indices indicating the order 21 // in which parameters are first referenced during evaluation of the 22 // callee, relative both to each other and to other effects of the 23 // callee (if any), such as arbitrary reads (rinf) and arbitrary 24 // effects (winf), including unknown control flow. Each parameter 25 // that is referenced appears once in the list. 26 // 27 // For example, the effects list of this function: 28 // 29 // func f(x, y, z int) int { 30 // return y + x + g() + z 31 // } 32 // 33 // is [1 0 -2 2], indicating reads of y and x, followed by the unknown 34 // effects of the g() call. and finally the read of parameter z. This 35 // information is used during inlining to ascertain when it is safe 36 // for parameter references to be replaced by their corresponding 37 // argument expressions. Such substitutions are permitted only when 38 // they do not cause "write" operations (those with effects) to 39 // commute with "read" operations (those that have no effect but are 40 // not pure). Impure operations may be reordered with other impure 41 // operations, and pure operations may be reordered arbitrarily. 42 // 43 // The analysis ignores the effects of runtime panics, on the 44 // assumption that well-behaved programs shouldn't encounter them. 45 func calleefx(info *types.Info, body *ast.BlockStmt, paramInfos map[*types.Var]*paramInfo) []int { 46 // This traversal analyzes the callee's statements (in syntax 47 // form, though one could do better with SSA) to compute the 48 // sequence of events of the following kinds: 49 // 50 // 1 read of a parameter variable. 51 // 2. reads from other memory. 52 // 3. writes to memory 53 54 var effects []int // indices of parameters, or rinf/winf (-ve) 55 seen := make(map[int]bool) 56 effect := func(i int) { 57 if !seen[i] { 58 seen[i] = true 59 effects = append(effects, i) 60 } 61 } 62 63 // unknown is called for statements of unknown effects (or control). 64 unknown := func() { 65 effect(winf) 66 67 // Ensure that all remaining parameters are "seen" 68 // after we go into the unknown (unless they are 69 // unreferenced by the function body). This lets us 70 // not bother implementing the complete traversal into 71 // control structures. 72 // 73 // TODO(adonovan): add them in a deterministic order. 74 // (This is not a bug but determinism is good.) 75 for _, pinfo := range paramInfos { 76 if !pinfo.IsResult && len(pinfo.Refs) > 0 { 77 effect(pinfo.Index) 78 } 79 } 80 } 81 82 var visitExpr func(n ast.Expr) 83 var visitStmt func(n ast.Stmt) bool 84 visitExpr = func(n ast.Expr) { 85 switch n := n.(type) { 86 case *ast.Ident: 87 if v, ok := info.Uses[n].(*types.Var); ok && !v.IsField() { 88 // Use of global? 89 if v.Parent() == v.Pkg().Scope() { 90 effect(rinf) // read global var 91 } 92 93 // Use of parameter? 94 if pinfo, ok := paramInfos[v]; ok && !pinfo.IsResult { 95 effect(pinfo.Index) // read parameter var 96 } 97 98 // Use of local variables is ok. 99 } 100 101 case *ast.BasicLit: 102 // no effect 103 104 case *ast.FuncLit: 105 // A func literal has no read or write effect 106 // until called, and (most) function calls are 107 // considered to have arbitrary effects. 108 // So, no effect. 109 110 case *ast.CompositeLit: 111 for _, elt := range n.Elts { 112 visitExpr(elt) // note: visits KeyValueExpr 113 } 114 115 case *ast.ParenExpr: 116 visitExpr(n.X) 117 118 case *ast.SelectorExpr: 119 if seln, ok := info.Selections[n]; ok { 120 visitExpr(n.X) 121 122 // See types.SelectionKind for background. 123 switch seln.Kind() { 124 case types.MethodExpr: 125 // A method expression T.f acts like a 126 // reference to a func decl, 127 // so it doesn't read x until called. 128 129 case types.MethodVal, types.FieldVal: 130 // A field or method value selection x.f 131 // reads x if the selection indirects a pointer. 132 133 if indirectSelection(seln) { 134 effect(rinf) 135 } 136 } 137 } else { 138 // qualified identifier: treat like unqualified 139 visitExpr(n.Sel) 140 } 141 142 case *ast.IndexExpr: 143 if tv := info.Types[n.Index]; tv.IsType() { 144 // no effect (G[T] instantiation) 145 } else { 146 visitExpr(n.X) 147 visitExpr(n.Index) 148 switch tv.Type.Underlying().(type) { 149 case *types.Slice, *types.Pointer: // []T, *[n]T (not string, [n]T) 150 effect(rinf) // indirect read of slice/array element 151 } 152 } 153 154 case *ast.IndexListExpr: 155 // no effect (M[K,V] instantiation) 156 157 case *ast.SliceExpr: 158 visitExpr(n.X) 159 visitExpr(n.Low) 160 visitExpr(n.High) 161 visitExpr(n.Max) 162 163 case *ast.TypeAssertExpr: 164 visitExpr(n.X) 165 166 case *ast.CallExpr: 167 if info.Types[n.Fun].IsType() { 168 // conversion T(x) 169 visitExpr(n.Args[0]) 170 } else { 171 // call f(args) 172 visitExpr(n.Fun) 173 for i, arg := range n.Args { 174 if i == 0 && info.Types[arg].IsType() { 175 continue // new(T), make(T, n) 176 } 177 visitExpr(arg) 178 } 179 180 // The pure built-ins have no effects beyond 181 // those of their operands (not even memory reads). 182 // All other calls have unknown effects. 183 if !callsPureBuiltin(info, n) { 184 unknown() // arbitrary effects 185 } 186 } 187 188 case *ast.StarExpr: 189 visitExpr(n.X) 190 effect(rinf) // *ptr load or store depends on state of heap 191 192 case *ast.UnaryExpr: // + - ! ^ & ~ <- 193 visitExpr(n.X) 194 if n.Op == token.ARROW { 195 unknown() // effect: channel receive 196 } 197 198 case *ast.BinaryExpr: 199 visitExpr(n.X) 200 visitExpr(n.Y) 201 202 case *ast.KeyValueExpr: 203 visitExpr(n.Key) // may be a struct field 204 visitExpr(n.Value) 205 206 case *ast.BadExpr: 207 // no effect 208 209 case nil: 210 // optional subtree 211 212 default: 213 // type syntax: unreachable given traversal 214 panic(n) 215 } 216 } 217 218 // visitStmt's result indicates the continuation: 219 // false for return, true for the next statement. 220 // 221 // We could treat return as an unknown, but this way 222 // yields definite effects for simple sequences like 223 // {S1; S2; return}, so unreferenced parameters are 224 // not spuriously added to the effects list, and thus 225 // not spuriously disqualified from elimination. 226 visitStmt = func(n ast.Stmt) bool { 227 switch n := n.(type) { 228 case *ast.DeclStmt: 229 decl := n.Decl.(*ast.GenDecl) 230 for _, spec := range decl.Specs { 231 switch spec := spec.(type) { 232 case *ast.ValueSpec: 233 for _, v := range spec.Values { 234 visitExpr(v) 235 } 236 237 case *ast.TypeSpec: 238 // no effect 239 } 240 } 241 242 case *ast.LabeledStmt: 243 return visitStmt(n.Stmt) 244 245 case *ast.ExprStmt: 246 visitExpr(n.X) 247 248 case *ast.SendStmt: 249 visitExpr(n.Chan) 250 visitExpr(n.Value) 251 unknown() // effect: channel send 252 253 case *ast.IncDecStmt: 254 visitExpr(n.X) 255 unknown() // effect: variable increment 256 257 case *ast.AssignStmt: 258 for _, lhs := range n.Lhs { 259 visitExpr(lhs) 260 } 261 for _, rhs := range n.Rhs { 262 visitExpr(rhs) 263 } 264 for _, lhs := range n.Lhs { 265 id, _ := lhs.(*ast.Ident) 266 if id != nil && id.Name == "_" { 267 continue // blank assign has no effect 268 } 269 if n.Tok == token.DEFINE && id != nil && info.Defs[id] != nil { 270 continue // new var declared by := has no effect 271 } 272 unknown() // assignment to existing var 273 break 274 } 275 276 case *ast.GoStmt: 277 visitExpr(n.Call.Fun) 278 for _, arg := range n.Call.Args { 279 visitExpr(arg) 280 } 281 unknown() // effect: create goroutine 282 283 case *ast.DeferStmt: 284 visitExpr(n.Call.Fun) 285 for _, arg := range n.Call.Args { 286 visitExpr(arg) 287 } 288 unknown() // effect: push defer 289 290 case *ast.ReturnStmt: 291 for _, res := range n.Results { 292 visitExpr(res) 293 } 294 return false 295 296 case *ast.BlockStmt: 297 for _, stmt := range n.List { 298 if !visitStmt(stmt) { 299 return false 300 } 301 } 302 303 case *ast.BranchStmt: 304 unknown() // control flow 305 306 case *ast.IfStmt: 307 visitStmt(n.Init) 308 visitExpr(n.Cond) 309 unknown() // control flow 310 311 case *ast.SwitchStmt: 312 visitStmt(n.Init) 313 visitExpr(n.Tag) 314 unknown() // control flow 315 316 case *ast.TypeSwitchStmt: 317 visitStmt(n.Init) 318 visitStmt(n.Assign) 319 unknown() // control flow 320 321 case *ast.SelectStmt: 322 unknown() // control flow 323 324 case *ast.ForStmt: 325 visitStmt(n.Init) 326 visitExpr(n.Cond) 327 unknown() // control flow 328 329 case *ast.RangeStmt: 330 visitExpr(n.X) 331 unknown() // control flow 332 333 case *ast.EmptyStmt, *ast.BadStmt: 334 // no effect 335 336 case nil: 337 // optional subtree 338 339 default: 340 panic(n) 341 } 342 return true 343 } 344 visitStmt(body) 345 346 return effects 347 }