github.com/amarpal/go-tools@v0.0.0-20240422043104-40142f59f616/go/ir/source.go (about) 1 // Copyright 2013 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package ir 6 7 // This file defines utilities for working with source positions 8 // or source-level named entities ("objects"). 9 10 // TODO(adonovan): test that {Value,Instruction}.Pos() positions match 11 // the originating syntax, as specified. 12 13 import ( 14 "go/ast" 15 "go/token" 16 "go/types" 17 ) 18 19 // EnclosingFunction returns the function that contains the syntax 20 // node denoted by path. 21 // 22 // Syntax associated with package-level variable specifications is 23 // enclosed by the package's init() function. 24 // 25 // Returns nil if not found; reasons might include: 26 // - the node is not enclosed by any function. 27 // - the node is within an anonymous function (FuncLit) and 28 // its IR function has not been created yet 29 // (pkg.Build() has not yet been called). 30 func EnclosingFunction(pkg *Package, path []ast.Node) *Function { 31 // Start with package-level function... 32 fn := findEnclosingPackageLevelFunction(pkg, path) 33 if fn == nil { 34 return nil // not in any function 35 } 36 37 // ...then walk down the nested anonymous functions. 38 n := len(path) 39 outer: 40 for i := range path { 41 if lit, ok := path[n-1-i].(*ast.FuncLit); ok { 42 for _, anon := range fn.AnonFuncs { 43 if anon.Pos() == lit.Type.Func { 44 fn = anon 45 continue outer 46 } 47 } 48 // IR function not found: 49 // - package not yet built, or maybe 50 // - builder skipped FuncLit in dead block 51 // (in principle; but currently the Builder 52 // generates even dead FuncLits). 53 return nil 54 } 55 } 56 return fn 57 } 58 59 // HasEnclosingFunction returns true if the AST node denoted by path 60 // is contained within the declaration of some function or 61 // package-level variable. 62 // 63 // Unlike EnclosingFunction, the behaviour of this function does not 64 // depend on whether IR code for pkg has been built, so it can be 65 // used to quickly reject check inputs that will cause 66 // EnclosingFunction to fail, prior to IR building. 67 func HasEnclosingFunction(pkg *Package, path []ast.Node) bool { 68 return findEnclosingPackageLevelFunction(pkg, path) != nil 69 } 70 71 // findEnclosingPackageLevelFunction returns the Function 72 // corresponding to the package-level function enclosing path. 73 func findEnclosingPackageLevelFunction(pkg *Package, path []ast.Node) *Function { 74 if n := len(path); n >= 2 { // [... {Gen,Func}Decl File] 75 switch decl := path[n-2].(type) { 76 case *ast.GenDecl: 77 if decl.Tok == token.VAR && n >= 3 { 78 // Package-level 'var' initializer. 79 return pkg.init 80 } 81 82 case *ast.FuncDecl: 83 // Declared function/method. 84 fn := findNamedFunc(pkg, decl.Pos()) 85 if fn == nil && decl.Recv == nil && decl.Name.Name == "init" { 86 // Hack: return non-nil when IR is not yet 87 // built so that HasEnclosingFunction works. 88 return pkg.init 89 } 90 return fn 91 } 92 } 93 return nil // not in any function 94 } 95 96 // findNamedFunc returns the named function whose FuncDecl.Ident is at 97 // position pos. 98 func findNamedFunc(pkg *Package, pos token.Pos) *Function { 99 for _, fn := range pkg.Functions { 100 if fn.Pos() == pos { 101 return fn 102 } 103 } 104 return nil 105 } 106 107 // ValueForExpr returns the IR Value that corresponds to non-constant 108 // expression e. 109 // 110 // It returns nil if no value was found, e.g. 111 // - the expression is not lexically contained within f; 112 // - f was not built with debug information; or 113 // - e is a constant expression. (For efficiency, no debug 114 // information is stored for constants. Use 115 // go/types.Info.Types[e].Value instead.) 116 // - e is a reference to nil or a built-in function. 117 // - the value was optimised away. 118 // 119 // If e is an addressable expression used in an lvalue context, 120 // value is the address denoted by e, and isAddr is true. 121 // 122 // The types of e (or &e, if isAddr) and the result are equal 123 // (modulo "untyped" bools resulting from comparisons). 124 // 125 // (Tip: to find the ir.Value given a source position, use 126 // astutil.PathEnclosingInterval to locate the ast.Node, then 127 // EnclosingFunction to locate the Function, then ValueForExpr to find 128 // the ir.Value.) 129 func (f *Function) ValueForExpr(e ast.Expr) (value Value, isAddr bool) { 130 if f.debugInfo() { // (opt) 131 e = unparen(e) 132 for _, b := range f.Blocks { 133 for _, instr := range b.Instrs { 134 if ref, ok := instr.(*DebugRef); ok { 135 if ref.Expr == e { 136 return ref.X, ref.IsAddr 137 } 138 } 139 } 140 } 141 } 142 return 143 } 144 145 // --- Lookup functions for source-level named entities (types.Objects) --- 146 147 // Package returns the IR Package corresponding to the specified 148 // type-checker package object. 149 // It returns nil if no such IR package has been created. 150 func (prog *Program) Package(obj *types.Package) *Package { 151 return prog.packages[obj] 152 } 153 154 // packageLevelValue returns the package-level value corresponding to 155 // the specified named object, which may be a package-level const 156 // (*Const), var (*Global) or func (*Function) of some package in 157 // prog. It returns nil if the object is not found. 158 func (prog *Program) packageLevelValue(obj types.Object) Value { 159 if pkg, ok := prog.packages[obj.Pkg()]; ok { 160 return pkg.values[obj] 161 } 162 return nil 163 } 164 165 // FuncValue returns the concrete Function denoted by the source-level 166 // named function obj, or nil if obj denotes an interface method. 167 // 168 // TODO(adonovan): check the invariant that obj.Type() matches the 169 // result's Signature, both in the params/results and in the receiver. 170 func (prog *Program) FuncValue(obj *types.Func) *Function { 171 obj = obj.Origin() 172 fn, _ := prog.packageLevelValue(obj).(*Function) 173 return fn 174 } 175 176 // ConstValue returns the IR Value denoted by the source-level named 177 // constant obj. 178 func (prog *Program) ConstValue(obj *types.Const) *Const { 179 // TODO(adonovan): opt: share (don't reallocate) 180 // Consts for const objects and constant ast.Exprs. 181 182 // Universal constant? {true,false,nil} 183 if obj.Parent() == types.Universe { 184 return NewConst(obj.Val(), obj.Type(), nil) 185 } 186 // Package-level named constant? 187 if v := prog.packageLevelValue(obj); v != nil { 188 return v.(*Const) 189 } 190 return NewConst(obj.Val(), obj.Type(), nil) 191 } 192 193 // VarValue returns the IR Value that corresponds to a specific 194 // identifier denoting the source-level named variable obj. 195 // 196 // VarValue returns nil if a local variable was not found, perhaps 197 // because its package was not built, the debug information was not 198 // requested during IR construction, or the value was optimized away. 199 // 200 // ref is the path to an ast.Ident (e.g. from PathEnclosingInterval), 201 // and that ident must resolve to obj. 202 // 203 // pkg is the package enclosing the reference. (A reference to a var 204 // always occurs within a function, so we need to know where to find it.) 205 // 206 // If the identifier is a field selector and its base expression is 207 // non-addressable, then VarValue returns the value of that field. 208 // For example: 209 // 210 // func f() struct {x int} 211 // f().x // VarValue(x) returns a *Field instruction of type int 212 // 213 // All other identifiers denote addressable locations (variables). 214 // For them, VarValue may return either the variable's address or its 215 // value, even when the expression is evaluated only for its value; the 216 // situation is reported by isAddr, the second component of the result. 217 // 218 // If !isAddr, the returned value is the one associated with the 219 // specific identifier. For example, 220 // 221 // var x int // VarValue(x) returns Const 0 here 222 // x = 1 // VarValue(x) returns Const 1 here 223 // 224 // It is not specified whether the value or the address is returned in 225 // any particular case, as it may depend upon optimizations performed 226 // during IR code generation, such as registerization, constant 227 // folding, avoidance of materialization of subexpressions, etc. 228 func (prog *Program) VarValue(obj *types.Var, pkg *Package, ref []ast.Node) (value Value, isAddr bool) { 229 // All references to a var are local to some function, possibly init. 230 fn := EnclosingFunction(pkg, ref) 231 if fn == nil { 232 return // e.g. def of struct field; IR not built? 233 } 234 235 id := ref[0].(*ast.Ident) 236 237 // Defining ident of a parameter? 238 if id.Pos() == obj.Pos() { 239 for _, param := range fn.Params { 240 if param.Object() == obj { 241 return param, false 242 } 243 } 244 } 245 246 // Other ident? 247 for _, b := range fn.Blocks { 248 for _, instr := range b.Instrs { 249 if dr, ok := instr.(*DebugRef); ok { 250 if dr.Pos() == id.Pos() { 251 return dr.X, dr.IsAddr 252 } 253 } 254 } 255 } 256 257 // Defining ident of package-level var? 258 if v := prog.packageLevelValue(obj); v != nil { 259 return v.(*Global), true 260 } 261 262 return // e.g. debug info not requested, or var optimized away 263 }