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.
7 // This file defines utilities for working with source positions
8 // or source-level named entities ("objects").
10 // TODO(adonovan): test that {Value,Instruction}.Pos() positions match
11 // the originating syntax, as specified.
19 // EnclosingFunction returns the function that contains the syntax
20 // node denoted by path.
22 // Syntax associated with package-level variable specifications is
23 // enclosed by the package's init() function.
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).
31 func EnclosingFunction(pkg *Package, path []ast.Node) *Function {
32 // Start with package-level function...
33 fn := findEnclosingPackageLevelFunction(pkg, path)
35 return nil // not in any function
38 // ...then walk down the nested anonymous functions.
42 if lit, ok := path[n-1-i].(*ast.FuncLit); ok {
43 for _, anon := range fn.AnonFuncs {
44 if anon.Pos() == lit.Type.Func {
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).
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.
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.
69 func HasEnclosingFunction(pkg *Package, path []ast.Node) bool {
70 return findEnclosingPackageLevelFunction(pkg, path) != nil
73 // findEnclosingPackageLevelFunction returns the Function
74 // corresponding to the package-level function enclosing path.
76 func 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) {
80 if decl.Tok == token.VAR && n >= 3 {
81 // Package-level 'var' initializer.
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.
96 return nil // not in any function
99 // findNamedFunc returns the named function whose FuncDecl.Ident is at
102 func findNamedFunc(pkg *Package, pos token.Pos) *Function {
103 for _, fn := range pkg.Functions {
111 // ValueForExpr returns the IR Value that corresponds to non-constant
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.
123 // If e is an addressable expression used in an lvalue context,
124 // value is the address denoted by e, and isAddr is true.
126 // The types of e (or &e, if isAddr) and the result are equal
127 // (modulo "untyped" bools resulting from comparisons).
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
134 func (f *Function) ValueForExpr(e ast.Expr) (value Value, isAddr bool) {
135 if f.debugInfo() { // (opt)
137 for _, b := range f.Blocks {
138 for _, instr := range b.Instrs {
139 if ref, ok := instr.(*DebugRef); ok {
141 return ref.X, ref.IsAddr
150 // --- Lookup functions for source-level named entities (types.Objects) ---
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.
156 func (prog *Program) Package(obj *types.Package) *Package {
157 return prog.packages[obj]
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.
165 func (prog *Program) packageLevelValue(obj types.Object) Value {
166 if pkg, ok := prog.packages[obj.Pkg()]; ok {
167 return pkg.values[obj]
172 // FuncValue returns the concrete Function denoted by the source-level
173 // named function obj, or nil if obj denotes an interface method.
175 // TODO(adonovan): check the invariant that obj.Type() matches the
176 // result's Signature, both in the params/results and in the receiver.
178 func (prog *Program) FuncValue(obj *types.Func) *Function {
179 fn, _ := prog.packageLevelValue(obj).(*Function)
183 // ConstValue returns the IR Value denoted by the source-level named
186 func (prog *Program) ConstValue(obj *types.Const) *Const {
187 // TODO(adonovan): opt: share (don't reallocate)
188 // Consts for const objects and constant ast.Exprs.
190 // Universal constant? {true,false,nil}
191 if obj.Parent() == types.Universe {
192 return NewConst(obj.Val(), obj.Type())
194 // Package-level named constant?
195 if v := prog.packageLevelValue(obj); v != nil {
198 return NewConst(obj.Val(), obj.Type())
201 // VarValue returns the IR Value that corresponds to a specific
202 // identifier denoting the source-level named variable obj.
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.
208 // ref is the path to an ast.Ident (e.g. from PathEnclosingInterval),
209 // and that ident must resolve to obj.
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.)
214 // If the identifier is a field selector and its base expression is
215 // non-addressable, then VarValue returns the value of that field.
217 // func f() struct {x int}
218 // f().x // VarValue(x) returns a *Field instruction of type int
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.
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
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.
235 func (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)
239 return // e.g. def of struct field; IR not built?
242 id := ref[0].(*ast.Ident)
244 // Defining ident of a parameter?
245 if id.Pos() == obj.Pos() {
246 for _, param := range fn.Params {
247 if param.Object() == obj {
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
264 // Defining ident of package-level var?
265 if v := prog.packageLevelValue(obj); v != nil {
266 return v.(*Global), true
269 return // e.g. debug info not requested, or var optimized away