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),
// 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
}
//
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
}
}
}
- 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}
}
// 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)}
}
// 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
)
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] }
)
-// 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.
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
)
// 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
// 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
}
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)
+ }
}
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
}
}
}
- p.expectSemi()
+ p.expectSemi() // call before accessing p.linecomment
return &ast.Field{doc, idents, typ, tag, p.lineComment}
}
}
-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"))
}
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()
}
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
}
}
-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"))
}
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)
}
-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()
}
-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
}
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}
}
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:
}
-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}
}
lbrace := p.expect(token.LBRACE)
+ p.openScope()
list := p.parseStmtList()
+ p.closeScope()
rbrace := p.expect(token.RBRACE)
return &ast.BlockStmt{lbrace, list, rbrace}
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}
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}
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}
}
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}
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}
}
}
pos := p.expect(token.IF)
+ p.openScope()
+ defer p.closeScope()
var s ast.Stmt
var x ast.Expr
defer un(trace(p, "CaseClause"))
}
- // SwitchCase
pos := p.pos
var x []ast.Expr
if p.tok == token.CASE {
}
colon := p.expect(token.COLON)
+ p.openScope()
body := p.parseStmtList()
+ p.closeScope()
return &ast.CaseClause{pos, x, colon, body}
}
defer un(trace(p, "TypeCaseClause"))
}
- // TypeSwitchCase
pos := p.pos
var types []ast.Expr
if p.tok == token.CASE {
}
colon := p.expect(token.COLON)
+ p.openScope()
body := p.parseStmtList()
+ p.closeScope()
return &ast.TypeCaseClause{pos, types, colon, body}
}
}
pos := p.expect(token.SWITCH)
+ p.openScope()
+ defer p.closeScope()
var s1, s2 ast.Stmt
if p.tok != token.LBRACE {
defer un(trace(p, "CommClause"))
}
- // CommCase
+ p.openScope()
pos := p.pos
var comm ast.Stmt
if p.tok == token.CASE {
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")
}
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 {
colon := p.expect(token.COLON)
body := p.parseStmtList()
+ p.closeScope()
return &ast.CommClause{pos, comm, colon, body}
}
}
pos := p.expect(token.FOR)
+ p.openScope()
+ defer p.closeScope()
var s1, s2, s3 ast.Stmt
if p.tok != token.LBRACE {
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}
}
}
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()
}
} 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}
}
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
}
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
}
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
}
}
-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 {
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
}
// 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()
}
}
- 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}
}
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)
}
TARG=go/typechecker
GOFILES=\
scope.go\
+ type.go\
typechecker.go\
universe.go\
// 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 {
}
-// 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)
}
// 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 {
}
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")
-}
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) _() {}
--- /dev/null
+// 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",
+}
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
- 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
}
// 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
}
}
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")
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:
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:
} 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
}
}
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
}
}()
// resolve non-type objects
- typ := obj.Type
+ typ, _ := obj.Type.(*Type)
if typ == nil {
switch obj.Kind {
case ast.Bad:
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
}
// 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
+ }
}
}
}
-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)
}
-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
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
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
// type literal
typ = def
if typ == nil {
- typ = ast.NewType(ast.BadType)
+ typ = NewType(BadType)
}
typ.Expr = x
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)
func testFilter(f *os.FileInfo) bool {
- return strings.HasSuffix(f.Name, ".go") && f.Name[0] != '.'
+ return strings.HasSuffix(f.Name, ".src") && f.Name[0] != '.'
}
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