diff --git a/go/types/api.go b/go/types/api.go index 34bd3c83..4c8a3a43 100644 --- a/go/types/api.go +++ b/go/types/api.go @@ -87,30 +87,31 @@ type Config struct { // Info holds result type information for a package. type Info struct { - // If Types != nil, it records the type for each expression that is - // type-checked. Expressions corresponding to identifiers on the lhs - // of a declaration are recorded in Objects, not Types. + // If Types != nil, it records the expression and corresponding type + // for each expression that is type-checked. Identifiers declaring a + // variable are recorded in Objects, not Types. Types map[ast.Expr]Type - // If Values != nil, it records the value of each constant expression - // that is type-checked. + // If Values != nil, it records the expression and corresponding value + // for each constant expression that is type-checked. Values map[ast.Expr]exact.Value - // If Objects != nil, it records the object denoted by each identifier - // that is type-checked (including package names, dots "." of dot-imports, - // and blank "_" identifiers). For identifiers that were not declared, - // the corresponding object is nil. + // If Objects != nil, it records the identifier and corresponding object + // for each identifier that is type-checked (including package names, + // dots "." of dot-imports, and blank "_" identifiers). For identifiers + // that were not declared due to an error, the corresponding object is nil. // BUG(gri) Label identifiers in break, continue, or goto statements // are not recorded. Objects map[*ast.Ident]Object - // If Implicits != nil, it records the object for each node that implicitly - // declares objects. The following node and object types may appear: + // If Implicits != nil, it records the node and corresponding object for + // each node that is type-checked and that implicitly declared an object. + // The following node and object types may appear: // // node declared object // // *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 // Implicits map[ast.Node]Object diff --git a/go/types/assignments.go b/go/types/assignments.go index de4b28fe..ca9a0eb2 100644 --- a/go/types/assignments.go +++ b/go/types/assignments.go @@ -288,17 +288,11 @@ func (check *checker) shortVarDecl(lhs, rhs []ast.Expr) { check.initVars(vars, rhs, true) // declare variables - n := 0 // number of new variables + n := scope.NumEntries() for _, obj := range vars { - if obj.name == "_" { - obj.setParent(scope) - continue // blank identifiers are not visible - } - if scope.Insert(obj) == nil { - n++ // new declaration - } + scope.Insert(obj) } - if n == 0 { + if n == scope.NumEntries() { check.errorf(vars[0].Pos(), "no new variables on left side of :=") } } diff --git a/go/types/gcimporter.go b/go/types/gcimporter.go index cbd6f3e5..3403b9ea 100644 --- a/go/types/gcimporter.go +++ b/go/types/gcimporter.go @@ -494,7 +494,7 @@ func (p *gcParser) parseStructType() Type { p.expectKeyword("struct") p.expect('{') - scope := NewScope(nil) + var fset objset for i := 0; p.tok != '}'; i++ { if i > 0 { p.expect(';') @@ -506,15 +506,13 @@ func (p *gcParser) parseStructType() Type { if tags != nil { tags = append(tags, tag) } - if fld.name == "_" { - // blank identifiers are not declared - fld.setParent(scope) - } else if alt := scope.Insert(fld); alt != nil { + if alt := fset.insert(fld); alt != nil { pname := "" if pkg := alt.Pkg(); pkg != nil { pname = pkg.name } p.errorf("multiple fields named %s.%s", pname, alt.Name()) + continue } fields = append(fields, fld) } @@ -598,12 +596,12 @@ func (p *gcParser) parseSignature() *Signature { // visible in the export data. // func (p *gcParser) parseInterfaceType() Type { - var scope *Scope // lazily allocated (empty interfaces are not uncommon) + typ := new(Interface) var methods []*Func - typ := new(Interface) p.expectKeyword("interface") p.expect('{') + var mset objset for i := 0; p.tok != '}'; i++ { if i > 0 { p.expect(';') @@ -614,17 +612,15 @@ func (p *gcParser) parseInterfaceType() Type { // typ, for less verbose printing of interface method signatures. sig.recv = NewVar(token.NoPos, pkg, "", typ) m := NewFunc(token.NoPos, pkg, name, sig) - if scope == nil { - scope = NewScope(nil) - } - if alt := scope.Insert(m); alt != nil { + if alt := mset.insert(m); alt != nil { p.errorf("multiple methods named %s.%s", alt.Pkg().name, alt.Name()) + continue } methods = append(methods, m) } p.expect('}') - typ.methods = methods + typ.methods = methods return typ } diff --git a/go/types/lookup.go b/go/types/lookup.go index 325bef3c..1bbb1d21 100644 --- a/go/types/lookup.go +++ b/go/types/lookup.go @@ -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. func fieldIndex(fields []*Var, pkg *Package, name string) int { - if name == "_" { - return -1 // blank identifiers are never found - } - for i, f := range fields { - if f.sameId(pkg, name) { - return i + if name != "_" { + for i, f := range fields { + if f.sameId(pkg, name) { + return i + } } } 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). func lookupMethod(methods []*Func, pkg *Package, name string) (int, *Func) { - assert(name != "_") - for i, m := range methods { - if m.sameId(pkg, name) { - return i, m + if name != "_" { + for i, m := range methods { + if m.sameId(pkg, name) { + return i, m + } } } return -1, nil diff --git a/go/types/objset.go b/go/types/objset.go index afb92c10..2a5397e3 100644 --- a/go/types/objset.go +++ b/go/types/objset.go @@ -19,11 +19,13 @@ type objset struct { // insert attempts to insert an object obj into objset s. // If s already contains an alternative object alt with // the same name, insert leaves s unchanged and returns alt. -// Otherwise it inserts obj and returns nil. -// The object name must not be blank _. -func (s *objset) insert(obj Object) (alt Object) { +// Otherwise it inserts obj and returns nil. Objects with +// blank "_" names are ignored. +func (s *objset) insert(obj Object) Object { name := obj.Name() - assert(name != "_") + if name == "_" { + return nil + } id := Id(obj.Pkg(), name) if alt := s.objmap[id]; alt != nil { return alt diff --git a/go/types/resolver.go b/go/types/resolver.go index 360959c1..d524bef0 100644 --- a/go/types/resolver.go +++ b/go/types/resolver.go @@ -24,10 +24,7 @@ func (check *checker) reportAltDecl(obj Object) { } func (check *checker) declareObj(scope *Scope, id *ast.Ident, obj Object) { - if obj.Name() == "_" { - // blank identifiers are not declared - obj.setParent(scope) - } else if alt := scope.Insert(obj); alt != nil { + if alt := scope.Insert(obj); alt != nil { check.errorf(obj.Pos(), "%s redeclared in this block", obj.Name()) check.reportAltDecl(alt) 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) { - if obj.Name() == "_" { - // blank identifiers are not declared - } else if alt := oset.insert(obj); alt != nil { + if alt := oset.insert(obj); alt != nil { check.errorf(obj.Pos(), "%s redeclared", obj.Name()) check.reportAltDecl(alt) 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 if t, _ := named.underlying.(*Struct); t != nil { for _, fld := range t.fields { - if fld.name != "_" { - assert(mset.insert(fld) == nil) - } + assert(mset.insert(fld) == nil) } } diff --git a/go/types/scope.go b/go/types/scope.go index 062dbd16..312db6a5 100644 --- a/go/types/scope.go +++ b/go/types/scope.go @@ -106,10 +106,20 @@ func (s *Scope) LookupParent(name string) Object { // If s already contains an alternative object alt with // the same name, Insert leaves s unchanged and returns alt. // 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 { 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 { return alt }