github.com/rsc/go@v0.0.0-20150416155037-e040fd465409/src/go/types/check.go (about) 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 7 package types 8 9 import ( 10 "go/ast" 11 "go/exact" 12 "go/token" 13 ) 14 15 // debugging/development support 16 const ( 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 // 32 const strict = false 33 34 // exprInfo stores information about an untyped expression. 35 type exprInfo struct { 36 isLhs bool // expression is lhs operand of a shift with delayed type-check 37 mode operandMode 38 typ *Basic 39 val exact.Value // constant value; or nil (if not a constant) 40 } 41 42 // funcInfo stores the information required for type-checking a function. 43 type 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. 51 type 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 exact.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. 62 type 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 87 // debugging 88 indent int // indentation for tracing 89 } 90 91 // addUnusedImport adds the position of a dot-imported package 92 // pkg to the map of dot imports for the given file scope. 93 func (check *Checker) addUnusedDotImport(scope *Scope, pkg *Package, pos token.Pos) { 94 mm := check.unusedDotImports 95 if mm == nil { 96 mm = make(map[*Scope]map[*Package]token.Pos) 97 check.unusedDotImports = mm 98 } 99 m := mm[scope] 100 if m == nil { 101 m = make(map[*Package]token.Pos) 102 mm[scope] = m 103 } 104 m[pkg] = pos 105 } 106 107 // addDeclDep adds the dependency edge (check.decl -> to) if check.decl exists 108 func (check *Checker) addDeclDep(to Object) { 109 from := check.decl 110 if from == nil { 111 return // not in a package-level init expression 112 } 113 if _, found := check.objMap[to]; !found { 114 return // to is not a package-level object 115 } 116 from.addDep(to) 117 } 118 119 func (check *Checker) assocMethod(tname string, meth *Func) { 120 m := check.methods 121 if m == nil { 122 m = make(map[string][]*Func) 123 check.methods = m 124 } 125 m[tname] = append(m[tname], meth) 126 } 127 128 func (check *Checker) rememberUntyped(e ast.Expr, lhs bool, mode operandMode, typ *Basic, val exact.Value) { 129 m := check.untyped 130 if m == nil { 131 m = make(map[ast.Expr]exprInfo) 132 check.untyped = m 133 } 134 m[e] = exprInfo{lhs, mode, typ, val} 135 } 136 137 func (check *Checker) later(name string, decl *declInfo, sig *Signature, body *ast.BlockStmt) { 138 check.funcs = append(check.funcs, funcInfo{name, decl, sig, body}) 139 } 140 141 func (check *Checker) delay(f func()) { 142 check.delayed = append(check.delayed, f) 143 } 144 145 // NewChecker returns a new Checker instance for a given package. 146 // Package files may be added incrementally via checker.Files. 147 func NewChecker(conf *Config, fset *token.FileSet, pkg *Package, info *Info) *Checker { 148 // make sure we have a configuration 149 if conf == nil { 150 conf = new(Config) 151 } 152 153 // make sure we have an info struct 154 if info == nil { 155 info = new(Info) 156 } 157 158 return &Checker{ 159 conf: conf, 160 fset: fset, 161 pkg: pkg, 162 Info: info, 163 objMap: make(map[Object]*declInfo), 164 } 165 } 166 167 // initFiles initializes the files-specific portion of checker. 168 // The provided files must all belong to the same package. 169 func (check *Checker) initFiles(files []*ast.File) { 170 // start with a clean slate (check.Files may be called multiple times) 171 check.files = nil 172 check.unusedDotImports = nil 173 174 check.firstErr = nil 175 check.methods = nil 176 check.untyped = nil 177 check.funcs = nil 178 check.delayed = nil 179 180 // determine package name and collect valid files 181 pkg := check.pkg 182 for _, file := range files { 183 switch name := file.Name.Name; pkg.name { 184 case "": 185 if name != "_" { 186 pkg.name = name 187 } else { 188 check.errorf(file.Name.Pos(), "invalid package name _") 189 } 190 fallthrough 191 192 case name: 193 check.files = append(check.files, file) 194 195 default: 196 check.errorf(file.Package, "package %s; expected %s", name, pkg.name) 197 // ignore this file 198 } 199 } 200 } 201 202 // A bailout panic is used for early termination. 203 type bailout struct{} 204 205 func (check *Checker) handleBailout(err *error) { 206 switch p := recover().(type) { 207 case nil, bailout: 208 // normal return or early exit 209 *err = check.firstErr 210 default: 211 // re-panic 212 panic(p) 213 } 214 } 215 216 // Files checks the provided files as part of the checker's package. 217 func (check *Checker) Files(files []*ast.File) (err error) { 218 defer check.handleBailout(&err) 219 220 check.initFiles(files) 221 222 check.collectObjects() 223 224 check.packageObjects(check.resolveOrder()) 225 226 check.functionBodies() 227 228 check.initOrder() 229 230 if !check.conf.DisableUnusedImportCheck { 231 check.unusedImports() 232 } 233 234 // perform delayed checks 235 for _, f := range check.delayed { 236 f() 237 } 238 239 check.recordUntyped() 240 241 check.pkg.complete = true 242 return 243 } 244 245 func (check *Checker) recordUntyped() { 246 if !debug && check.Types == nil { 247 return // nothing to do 248 } 249 250 for x, info := range check.untyped { 251 if debug && isTyped(info.typ) { 252 check.dump("%s: %s (type %s) is typed", x.Pos(), x, info.typ) 253 unreachable() 254 } 255 check.recordTypeAndValue(x, info.mode, info.typ, info.val) 256 } 257 } 258 259 func (check *Checker) recordTypeAndValue(x ast.Expr, mode operandMode, typ Type, val exact.Value) { 260 assert(x != nil) 261 assert(typ != nil) 262 if mode == invalid { 263 return // omit 264 } 265 assert(typ != nil) 266 if mode == constant { 267 assert(val != nil) 268 assert(typ == Typ[Invalid] || isConstType(typ)) 269 } 270 if m := check.Types; m != nil { 271 m[x] = TypeAndValue{mode, typ, val} 272 } 273 } 274 275 func (check *Checker) recordBuiltinType(f ast.Expr, sig *Signature) { 276 // f must be a (possibly parenthesized) identifier denoting a built-in 277 // (built-ins in package unsafe always produce a constant result and 278 // we don't record their signatures, so we don't see qualified idents 279 // here): record the signature for f and possible children. 280 for { 281 check.recordTypeAndValue(f, builtin, sig, nil) 282 switch p := f.(type) { 283 case *ast.Ident: 284 return // we're done 285 case *ast.ParenExpr: 286 f = p.X 287 default: 288 unreachable() 289 } 290 } 291 } 292 293 func (check *Checker) recordCommaOkTypes(x ast.Expr, a [2]Type) { 294 assert(x != nil) 295 if a[0] == nil || a[1] == nil { 296 return 297 } 298 assert(isTyped(a[0]) && isTyped(a[1]) && isBoolean(a[1])) 299 if m := check.Types; m != nil { 300 for { 301 tv := m[x] 302 assert(tv.Type != nil) // should have been recorded already 303 pos := x.Pos() 304 tv.Type = NewTuple( 305 NewVar(pos, check.pkg, "", a[0]), 306 NewVar(pos, check.pkg, "", a[1]), 307 ) 308 m[x] = tv 309 // if x is a parenthesized expression (p.X), update p.X 310 p, _ := x.(*ast.ParenExpr) 311 if p == nil { 312 break 313 } 314 x = p.X 315 } 316 } 317 } 318 319 func (check *Checker) recordDef(id *ast.Ident, obj Object) { 320 assert(id != nil) 321 if m := check.Defs; m != nil { 322 m[id] = obj 323 } 324 } 325 326 func (check *Checker) recordUse(id *ast.Ident, obj Object) { 327 assert(id != nil) 328 assert(obj != nil) 329 if m := check.Uses; m != nil { 330 m[id] = obj 331 } 332 } 333 334 func (check *Checker) recordImplicit(node ast.Node, obj Object) { 335 assert(node != nil) 336 assert(obj != nil) 337 if m := check.Implicits; m != nil { 338 m[node] = obj 339 } 340 } 341 342 func (check *Checker) recordSelection(x *ast.SelectorExpr, kind SelectionKind, recv Type, obj Object, index []int, indirect bool) { 343 assert(obj != nil && (recv == nil || len(index) > 0)) 344 check.recordUse(x.Sel, obj) 345 // TODO(gri) Should we also call recordTypeAndValue? 346 if m := check.Selections; m != nil { 347 m[x] = &Selection{kind, recv, obj, index, indirect} 348 } 349 } 350 351 func (check *Checker) recordScope(node ast.Node, scope *Scope) { 352 assert(node != nil) 353 assert(scope != nil) 354 if m := check.Scopes; m != nil { 355 m[node] = scope 356 } 357 }