1// Copyright 2011 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// This file implements the Check function, which drives type-checking.
6
7package types
8
9import (
10	"go/ast"
11	"go/constant"
12	"go/token"
13)
14
15// debugging/development support
16const (
17	debug = false // leave on during development
18	trace = false // turn on for detailed type resolution traces
19)
20
21// If Strict is set, the type-checker enforces additional
22// rules not specified by the Go 1 spec, but which will
23// catch guaranteed run-time errors if the respective
24// code is executed. In other words, programs passing in
25// Strict mode are Go 1 compliant, but not all Go 1 programs
26// will pass in Strict mode. The additional rules are:
27//
28// - A type assertion x.(T) where T is an interface type
29//   is invalid if any (statically known) method that exists
30//   for both x and T have different signatures.
31//
32const strict = false
33
34// exprInfo stores information about an untyped expression.
35type exprInfo struct {
36	isLhs bool // expression is lhs operand of a shift with delayed type-check
37	mode  operandMode
38	typ   *Basic
39	val   constant.Value // constant value; or nil (if not a constant)
40}
41
42// funcInfo stores the information required for type-checking a function.
43type funcInfo struct {
44	name string    // for debugging/tracing only
45	decl *declInfo // for cycle detection
46	sig  *Signature
47	body *ast.BlockStmt
48}
49
50// A context represents the context within which an object is type-checked.
51type context struct {
52	decl          *declInfo      // package-level declaration whose init expression/function body is checked
53	scope         *Scope         // top-most scope for lookups
54	iota          constant.Value // value of iota in a constant declaration; nil otherwise
55	sig           *Signature     // function signature if inside a function; nil otherwise
56	hasLabel      bool           // set if a function makes use of labels (only ~1% of functions); unused outside functions
57	hasCallOrRecv bool           // set if an expression contains a function call or channel receive operation
58}
59
60// A Checker maintains the state of the type checker.
61// It must be created with NewChecker.
62type Checker struct {
63	// package information
64	// (initialized by NewChecker, valid for the life-time of checker)
65	conf *Config
66	fset *token.FileSet
67	pkg  *Package
68	*Info
69	objMap map[Object]*declInfo // maps package-level object to declaration info
70
71	// information collected during type-checking of a set of package files
72	// (initialized by Files, valid only for the duration of check.Files;
73	// maps and lists are allocated on demand)
74	files            []*ast.File                       // package files
75	unusedDotImports map[*Scope]map[*Package]token.Pos // positions of unused dot-imported packages for each file scope
76
77	firstErr error                 // first error encountered
78	methods  map[string][]*Func    // maps type names to associated methods
79	untyped  map[ast.Expr]exprInfo // map of expressions without final type
80	funcs    []funcInfo            // list of functions to type-check
81	delayed  []func()              // delayed checks requiring fully setup types
82
83	// context within which the current object is type-checked
84	// (valid only for the duration of type-checking a specific object)
85	context
86	pos token.Pos // if valid, identifiers are looked up as if at position pos (used by Eval)
87
88	// debugging
89	indent int // indentation for tracing
90}
91
92// addUnusedImport adds the position of a dot-imported package
93// pkg to the map of dot imports for the given file scope.
94func (check *Checker) addUnusedDotImport(scope *Scope, pkg *Package, pos token.Pos) {
95	mm := check.unusedDotImports
96	if mm == nil {
97		mm = make(map[*Scope]map[*Package]token.Pos)
98		check.unusedDotImports = mm
99	}
100	m := mm[scope]
101	if m == nil {
102		m = make(map[*Package]token.Pos)
103		mm[scope] = m
104	}
105	m[pkg] = pos
106}
107
108// addDeclDep adds the dependency edge (check.decl -> to) if check.decl exists
109func (check *Checker) addDeclDep(to Object) {
110	from := check.decl
111	if from == nil {
112		return // not in a package-level init expression
113	}
114	if _, found := check.objMap[to]; !found {
115		return // to is not a package-level object
116	}
117	from.addDep(to)
118}
119
120func (check *Checker) assocMethod(tname string, meth *Func) {
121	m := check.methods
122	if m == nil {
123		m = make(map[string][]*Func)
124		check.methods = m
125	}
126	m[tname] = append(m[tname], meth)
127}
128
129func (check *Checker) rememberUntyped(e ast.Expr, lhs bool, mode operandMode, typ *Basic, val constant.Value) {
130	m := check.untyped
131	if m == nil {
132		m = make(map[ast.Expr]exprInfo)
133		check.untyped = m
134	}
135	m[e] = exprInfo{lhs, mode, typ, val}
136}
137
138func (check *Checker) later(name string, decl *declInfo, sig *Signature, body *ast.BlockStmt) {
139	check.funcs = append(check.funcs, funcInfo{name, decl, sig, body})
140}
141
142func (check *Checker) delay(f func()) {
143	check.delayed = append(check.delayed, f)
144}
145
146// NewChecker returns a new Checker instance for a given package.
147// Package files may be added incrementally via checker.Files.
148func NewChecker(conf *Config, fset *token.FileSet, pkg *Package, info *Info) *Checker {
149	// make sure we have a configuration
150	if conf == nil {
151		conf = new(Config)
152	}
153
154	// make sure we have an info struct
155	if info == nil {
156		info = new(Info)
157	}
158
159	return &Checker{
160		conf:   conf,
161		fset:   fset,
162		pkg:    pkg,
163		Info:   info,
164		objMap: make(map[Object]*declInfo),
165	}
166}
167
168// initFiles initializes the files-specific portion of checker.
169// The provided files must all belong to the same package.
170func (check *Checker) initFiles(files []*ast.File) {
171	// start with a clean slate (check.Files may be called multiple times)
172	check.files = nil
173	check.unusedDotImports = nil
174
175	check.firstErr = nil
176	check.methods = nil
177	check.untyped = nil
178	check.funcs = nil
179	check.delayed = nil
180
181	// determine package name and collect valid files
182	pkg := check.pkg
183	for _, file := range files {
184		switch name := file.Name.Name; pkg.name {
185		case "":
186			if name != "_" {
187				pkg.name = name
188			} else {
189				check.errorf(file.Name.Pos(), "invalid package name _")
190			}
191			fallthrough
192
193		case name:
194			check.files = append(check.files, file)
195
196		default:
197			check.errorf(file.Package, "package %s; expected %s", name, pkg.name)
198			// ignore this file
199		}
200	}
201}
202
203// A bailout panic is used for early termination.
204type bailout struct{}
205
206func (check *Checker) handleBailout(err *error) {
207	switch p := recover().(type) {
208	case nil, bailout:
209		// normal return or early exit
210		*err = check.firstErr
211	default:
212		// re-panic
213		panic(p)
214	}
215}
216
217// Files checks the provided files as part of the checker's package.
218func (check *Checker) Files(files []*ast.File) (err error) {
219	defer check.handleBailout(&err)
220
221	check.initFiles(files)
222
223	check.collectObjects()
224
225	check.packageObjects(check.resolveOrder())
226
227	check.functionBodies()
228
229	check.initOrder()
230
231	if !check.conf.DisableUnusedImportCheck {
232		check.unusedImports()
233	}
234
235	// perform delayed checks
236	for _, f := range check.delayed {
237		f()
238	}
239
240	check.recordUntyped()
241
242	check.pkg.complete = true
243	return
244}
245
246func (check *Checker) recordUntyped() {
247	if !debug && check.Types == nil {
248		return // nothing to do
249	}
250
251	for x, info := range check.untyped {
252		if debug && isTyped(info.typ) {
253			check.dump("%s: %s (type %s) is typed", x.Pos(), x, info.typ)
254			unreachable()
255		}
256		check.recordTypeAndValue(x, info.mode, info.typ, info.val)
257	}
258}
259
260func (check *Checker) recordTypeAndValue(x ast.Expr, mode operandMode, typ Type, val constant.Value) {
261	assert(x != nil)
262	assert(typ != nil)
263	if mode == invalid {
264		return // omit
265	}
266	assert(typ != nil)
267	if mode == constant_ {
268		assert(val != nil)
269		assert(typ == Typ[Invalid] || isConstType(typ))
270	}
271	if m := check.Types; m != nil {
272		m[x] = TypeAndValue{mode, typ, val}
273	}
274}
275
276func (check *Checker) recordBuiltinType(f ast.Expr, sig *Signature) {
277	// f must be a (possibly parenthesized) identifier denoting a built-in
278	// (built-ins in package unsafe always produce a constant result and
279	// we don't record their signatures, so we don't see qualified idents
280	// here): record the signature for f and possible children.
281	for {
282		check.recordTypeAndValue(f, builtin, sig, nil)
283		switch p := f.(type) {
284		case *ast.Ident:
285			return // we're done
286		case *ast.ParenExpr:
287			f = p.X
288		default:
289			unreachable()
290		}
291	}
292}
293
294func (check *Checker) recordCommaOkTypes(x ast.Expr, a [2]Type) {
295	assert(x != nil)
296	if a[0] == nil || a[1] == nil {
297		return
298	}
299	assert(isTyped(a[0]) && isTyped(a[1]) && isBoolean(a[1]))
300	if m := check.Types; m != nil {
301		for {
302			tv := m[x]
303			assert(tv.Type != nil) // should have been recorded already
304			pos := x.Pos()
305			tv.Type = NewTuple(
306				NewVar(pos, check.pkg, "", a[0]),
307				NewVar(pos, check.pkg, "", a[1]),
308			)
309			m[x] = tv
310			// if x is a parenthesized expression (p.X), update p.X
311			p, _ := x.(*ast.ParenExpr)
312			if p == nil {
313				break
314			}
315			x = p.X
316		}
317	}
318}
319
320func (check *Checker) recordDef(id *ast.Ident, obj Object) {
321	assert(id != nil)
322	if m := check.Defs; m != nil {
323		m[id] = obj
324	}
325}
326
327func (check *Checker) recordUse(id *ast.Ident, obj Object) {
328	assert(id != nil)
329	assert(obj != nil)
330	if m := check.Uses; m != nil {
331		m[id] = obj
332	}
333}
334
335func (check *Checker) recordImplicit(node ast.Node, obj Object) {
336	assert(node != nil)
337	assert(obj != nil)
338	if m := check.Implicits; m != nil {
339		m[node] = obj
340	}
341}
342
343func (check *Checker) recordSelection(x *ast.SelectorExpr, kind SelectionKind, recv Type, obj Object, index []int, indirect bool) {
344	assert(obj != nil && (recv == nil || len(index) > 0))
345	check.recordUse(x.Sel, obj)
346	// TODO(gri) Should we also call recordTypeAndValue?
347	if m := check.Selections; m != nil {
348		m[x] = &Selection{kind, recv, obj, index, indirect}
349	}
350}
351
352func (check *Checker) recordScope(node ast.Node, scope *Scope) {
353	assert(node != nil)
354	assert(scope != nil)
355	if m := check.Scopes; m != nil {
356		m[node] = scope
357	}
358}
359