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