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