1
0
mirror of https://github.com/golang/go synced 2024-11-20 01:34:41 -07:00

go/ast, go/parser: populate identifier scopes at parse time

The parser populates all scopes for a given file (except
type-related scopes for structs, interfaces, and methods
of types) at parse time.

A new parser flag, DeclarationErrors, enables error messages
related to declaration errors (as far as it is possible to
provide them).

The resulting AST has all (non-field, non-method) identifiers
resolved that can be resolved w/o doing imports or parsing
missing package files.

The ast.File node contains the (partially complete)
package scope and a list of unresolved global identifiers.

All type-specific data structures have been removed from the AST.

The existing typechecker is functional but needs to be adjusted
(simplified) accordingly. Utility functions to resolve all
identifiers for a package (after handling imports and parsing
all package files) are  missing.

Unrelated changes:
- Rename typechecker/testdata files to that they are not considered
  by gofmt.
- Minor cleanups/simplifications.

Parses all .go files in src and misc without declaration errors.
Runs all tests. Changes do not affect gofmt output.

R=rsc
CC=golang-dev
https://golang.org/cl/4244049
This commit is contained in:
Robert Griesemer 2011-03-07 11:01:23 -08:00
parent 4b4a1ea899
commit 021b040a1e
16 changed files with 560 additions and 345 deletions

View File

@ -27,7 +27,7 @@ var stmtTests = []test{
CErr("i, u := 1, 2", atLeastOneDecl), CErr("i, u := 1, 2", atLeastOneDecl),
Val2("i, x := 2, f", "i", 2, "x", 1.0), Val2("i, x := 2, f", "i", 2, "x", 1.0),
// Various errors // Various errors
CErr("1 := 2", "left side of := must be a name"), CErr("1 := 2", "expected identifier"),
CErr("c, a := 1, 1", "cannot assign"), CErr("c, a := 1, 1", "cannot assign"),
// Unpacking // Unpacking
Val2("x, y := oneTwo()", "x", 1, "y", 2), Val2("x, y := oneTwo()", "x", 1, "y", 2),

View File

@ -941,6 +941,8 @@ type File struct {
Package token.Pos // position of "package" keyword Package token.Pos // position of "package" keyword
Name *Ident // package name Name *Ident // package name
Decls []Decl // top-level declarations; or nil Decls []Decl // top-level declarations; or nil
Scope *Scope // package scope
Unresolved []*Ident // unresolved global identifiers
Comments []*CommentGroup // list of all comments in the source file Comments []*CommentGroup // list of all comments in the source file
} }
@ -959,7 +961,7 @@ func (f *File) End() token.Pos {
// //
type Package struct { type Package struct {
Name string // package name Name string // package name
Scope *Scope // package scope; or nil Scope *Scope // package scope
Files map[string]*File // Go source files by filename Files map[string]*File // Go source files by filename
} }

View File

@ -425,5 +425,6 @@ func MergePackageFiles(pkg *Package, mode MergeMode) *File {
} }
} }
return &File{doc, pos, NewIdent(pkg.Name), decls, comments} // TODO(gri) need to compute pkgScope and unresolved identifiers!
return &File{doc, pos, NewIdent(pkg.Name), decls, nil, nil, comments}
} }

View File

@ -2,31 +2,31 @@
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
// This file implements scopes, the objects they contain, // This file implements scopes and the objects they contain.
// and object types.
package ast package ast
import (
"bytes"
"fmt"
"go/token"
)
// A Scope maintains the set of named language entities declared // A Scope maintains the set of named language entities declared
// in the scope and a link to the immediately surrounding (outer) // in the scope and a link to the immediately surrounding (outer)
// scope. // scope.
// //
type Scope struct { type Scope struct {
Outer *Scope Outer *Scope
Objects []*Object // in declaration order Objects map[string]*Object
// Implementation note: In some cases (struct fields,
// function parameters) we need the source order of
// variables. Thus for now, we store scope entries
// in a linear list. If scopes become very large
// (say, for packages), we may need to change this
// to avoid slow lookups.
} }
// NewScope creates a new scope nested in the outer scope. // NewScope creates a new scope nested in the outer scope.
func NewScope(outer *Scope) *Scope { func NewScope(outer *Scope) *Scope {
const n = 4 // initial scope capacity, must be > 0 const n = 4 // initial scope capacity
return &Scope{outer, make([]*Object, 0, n)} return &Scope{outer, make(map[string]*Object, n)}
} }
@ -34,73 +34,108 @@ func NewScope(outer *Scope) *Scope {
// found in scope s, otherwise it returns nil. Outer scopes // found in scope s, otherwise it returns nil. Outer scopes
// are ignored. // are ignored.
// //
// Lookup always returns nil if name is "_", even if the scope
// contains objects with that name.
//
func (s *Scope) Lookup(name string) *Object { func (s *Scope) Lookup(name string) *Object {
if name != "_" { return s.Objects[name]
for _, obj := range s.Objects {
if obj.Name == name {
return obj
}
}
}
return nil
} }
// Insert attempts to insert a named object into the scope s. // Insert attempts to insert a named object into the scope s.
// If the scope does not contain an object with that name yet // If the scope does not contain an object with that name yet,
// or if the object is named "_", Insert inserts the object // Insert inserts the object and returns it. Otherwise, Insert
// and returns it. Otherwise, Insert leaves the scope unchanged // leaves the scope unchanged and returns the object found in
// and returns the object found in the scope instead. // the scope instead.
// //
func (s *Scope) Insert(obj *Object) *Object { func (s *Scope) Insert(obj *Object) (alt *Object) {
alt := s.Lookup(obj.Name) if alt = s.Objects[obj.Name]; alt == nil {
if alt == nil { s.Objects[obj.Name] = obj
s.append(obj)
alt = obj alt = obj
} }
return alt return
} }
func (s *Scope) append(obj *Object) { // Debugging support
s.Objects = append(s.Objects, obj) func (s *Scope) String() string {
var buf bytes.Buffer
fmt.Fprintf(&buf, "scope %p {", s)
if s != nil && len(s.Objects) > 0 {
fmt.Fprintln(&buf)
for _, obj := range s.Objects {
fmt.Fprintf(&buf, "\t%s %s\n", obj.Kind, obj.Name)
} }
}
fmt.Fprintf(&buf, "}\n")
return buf.String()
}
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
// Objects // Objects
// An Object describes a language entity such as a package, // An Object describes a named language entity such as a package,
// constant, type, variable, or function (incl. methods). // constant, type, variable, function (incl. methods), or label.
// //
type Object struct { type Object struct {
Kind Kind Kind ObjKind
Name string // declared name Name string // declared name
Type *Type Decl interface{} // corresponding Field, XxxSpec, FuncDecl, or LabeledStmt; or nil
Decl interface{} // corresponding Field, XxxSpec or FuncDecl Type interface{} // place holder for type information; may be nil
N int // value of iota for this declaration
} }
// NewObj creates a new object of a given kind and name. // NewObj creates a new object of a given kind and name.
func NewObj(kind Kind, name string) *Object { func NewObj(kind ObjKind, name string) *Object {
return &Object{Kind: kind, Name: name} return &Object{Kind: kind, Name: name}
} }
// Kind describes what an object represents. // Pos computes the source position of the declaration of an object name.
type Kind int // The result may be an invalid position if it cannot be computed
// (obj.Decl may be nil or not correct).
func (obj *Object) Pos() token.Pos {
name := obj.Name
switch d := obj.Decl.(type) {
case *Field:
for _, n := range d.Names {
if n.Name == name {
return n.Pos()
}
}
case *ValueSpec:
for _, n := range d.Names {
if n.Name == name {
return n.Pos()
}
}
case *TypeSpec:
if d.Name.Name == name {
return d.Name.Pos()
}
case *FuncDecl:
if d.Name.Name == name {
return d.Name.Pos()
}
case *LabeledStmt:
if d.Label.Name == name {
return d.Label.Pos()
}
}
return token.NoPos
}
// ObKind describes what an object represents.
type ObjKind int
// The list of possible Object kinds. // The list of possible Object kinds.
const ( const (
Bad Kind = iota // for error handling Bad ObjKind = iota // for error handling
Pkg // package Pkg // package
Con // constant Con // constant
Typ // type Typ // type
Var // variable Var // variable
Fun // function or method Fun // function or method
Lbl // label
) )
@ -111,132 +146,8 @@ var objKindStrings = [...]string{
Typ: "type", Typ: "type",
Var: "var", Var: "var",
Fun: "func", Fun: "func",
Lbl: "label",
} }
func (kind Kind) String() string { return objKindStrings[kind] } func (kind ObjKind) String() string { return objKindStrings[kind] }
// IsExported returns whether obj is exported.
func (obj *Object) IsExported() bool { return IsExported(obj.Name) }
// ----------------------------------------------------------------------------
// Types
// A Type represents a Go type.
type Type struct {
Form Form
Obj *Object // corresponding type name, or nil
Scope *Scope // fields and methods, always present
N uint // basic type id, array length, number of function results, or channel direction
Key, Elt *Type // map key and array, pointer, slice, map or channel element
Params *Scope // function (receiver, input and result) parameters, tuple expressions (results of function calls), or nil
Expr Expr // corresponding AST expression
}
// NewType creates a new type of a given form.
func NewType(form Form) *Type {
return &Type{Form: form, Scope: NewScope(nil)}
}
// Form describes the form of a type.
type Form int
// The list of possible type forms.
const (
BadType Form = iota // for error handling
Unresolved // type not fully setup
Basic
Array
Struct
Pointer
Function
Method
Interface
Slice
Map
Channel
Tuple
)
var formStrings = [...]string{
BadType: "badType",
Unresolved: "unresolved",
Basic: "basic",
Array: "array",
Struct: "struct",
Pointer: "pointer",
Function: "function",
Method: "method",
Interface: "interface",
Slice: "slice",
Map: "map",
Channel: "channel",
Tuple: "tuple",
}
func (form Form) String() string { return formStrings[form] }
// The list of basic type id's.
const (
Bool = iota
Byte
Uint
Int
Float
Complex
Uintptr
String
Uint8
Uint16
Uint32
Uint64
Int8
Int16
Int32
Int64
Float32
Float64
Complex64
Complex128
// TODO(gri) ideal types are missing
)
var BasicTypes = map[uint]string{
Bool: "bool",
Byte: "byte",
Uint: "uint",
Int: "int",
Float: "float",
Complex: "complex",
Uintptr: "uintptr",
String: "string",
Uint8: "uint8",
Uint16: "uint16",
Uint32: "uint32",
Uint64: "uint64",
Int8: "int8",
Int16: "int16",
Int32: "int32",
Int64: "int64",
Float32: "float32",
Float64: "float64",
Complex64: "complex64",
Complex128: "complex128",
}

