github.com/aloncn/graphics-go@v0.0.1/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/constant" 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 constant.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 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. 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 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. 94 func (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 109 func (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 120 func (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 129 func (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 138 func (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 142 func (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. 148 func 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. 170 func (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. 204 type bailout struct{} 205 206 func (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. 218 func (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 246 func (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 260 func (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 276 func (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 294 func (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 320 func (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 327 func (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 335 func (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 343 func (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 352 func (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 }