Left: | ||
Right: |
OLD | NEW |
---|---|
1 // Copyright 2011 The Go Authors. All rights reserved. | 1 // Copyright 2011 The Go Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style | 2 // Use of this source code is governed by a BSD-style |
3 // license that can be found in the LICENSE file. | 3 // license that can be found in the LICENSE file. |
4 | 4 |
5 // This file implements the Check function, which typechecks a package. | 5 // This file implements the Check function, which typechecks a package. |
6 | 6 |
7 package types | 7 package types |
8 | 8 |
9 import ( | 9 import ( |
10 "fmt" | 10 "fmt" |
11 "go/ast" | 11 "go/ast" |
12 "go/scanner" | |
12 "go/token" | 13 "go/token" |
13 "sort" | 14 "sort" |
14 ) | 15 ) |
15 | 16 |
16 // enable for debugging | 17 // enable for debugging |
17 const trace = false | 18 const trace = false |
18 | 19 |
19 type checker struct { | 20 type checker struct { |
20 » fset *token.FileSet | 21 » ctxt *Context |
21 » pkg *ast.Package | 22 » fset *token.FileSet |
22 » errh func(token.Pos, string) | 23 » files []*ast.File |
23 » mapf func(ast.Expr, Type) | |
24 | 24 |
25 // lazily initialized | 25 // lazily initialized |
26 firsterr error | 26 firsterr error |
27 filenames []string // sorted list of package file n ames for reproducible iteration order | |
28 initexprs map[*ast.ValueSpec][]ast.Expr // "inherited" initialization ex pressions for constant declarations | 27 initexprs map[*ast.ValueSpec][]ast.Expr // "inherited" initialization ex pressions for constant declarations |
29 » functypes []*Signature // stack of function signatures; actively typechecked function on top | 28 » funclist []function // list of functions/methods wit h correct signatures and non-empty bodies |
29 » funcsig *Signature // signature of currently typech ecked function | |
30 pos []token.Pos // stack of expr positions; debu gging support, used if trace is set | 30 pos []token.Pos // stack of expr positions; debu gging support, used if trace is set |
31 } | 31 } |
32 | 32 |
33 type function struct { | |
34 obj *ast.Object // for debugging/tracing only | |
35 sig *Signature | |
36 body *ast.BlockStmt | |
37 } | |
38 | |
39 // later adds a function with non-empty body to the list of functions | |
40 // that need to be processed after all package-level declarations | |
41 // are typechecked. | |
42 // | |
43 func (check *checker) later(obj *ast.Object, sig *Signature, body *ast.BlockStmt ) { | |
44 // functions implemented elsewhere (say in assembly) have no body | |
45 if body != nil { | |
46 check.funclist = append(check.funclist, function{obj, sig, body} ) | |
47 } | |
48 } | |
49 | |
33 // declare declares an object of the given kind and name (ident) in scope; | 50 // declare declares an object of the given kind and name (ident) in scope; |
34 // decl is the corresponding declaration in the AST. An error is reported | 51 // decl is the corresponding declaration in the AST. An error is reported |
35 // if the object was declared before. | 52 // if the object was declared before. |
36 // | 53 // |
37 // TODO(gri) This is very similar to the declare function in go/parser; it | 54 // TODO(gri) This is very similar to the declare function in go/parser; it |
38 // is only used to associate methods with their respective receiver base types. | 55 // is only used to associate methods with their respective receiver base types. |
39 // In a future version, it might be simpler and cleaner to do all the resolution | 56 // In a future version, it might be simpler and cleaner to do all the resolution |
40 // in the type-checking phase. It would simplify the parser, AST, and also | 57 // in the type-checking phase. It would simplify the parser, AST, and also |
41 // reduce some amount of code duplication. | 58 // reduce some amount of code duplication. |
42 // | 59 // |
(...skipping 60 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
103 if len(rhs) > 0 { | 120 if len(rhs) > 0 { |
104 // TODO(gri) should try to avoid this conversion | 121 // TODO(gri) should try to avoid this conversion |
105 lhx := make([]ast.Expr, len(lhs)) | 122 lhx := make([]ast.Expr, len(lhs)) |
106 for i, e := range lhs { | 123 for i, e := range lhs { |
107 lhx[i] = e | 124 lhx[i] = e |
108 } | 125 } |
109 check.assignNtoM(lhx, rhs, true, iota) | 126 check.assignNtoM(lhx, rhs, true, iota) |
110 } | 127 } |
111 } | 128 } |
112 | 129 |
113 func (check *checker) function(typ *Signature, body *ast.BlockStmt) { | |
114 check.functypes = append(check.functypes, typ) | |
115 check.stmt(body) | |
116 check.functypes = check.functypes[0 : len(check.functypes)-1] | |
adonovan
2012/12/26 20:37:33
Small nitpick: this way of implementing a stack le
gri
2012/12/26 20:42:09
This code is gone...
| |
117 } | |
118 | |
119 // object typechecks an object by assigning it a type; obj.Type must be nil. | 130 // object typechecks an object by assigning it a type; obj.Type must be nil. |
120 // Callers must check obj.Type before calling object; this eliminates a call | 131 // Callers must check obj.Type before calling object; this eliminates a call |
121 // for each identifier that has been typechecked already, a common scenario. | 132 // for each identifier that has been typechecked already, a common scenario. |
122 // | 133 // |
123 func (check *checker) object(obj *ast.Object, cycleOk bool) { | 134 func (check *checker) object(obj *ast.Object, cycleOk bool) { |
124 assert(obj.Type == nil) | 135 assert(obj.Type == nil) |
125 | 136 |
126 switch obj.Kind { | 137 switch obj.Kind { |
127 case ast.Bad, ast.Pkg: | 138 case ast.Bad, ast.Pkg: |
128 // nothing to do | 139 // nothing to do |
(...skipping 28 matching lines...) Expand all Loading... | |
157 typ.Underlying = underlying(check.typ(obj.Decl.(*ast.TypeSpec).T ype, cycleOk)) | 168 typ.Underlying = underlying(check.typ(obj.Decl.(*ast.TypeSpec).T ype, cycleOk)) |
158 // typecheck associated method signatures | 169 // typecheck associated method signatures |
159 if obj.Data != nil { | 170 if obj.Data != nil { |
160 scope := obj.Data.(*ast.Scope) | 171 scope := obj.Data.(*ast.Scope) |
161 switch t := typ.Underlying.(type) { | 172 switch t := typ.Underlying.(type) { |
162 case *Struct: | 173 case *Struct: |
163 // struct fields must not conflict with methods | 174 // struct fields must not conflict with methods |
164 for _, f := range t.Fields { | 175 for _, f := range t.Fields { |
165 if m := scope.Lookup(f.Name); m != nil { | 176 if m := scope.Lookup(f.Name); m != nil { |
166 check.errorf(m.Pos(), "type %s h as both field and method named %s", obj.Name, f.Name) | 177 check.errorf(m.Pos(), "type %s h as both field and method named %s", obj.Name, f.Name) |
178 // ok to continue | |
167 } | 179 } |
168 } | 180 } |
169 // ok to continue | |
170 case *Interface: | 181 case *Interface: |
171 // methods cannot be associated with an interfac e type | 182 // methods cannot be associated with an interfac e type |
172 for _, m := range scope.Objects { | 183 for _, m := range scope.Objects { |
173 recv := m.Decl.(*ast.FuncDecl).Recv.List [0].Type | 184 recv := m.Decl.(*ast.FuncDecl).Recv.List [0].Type |
174 check.errorf(recv.Pos(), "invalid receiv er type %s (%s is an interface type)", obj.Name, obj.Name) | 185 check.errorf(recv.Pos(), "invalid receiv er type %s (%s is an interface type)", obj.Name, obj.Name) |
186 // ok to continue | |
175 } | 187 } |
176 // ok to continue | |
177 } | 188 } |
178 // typecheck method signatures | 189 // typecheck method signatures |
179 » » » for _, m := range scope.Objects { | 190 » » » for _, obj := range scope.Objects { |
180 » » » » mdecl := m.Decl.(*ast.FuncDecl) | 191 » » » » mdecl := obj.Decl.(*ast.FuncDecl) |
181 » » » » // TODO(gri) At the moment, the receiver is type -checked when checking | 192 » » » » sig := check.typ(mdecl.Type, cycleOk).(*Signatur e) |
182 » » » » // the method body. Also, we don't properly trac k if the receiver is | 193 » » » » params, _ := check.collectParams(mdecl.Recv, fal se) |
183 » » » » // a pointer (i.e., currently, method sets are t oo large). FIX THIS. | 194 » » » » sig.Recv = params[0] // the parser/assocMethod e nsure there is exactly one parameter |
184 » » » » mtyp := check.typ(mdecl.Type, cycleOk).(*Signatu re) | 195 » » » » obj.Type = sig |
185 » » » » m.Type = mtyp | 196 » » » » check.later(obj, sig, mdecl.Body) |
186 } | 197 } |
187 } | 198 } |
188 | 199 |
189 case ast.Fun: | 200 case ast.Fun: |
190 fdecl := obj.Decl.(*ast.FuncDecl) | 201 fdecl := obj.Decl.(*ast.FuncDecl) |
191 » » check.collectParams(fdecl.Recv, false) // ensure method base is type-checked | 202 » » // methods are typechecked when their receivers are typechecked |
192 » » ftyp := check.typ(fdecl.Type, cycleOk).(*Signature) | 203 » » if fdecl.Recv == nil { |
193 » » obj.Type = ftyp | 204 » » » sig := check.typ(fdecl.Type, cycleOk).(*Signature) |
194 » » // functions implemented elsewhere (say in assembly) have no bod y | 205 » » » if obj.Name == "init" && (len(sig.Params) != 0 || len(si g.Results) != 0) { |
195 » » if fdecl.Body != nil { | 206 » » » » check.errorf(fdecl.Pos(), "func init must have n o arguments and no return values") |
196 » » » check.function(ftyp, fdecl.Body) | 207 » » » » // ok to continue |
208 » » » } | |
209 » » » obj.Type = sig | |
210 » » » check.later(obj, sig, fdecl.Body) | |
197 } | 211 } |
198 | 212 |
199 default: | 213 default: |
200 panic("unreachable") | 214 panic("unreachable") |
201 } | 215 } |
202 } | 216 } |
203 | 217 |
204 // assocInitvals associates "inherited" initialization expressions | 218 // assocInitvals associates "inherited" initialization expressions |
205 // with the corresponding *ast.ValueSpec in the check.initexprs map | 219 // with the corresponding *ast.ValueSpec in the check.initexprs map |
206 // for constant declarations without explicit initialization expressions. | 220 // for constant declarations without explicit initialization expressions. |
(...skipping 19 matching lines...) Expand all Loading... | |
226 // | 240 // |
227 func (check *checker) assocMethod(meth *ast.FuncDecl) { | 241 func (check *checker) assocMethod(meth *ast.FuncDecl) { |
228 // The receiver type is one of the following (enforced by parser): | 242 // The receiver type is one of the following (enforced by parser): |
229 // - *ast.Ident | 243 // - *ast.Ident |
230 // - *ast.StarExpr{*ast.Ident} | 244 // - *ast.StarExpr{*ast.Ident} |
231 // - *ast.BadExpr (parser error) | 245 // - *ast.BadExpr (parser error) |
232 typ := meth.Recv.List[0].Type | 246 typ := meth.Recv.List[0].Type |
233 if ptr, ok := typ.(*ast.StarExpr); ok { | 247 if ptr, ok := typ.(*ast.StarExpr); ok { |
234 typ = ptr.X | 248 typ = ptr.X |
235 } | 249 } |
236 » // determine receiver base type object (or nil if error) | 250 » // determine receiver base type object |
237 var obj *ast.Object | 251 var obj *ast.Object |
238 if ident, ok := typ.(*ast.Ident); ok && ident.Obj != nil { | 252 if ident, ok := typ.(*ast.Ident); ok && ident.Obj != nil { |
239 obj = ident.Obj | 253 obj = ident.Obj |
240 if obj.Kind != ast.Typ { | 254 if obj.Kind != ast.Typ { |
241 check.errorf(ident.Pos(), "%s is not a type", ident.Name ) | 255 check.errorf(ident.Pos(), "%s is not a type", ident.Name ) |
242 » » » obj = nil | 256 » » » return // ignore this method |
243 } | 257 } |
244 // TODO(gri) determine if obj was defined in this package | 258 // TODO(gri) determine if obj was defined in this package |
245 /* | 259 /* |
246 if check.notLocal(obj) { | 260 if check.notLocal(obj) { |
247 check.errorf(ident.Pos(), "cannot define methods on non-local type %s", ident.Name) | 261 check.errorf(ident.Pos(), "cannot define methods on non-local type %s", ident.Name) |
248 » » » » obj = nil | 262 » » » » return // ignore this method |
249 } | 263 } |
250 */ | 264 */ |
251 } else { | 265 } else { |
252 // If it's not an identifier or the identifier wasn't declared/r esolved, | 266 // If it's not an identifier or the identifier wasn't declared/r esolved, |
253 // the parser/resolver already reported an error. Nothing to do here. | 267 // the parser/resolver already reported an error. Nothing to do here. |
268 return // ignore this method | |
254 } | 269 } |
255 » // determine base type scope (or nil if error) | 270 » // declare method in receiver base type scope |
256 var scope *ast.Scope | 271 var scope *ast.Scope |
257 » if obj != nil { | 272 » if obj.Data != nil { |
258 » » if obj.Data != nil { | 273 » » scope = obj.Data.(*ast.Scope) |
259 » » » scope = obj.Data.(*ast.Scope) | |
260 » » } else { | |
261 » » » scope = ast.NewScope(nil) | |
262 » » » obj.Data = scope | |
263 » » } | |
264 } else { | 274 } else { |
265 // use a dummy scope so that meth can be declared in | |
266 // presence of an error and get an associated object | |
267 // (always use a new scope so that we don't get double | |
268 // declaration errors) | |
269 scope = ast.NewScope(nil) | 275 scope = ast.NewScope(nil) |
276 obj.Data = scope | |
270 } | 277 } |
271 check.declare(scope, ast.Fun, meth.Name, meth) | 278 check.declare(scope, ast.Fun, meth.Name, meth) |
272 } | 279 } |
273 | 280 |
274 func (check *checker) assocInitvalsOrMethod(decl ast.Decl) { | 281 func (check *checker) assocInitvalsOrMethod(decl ast.Decl) { |
275 switch d := decl.(type) { | 282 switch d := decl.(type) { |
276 case *ast.GenDecl: | 283 case *ast.GenDecl: |
277 if d.Tok == token.CONST { | 284 if d.Tok == token.CONST { |
278 check.assocInitvals(d) | 285 check.assocInitvals(d) |
279 } | 286 } |
(...skipping 21 matching lines...) Expand all Loading... | |
301 } | 308 } |
302 case *ast.TypeSpec: | 309 case *ast.TypeSpec: |
303 if obj := s.Name.Obj; obj.Type == nil { | 310 if obj := s.Name.Obj; obj.Type == nil { |
304 check.object(obj, false) | 311 check.object(obj, false) |
305 } | 312 } |
306 default: | 313 default: |
307 check.invalidAST(s.Pos(), "unknown ast.Spec node %T", s) | 314 check.invalidAST(s.Pos(), "unknown ast.Spec node %T", s) |
308 } | 315 } |
309 } | 316 } |
310 case *ast.FuncDecl: | 317 case *ast.FuncDecl: |
311 » » if d.Name.Name == "init" { | 318 » » // methods are checked when their respective base types are chec ked |
312 » » » // initialization function | 319 » » if d.Recv != nil { |
313 » » » // TODO(gri) ignore for now (has no object associated wi th it) | |
314 » » » // (should probably collect in a first phase and properl y initialize) | |
315 return | 320 return |
316 } | 321 } |
317 » » if obj := d.Name.Obj; obj.Type == nil { | 322 » » obj := d.Name.Obj |
323 » » // Initialization functions don't have an object associated with them | |
324 » » // since they are not in any scope. Create a dummy object for th em. | |
325 » » if d.Name.Name == "init" { | |
326 » » » assert(obj == nil) // all other functions should have an object | |
327 » » » obj = ast.NewObj(ast.Fun, d.Name.Name) | |
328 » » » obj.Decl = d | |
329 » » » d.Name.Obj = obj | |
330 » » } | |
331 » » if obj.Type == nil { | |
318 check.object(obj, false) | 332 check.object(obj, false) |
319 } | 333 } |
320 default: | 334 default: |
321 check.invalidAST(d.Pos(), "unknown ast.Decl node %T", d) | 335 check.invalidAST(d.Pos(), "unknown ast.Decl node %T", d) |
322 } | 336 } |
323 } | 337 } |
324 | 338 |
325 // iterate calls f for each package-level declaration. | 339 // iterate calls f for each package-level declaration. |
326 func (check *checker) iterate(f func(*checker, ast.Decl)) { | 340 func (check *checker) iterate(f func(*checker, ast.Decl)) { |
327 » list := check.filenames | 341 » for _, file := range check.files { |
328 | 342 » » for _, decl := range file.Decls { |
329 » if list == nil { | |
330 » » // initialize lazily | |
331 » » for filename := range check.pkg.Files { | |
332 » » » list = append(list, filename) | |
333 » » } | |
334 » » sort.Strings(list) | |
335 » » check.filenames = list | |
336 » } | |
337 | |
338 » for _, filename := range list { | |
339 » » for _, decl := range check.pkg.Files[filename].Decls { | |
340 f(check, decl) | 343 f(check, decl) |
341 } | 344 } |
342 } | 345 } |
343 } | 346 } |
344 | 347 |
348 // sortedFiles returns the sorted list of package files given a package file map . | |
349 func sortedFiles(m map[string]*ast.File) []*ast.File { | |
350 keys := make([]string, len(m)) | |
351 i := 0 | |
352 for k, _ := range m { | |
353 keys[i] = k | |
354 i++ | |
355 } | |
356 sort.Strings(keys) | |
357 | |
358 files := make([]*ast.File, len(m)) | |
359 for i, k := range keys { | |
360 files[i] = m[k] | |
361 } | |
362 | |
363 return files | |
364 } | |
365 | |
345 // A bailout panic is raised to indicate early termination. | 366 // A bailout panic is raised to indicate early termination. |
346 type bailout struct{} | 367 type bailout struct{} |
347 | 368 |
348 func check(fset *token.FileSet, pkg *ast.Package, errh func(token.Pos, string), f func(ast.Expr, Type)) (err error) { | 369 func check(ctxt *Context, fset *token.FileSet, files map[string]*ast.File) (pkg *ast.Package, err error) { |
349 // initialize checker | 370 // initialize checker |
350 » var check checker | 371 » check := checker{ |
351 » check.fset = fset | 372 » » ctxt: ctxt, |
352 » check.pkg = pkg | 373 » » fset: fset, |
353 » check.errh = errh | 374 » » files: sortedFiles(files), |
354 » check.mapf = f | 375 » » initexprs: make(map[*ast.ValueSpec][]ast.Expr), |
355 » check.initexprs = make(map[*ast.ValueSpec][]ast.Expr) | 376 » } |
356 | 377 |
357 // handle panics | 378 // handle panics |
358 defer func() { | 379 defer func() { |
359 switch p := recover().(type) { | 380 switch p := recover().(type) { |
360 case nil: | 381 case nil: |
361 // normal return - nothing to do | 382 // normal return - nothing to do |
362 case bailout: | 383 case bailout: |
363 // early exit | 384 // early exit |
364 err = check.firsterr | 385 err = check.firsterr |
365 default: | 386 default: |
366 // unexpected panic: don't crash clients | 387 // unexpected panic: don't crash clients |
367 // panic(p) // enable for debugging | 388 // panic(p) // enable for debugging |
368 » » » err = fmt.Errorf("types.check internal error: %v", p) | 389 » » » // TODO(gri) add a test case for this scenario |
390 » » » err = fmt.Errorf("types internal error: %v", p) | |
369 } | 391 } |
370 }() | 392 }() |
371 | 393 |
394 // resolve identifiers | |
395 imp := ctxt.Import | |
396 if imp == nil { | |
397 imp = GcImport | |
398 } | |
399 pkg, err = ast.NewPackage(fset, files, imp, Universe) | |
400 if err != nil { | |
401 if list, _ := err.(scanner.ErrorList); len(list) > 0 { | |
402 for _, err := range list { | |
403 check.err(err) | |
404 } | |
405 } else { | |
406 check.err(err) | |
407 } | |
408 } | |
409 | |
372 // determine missing constant initialization expressions | 410 // determine missing constant initialization expressions |
373 // and associate methods with types | 411 // and associate methods with types |
374 check.iterate((*checker).assocInitvalsOrMethod) | 412 check.iterate((*checker).assocInitvalsOrMethod) |
375 | 413 |
376 // typecheck all declarations | 414 // typecheck all declarations |
377 check.iterate((*checker).decl) | 415 check.iterate((*checker).decl) |
378 | 416 |
417 // typecheck all function/method bodies | |
418 // (funclist may grow when checking statements - do not use range clause !) | |
419 for i := 0; i < len(check.funclist); i++ { | |
420 f := check.funclist[i] | |
421 if trace { | |
422 s := "<function literal>" | |
423 if f.obj != nil { | |
424 s = f.obj.Name | |
425 } | |
426 fmt.Println("---", s) | |
427 } | |
428 check.funcsig = f.sig | |
429 check.stmtList(f.body.List) | |
430 } | |
431 | |
379 return | 432 return |
380 } | 433 } |
OLD | NEW |