View File

@ -17,10 +17,6 @@ import (
) )
// noPos is used when there is no corresponding source position for a token.
var noPos token.Position
// The mode parameter to the Parse* functions is a set of flags (or 0). // The mode parameter to the Parse* functions is a set of flags (or 0).
// They control the amount of source code parsed and other optional // They control the amount of source code parsed and other optional
// parser functionality. // parser functionality.
@ -30,6 +26,7 @@ const (
ImportsOnly // parsing stops after import declarations ImportsOnly // parsing stops after import declarations
ParseComments // parse comments and add them to AST ParseComments // parse comments and add them to AST
Trace // print a trace of parsed productions Trace // print a trace of parsed productions
DeclarationErrors // report declaration errors
) )
@ -46,8 +43,8 @@ type parser struct {
// Comments // Comments
comments []*ast.CommentGroup comments []*ast.CommentGroup
leadComment *ast.CommentGroup // the last lead comment leadComment *ast.CommentGroup // last lead comment
lineComment *ast.CommentGroup // the last line comment lineComment *ast.CommentGroup // last line comment
// Next token // Next token
pos token.Pos // token position pos token.Pos // token position
@ -56,6 +53,16 @@ type parser struct {
// Non-syntactic parser control // Non-syntactic parser control
exprLev int // < 0: in control clause, >= 0: in expression exprLev int // < 0: in control clause, >= 0: in expression
// Ordinary identifer scopes
pkgScope *ast.Scope // pkgScope.Outer == nil
topScope *ast.Scope // top-most scope; may be pkgScope
unresolved []*ast.Ident // unresolved global identifiers
// Label scope
// (maintained by open/close LabelScope)
labelScope *ast.Scope // label scope for current function
targetStack [][]*ast.Ident // stack of unresolved labels
} }
@ -72,9 +79,117 @@ func scannerMode(mode uint) uint {
func (p *parser) init(fset *token.FileSet, filename string, src []byte, mode uint) { func (p *parser) init(fset *token.FileSet, filename string, src []byte, mode uint) {
p.file = fset.AddFile(filename, fset.Base(), len(src)) p.file = fset.AddFile(filename, fset.Base(), len(src))
p.scanner.Init(p.file, src, p, scannerMode(mode)) p.scanner.Init(p.file, src, p, scannerMode(mode))
p.mode = mode p.mode = mode
p.trace = mode&Trace != 0 // for convenience (p.trace is used frequently) p.trace = mode&Trace != 0 // for convenience (p.trace is used frequently)
p.next() p.next()
// set up the pkgScope here (as opposed to in parseFile) because
// there are other parser entry points (ParseExpr, etc.)
p.openScope()
p.pkgScope = p.topScope
// for the same reason, set up a label scope
p.openLabelScope()
}
// ----------------------------------------------------------------------------
// Scoping support
func (p *parser) openScope() {
p.topScope = ast.NewScope(p.topScope)
}
func (p *parser) closeScope() {
p.topScope = p.topScope.Outer
}
func (p *parser) openLabelScope() {
p.labelScope = ast.NewScope(p.labelScope)
p.targetStack = append(p.targetStack, nil)
}
func (p *parser) closeLabelScope() {
// resolve labels
n := len(p.targetStack) - 1
scope := p.labelScope
for _, ident := range p.targetStack[n] {
ident.Obj = scope.Lookup(ident.Name)
if ident.Obj == nil && p.mode&DeclarationErrors != 0 {
p.error(ident.Pos(), fmt.Sprintf("label %s undefined", ident.Name))
}
}
// pop label scope
p.targetStack = p.targetStack[0:n]
p.labelScope = p.labelScope.Outer
}
func (p *parser) declare(decl interface{}, scope *ast.Scope, kind ast.ObjKind, idents ...*ast.Ident) {
for _, ident := range idents {
if ident.Name != "_" {
obj := ast.NewObj(kind, ident.Name)
// remember the corresponding declaration for redeclaration
// errors and global variable resolution/typechecking phase
obj.Decl = decl
alt := scope.Insert(obj)
if alt != obj && p.mode&DeclarationErrors != 0 {
prevDecl := ""
if pos := alt.Pos(); pos.IsValid() {
prevDecl = fmt.Sprintf("\n\tprevious declaration at %s", p.file.Position(pos))
}
p.error(ident.Pos(), fmt.Sprintf("%s redeclared in this block%s", ident.Name, prevDecl))
}
ident.Obj = obj
}
}
}
func (p *parser) shortVarDecl(idents []*ast.Ident) {
// Go spec: A short variable declaration may redeclare variables
// provided they were originally declared in the same block with
// the same type, and at least one of the non-blank variables is new.
n := 0 // number of new variables
for _, ident := range idents {
if ident.Name != "_" {
obj := ast.NewObj(ast.Var, ident.Name)
// short var declarations cannot have redeclaration errors
// and are not global => no need to remember the respective
// declaration
alt := p.topScope.Insert(obj)
if alt == obj {
n++ // new declaration
}
ident.Obj = alt
}
}
if n == 0 && p.mode&DeclarationErrors != 0 {
p.error(idents[0].Pos(), "no new variables on left side of :=")
}
}
func (p *parser) resolve(ident *ast.Ident) {
if ident.Name == "_" {
return
}
// try to resolve the identifier
for s := p.topScope; s != nil; s = s.Outer {
if obj := s.Lookup(ident.Name); obj != nil {
ident.Obj = obj
return
}
}
// collect unresolved global identifiers; ignore the others
if p.topScope == p.pkgScope {
p.unresolved = append(p.unresolved, ident)
}
} }
@ -339,13 +454,16 @@ func (p *parser) parseQualifiedIdent() ast.Expr {
defer un(trace(p, "QualifiedIdent")) defer un(trace(p, "QualifiedIdent"))
} }
var x ast.Expr = p.parseIdent() ident := p.parseIdent()
p.resolve(ident)
var x ast.Expr = ident
if p.tok == token.PERIOD { if p.tok == token.PERIOD {
// first identifier is a package identifier // first identifier is a package identifier
p.next() p.next()
sel := p.parseIdent() sel := p.parseIdent()
x = &ast.SelectorExpr{x, sel} x = &ast.SelectorExpr{x, sel}
} }
return x return x
} }
@ -426,7 +544,7 @@ func (p *parser) parseFieldDecl() *ast.Field {
} }
} }
p.expectSemi() p.expectSemi() // call before accessing p.linecomment
return &ast.Field{doc, idents, typ, tag, p.lineComment} return &ast.Field{doc, idents, typ, tag, p.lineComment}
} }
@ -519,7 +637,7 @@ func (p *parser) parseVarList(isParam bool) (list []ast.Expr, typ ast.Expr) {
} }
func (p *parser) parseParameterList(ellipsisOk bool) (params []*ast.Field) { func (p *parser) parseParameterList(scope *ast.Scope, ellipsisOk bool) (params []*ast.Field) {
if p.trace { if p.trace {
defer un(trace(p, "ParameterList")) defer un(trace(p, "ParameterList"))
} }
@ -528,7 +646,11 @@ func (p *parser) parseParameterList(ellipsisOk bool) (params []*ast.Field) {
if typ != nil { if typ != nil {
// IdentifierList Type // IdentifierList Type
idents := p.makeIdentList(list) idents := p.makeIdentList(list)
params = append(params, &ast.Field{nil, idents, typ, nil, nil}) field := &ast.Field{nil, idents, typ, nil, nil}
params = append(params, field)
// Go spec: The scope of an identifier denoting a function
// parameter or result variable is the function body.
p.declare(field, scope, ast.Var, idents...)
if p.tok == token.COMMA { if p.tok == token.COMMA {
p.next() p.next()
} }
@ -536,7 +658,11 @@ func (p *parser) parseParameterList(ellipsisOk bool) (params []*ast.Field) {
for p.tok != token.RPAREN && p.tok != token.EOF { for p.tok != token.RPAREN && p.tok != token.EOF {
idents := p.parseIdentList() idents := p.parseIdentList()
typ := p.parseVarType(ellipsisOk) typ := p.parseVarType(ellipsisOk)
params = append(params, &ast.Field{nil, idents, typ, nil, nil}) field := &ast.Field{nil, idents, typ, nil, nil}
params = append(params, field)
// Go spec: The scope of an identifier denoting a function
// parameter or result variable is the function body.
p.declare(field, scope, ast.Var, idents...)
if p.tok != token.COMMA { if p.tok != token.COMMA {
break break
} }
@ -555,7 +681,7 @@ func (p *parser) parseParameterList(ellipsisOk bool) (params []*ast.Field) {
} }
func (p *parser) parseParameters(ellipsisOk bool) *ast.FieldList { func (p *parser) parseParameters(scope *ast.Scope, ellipsisOk bool) *ast.FieldList {
if p.trace { if p.trace {
defer un(trace(p, "Parameters")) defer un(trace(p, "Parameters"))
} }
@ -563,7 +689,7 @@ func (p *parser) parseParameters(ellipsisOk bool) *ast.FieldList {
var params []*ast.Field var params []*ast.Field
lparen := p.expect(token.LPAREN) lparen := p.expect(token.LPAREN)
if p.tok != token.RPAREN { if p.tok != token.RPAREN {
params = p.parseParameterList(ellipsisOk) params = p.parseParameterList(scope, ellipsisOk)
} }
rparen := p.expect(token.RPAREN) rparen := p.expect(token.RPAREN)
@ -571,13 +697,13 @@ func (p *parser) parseParameters(ellipsisOk bool) *ast.FieldList {
} }
func (p *parser) parseResult() *ast.FieldList { func (p *parser) parseResult(scope *ast.Scope) *ast.FieldList {
if p.trace { if p.trace {
defer un(trace(p, "Result")) defer un(trace(p, "Result"))
} }
if p.tok == token.LPAREN { if p.tok == token.LPAREN {
return p.parseParameters(false) return p.parseParameters(scope, false)
} }
typ := p.tryType() typ := p.tryType()
@ -591,27 +717,28 @@ func (p *parser) parseResult() *ast.FieldList {
} }
func (p *parser) parseSignature() (params, results *ast.FieldList) { func (p *parser) parseSignature(scope *ast.Scope) (params, results *ast.FieldList) {
if p.trace { if p.trace {
defer un(trace(p, "Signature")) defer un(trace(p, "Signature"))
} }
params = p.parseParameters(true) params = p.parseParameters(scope, true)
results = p.parseResult() results = p.parseResult(scope)
return return
} }
func (p *parser) parseFuncType() *ast.FuncType { func (p *parser) parseFuncType() (*ast.FuncType, *ast.Scope) {
if p.trace { if p.trace {
defer un(trace(p, "FuncType")) defer un(trace(p, "FuncType"))
} }
pos := p.expect(token.FUNC) pos := p.expect(token.FUNC)
params, results := p.parseSignature() scope := ast.NewScope(p.topScope) // function scope
params, results := p.parseSignature(scope)
return &ast.FuncType{pos, params, results} return &ast.FuncType{pos, params, results}, scope
} }
@ -627,13 +754,14 @@ func (p *parser) parseMethodSpec() *ast.Field {
if ident, isIdent := x.(*ast.Ident); isIdent && p.tok == token.LPAREN { if ident, isIdent := x.(*ast.Ident); isIdent && p.tok == token.LPAREN {
// method // method
idents = []*ast.Ident{ident} idents = []*ast.Ident{ident}
params, results := p.parseSignature() scope := ast.NewScope(nil) // method scope
params, results := p.parseSignature(scope)
typ = &ast.FuncType{token.NoPos, params, results} typ = &ast.FuncType{token.NoPos, params, results}
} else { } else {
// embedded interface // embedded interface
typ = x typ = x
} }
p.expectSemi() p.expectSemi() // call before accessing p.linecomment
return &ast.Field{doc, idents, typ, nil, p.lineComment} return &ast.Field{doc, idents, typ, nil, p.lineComment}
} }
@ -706,7 +834,8 @@ func (p *parser) tryRawType(ellipsisOk bool) ast.Expr {
case token.MUL: case token.MUL:
return p.parsePointerType() return p.parsePointerType()
case token.FUNC: case token.FUNC:
return p.parseFuncType() typ, _ := p.parseFuncType()
return typ
case token.INTERFACE: case token.INTERFACE:
return p.parseInterfaceType() return p.parseInterfaceType()
case token.MAP: case token.MAP:
@ -745,13 +874,17 @@ func (p *parser) parseStmtList() (list []ast.Stmt) {
} }
func (p *parser) parseBody() *ast.BlockStmt { func (p *parser) parseBody(scope *ast.Scope) *ast.BlockStmt {
if p.trace { if p.trace {
defer un(trace(p, "Body")) defer un(trace(p, "Body"))
} }
lbrace := p.expect(token.LBRACE) lbrace := p.expect(token.LBRACE)
p.topScope = scope // open function scope
p.openLabelScope()
list := p.parseStmtList() list := p.parseStmtList()
p.closeLabelScope()
p.closeScope()
rbrace := p.expect(token.RBRACE) rbrace := p.expect(token.RBRACE)
return &ast.BlockStmt{lbrace, list, rbrace} return &ast.BlockStmt{lbrace, list, rbrace}
@ -764,7 +897,9 @@ func (p *parser) parseBlockStmt() *ast.BlockStmt {
} }
lbrace := p.expect(token.LBRACE) lbrace := p.expect(token.LBRACE)
p.openScope()
list := p.parseStmtList() list := p.parseStmtList()
p.closeScope()
rbrace := p.expect(token.RBRACE) rbrace := p.expect(token.RBRACE)
return &ast.BlockStmt{lbrace, list, rbrace} return &ast.BlockStmt{lbrace, list, rbrace}
@ -779,14 +914,14 @@ func (p *parser) parseFuncTypeOrLit() ast.Expr {
defer un(trace(p, "FuncTypeOrLit")) defer un(trace(p, "FuncTypeOrLit"))
} }
typ := p.parseFuncType() typ, scope := p.parseFuncType()
if p.tok != token.LBRACE { if p.tok != token.LBRACE {
// function type only // function type only
return typ return typ
} }
p.exprLev++ p.exprLev++
body := p.parseBody() body := p.parseBody(scope)
p.exprLev-- p.exprLev--
return &ast.FuncLit{typ, body} return &ast.FuncLit{typ, body}
@ -803,7 +938,9 @@ func (p *parser) parseOperand() ast.Expr {
switch p.tok { switch p.tok {
case token.IDENT: case token.IDENT:
return p.parseIdent() ident := p.parseIdent()
p.resolve(ident)
return ident
case token.INT, token.FLOAT, token.IMAG, token.CHAR, token.STRING: case token.INT, token.FLOAT, token.IMAG, token.CHAR, token.STRING:
x := &ast.BasicLit{p.pos, p.tok, p.lit} x := &ast.BasicLit{p.pos, p.tok, p.lit}
@ -1202,6 +1339,9 @@ func (p *parser) parseSimpleStmt(labelOk bool) ast.Stmt {
pos, tok := p.pos, p.tok pos, tok := p.pos, p.tok
p.next() p.next()
y := p.parseExprList() y := p.parseExprList()
if tok == token.DEFINE {
p.shortVarDecl(p.makeIdentList(x))
}
return &ast.AssignStmt{x, pos, tok, y} return &ast.AssignStmt{x, pos, tok, y}
} }
@ -1216,7 +1356,12 @@ func (p *parser) parseSimpleStmt(labelOk bool) ast.Stmt {
colon := p.pos colon := p.pos
p.next() p.next()
if label, isIdent := x[0].(*ast.Ident); labelOk && isIdent { if label, isIdent := x[0].(*ast.Ident); labelOk && isIdent {
return &ast.LabeledStmt{label, colon, p.parseStmt()} // Go spec: The scope of a label is the body of the function
// in which it is declared and excludes the body of any nested
// function.
stmt := &ast.LabeledStmt{label, colon, p.parseStmt()}
p.declare(stmt, p.labelScope, ast.Lbl, label)
return stmt
} }
p.error(x[0].Pos(), "illegal label declaration") p.error(x[0].Pos(), "illegal label declaration")
return &ast.BadStmt{x[0].Pos(), colon + 1} return &ast.BadStmt{x[0].Pos(), colon + 1}
@ -1304,14 +1449,17 @@ func (p *parser) parseBranchStmt(tok token.Token) *ast.BranchStmt {
defer un(trace(p, "BranchStmt")) defer un(trace(p, "BranchStmt"))
} }
s := &ast.BranchStmt{p.pos, tok, nil} pos := p.expect(tok)
p.expect(tok) var label *ast.Ident
if tok != token.FALLTHROUGH && p.tok == token.IDENT { if tok != token.FALLTHROUGH && p.tok == token.IDENT {
s.Label = p.parseIdent() label = p.parseIdent()
// add to list of unresolved targets
n := len(p.targetStack) - 1
p.targetStack[n] = append(p.targetStack[n], label)
} }
p.expectSemi() p.expectSemi()
return s return &ast.BranchStmt{pos, tok, label}
} }
@ -1333,6 +1481,8 @@ func (p *parser) parseIfStmt() *ast.IfStmt {
} }
pos := p.expect(token.IF) pos := p.expect(token.IF)
p.openScope()
defer p.closeScope()
var s ast.Stmt var s ast.Stmt
var x ast.Expr var x ast.Expr
@ -1373,7 +1523,6 @@ func (p *parser) parseCaseClause() *ast.CaseClause {
defer un(trace(p, "CaseClause")) defer un(trace(p, "CaseClause"))
} }
// SwitchCase
pos := p.pos pos := p.pos
var x []ast.Expr var x []ast.Expr
if p.tok == token.CASE { if p.tok == token.CASE {
@ -1384,7 +1533,9 @@ func (p *parser) parseCaseClause() *ast.CaseClause {
} }
colon := p.expect(token.COLON) colon := p.expect(token.COLON)
p.openScope()
body := p.parseStmtList() body := p.parseStmtList()
p.closeScope()
return &ast.CaseClause{pos, x, colon, body} return &ast.CaseClause{pos, x, colon, body}
} }
@ -1410,7 +1561,6 @@ func (p *parser) parseTypeCaseClause() *ast.TypeCaseClause {
defer un(trace(p, "TypeCaseClause")) defer un(trace(p, "TypeCaseClause"))
} }
// TypeSwitchCase
pos := p.pos pos := p.pos
var types []ast.Expr var types []ast.Expr
if p.tok == token.CASE { if p.tok == token.CASE {
@ -1421,7 +1571,9 @@ func (p *parser) parseTypeCaseClause() *ast.TypeCaseClause {
} }
colon := p.expect(token.COLON) colon := p.expect(token.COLON)
p.openScope()
body := p.parseStmtList() body := p.parseStmtList()
p.closeScope()
return &ast.TypeCaseClause{pos, types, colon, body} return &ast.TypeCaseClause{pos, types, colon, body}
} }
@ -1447,6 +1599,8 @@ func (p *parser) parseSwitchStmt() ast.Stmt {
} }
pos := p.expect(token.SWITCH) pos := p.expect(token.SWITCH)
p.openScope()
defer p.closeScope()
var s1, s2 ast.Stmt var s1, s2 ast.Stmt
if p.tok != token.LBRACE { if p.tok != token.LBRACE {
@ -1497,7 +1651,7 @@ func (p *parser) parseCommClause() *ast.CommClause {
defer un(trace(p, "CommClause")) defer un(trace(p, "CommClause"))
} }
// CommCase p.openScope()
pos := p.pos pos := p.pos
var comm ast.Stmt var comm ast.Stmt
if p.tok == token.CASE { if p.tok == token.CASE {
@ -1518,7 +1672,7 @@ func (p *parser) parseCommClause() *ast.CommClause {
pos := p.pos pos := p.pos
tok := p.tok tok := p.tok
var rhs ast.Expr var rhs ast.Expr
if p.tok == token.ASSIGN || p.tok == token.DEFINE { if tok == token.ASSIGN || tok == token.DEFINE {
// RecvStmt with assignment // RecvStmt with assignment
if len(lhs) > 2 { if len(lhs) > 2 {
p.errorExpected(lhs[0].Pos(), "1 or 2 expressions") p.errorExpected(lhs[0].Pos(), "1 or 2 expressions")
@ -1527,6 +1681,9 @@ func (p *parser) parseCommClause() *ast.CommClause {
} }
p.next() p.next()
rhs = p.parseExpr() rhs = p.parseExpr()
if tok == token.DEFINE {
p.shortVarDecl(p.makeIdentList(lhs))
}
} else { } else {
// rhs must be single receive operation // rhs must be single receive operation
if len(lhs) > 1 { if len(lhs) > 1 {
@ -1552,6 +1709,7 @@ func (p *parser) parseCommClause() *ast.CommClause {
colon := p.expect(token.COLON) colon := p.expect(token.COLON)
body := p.parseStmtList() body := p.parseStmtList()
p.closeScope()
return &ast.CommClause{pos, comm, colon, body} return &ast.CommClause{pos, comm, colon, body}
} }
@ -1582,6 +1740,8 @@ func (p *parser) parseForStmt() ast.Stmt {
} }
pos := p.expect(token.FOR) pos := p.expect(token.FOR)
p.openScope()
defer p.closeScope()
var s1, s2, s3 ast.Stmt var s1, s2, s3 ast.Stmt
if p.tok != token.LBRACE { if p.tok != token.LBRACE {
@ -1631,20 +1791,18 @@ func (p *parser) parseForStmt() ast.Stmt {
return &ast.BadStmt{pos, body.End()} return &ast.BadStmt{pos, body.End()}
} }
if rhs, isUnary := as.Rhs[0].(*ast.UnaryExpr); isUnary && rhs.Op == token.RANGE { if rhs, isUnary := as.Rhs[0].(*ast.UnaryExpr); isUnary && rhs.Op == token.RANGE {
// rhs is range expression; check lhs // rhs is range expression
// (any short variable declaration was handled by parseSimpleStat above)
return &ast.RangeStmt{pos, key, value, as.TokPos, as.Tok, rhs.X, body} return &ast.RangeStmt{pos, key, value, as.TokPos, as.Tok, rhs.X, body}
} else { }
p.errorExpected(s2.Pos(), "range clause") p.errorExpected(s2.Pos(), "range clause")
return &ast.BadStmt{pos, body.End()} return &ast.BadStmt{pos, body.End()}
} }
} else {
// regular for statement // regular for statement
return &ast.ForStmt{pos, s1, p.makeExpr(s2), s3, body} return &ast.ForStmt{pos, s1, p.makeExpr(s2), s3, body}
} }
panic("unreachable")
}
func (p *parser) parseStmt() (s ast.Stmt) { func (p *parser) parseStmt() (s ast.Stmt) {
if p.trace { if p.trace {
@ -1715,10 +1873,11 @@ func parseImportSpec(p *parser, doc *ast.CommentGroup) ast.Spec {
} }
var ident *ast.Ident var ident *ast.Ident
if p.tok == token.PERIOD { switch p.tok {
case token.PERIOD:
ident = &ast.Ident{p.pos, ".", nil} ident = &ast.Ident{p.pos, ".", nil}
p.next() p.next()
} else if p.tok == token.IDENT { case token.IDENT:
ident = p.parseIdent() ident = p.parseIdent()
} }
@ -1729,7 +1888,7 @@ func parseImportSpec(p *parser, doc *ast.CommentGroup) ast.Spec {
} else { } else {
p.expect(token.STRING) // use expect() error handling p.expect(token.STRING) // use expect() error handling
} }
p.expectSemi() p.expectSemi() // call before accessing p.linecomment
return &ast.ImportSpec{doc, ident, path, p.lineComment} return &ast.ImportSpec{doc, ident, path, p.lineComment}
} }
@ -1747,9 +1906,16 @@ func parseConstSpec(p *parser, doc *ast.CommentGroup) ast.Spec {
p.expect(token.ASSIGN) p.expect(token.ASSIGN)
values = p.parseExprList() values = p.parseExprList()
} }
p.expectSemi() p.expectSemi() // call before accessing p.linecomment
return &ast.ValueSpec{doc, idents, typ, values, p.lineComment} // Go spec: The scope of a constant or variable identifier declared inside
// a function begins at the end of the ConstSpec or VarSpec and ends at
// the end of the innermost containing block.
// (Global identifiers are resolved in a separate phase after parsing.)
spec := &ast.ValueSpec{doc, idents, typ, values, p.lineComment}
p.declare(spec, p.topScope, ast.Con, idents...)
return spec
} }
@ -1760,9 +1926,16 @@ func parseTypeSpec(p *parser, doc *ast.CommentGroup) ast.Spec {
ident := p.parseIdent() ident := p.parseIdent()
typ := p.parseType() typ := p.parseType()
p.expectSemi() p.expectSemi() // call before accessing p.linecomment
return &ast.TypeSpec{doc, ident, typ, p.lineComment} // Go spec: The scope of a type identifier declared inside a function begins
// at the identifier in the TypeSpec and ends at the end of the innermost
// containing block.
// (Global identifiers are resolved in a separate phase after parsing.)
spec := &ast.TypeSpec{doc, ident, typ, p.lineComment}
p.declare(spec, p.topScope, ast.Typ, ident)
return spec
} }
@ -1778,9 +1951,16 @@ func parseVarSpec(p *parser, doc *ast.CommentGroup) ast.Spec {
p.expect(token.ASSIGN) p.expect(token.ASSIGN)
values = p.parseExprList() values = p.parseExprList()
} }
p.expectSemi() p.expectSemi() // call before accessing p.linecomment
return &ast.ValueSpec{doc, idents, typ, values, p.lineComment} // Go spec: The scope of a constant or variable identifier declared inside
// a function begins at the end of the ConstSpec or VarSpec and ends at
// the end of the innermost containing block.
// (Global identifiers are resolved in a separate phase after parsing.)
spec := &ast.ValueSpec{doc, idents, typ, values, p.lineComment}
p.declare(spec, p.topScope, ast.Var, idents...)
return spec
} }
@ -1809,13 +1989,13 @@ func (p *parser) parseGenDecl(keyword token.Token, f parseSpecFunction) *ast.Gen
} }
func (p *parser) parseReceiver() *ast.FieldList { func (p *parser) parseReceiver(scope *ast.Scope) *ast.FieldList {
if p.trace { if p.trace {
defer un(trace(p, "Receiver")) defer un(trace(p, "Receiver"))
} }
pos := p.pos pos := p.pos
par := p.parseParameters(false) par := p.parseParameters(scope, false)
// must have exactly one receiver // must have exactly one receiver
if par.NumFields() != 1 { if par.NumFields() != 1 {
@ -1844,22 +2024,37 @@ func (p *parser) parseFuncDecl() *ast.FuncDecl {
doc := p.leadComment doc := p.leadComment
pos := p.expect(token.FUNC) pos := p.expect(token.FUNC)
scope := ast.NewScope(p.topScope) // function scope
var recv *ast.FieldList var recv *ast.FieldList
if p.tok == token.LPAREN { if p.tok == token.LPAREN {
recv = p.parseReceiver() recv = p.parseReceiver(scope)
} }
ident := p.parseIdent() ident := p.parseIdent()
params, results := p.parseSignature()
params, results := p.parseSignature(scope)
var body *ast.BlockStmt var body *ast.BlockStmt
if p.tok == token.LBRACE { if p.tok == token.LBRACE {
body = p.parseBody() body = p.parseBody(scope)
} }
p.expectSemi() p.expectSemi()
return &ast.FuncDecl{doc, recv, ident, &ast.FuncType{pos, params, results}, body} decl := &ast.FuncDecl{doc, recv, ident, &ast.FuncType{pos, params, results}, body}
if recv == nil {
// Go spec: The scope of an identifier denoting a constant, type,
// variable, or function (but not method) declared at top level
// (outside any function) is the package block.
//
// init() functions cannot be referred to and there may
// be more than one - don't put them in the pkgScope
if ident.Name != "init" {
p.declare(decl, p.pkgScope, ast.Fun, ident)
}
}
return decl
} }
@ -1918,6 +2113,8 @@ func (p *parser) parseFile() *ast.File {
// package clause // package clause
doc := p.leadComment doc := p.leadComment
pos := p.expect(token.PACKAGE) pos := p.expect(token.PACKAGE)
// Go spec: The package clause is not a declaration;
// the package name does not appear in any scope.
ident := p.parseIdent() ident := p.parseIdent()
p.expectSemi() p.expectSemi()
@ -1940,5 +2137,20 @@ func (p *parser) parseFile() *ast.File {
} }
} }
return &ast.File{doc, pos, ident, decls, p.comments} if p.topScope != p.pkgScope {
panic("internal error: imbalanced scopes")
}
// resolve global identifiers within the same file
i := 0
for _, ident := range p.unresolved {
// i <= index for current ident
ident.Obj = p.pkgScope.Lookup(ident.Name)
if ident.Obj == nil {
p.unresolved[i] = ident
i++
}
}
return &ast.File{doc, pos, ident, decls, p.pkgScope, p.unresolved[0:i], p.comments}
} }

