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