1
0
mirror of https://github.com/golang/go synced 2024-10-01 07:28:35 -06:00

go.tools/go/types: handle blank _ methods in interface types

The spec does not exclude blank _ method names in interfaces
from the uniqueness criteria; i.e., at most one blank method
may appear in an interface type.

Arguably the spec is vague (and possibly incorrect) here.
gccgo handles it the same way. gc crashes with an internal
compiler error.

R=adonovan
CC=golang-dev
https://golang.org/cl/16380043
This commit is contained in:
Robert Griesemer 2013-10-24 09:12:28 -07:00
parent 40b09326ee
commit ad46727525
5 changed files with 56 additions and 44 deletions

View File

@ -520,13 +520,15 @@ func (p *gcParser) parseStructType() Type {
if tags != nil {
tags = append(tags, tag)
}
if alt := fset.insert(fld); alt != nil {
pname := "<no pkg name>"
if pkg := alt.Pkg(); pkg != nil {
pname = pkg.name
if fld.name != "_" {
if alt := fset.insert(fld); alt != nil {
pname := "<no pkg name>"
if pkg := alt.Pkg(); pkg != nil {
pname = pkg.name
}
p.errorf("multiple fields named %s.%s", pname, alt.Name())
continue
}
p.errorf("multiple fields named %s.%s", pname, alt.Name())
continue
}
fields = append(fields, fld)
}

View File

@ -12,27 +12,20 @@ package types
// An objset is a set of objects identified by their unique id.
// The zero value for objset is a ready-to-use empty objset.
type objset struct {
elems map[string]Object // allocated lazily
}
type objset map[string]Object // initialized lazily
// 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. Objects with
// blank "_" names are ignored.
// Otherwise it inserts obj and returns nil.
func (s *objset) insert(obj Object) Object {
name := obj.Name()
if name == "_" {
return nil
}
id := Id(obj.Pkg(), name)
if alt := s.elems[id]; alt != nil {
id := obj.Id()
if alt := (*s)[id]; alt != nil {
return alt
}
if s.elems == nil {
s.elems = make(map[string]Object)
if *s == nil {
*s = make(map[string]Object)
}
s.elems[id] = obj
(*s)[id] = obj
return nil
}

View File

@ -628,23 +628,27 @@ 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 {
assert(mset.insert(fld) == nil)
if fld.name != "_" {
assert(mset.insert(fld) == nil)
}
}
}
// check each method
for _, m := range methods {
if alt := mset.insert(m); alt != nil {
switch alt.(type) {
case *Var:
check.errorf(m.pos, "field and method with the same name %s", m.name)
case *Func:
check.errorf(m.pos, "method %s already declared for %s", m.name, named)
default:
unreachable()
if m.name != "_" {
if alt := mset.insert(m); alt != nil {
switch alt.(type) {
case *Var:
check.errorf(m.pos, "field and method with the same name %s", m.name)
case *Func:
check.errorf(m.pos, "method %s already declared for %s", m.name, named)
default:
unreachable()
}
check.reportAltDecl(alt)
continue
}
check.reportAltDecl(alt)
continue
}
check.recordObject(check.objMap[m].fdecl.Name, m)
check.objDecl(m, nil, true)

View File

@ -194,15 +194,13 @@ func (S0) m2(x *S0 /* ERROR "field or method" */ .m2) {}
func (S0) m3() (x S0 /* ERROR "field or method" */ .m3) { return }
func (S0) m4() (x *S0 /* ERROR "field or method" */ .m4) { return }
// interfaces may have blank methods
// interfaces may have at most one blank method
type BlankI interface {
_()
_(int)
_() int
_(int) int
_ /* ERROR redeclared */ ()
}
// non-interface types may have blank methods
// non-interface types may have multiple blank methods
type BlankT struct{}
func (BlankT) _() {}
@ -217,3 +215,12 @@ func _() {
i = x /* ERROR "cannot assign" */
_ = i
}
// assignability of interfaces with blank methods
func _() {
var x1, x1b interface { _() }
var x2 interface { _() }
x1 = x2
_ = x1
x1 = x1b
}

View File

@ -54,7 +54,7 @@ func (check *checker) ident(x *operand, e *ast.Ident, def *Named, cycleOk bool)
case *Const:
// The constant may be dot-imported. Mark it as used so that
// later we can determine if the corresponding dot-imported
// packages was used. Same applies for other objects, below.
// package was used. Same applies for other objects, below.
// (This code is only used for dot-imports. Without them, we
// would only have to mark Vars.)
obj.used = true
@ -407,15 +407,12 @@ func (check *checker) collectParams(scope *Scope, list *ast.FieldList, variadicO
return
}
func (check *checker) declareInSet(oset *objset, pos token.Pos, id *ast.Ident, obj Object) bool {
func (check *checker) declareInSet(oset *objset, pos token.Pos, obj Object) bool {
if alt := oset.insert(obj); alt != nil {
check.errorf(pos, "%s redeclared", obj.Name())
check.reportAltDecl(alt)
return false
}
if id != nil {
check.recordObject(id, obj)
}
return true
}
@ -453,10 +450,15 @@ func (check *checker) interfaceType(ityp *ast.InterfaceType, def *Named, cycleOk
// Don't type-check signature yet - use an
// empty signature now and update it later.
m := NewFunc(pos, check.pkg, name.Name, new(Signature))
if check.declareInSet(&mset, pos, name, m) {
// spec: "As with all method sets, in an interface type,
// each method must have a unique name."
// (The spec does not exclude blank _ identifiers for
// interface methods.)
if check.declareInSet(&mset, pos, m) {
iface.methods = append(iface.methods, m)
iface.allMethods = append(iface.allMethods, m)
signatures = append(signatures, f.Type)
check.recordObject(name, m)
}
} else {
// embedded type
@ -507,7 +509,7 @@ func (check *checker) interfaceType(ityp *ast.InterfaceType, def *Named, cycleOk
iface.types = append(iface.types, named)
// collect embedded methods
for _, m := range embed.allMethods {
if check.declareInSet(&mset, pos, nil, m) {
if check.declareInSet(&mset, pos, m) {
iface.allMethods = append(iface.allMethods, m)
}
}
@ -583,8 +585,12 @@ func (check *checker) collectFields(list *ast.FieldList, cycleOk bool) (fields [
}
fld := NewField(pos, check.pkg, name, typ, anonymous)
if check.declareInSet(&fset, pos, ident, fld) {
// spec: "Within a struct, non-blank field names must be unique."
if name == "_" || check.declareInSet(&fset, pos, fld) {
fields = append(fields, fld)
if ident != nil {
check.recordObject(ident, fld)
}
}
}