go.tools/go/types: simplify handling of blank _ identifiers

Also: Fixes a couple of places where scopes instead
      of objsets were used (missed in previous CL).

R=adonovan
CC=golang-dev
https://golang.org/cl/11419047
This commit is contained in:
Robert Griesemer 2013-07-24 12:39:11 -07:00
parent d0657ebb0d
commit 6dbbc59ae0
7 changed files with 55 additions and 59 deletions

View File

@ -87,30 +87,31 @@ type Config struct {
// Info holds result type information for a package. // Info holds result type information for a package.
type Info struct { type Info struct {
// If Types != nil, it records the type for each expression that is // If Types != nil, it records the expression and corresponding type
// type-checked. Expressions corresponding to identifiers on the lhs // for each expression that is type-checked. Identifiers declaring a
// of a declaration are recorded in Objects, not Types. // variable are recorded in Objects, not Types.
Types map[ast.Expr]Type Types map[ast.Expr]Type
// If Values != nil, it records the value of each constant expression // If Values != nil, it records the expression and corresponding value
// that is type-checked. // for each constant expression that is type-checked.
Values map[ast.Expr]exact.Value Values map[ast.Expr]exact.Value
// If Objects != nil, it records the object denoted by each identifier // If Objects != nil, it records the identifier and corresponding object
// that is type-checked (including package names, dots "." of dot-imports, // for each identifier that is type-checked (including package names,
// and blank "_" identifiers). For identifiers that were not declared, // dots "." of dot-imports, and blank "_" identifiers). For identifiers
// the corresponding object is nil. // that were not declared due to an error, the corresponding object is nil.
// BUG(gri) Label identifiers in break, continue, or goto statements // BUG(gri) Label identifiers in break, continue, or goto statements
// are not recorded. // are not recorded.
Objects map[*ast.Ident]Object Objects map[*ast.Ident]Object
// If Implicits != nil, it records the object for each node that implicitly // If Implicits != nil, it records the node and corresponding object for
// declares objects. The following node and object types may appear: // each node that is type-checked and that implicitly declared an object.
// The following node and object types may appear:
// //
// node declared object // node declared object
// //
// *ast.ImportSpec *Package (imports w/o renames), or imported objects (dot-imports) // *ast.ImportSpec *Package (imports w/o renames), or imported objects (dot-imports)
// *ast.CaseClause type-specific *Var introduced for each single-type type switch clause // *ast.CaseClause type-specific *Var for each type switch case clause (incl. default)
// *ast.Field anonymous struct field or parameter *Var // *ast.Field anonymous struct field or parameter *Var
// //
Implicits map[ast.Node]Object Implicits map[ast.Node]Object

View File

@ -288,17 +288,11 @@ func (check *checker) shortVarDecl(lhs, rhs []ast.Expr) {
check.initVars(vars, rhs, true) check.initVars(vars, rhs, true)
// declare variables // declare variables
n := 0 // number of new variables n := scope.NumEntries()
for _, obj := range vars { for _, obj := range vars {
if obj.name == "_" { scope.Insert(obj)
obj.setParent(scope)
continue // blank identifiers are not visible
}
if scope.Insert(obj) == nil {
n++ // new declaration
}
} }
if n == 0 { if n == scope.NumEntries() {
check.errorf(vars[0].Pos(), "no new variables on left side of :=") check.errorf(vars[0].Pos(), "no new variables on left side of :=")
} }
} }

View File

@ -494,7 +494,7 @@ func (p *gcParser) parseStructType() Type {
p.expectKeyword("struct") p.expectKeyword("struct")
p.expect('{') p.expect('{')
scope := NewScope(nil) var fset objset
for i := 0; p.tok != '}'; i++ { for i := 0; p.tok != '}'; i++ {
if i > 0 { if i > 0 {
p.expect(';') p.expect(';')
@ -506,15 +506,13 @@ func (p *gcParser) parseStructType() Type {
if tags != nil { if tags != nil {
tags = append(tags, tag) tags = append(tags, tag)
} }
if fld.name == "_" { if alt := fset.insert(fld); alt != nil {
// blank identifiers are not declared
fld.setParent(scope)
} else if alt := scope.Insert(fld); alt != nil {
pname := "<no pkg name>" pname := "<no pkg name>"
if pkg := alt.Pkg(); pkg != nil { if pkg := alt.Pkg(); pkg != nil {
pname = pkg.name pname = pkg.name
} }
p.errorf("multiple fields named %s.%s", pname, alt.Name()) p.errorf("multiple fields named %s.%s", pname, alt.Name())
continue
} }
fields = append(fields, fld) fields = append(fields, fld)
} }
@ -598,12 +596,12 @@ func (p *gcParser) parseSignature() *Signature {
// visible in the export data. // visible in the export data.
// //
func (p *gcParser) parseInterfaceType() Type { func (p *gcParser) parseInterfaceType() Type {
var scope *Scope // lazily allocated (empty interfaces are not uncommon) typ := new(Interface)
var methods []*Func var methods []*Func
typ := new(Interface)
p.expectKeyword("interface") p.expectKeyword("interface")
p.expect('{') p.expect('{')
var mset objset
for i := 0; p.tok != '}'; i++ { for i := 0; p.tok != '}'; i++ {
if i > 0 { if i > 0 {
p.expect(';') p.expect(';')
@ -614,17 +612,15 @@ func (p *gcParser) parseInterfaceType() Type {
// typ, for less verbose printing of interface method signatures. // typ, for less verbose printing of interface method signatures.
sig.recv = NewVar(token.NoPos, pkg, "", typ) sig.recv = NewVar(token.NoPos, pkg, "", typ)
m := NewFunc(token.NoPos, pkg, name, sig) m := NewFunc(token.NoPos, pkg, name, sig)
if scope == nil { if alt := mset.insert(m); alt != nil {
scope = NewScope(nil)
}
if alt := scope.Insert(m); alt != nil {
p.errorf("multiple methods named %s.%s", alt.Pkg().name, alt.Name()) p.errorf("multiple methods named %s.%s", alt.Pkg().name, alt.Name())
continue
} }
methods = append(methods, m) methods = append(methods, m)
} }
p.expect('}') p.expect('}')
typ.methods = methods
typ.methods = methods
return typ return typ
} }

View File

@ -314,12 +314,11 @@ func concat(list []int, i int) []int {
// fieldIndex returns the index for the field with matching package and name, or a value < 0. // fieldIndex returns the index for the field with matching package and name, or a value < 0.
func fieldIndex(fields []*Var, pkg *Package, name string) int { func fieldIndex(fields []*Var, pkg *Package, name string) int {
if name == "_" { if name != "_" {
return -1 // blank identifiers are never found for i, f := range fields {
} if f.sameId(pkg, name) {
for i, f := range fields { return i
if f.sameId(pkg, name) { }
return i
} }
} }
return -1 return -1
@ -327,10 +326,11 @@ func fieldIndex(fields []*Var, pkg *Package, name string) int {
// lookupMethod returns the index of and method with matching package and name, or (-1, nil). // lookupMethod returns the index of and method with matching package and name, or (-1, nil).
func lookupMethod(methods []*Func, pkg *Package, name string) (int, *Func) { func lookupMethod(methods []*Func, pkg *Package, name string) (int, *Func) {
assert(name != "_") if name != "_" {
for i, m := range methods { for i, m := range methods {
if m.sameId(pkg, name) { if m.sameId(pkg, name) {
return i, m return i, m
}
} }
} }
return -1, nil return -1, nil

View File

@ -19,11 +19,13 @@ type objset struct {
// insert attempts to insert an object obj into objset s. // insert attempts to insert an object obj into objset s.
// If s already contains an alternative object alt with // If s already contains an alternative object alt with
// the same name, insert leaves s unchanged and returns alt. // the same name, insert leaves s unchanged and returns alt.
// Otherwise it inserts obj and returns nil. // Otherwise it inserts obj and returns nil. Objects with
// The object name must not be blank _. // blank "_" names are ignored.
func (s *objset) insert(obj Object) (alt Object) { func (s *objset) insert(obj Object) Object {
name := obj.Name() name := obj.Name()
assert(name != "_") if name == "_" {
return nil
}
id := Id(obj.Pkg(), name) id := Id(obj.Pkg(), name)
if alt := s.objmap[id]; alt != nil { if alt := s.objmap[id]; alt != nil {
return alt return alt

View File

@ -24,10 +24,7 @@ func (check *checker) reportAltDecl(obj Object) {
} }
func (check *checker) declareObj(scope *Scope, id *ast.Ident, obj Object) { func (check *checker) declareObj(scope *Scope, id *ast.Ident, obj Object) {
if obj.Name() == "_" { if alt := scope.Insert(obj); alt != nil {
// blank identifiers are not declared
obj.setParent(scope)
} else if alt := scope.Insert(obj); alt != nil {
check.errorf(obj.Pos(), "%s redeclared in this block", obj.Name()) check.errorf(obj.Pos(), "%s redeclared in this block", obj.Name())
check.reportAltDecl(alt) check.reportAltDecl(alt)
obj = nil obj = nil
@ -38,9 +35,7 @@ func (check *checker) declareObj(scope *Scope, id *ast.Ident, obj Object) {
} }
func (check *checker) declareFld(oset *objset, id *ast.Ident, obj Object) { func (check *checker) declareFld(oset *objset, id *ast.Ident, obj Object) {
if obj.Name() == "_" { if alt := oset.insert(obj); alt != nil {
// blank identifiers are not declared
} else if alt := oset.insert(obj); alt != nil {
check.errorf(obj.Pos(), "%s redeclared", obj.Name()) check.errorf(obj.Pos(), "%s redeclared", obj.Name())
check.reportAltDecl(alt) check.reportAltDecl(alt)
obj = nil obj = nil
@ -525,9 +520,7 @@ func (check *checker) typeDecl(obj *TypeName, typ ast.Expr, def *Named, cycleOk
// TODO(gri) consider keeping the objset with the struct instead // TODO(gri) consider keeping the objset with the struct instead
if t, _ := named.underlying.(*Struct); t != nil { if t, _ := named.underlying.(*Struct); t != nil {
for _, fld := range t.fields { for _, fld := range t.fields {
if fld.name != "_" { assert(mset.insert(fld) == nil)
assert(mset.insert(fld) == nil)
}
} }
} }

View File

@ -106,10 +106,20 @@ func (s *Scope) LookupParent(name string) Object {
// If s already contains an alternative object alt with // If s already contains an alternative object alt with
// the same name, Insert leaves s unchanged and returns alt. // the same name, Insert leaves s unchanged and returns alt.
// Otherwise it inserts obj, sets the object's scope to // Otherwise it inserts obj, sets the object's scope to
// s, and returns nil. The object name must not be blank _. // s, and returns nil. Objects with blank "_" names are
// not inserted, but have their parent field set to s.
func (s *Scope) Insert(obj Object) Object { func (s *Scope) Insert(obj Object) Object {
name := obj.Name() name := obj.Name()
assert(name != "_")
// spec: "The blank identifier, represented by the underscore
// character _, may be used in a declaration like any other
// identifier but the declaration does not introduce a new
// binding."
if name == "_" {
obj.setParent(s)
return nil
}
if alt := s.Lookup(name); alt != nil { if alt := s.Lookup(name); alt != nil {
return alt return alt
} }