View File

@ -73,7 +73,7 @@ var validFiles = []string{
func TestParse3(t *testing.T) { func TestParse3(t *testing.T) {
for _, filename := range validFiles { for _, filename := range validFiles {
_, err := ParseFile(fset, filename, nil, 0) _, err := ParseFile(fset, filename, nil, DeclarationErrors)
if err != nil { if err != nil {
t.Errorf("ParseFile(%s): %v", filename, err) t.Errorf("ParseFile(%s): %v", filename, err)
} }

View File

@ -7,6 +7,7 @@ include ../../../Make.inc
TARG=go/typechecker TARG=go/typechecker
GOFILES=\ GOFILES=\
scope.go\ scope.go\
type.go\
typechecker.go\ typechecker.go\
universe.go\ universe.go\

View File

@ -2,15 +2,15 @@
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
// This file implements scope support functions. // DEPRECATED FILE - WILL GO AWAY EVENTUALLY.
//
// Scope handling is now done in go/parser.
// The functionality here is only present to
// keep the typechecker running for now.
package typechecker package typechecker
import ( import "go/ast"
"fmt"
"go/ast"
"go/token"
)
func (tc *typechecker) openScope() *ast.Scope { func (tc *typechecker) openScope() *ast.Scope {
@ -24,52 +24,25 @@ func (tc *typechecker) closeScope() {
} }
// objPos computes the source position of the declaration of an object name.
// Only required for error reporting, so doesn't have to be fast.
func objPos(obj *ast.Object) (pos token.Pos) {
switch d := obj.Decl.(type) {
case *ast.Field:
for _, n := range d.Names {
if n.Name == obj.Name {
return n.Pos()
}
}
case *ast.ValueSpec:
for _, n := range d.Names {
if n.Name == obj.Name {
return n.Pos()
}
}
case *ast.TypeSpec:
return d.Name.Pos()
case *ast.FuncDecl:
return d.Name.Pos()
}
if debug {
fmt.Printf("decl = %T\n", obj.Decl)
}
panic("unreachable")
}
// declInScope declares an object of a given kind and name in scope and sets the object's Decl and N fields. // declInScope declares an object of a given kind and name in scope and sets the object's Decl and N fields.
// It returns the newly allocated object. If an object with the same name already exists in scope, an error // It returns the newly allocated object. If an object with the same name already exists in scope, an error
// is reported and the object is not inserted. // is reported and the object is not inserted.
// (Objects with _ name are always inserted into a scope without errors, but they cannot be found.) func (tc *typechecker) declInScope(scope *ast.Scope, kind ast.ObjKind, name *ast.Ident, decl interface{}, n int) *ast.Object {
func (tc *typechecker) declInScope(scope *ast.Scope, kind ast.Kind, name *ast.Ident, decl interface{}, n int) *ast.Object {
obj := ast.NewObj(kind, name.Name) obj := ast.NewObj(kind, name.Name)
obj.Decl = decl obj.Decl = decl
obj.N = n //obj.N = n
name.Obj = obj name.Obj = obj
if name.Name != "_" {
if alt := scope.Insert(obj); alt != obj { if alt := scope.Insert(obj); alt != obj {
tc.Errorf(name.Pos(), "%s already declared at %s", name.Name, objPos(alt)) tc.Errorf(name.Pos(), "%s already declared at %s", name.Name, tc.fset.Position(alt.Pos()).String())
}
} }
return obj return obj
} }
// decl is the same as declInScope(tc.topScope, ...) // decl is the same as declInScope(tc.topScope, ...)
func (tc *typechecker) decl(kind ast.Kind, name *ast.Ident, decl interface{}, n int) *ast.Object { func (tc *typechecker) decl(kind ast.ObjKind, name *ast.Ident, decl interface{}, n int) *ast.Object {
return tc.declInScope(tc.topScope, kind, name, decl, n) return tc.declInScope(tc.topScope, kind, name, decl, n)
} }
@ -91,7 +64,7 @@ func (tc *typechecker) find(name *ast.Ident) (obj *ast.Object) {
// findField returns the object with the given name if visible in the type's scope. // findField returns the object with the given name if visible in the type's scope.
// If no such object is found, an error is reported and a bad object is returned instead. // If no such object is found, an error is reported and a bad object is returned instead.
func (tc *typechecker) findField(typ *ast.Type, name *ast.Ident) (obj *ast.Object) { func (tc *typechecker) findField(typ *Type, name *ast.Ident) (obj *ast.Object) {
// TODO(gri) This is simplistic at the moment and ignores anonymous fields. // TODO(gri) This is simplistic at the moment and ignores anonymous fields.
obj = typ.Scope.Lookup(name.Name) obj = typ.Scope.Lookup(name.Name)
if obj == nil { if obj == nil {
@ -100,20 +73,3 @@ func (tc *typechecker) findField(typ *ast.Type, name *ast.Ident) (obj *ast.Objec
} }
return return
} }
// printScope prints the objects in a scope.
func printScope(scope *ast.Scope) {
fmt.Printf("scope %p {", scope)
if scope != nil && len(scope.Objects) > 0 {
fmt.Println()
for _, obj := range scope.Objects {
form := "void"
if obj.Type != nil {
form = obj.Type.Form.String()
}
fmt.Printf("\t%s\t%s\n", obj.Name, form)
}
}
fmt.Printf("}\n")
}

View File

@ -27,8 +27,11 @@ func (T) m1 /* ERROR "already declared" */ () {}
func (x *T) m2(u, x /* ERROR "already declared" */ int) {} func (x *T) m2(u, x /* ERROR "already declared" */ int) {}
func (x *T) m3(a, b, c int) (u, x /* ERROR "already declared" */ int) {} func (x *T) m3(a, b, c int) (u, x /* ERROR "already declared" */ int) {}
func (T) _(x, x /* ERROR "already declared" */ int) {} // The following are disabled for now because the typechecker
func (T) _() (x, x /* ERROR "already declared" */ int) {} // in in the process of being rewritten and cannot handle them
// at the moment
//func (T) _(x, x /* "already declared" */ int) {}
//func (T) _() (x, x /* "already declared" */ int) {}
//func (PT) _() {} //func (PT) _() {}

View File

@ -0,0 +1,125 @@
// Copyright 2010 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package typechecker
import "go/ast"
// A Type represents a Go type.
type Type struct {
Form Form
Obj *ast.Object // corresponding type name, or nil
Scope *ast.Scope // fields and methods, always present
N uint // basic type id, array length, number of function results, or channel direction
Key, Elt *Type // map key and array, pointer, slice, map or channel element
Params *ast.Scope // function (receiver, input and result) parameters, tuple expressions (results of function calls), or nil
Expr ast.Expr // corresponding AST expression
}
// NewType creates a new type of a given form.
func NewType(form Form) *Type {
return &Type{Form: form, Scope: ast.NewScope(nil)}
}
// Form describes the form of a type.
type Form int
// The list of possible type forms.
const (
BadType Form = iota // for error handling
Unresolved // type not fully setup
Basic
Array
Struct
Pointer
Function
Method
Interface
Slice
Map
Channel
Tuple
)
var formStrings = [...]string{
BadType: "badType",
Unresolved: "unresolved",
Basic: "basic",
Array: "array",
Struct: "struct",
Pointer: "pointer",
Function: "function",
Method: "method",
Interface: "interface",
Slice: "slice",
Map: "map",
Channel: "channel",
Tuple: "tuple",
}
func (form Form) String() string { return formStrings[form] }
// The list of basic type id's.
const (
Bool = iota
Byte
Uint
Int
Float
Complex
Uintptr
String
Uint8
Uint16
Uint32
Uint64
Int8
Int16
Int32
Int64
Float32
Float64
Complex64
Complex128
// TODO(gri) ideal types are missing
)
var BasicTypes = map[uint]string{
Bool: "bool",
Byte: "byte",
Uint: "uint",
Int: "int",
Float: "float",
Complex: "complex",
Uintptr: "uintptr",
String: "string",
Uint8: "uint8",
Uint16: "uint16",
Uint32: "uint32",
Uint64: "uint64",
Int8: "int8",
Int16: "int16",
Int32: "int32",
Int64: "int64",
Float32: "float32",
Float64: "float64",
Complex64: "complex64",
Complex128: "complex128",
}

View File

@ -65,6 +65,7 @@ type typechecker struct {
fset *token.FileSet fset *token.FileSet
scanner.ErrorVector scanner.ErrorVector
importer Importer importer Importer
globals []*ast.Object // list of global objects
topScope *ast.Scope // current top-most scope topScope *ast.Scope // current top-most scope
cyclemap map[*ast.Object]bool // for cycle detection cyclemap map[*ast.Object]bool // for cycle detection
iota int // current value of iota iota int // current value of iota
@ -94,7 +95,7 @@ phase 1: declare all global objects; also collect all function and method declar
- report global double declarations - report global double declarations
phase 2: bind methods to their receiver base types phase 2: bind methods to their receiver base types
- received base types must be declared in the package, thus for - receiver base types must be declared in the package, thus for
each method a corresponding (unresolved) type must exist each method a corresponding (unresolved) type must exist
- report method double declarations and errors with base types - report method double declarations and errors with base types
@ -142,16 +143,16 @@ func (tc *typechecker) checkPackage(pkg *ast.Package) {
} }
// phase 3: resolve all global objects // phase 3: resolve all global objects
// (note that objects with _ name are also in the scope)
tc.cyclemap = make(map[*ast.Object]bool) tc.cyclemap = make(map[*ast.Object]bool)
for _, obj := range tc.topScope.Objects { for _, obj := range tc.globals {
tc.resolve(obj) tc.resolve(obj)
} }
assert(len(tc.cyclemap) == 0) assert(len(tc.cyclemap) == 0)
// 4: sequentially typecheck function and method bodies // 4: sequentially typecheck function and method bodies
for _, f := range funcs { for _, f := range funcs {
tc.checkBlock(f.Body.List, f.Name.Obj.Type) ftype, _ := f.Name.Obj.Type.(*Type)
tc.checkBlock(f.Body.List, ftype)
} }
pkg.Scope = tc.topScope pkg.Scope = tc.topScope
@ -183,11 +184,11 @@ func (tc *typechecker) declGlobal(global ast.Decl) {
} }
} }
for _, name := range s.Names { for _, name := range s.Names {
tc.decl(ast.Con, name, s, iota) tc.globals = append(tc.globals, tc.decl(ast.Con, name, s, iota))
} }
case token.VAR: case token.VAR:
for _, name := range s.Names { for _, name := range s.Names {
tc.decl(ast.Var, name, s, 0) tc.globals = append(tc.globals, tc.decl(ast.Var, name, s, 0))
} }
default: default:
panic("unreachable") panic("unreachable")
@ -196,9 +197,10 @@ func (tc *typechecker) declGlobal(global ast.Decl) {
iota++ iota++
case *ast.TypeSpec: case *ast.TypeSpec:
obj := tc.decl(ast.Typ, s.Name, s, 0) obj := tc.decl(ast.Typ, s.Name, s, 0)
tc.globals = append(tc.globals, obj)
// give all type objects an unresolved type so // give all type objects an unresolved type so
// that we can collect methods in the type scope // that we can collect methods in the type scope
typ := ast.NewType(ast.Unresolved) typ := NewType(Unresolved)
obj.Type = typ obj.Type = typ
typ.Obj = obj typ.Obj = obj
default: default:
@ -208,7 +210,7 @@ func (tc *typechecker) declGlobal(global ast.Decl) {
case *ast.FuncDecl: case *ast.FuncDecl:
if d.Recv == nil { if d.Recv == nil {
tc.decl(ast.Fun, d.Name, d, 0) tc.globals = append(tc.globals, tc.decl(ast.Fun, d.Name, d, 0))
} }
default: default:
@ -239,8 +241,8 @@ func (tc *typechecker) bindMethod(method *ast.FuncDecl) {
} else if obj.Kind != ast.Typ { } else if obj.Kind != ast.Typ {
tc.Errorf(name.Pos(), "invalid receiver: %s is not a type", name.Name) tc.Errorf(name.Pos(), "invalid receiver: %s is not a type", name.Name)
} else { } else {
typ := obj.Type typ := obj.Type.(*Type)
assert(typ.Form == ast.Unresolved) assert(typ.Form == Unresolved)
scope = typ.Scope scope = typ.Scope
} }
} }
@ -261,7 +263,7 @@ func (tc *typechecker) bindMethod(method *ast.FuncDecl) {
func (tc *typechecker) resolve(obj *ast.Object) { func (tc *typechecker) resolve(obj *ast.Object) {
// check for declaration cycles // check for declaration cycles
if tc.cyclemap[obj] { if tc.cyclemap[obj] {
tc.Errorf(objPos(obj), "illegal cycle in declaration of %s", obj.Name) tc.Errorf(obj.Pos(), "illegal cycle in declaration of %s", obj.Name)
obj.Kind = ast.Bad obj.Kind = ast.Bad
return return
} }
@ -271,7 +273,7 @@ func (tc *typechecker) resolve(obj *ast.Object) {
}() }()
// resolve non-type objects // resolve non-type objects
typ := obj.Type typ, _ := obj.Type.(*Type)
if typ == nil { if typ == nil {
switch obj.Kind { switch obj.Kind {
case ast.Bad: case ast.Bad:
@ -282,12 +284,12 @@ func (tc *typechecker) resolve(obj *ast.Object) {
case ast.Var: case ast.Var:
tc.declVar(obj) tc.declVar(obj)
//obj.Type = tc.typeFor(nil, obj.Decl.(*ast.ValueSpec).Type, false) obj.Type = tc.typeFor(nil, obj.Decl.(*ast.ValueSpec).Type, false)
case ast.Fun: case ast.Fun:
obj.Type = ast.NewType(ast.Function) obj.Type = NewType(Function)
t := obj.Decl.(*ast.FuncDecl).Type t := obj.Decl.(*ast.FuncDecl).Type
tc.declSignature(obj.Type, nil, t.Params, t.Results) tc.declSignature(obj.Type.(*Type), nil, t.Params, t.Results)
default: default:
// type objects have non-nil types when resolve is called // type objects have non-nil types when resolve is called
@ -300,34 +302,36 @@ func (tc *typechecker) resolve(obj *ast.Object) {
} }
// resolve type objects // resolve type objects
if typ.Form == ast.Unresolved { if typ.Form == Unresolved {
tc.typeFor(typ, typ.Obj.Decl.(*ast.TypeSpec).Type, false) tc.typeFor(typ, typ.Obj.Decl.(*ast.TypeSpec).Type, false)
// provide types for all methods // provide types for all methods
for _, obj := range typ.Scope.Objects { for _, obj := range typ.Scope.Objects {
if obj.Kind == ast.Fun { if obj.Kind == ast.Fun {
assert(obj.Type == nil) assert(obj.Type == nil)
obj.Type = ast.NewType(ast.Method) obj.Type = NewType(Method)
f := obj.Decl.(*ast.FuncDecl) f := obj.Decl.(*ast.FuncDecl)
t := f.Type t := f.Type
tc.declSignature(obj.Type, f.Recv, t.Params, t.Results) tc.declSignature(obj.Type.(*Type), f.Recv, t.Params, t.Results)
} }
} }
} }
} }
func (tc *typechecker) checkBlock(body []ast.Stmt, ftype *ast.Type) { func (tc *typechecker) checkBlock(body []ast.Stmt, ftype *Type) {
tc.openScope() tc.openScope()
defer tc.closeScope() defer tc.closeScope()
// inject function/method parameters into block scope, if any // inject function/method parameters into block scope, if any
if ftype != nil { if ftype != nil {
for _, par := range ftype.Params.Objects { for _, par := range ftype.Params.Objects {
if par.Name != "_" {
obj := tc.topScope.Insert(par) obj := tc.topScope.Insert(par)
assert(obj == par) // ftype has no double declarations assert(obj == par) // ftype has no double declarations
} }
} }
}
for _, stmt := range body { for _, stmt := range body {
tc.checkStmt(stmt) tc.checkStmt(stmt)
@ -362,8 +366,8 @@ func (tc *typechecker) declFields(scope *ast.Scope, fields *ast.FieldList, ref b
} }
func (tc *typechecker) declSignature(typ *ast.Type, recv, params, results *ast.FieldList) { func (tc *typechecker) declSignature(typ *Type, recv, params, results *ast.FieldList) {
assert((typ.Form == ast.Method) == (recv != nil)) assert((typ.Form == Method) == (recv != nil))
typ.Params = ast.NewScope(nil) typ.Params = ast.NewScope(nil)
tc.declFields(typ.Params, recv, true) tc.declFields(typ.Params, recv, true)
tc.declFields(typ.Params, params, true) tc.declFields(typ.Params, params, true)
@ -371,7 +375,7 @@ func (tc *typechecker) declSignature(typ *ast.Type, recv, params, results *ast.F
} }
func (tc *typechecker) typeFor(def *ast.Type, x ast.Expr, ref bool) (typ *ast.Type) { func (tc *typechecker) typeFor(def *Type, x ast.Expr, ref bool) (typ *Type) {
x = unparen(x) x = unparen(x)
// type name // type name
@ -381,10 +385,10 @@ func (tc *typechecker) typeFor(def *ast.Type, x ast.Expr, ref bool) (typ *ast.Ty
if obj.Kind != ast.Typ { if obj.Kind != ast.Typ {
tc.Errorf(t.Pos(), "%s is not a type", t.Name) tc.Errorf(t.Pos(), "%s is not a type", t.Name)
if def == nil { if def == nil {
typ = ast.NewType(ast.BadType) typ = NewType(BadType)
} else { } else {
typ = def typ = def
typ.Form = ast.BadType typ.Form = BadType
} }
typ.Expr = x typ.Expr = x
return return
@ -393,7 +397,7 @@ func (tc *typechecker) typeFor(def *ast.Type, x ast.Expr, ref bool) (typ *ast.Ty
if !ref { if !ref {
tc.resolve(obj) // check for cycles even if type resolved tc.resolve(obj) // check for cycles even if type resolved
} }
typ = obj.Type typ = obj.Type.(*Type)
if def != nil { if def != nil {
// new type declaration: copy type structure // new type declaration: copy type structure
@ -410,7 +414,7 @@ func (tc *typechecker) typeFor(def *ast.Type, x ast.Expr, ref bool) (typ *ast.Ty
// type literal // type literal
typ = def typ = def
if typ == nil { if typ == nil {
typ = ast.NewType(ast.BadType) typ = NewType(BadType)
} }
typ.Expr = x typ.Expr = x
@ -419,42 +423,42 @@ func (tc *typechecker) typeFor(def *ast.Type, x ast.Expr, ref bool) (typ *ast.Ty
if debug { if debug {
fmt.Println("qualified identifier unimplemented") fmt.Println("qualified identifier unimplemented")
} }
typ.Form = ast.BadType typ.Form = BadType
case *ast.StarExpr: case *ast.StarExpr:
typ.Form = ast.Pointer typ.Form = Pointer
typ.Elt = tc.typeFor(nil, t.X, true) typ.Elt = tc.typeFor(nil, t.X, true)
case *ast.ArrayType: case *ast.ArrayType:
if t.Len != nil { if t.Len != nil {
typ.Form = ast.Array typ.Form = Array
// TODO(gri) compute the real length // TODO(gri) compute the real length
// (this may call resolve recursively) // (this may call resolve recursively)
(*typ).N = 42 (*typ).N = 42
} else { } else {
typ.Form = ast.Slice typ.Form = Slice
} }
typ.Elt = tc.typeFor(nil, t.Elt, t.Len == nil) typ.Elt = tc.typeFor(nil, t.Elt, t.Len == nil)
case *ast.StructType: case *ast.StructType:
typ.Form = ast.Struct typ.Form = Struct
tc.declFields(typ.Scope, t.Fields, false) tc.declFields(typ.Scope, t.Fields, false)
case *ast.FuncType: case *ast.FuncType:
typ.Form = ast.Function typ.Form = Function
tc.declSignature(typ, nil, t.Params, t.Results) tc.declSignature(typ, nil, t.Params, t.Results)
case *ast.InterfaceType: case *ast.InterfaceType:
typ.Form = ast.Interface typ.Form = Interface
tc.declFields(typ.Scope, t.Methods, true) tc.declFields(typ.Scope, t.Methods, true)
case *ast.MapType: case *ast.MapType:
typ.Form = ast.Map typ.Form = Map
typ.Key = tc.typeFor(nil, t.Key, true) typ.Key = tc.typeFor(nil, t.Key, true)
typ.Elt = tc.typeFor(nil, t.Value, true) typ.Elt = tc.typeFor(nil, t.Value, true)
case *ast.ChanType: case *ast.ChanType:
typ.Form = ast.Channel typ.Form = Channel
typ.N = uint(t.Dir) typ.N = uint(t.Dir)
typ.Elt = tc.typeFor(nil, t.Value, true) typ.Elt = tc.typeFor(nil, t.Value, true)

View File

@ -93,7 +93,7 @@ func expectedErrors(t *testing.T, pkg *ast.Package) (list scanner.ErrorList) {
func testFilter(f *os.FileInfo) bool { func testFilter(f *os.FileInfo) bool {
return strings.HasSuffix(f.Name, ".go") && f.Name[0] != '.' return strings.HasSuffix(f.Name, ".src") && f.Name[0] != '.'
} }

View File

@ -24,8 +24,8 @@ func init() {
Universe = ast.NewScope(nil) Universe = ast.NewScope(nil)
// basic types // basic types
for n, name := range ast.BasicTypes { for n, name := range BasicTypes {
typ := ast.NewType(ast.Basic) typ := NewType(Basic)
typ.N = n typ.N = n
obj := ast.NewObj(ast.Typ, name) obj := ast.NewObj(ast.Typ, name)
obj.Type = typ obj.Type = typ