GoPLS Viewer

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