From 021b040a1e9a6551dcaed2acfec0232f740ee416 Mon Sep 17 00:00:00 2001 From: Robert Griesemer Date: Mon, 7 Mar 2011 11:01:23 -0800 Subject: [PATCH] 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 --- src/pkg/exp/eval/stmt_test.go | 2 +- src/pkg/go/ast/ast.go | 14 +- src/pkg/go/ast/filter.go | 3 +- src/pkg/go/ast/scope.go | 261 +++++--------- src/pkg/go/parser/parser.go | 334 ++++++++++++++---- src/pkg/go/parser/parser_test.go | 2 +- src/pkg/go/typechecker/Makefile | 1 + src/pkg/go/typechecker/scope.go | 72 +--- .../testdata/{test0.go => test0.src} | 0 .../testdata/{test1.go => test1.src} | 0 .../testdata/{test3.go => test3.src} | 7 +- .../testdata/{test4.go => test4.src} | 0 src/pkg/go/typechecker/type.go | 125 +++++++ src/pkg/go/typechecker/typechecker.go | 78 ++-- src/pkg/go/typechecker/typechecker_test.go | 2 +- src/pkg/go/typechecker/universe.go | 4 +- 16 files changed, 560 insertions(+), 345 deletions(-) rename src/pkg/go/typechecker/testdata/{test0.go => test0.src} (100%) rename src/pkg/go/typechecker/testdata/{test1.go => test1.src} (100%) rename src/pkg/go/typechecker/testdata/{test3.go => test3.src} (80%) rename src/pkg/go/typechecker/testdata/{test4.go => test4.src} (100%) create mode 100644 src/pkg/go/typechecker/type.go diff --git a/src/pkg/exp/eval/stmt_test.go b/src/pkg/exp/eval/stmt_test.go index 4a883ef5ee..a8a3e16204 100644 --- a/src/pkg/exp/eval/stmt_test.go +++ b/src/pkg/exp/eval/stmt_test.go @@ -27,7 +27,7 @@ var stmtTests = []test{ CErr("i, u := 1, 2", atLeastOneDecl), Val2("i, x := 2, f", "i", 2, "x", 1.0), // Various errors - CErr("1 := 2", "left side of := must be a name"), + CErr("1 := 2", "expected identifier"), CErr("c, a := 1, 1", "cannot assign"), // Unpacking Val2("x, y := oneTwo()", "x", 1, "y", 2), diff --git a/src/pkg/go/ast/ast.go b/src/pkg/go/ast/ast.go index abafb5663b..feb31b631d 100644 --- a/src/pkg/go/ast/ast.go +++ b/src/pkg/go/ast/ast.go @@ -937,11 +937,13 @@ func (d *FuncDecl) declNode() {} // via Doc and Comment fields. // type File struct { - Doc *CommentGroup // associated documentation; or nil - Package token.Pos // position of "package" keyword - Name *Ident // package name - Decls []Decl // top-level declarations; or nil - Comments []*CommentGroup // list of all comments in the source file + Doc *CommentGroup // associated documentation; or nil + Package token.Pos // position of "package" keyword + Name *Ident // package name + 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 } @@ -959,7 +961,7 @@ func (f *File) End() token.Pos { // type Package struct { Name string // package name - Scope *Scope // package scope; or nil + Scope *Scope // package scope Files map[string]*File // Go source files by filename } diff --git a/src/pkg/go/ast/filter.go b/src/pkg/go/ast/filter.go index 0c3cef4b27..4da487ce02 100644 --- a/src/pkg/go/ast/filter.go +++ b/src/pkg/go/ast/filter.go @@ -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} } diff --git a/src/pkg/go/ast/scope.go b/src/pkg/go/ast/scope.go index 956a208aed..91866dcf57 100644 --- a/src/pkg/go/ast/scope.go +++ b/src/pkg/go/ast/scope.go @@ -2,31 +2,31 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// This file implements scopes, the objects they contain, -// and object types. +// This file implements scopes and the objects they contain. package ast +import ( + "bytes" + "fmt" + "go/token" +) + + // A Scope maintains the set of named language entities declared // in the scope and a link to the immediately surrounding (outer) // scope. // type Scope struct { Outer *Scope - Objects []*Object // in declaration order - // 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. + Objects map[string]*Object } // NewScope creates a new scope nested in the outer scope. func NewScope(outer *Scope) *Scope { - const n = 4 // initial scope capacity, must be > 0 - return &Scope{outer, make([]*Object, 0, n)} + const n = 4 // initial scope capacity + 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 // 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 { - if name != "_" { - for _, obj := range s.Objects { - if obj.Name == name { - return obj - } - } - } - return nil + return s.Objects[name] } // Insert attempts to insert a named object into the scope s. -// If the scope does not contain an object with that name yet -// or if the object is named "_", Insert inserts the object -// and returns it. Otherwise, Insert leaves the scope unchanged -// and returns the object found in the scope instead. +// If the scope does not contain an object with that name yet, +// Insert inserts the object and returns it. Otherwise, Insert +// leaves the scope unchanged and returns the object found in +// the scope instead. // -func (s *Scope) Insert(obj *Object) *Object { - alt := s.Lookup(obj.Name) - if alt == nil { - s.append(obj) +func (s *Scope) Insert(obj *Object) (alt *Object) { + if alt = s.Objects[obj.Name]; alt == nil { + s.Objects[obj.Name] = obj alt = obj } - return alt + return } -func (s *Scope) append(obj *Object) { - s.Objects = append(s.Objects, obj) +// Debugging support +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 -// An Object describes a language entity such as a package, -// constant, type, variable, or function (incl. methods). +// An Object describes a named language entity such as a package, +// constant, type, variable, function (incl. methods), or label. // type Object struct { - Kind Kind - Name string // declared name - Type *Type - Decl interface{} // corresponding Field, XxxSpec or FuncDecl - N int // value of iota for this declaration + Kind ObjKind + Name string // declared name + Decl interface{} // corresponding Field, XxxSpec, FuncDecl, or LabeledStmt; or nil + Type interface{} // place holder for type information; may be nil } // 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} } -// Kind describes what an object represents. -type Kind int +// Pos computes the source position of the declaration of an object name. +// 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. const ( - Bad Kind = iota // for error handling - Pkg // package - Con // constant - Typ // type - Var // variable - Fun // function or method + Bad ObjKind = iota // for error handling + Pkg // package + Con // constant + Typ // type + Var // variable + Fun // function or method + Lbl // label ) @@ -111,132 +146,8 @@ var objKindStrings = [...]string{ Typ: "type", Var: "var", Fun: "func", + Lbl: "label", } -func (kind Kind) 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", -} +func (kind ObjKind) String() string { return objKindStrings[kind] } diff --git a/src/pkg/go/parser/parser.go b/src/pkg/go/parser/parser.go index 7c5843f363..5d4814870c 100644 --- a/src/pkg/go/parser/parser.go +++ b/src/pkg/go/parser/parser.go @@ -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). // They control the amount of source code parsed and other optional // parser functionality. @@ -30,6 +26,7 @@ const ( ImportsOnly // parsing stops after import declarations ParseComments // parse comments and add them to AST Trace // print a trace of parsed productions + DeclarationErrors // report declaration errors ) @@ -46,8 +43,8 @@ type parser struct { // Comments comments []*ast.CommentGroup - leadComment *ast.CommentGroup // the last lead comment - lineComment *ast.CommentGroup // the last line comment + leadComment *ast.CommentGroup // last lead comment + lineComment *ast.CommentGroup // last line comment // Next token pos token.Pos // token position @@ -56,6 +53,16 @@ type parser struct { // Non-syntactic parser control 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) { p.file = fset.AddFile(filename, fset.Base(), len(src)) p.scanner.Init(p.file, src, p, scannerMode(mode)) + p.mode = mode p.trace = mode&Trace != 0 // for convenience (p.trace is used frequently) + 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")) } - var x ast.Expr = p.parseIdent() + ident := p.parseIdent() + p.resolve(ident) + var x ast.Expr = ident if p.tok == token.PERIOD { // first identifier is a package identifier p.next() sel := p.parseIdent() x = &ast.SelectorExpr{x, sel} } + 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} } @@ -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 { defer un(trace(p, "ParameterList")) } @@ -528,7 +646,11 @@ func (p *parser) parseParameterList(ellipsisOk bool) (params []*ast.Field) { if typ != nil { // IdentifierList Type 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 { p.next() } @@ -536,7 +658,11 @@ func (p *parser) parseParameterList(ellipsisOk bool) (params []*ast.Field) { for p.tok != token.RPAREN && p.tok != token.EOF { idents := p.parseIdentList() 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 { 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 { defer un(trace(p, "Parameters")) } @@ -563,7 +689,7 @@ func (p *parser) parseParameters(ellipsisOk bool) *ast.FieldList { var params []*ast.Field lparen := p.expect(token.LPAREN) if p.tok != token.RPAREN { - params = p.parseParameterList(ellipsisOk) + params = p.parseParameterList(scope, ellipsisOk) } 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 { defer un(trace(p, "Result")) } if p.tok == token.LPAREN { - return p.parseParameters(false) + return p.parseParameters(scope, false) } 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 { defer un(trace(p, "Signature")) } - params = p.parseParameters(true) - results = p.parseResult() + params = p.parseParameters(scope, true) + results = p.parseResult(scope) return } -func (p *parser) parseFuncType() *ast.FuncType { +func (p *parser) parseFuncType() (*ast.FuncType, *ast.Scope) { if p.trace { defer un(trace(p, "FuncType")) } 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 { // method 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} } else { // embedded interface typ = x } - p.expectSemi() + p.expectSemi() // call before accessing 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: return p.parsePointerType() case token.FUNC: - return p.parseFuncType() + typ, _ := p.parseFuncType() + return typ case token.INTERFACE: return p.parseInterfaceType() 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 { defer un(trace(p, "Body")) } lbrace := p.expect(token.LBRACE) + p.topScope = scope // open function scope + p.openLabelScope() list := p.parseStmtList() + p.closeLabelScope() + p.closeScope() rbrace := p.expect(token.RBRACE) return &ast.BlockStmt{lbrace, list, rbrace} @@ -764,7 +897,9 @@ func (p *parser) parseBlockStmt() *ast.BlockStmt { } lbrace := p.expect(token.LBRACE) + p.openScope() list := p.parseStmtList() + p.closeScope() rbrace := p.expect(token.RBRACE) return &ast.BlockStmt{lbrace, list, rbrace} @@ -779,14 +914,14 @@ func (p *parser) parseFuncTypeOrLit() ast.Expr { defer un(trace(p, "FuncTypeOrLit")) } - typ := p.parseFuncType() + typ, scope := p.parseFuncType() if p.tok != token.LBRACE { // function type only return typ } p.exprLev++ - body := p.parseBody() + body := p.parseBody(scope) p.exprLev-- return &ast.FuncLit{typ, body} @@ -803,7 +938,9 @@ func (p *parser) parseOperand() ast.Expr { switch p.tok { 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: 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 p.next() y := p.parseExprList() + if tok == token.DEFINE { + p.shortVarDecl(p.makeIdentList(x)) + } return &ast.AssignStmt{x, pos, tok, y} } @@ -1216,7 +1356,12 @@ func (p *parser) parseSimpleStmt(labelOk bool) ast.Stmt { colon := p.pos p.next() 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") 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")) } - s := &ast.BranchStmt{p.pos, tok, nil} - p.expect(tok) + pos := p.expect(tok) + var label *ast.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() - return s + return &ast.BranchStmt{pos, tok, label} } @@ -1333,6 +1481,8 @@ func (p *parser) parseIfStmt() *ast.IfStmt { } pos := p.expect(token.IF) + p.openScope() + defer p.closeScope() var s ast.Stmt var x ast.Expr @@ -1373,7 +1523,6 @@ func (p *parser) parseCaseClause() *ast.CaseClause { defer un(trace(p, "CaseClause")) } - // SwitchCase pos := p.pos var x []ast.Expr if p.tok == token.CASE { @@ -1384,7 +1533,9 @@ func (p *parser) parseCaseClause() *ast.CaseClause { } colon := p.expect(token.COLON) + p.openScope() body := p.parseStmtList() + p.closeScope() return &ast.CaseClause{pos, x, colon, body} } @@ -1410,7 +1561,6 @@ func (p *parser) parseTypeCaseClause() *ast.TypeCaseClause { defer un(trace(p, "TypeCaseClause")) } - // TypeSwitchCase pos := p.pos var types []ast.Expr if p.tok == token.CASE { @@ -1421,7 +1571,9 @@ func (p *parser) parseTypeCaseClause() *ast.TypeCaseClause { } colon := p.expect(token.COLON) + p.openScope() body := p.parseStmtList() + p.closeScope() return &ast.TypeCaseClause{pos, types, colon, body} } @@ -1447,6 +1599,8 @@ func (p *parser) parseSwitchStmt() ast.Stmt { } pos := p.expect(token.SWITCH) + p.openScope() + defer p.closeScope() var s1, s2 ast.Stmt if p.tok != token.LBRACE { @@ -1497,7 +1651,7 @@ func (p *parser) parseCommClause() *ast.CommClause { defer un(trace(p, "CommClause")) } - // CommCase + p.openScope() pos := p.pos var comm ast.Stmt if p.tok == token.CASE { @@ -1518,7 +1672,7 @@ func (p *parser) parseCommClause() *ast.CommClause { pos := p.pos tok := p.tok var rhs ast.Expr - if p.tok == token.ASSIGN || p.tok == token.DEFINE { + if tok == token.ASSIGN || tok == token.DEFINE { // RecvStmt with assignment if len(lhs) > 2 { p.errorExpected(lhs[0].Pos(), "1 or 2 expressions") @@ -1527,6 +1681,9 @@ func (p *parser) parseCommClause() *ast.CommClause { } p.next() rhs = p.parseExpr() + if tok == token.DEFINE { + p.shortVarDecl(p.makeIdentList(lhs)) + } } else { // rhs must be single receive operation if len(lhs) > 1 { @@ -1552,6 +1709,7 @@ func (p *parser) parseCommClause() *ast.CommClause { colon := p.expect(token.COLON) body := p.parseStmtList() + p.closeScope() return &ast.CommClause{pos, comm, colon, body} } @@ -1582,6 +1740,8 @@ func (p *parser) parseForStmt() ast.Stmt { } pos := p.expect(token.FOR) + p.openScope() + defer p.closeScope() var s1, s2, s3 ast.Stmt if p.tok != token.LBRACE { @@ -1631,18 +1791,16 @@ func (p *parser) parseForStmt() ast.Stmt { return &ast.BadStmt{pos, body.End()} } 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} - } else { - p.errorExpected(s2.Pos(), "range clause") - return &ast.BadStmt{pos, body.End()} } - } else { - // regular for statement - return &ast.ForStmt{pos, s1, p.makeExpr(s2), s3, body} + p.errorExpected(s2.Pos(), "range clause") + return &ast.BadStmt{pos, body.End()} } - panic("unreachable") + // regular for statement + return &ast.ForStmt{pos, s1, p.makeExpr(s2), s3, body} } @@ -1715,10 +1873,11 @@ func parseImportSpec(p *parser, doc *ast.CommentGroup) ast.Spec { } var ident *ast.Ident - if p.tok == token.PERIOD { + switch p.tok { + case token.PERIOD: ident = &ast.Ident{p.pos, ".", nil} p.next() - } else if p.tok == token.IDENT { + case token.IDENT: ident = p.parseIdent() } @@ -1729,7 +1888,7 @@ func parseImportSpec(p *parser, doc *ast.CommentGroup) ast.Spec { } else { 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} } @@ -1747,9 +1906,16 @@ func parseConstSpec(p *parser, doc *ast.CommentGroup) ast.Spec { p.expect(token.ASSIGN) values = p.parseExprList() } - p.expectSemi() + p.expectSemi() // call before accessing 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 &ast.ValueSpec{doc, idents, typ, values, p.lineComment} + return spec } @@ -1760,9 +1926,16 @@ func parseTypeSpec(p *parser, doc *ast.CommentGroup) ast.Spec { ident := p.parseIdent() typ := p.parseType() - p.expectSemi() + p.expectSemi() // call before accessing 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 &ast.TypeSpec{doc, ident, typ, p.lineComment} + return spec } @@ -1778,9 +1951,16 @@ func parseVarSpec(p *parser, doc *ast.CommentGroup) ast.Spec { p.expect(token.ASSIGN) values = p.parseExprList() } - p.expectSemi() + p.expectSemi() // call before accessing 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 &ast.ValueSpec{doc, idents, typ, values, p.lineComment} + 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 { defer un(trace(p, "Receiver")) } pos := p.pos - par := p.parseParameters(false) + par := p.parseParameters(scope, false) // must have exactly one receiver if par.NumFields() != 1 { @@ -1844,22 +2024,37 @@ func (p *parser) parseFuncDecl() *ast.FuncDecl { doc := p.leadComment pos := p.expect(token.FUNC) + scope := ast.NewScope(p.topScope) // function scope var recv *ast.FieldList if p.tok == token.LPAREN { - recv = p.parseReceiver() + recv = p.parseReceiver(scope) } ident := p.parseIdent() - params, results := p.parseSignature() + + params, results := p.parseSignature(scope) var body *ast.BlockStmt if p.tok == token.LBRACE { - body = p.parseBody() + body = p.parseBody(scope) } 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 doc := p.leadComment 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() 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} } diff --git a/src/pkg/go/parser/parser_test.go b/src/pkg/go/parser/parser_test.go index 38535627a7..8e07d9f87c 100644 --- a/src/pkg/go/parser/parser_test.go +++ b/src/pkg/go/parser/parser_test.go @@ -73,7 +73,7 @@ var validFiles = []string{ func TestParse3(t *testing.T) { for _, filename := range validFiles { - _, err := ParseFile(fset, filename, nil, 0) + _, err := ParseFile(fset, filename, nil, DeclarationErrors) if err != nil { t.Errorf("ParseFile(%s): %v", filename, err) } diff --git a/src/pkg/go/typechecker/Makefile b/src/pkg/go/typechecker/Makefile index 62b2aa7fe0..83af3ef4e4 100644 --- a/src/pkg/go/typechecker/Makefile +++ b/src/pkg/go/typechecker/Makefile @@ -7,6 +7,7 @@ include ../../../Make.inc TARG=go/typechecker GOFILES=\ scope.go\ + type.go\ typechecker.go\ universe.go\ diff --git a/src/pkg/go/typechecker/scope.go b/src/pkg/go/typechecker/scope.go index 114c93ea86..bd24f4ca42 100644 --- a/src/pkg/go/typechecker/scope.go +++ b/src/pkg/go/typechecker/scope.go @@ -2,15 +2,15 @@ // Use of this source code is governed by a BSD-style // 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 -import ( - "fmt" - "go/ast" - "go/token" -) +import "go/ast" 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. // 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. -// (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.Kind, name *ast.Ident, decl interface{}, n int) *ast.Object { +func (tc *typechecker) declInScope(scope *ast.Scope, kind ast.ObjKind, name *ast.Ident, decl interface{}, n int) *ast.Object { obj := ast.NewObj(kind, name.Name) obj.Decl = decl - obj.N = n + //obj.N = n name.Obj = obj - if alt := scope.Insert(obj); alt != obj { - tc.Errorf(name.Pos(), "%s already declared at %s", name.Name, objPos(alt)) + if name.Name != "_" { + if alt := scope.Insert(obj); alt != obj { + tc.Errorf(name.Pos(), "%s already declared at %s", name.Name, tc.fset.Position(alt.Pos()).String()) + } } return obj } // 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) } @@ -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. // 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. obj = typ.Scope.Lookup(name.Name) if obj == nil { @@ -100,20 +73,3 @@ func (tc *typechecker) findField(typ *ast.Type, name *ast.Ident) (obj *ast.Objec } 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") -} diff --git a/src/pkg/go/typechecker/testdata/test0.go b/src/pkg/go/typechecker/testdata/test0.src similarity index 100% rename from src/pkg/go/typechecker/testdata/test0.go rename to src/pkg/go/typechecker/testdata/test0.src diff --git a/src/pkg/go/typechecker/testdata/test1.go b/src/pkg/go/typechecker/testdata/test1.src similarity index 100% rename from src/pkg/go/typechecker/testdata/test1.go rename to src/pkg/go/typechecker/testdata/test1.src diff --git a/src/pkg/go/typechecker/testdata/test3.go b/src/pkg/go/typechecker/testdata/test3.src similarity index 80% rename from src/pkg/go/typechecker/testdata/test3.go rename to src/pkg/go/typechecker/testdata/test3.src index ea35808a09..2e1a9fa8f5 100644 --- a/src/pkg/go/typechecker/testdata/test3.go +++ b/src/pkg/go/typechecker/testdata/test3.src @@ -27,8 +27,11 @@ func (T) m1 /* ERROR "already declared" */ () {} 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 (T) _(x, x /* ERROR "already declared" */ int) {} -func (T) _() (x, x /* ERROR "already declared" */ int) {} +// The following are disabled for now because the typechecker +// 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) _() {} diff --git a/src/pkg/go/typechecker/testdata/test4.go b/src/pkg/go/typechecker/testdata/test4.src similarity index 100% rename from src/pkg/go/typechecker/testdata/test4.go rename to src/pkg/go/typechecker/testdata/test4.src diff --git a/src/pkg/go/typechecker/type.go b/src/pkg/go/typechecker/type.go new file mode 100644 index 0000000000..62b4e9d3e4 --- /dev/null +++ b/src/pkg/go/typechecker/type.go @@ -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", +} diff --git a/src/pkg/go/typechecker/typechecker.go b/src/pkg/go/typechecker/typechecker.go index e9aefa2402..4fc5647f0d 100644 --- a/src/pkg/go/typechecker/typechecker.go +++ b/src/pkg/go/typechecker/typechecker.go @@ -65,6 +65,7 @@ type typechecker struct { fset *token.FileSet scanner.ErrorVector importer Importer + globals []*ast.Object // list of global objects topScope *ast.Scope // current top-most scope cyclemap map[*ast.Object]bool // for cycle detection 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 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 - 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 - // (note that objects with _ name are also in the scope) tc.cyclemap = make(map[*ast.Object]bool) - for _, obj := range tc.topScope.Objects { + for _, obj := range tc.globals { tc.resolve(obj) } assert(len(tc.cyclemap) == 0) // 4: sequentially typecheck function and method bodies 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 @@ -183,11 +184,11 @@ func (tc *typechecker) declGlobal(global ast.Decl) { } } 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: 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: panic("unreachable") @@ -196,9 +197,10 @@ func (tc *typechecker) declGlobal(global ast.Decl) { iota++ case *ast.TypeSpec: obj := tc.decl(ast.Typ, s.Name, s, 0) + tc.globals = append(tc.globals, obj) // give all type objects an unresolved type so // that we can collect methods in the type scope - typ := ast.NewType(ast.Unresolved) + typ := NewType(Unresolved) obj.Type = typ typ.Obj = obj default: @@ -208,7 +210,7 @@ func (tc *typechecker) declGlobal(global ast.Decl) { case *ast.FuncDecl: 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: @@ -239,8 +241,8 @@ func (tc *typechecker) bindMethod(method *ast.FuncDecl) { } else if obj.Kind != ast.Typ { tc.Errorf(name.Pos(), "invalid receiver: %s is not a type", name.Name) } else { - typ := obj.Type - assert(typ.Form == ast.Unresolved) + typ := obj.Type.(*Type) + assert(typ.Form == Unresolved) scope = typ.Scope } } @@ -261,7 +263,7 @@ func (tc *typechecker) bindMethod(method *ast.FuncDecl) { func (tc *typechecker) resolve(obj *ast.Object) { // check for declaration cycles 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 return } @@ -271,7 +273,7 @@ func (tc *typechecker) resolve(obj *ast.Object) { }() // resolve non-type objects - typ := obj.Type + typ, _ := obj.Type.(*Type) if typ == nil { switch obj.Kind { case ast.Bad: @@ -282,12 +284,12 @@ func (tc *typechecker) resolve(obj *ast.Object) { case ast.Var: 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: - obj.Type = ast.NewType(ast.Function) + obj.Type = NewType(Function) 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: // type objects have non-nil types when resolve is called @@ -300,32 +302,34 @@ func (tc *typechecker) resolve(obj *ast.Object) { } // resolve type objects - if typ.Form == ast.Unresolved { + if typ.Form == Unresolved { tc.typeFor(typ, typ.Obj.Decl.(*ast.TypeSpec).Type, false) // provide types for all methods for _, obj := range typ.Scope.Objects { if obj.Kind == ast.Fun { assert(obj.Type == nil) - obj.Type = ast.NewType(ast.Method) + obj.Type = NewType(Method) f := obj.Decl.(*ast.FuncDecl) 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() defer tc.closeScope() // inject function/method parameters into block scope, if any if ftype != nil { for _, par := range ftype.Params.Objects { - obj := tc.topScope.Insert(par) - assert(obj == par) // ftype has no double declarations + if par.Name != "_" { + obj := tc.topScope.Insert(par) + assert(obj == par) // ftype has no double declarations + } } } @@ -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) { - assert((typ.Form == ast.Method) == (recv != nil)) +func (tc *typechecker) declSignature(typ *Type, recv, params, results *ast.FieldList) { + assert((typ.Form == Method) == (recv != nil)) typ.Params = ast.NewScope(nil) tc.declFields(typ.Params, recv, 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) // 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 { tc.Errorf(t.Pos(), "%s is not a type", t.Name) if def == nil { - typ = ast.NewType(ast.BadType) + typ = NewType(BadType) } else { typ = def - typ.Form = ast.BadType + typ.Form = BadType } typ.Expr = x return @@ -393,7 +397,7 @@ func (tc *typechecker) typeFor(def *ast.Type, x ast.Expr, ref bool) (typ *ast.Ty if !ref { tc.resolve(obj) // check for cycles even if type resolved } - typ = obj.Type + typ = obj.Type.(*Type) if def != nil { // 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 typ = def if typ == nil { - typ = ast.NewType(ast.BadType) + typ = NewType(BadType) } typ.Expr = x @@ -419,42 +423,42 @@ func (tc *typechecker) typeFor(def *ast.Type, x ast.Expr, ref bool) (typ *ast.Ty if debug { fmt.Println("qualified identifier unimplemented") } - typ.Form = ast.BadType + typ.Form = BadType case *ast.StarExpr: - typ.Form = ast.Pointer + typ.Form = Pointer typ.Elt = tc.typeFor(nil, t.X, true) case *ast.ArrayType: if t.Len != nil { - typ.Form = ast.Array + typ.Form = Array // TODO(gri) compute the real length // (this may call resolve recursively) (*typ).N = 42 } else { - typ.Form = ast.Slice + typ.Form = Slice } typ.Elt = tc.typeFor(nil, t.Elt, t.Len == nil) case *ast.StructType: - typ.Form = ast.Struct + typ.Form = Struct tc.declFields(typ.Scope, t.Fields, false) case *ast.FuncType: - typ.Form = ast.Function + typ.Form = Function tc.declSignature(typ, nil, t.Params, t.Results) case *ast.InterfaceType: - typ.Form = ast.Interface + typ.Form = Interface tc.declFields(typ.Scope, t.Methods, true) case *ast.MapType: - typ.Form = ast.Map + typ.Form = Map typ.Key = tc.typeFor(nil, t.Key, true) typ.Elt = tc.typeFor(nil, t.Value, true) case *ast.ChanType: - typ.Form = ast.Channel + typ.Form = Channel typ.N = uint(t.Dir) typ.Elt = tc.typeFor(nil, t.Value, true) diff --git a/src/pkg/go/typechecker/typechecker_test.go b/src/pkg/go/typechecker/typechecker_test.go index 33f4a6223f..3988ff1680 100644 --- a/src/pkg/go/typechecker/typechecker_test.go +++ b/src/pkg/go/typechecker/typechecker_test.go @@ -93,7 +93,7 @@ func expectedErrors(t *testing.T, pkg *ast.Package) (list scanner.ErrorList) { func testFilter(f *os.FileInfo) bool { - return strings.HasSuffix(f.Name, ".go") && f.Name[0] != '.' + return strings.HasSuffix(f.Name, ".src") && f.Name[0] != '.' } diff --git a/src/pkg/go/typechecker/universe.go b/src/pkg/go/typechecker/universe.go index db950737f3..cf4434993e 100644 --- a/src/pkg/go/typechecker/universe.go +++ b/src/pkg/go/typechecker/universe.go @@ -24,8 +24,8 @@ func init() { Universe = ast.NewScope(nil) // basic types - for n, name := range ast.BasicTypes { - typ := ast.NewType(ast.Basic) + for n, name := range BasicTypes { + typ := NewType(Basic) typ.N = n obj := ast.NewObj(ast.Typ, name) obj.Type = typ -- 2.50.0