Rietveld Code Review Tool
Help | Bug tracker | Discussion group | Source code | Sign in
(865)

Side by Side Diff: src/pkg/exp/types/check.go

Issue 6948071: code review 6948071: exp/types: configurable types.Check API (Closed)
Patch Set: diff -r aa5d9f234a8e https://code.google.com/p/go Created 11 years, 3 months ago
Left:
Right:
Use n/p to move between diff chunks; N/P to move between comments. Please Sign in to add in-line comments.
Jump to:
View unified diff | Download patch
OLDNEW
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
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
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
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
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 }
OLDNEW

Powered by Google App Engine
RSS Feeds Recent Issues | This issue
This is Rietveld f62528b