mirror of
https://github.com/golang/go
synced 2024-11-19 00:44:40 -07: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:
parent
40b09326ee
commit
ad46727525
@ -520,13 +520,15 @@ func (p *gcParser) parseStructType() Type {
|
|||||||
if tags != nil {
|
if tags != nil {
|
||||||
tags = append(tags, tag)
|
tags = append(tags, tag)
|
||||||
}
|
}
|
||||||
if alt := fset.insert(fld); alt != nil {
|
if fld.name != "_" {
|
||||||
pname := "<no pkg name>"
|
if alt := fset.insert(fld); alt != nil {
|
||||||
if pkg := alt.Pkg(); pkg != nil {
|
pname := "<no pkg name>"
|
||||||
pname = 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)
|
fields = append(fields, fld)
|
||||||
}
|
}
|
||||||
|
@ -12,27 +12,20 @@ package types
|
|||||||
|
|
||||||
// An objset is a set of objects identified by their unique id.
|
// An objset is a set of objects identified by their unique id.
|
||||||
// The zero value for objset is a ready-to-use empty objset.
|
// The zero value for objset is a ready-to-use empty objset.
|
||||||
type objset struct {
|
type objset map[string]Object // initialized lazily
|
||||||
elems map[string]Object // allocated lazily
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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. Objects with
|
// Otherwise it inserts obj and returns nil.
|
||||||
// blank "_" names are ignored.
|
|
||||||
func (s *objset) insert(obj Object) Object {
|
func (s *objset) insert(obj Object) Object {
|
||||||
name := obj.Name()
|
id := obj.Id()
|
||||||
if name == "_" {
|
if alt := (*s)[id]; alt != nil {
|
||||||
return nil
|
|
||||||
}
|
|
||||||
id := Id(obj.Pkg(), name)
|
|
||||||
if alt := s.elems[id]; alt != nil {
|
|
||||||
return alt
|
return alt
|
||||||
}
|
}
|
||||||
if s.elems == nil {
|
if *s == nil {
|
||||||
s.elems = make(map[string]Object)
|
*s = make(map[string]Object)
|
||||||
}
|
}
|
||||||
s.elems[id] = obj
|
(*s)[id] = obj
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -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
|
// 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 {
|
||||||
assert(mset.insert(fld) == nil)
|
if fld.name != "_" {
|
||||||
|
assert(mset.insert(fld) == nil)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// check each method
|
// check each method
|
||||||
for _, m := range methods {
|
for _, m := range methods {
|
||||||
if alt := mset.insert(m); alt != nil {
|
if m.name != "_" {
|
||||||
switch alt.(type) {
|
if alt := mset.insert(m); alt != nil {
|
||||||
case *Var:
|
switch alt.(type) {
|
||||||
check.errorf(m.pos, "field and method with the same name %s", m.name)
|
case *Var:
|
||||||
case *Func:
|
check.errorf(m.pos, "field and method with the same name %s", m.name)
|
||||||
check.errorf(m.pos, "method %s already declared for %s", m.name, named)
|
case *Func:
|
||||||
default:
|
check.errorf(m.pos, "method %s already declared for %s", m.name, named)
|
||||||
unreachable()
|
default:
|
||||||
|
unreachable()
|
||||||
|
}
|
||||||
|
check.reportAltDecl(alt)
|
||||||
|
continue
|
||||||
}
|
}
|
||||||
check.reportAltDecl(alt)
|
|
||||||
continue
|
|
||||||
}
|
}
|
||||||
check.recordObject(check.objMap[m].fdecl.Name, m)
|
check.recordObject(check.objMap[m].fdecl.Name, m)
|
||||||
check.objDecl(m, nil, true)
|
check.objDecl(m, nil, true)
|
||||||
|
17
go/types/testdata/decls0.src
vendored
17
go/types/testdata/decls0.src
vendored
@ -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) m3() (x S0 /* ERROR "field or method" */ .m3) { return }
|
||||||
func (S0) m4() (x *S0 /* ERROR "field or method" */ .m4) { 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 {
|
type BlankI interface {
|
||||||
_()
|
_()
|
||||||
_(int)
|
_ /* ERROR redeclared */ ()
|
||||||
_() int
|
|
||||||
_(int) int
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// non-interface types may have blank methods
|
// non-interface types may have multiple blank methods
|
||||||
type BlankT struct{}
|
type BlankT struct{}
|
||||||
|
|
||||||
func (BlankT) _() {}
|
func (BlankT) _() {}
|
||||||
@ -217,3 +215,12 @@ func _() {
|
|||||||
i = x /* ERROR "cannot assign" */
|
i = x /* ERROR "cannot assign" */
|
||||||
_ = i
|
_ = i
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// assignability of interfaces with blank methods
|
||||||
|
func _() {
|
||||||
|
var x1, x1b interface { _() }
|
||||||
|
var x2 interface { _() }
|
||||||
|
x1 = x2
|
||||||
|
_ = x1
|
||||||
|
x1 = x1b
|
||||||
|
}
|
@ -54,7 +54,7 @@ func (check *checker) ident(x *operand, e *ast.Ident, def *Named, cycleOk bool)
|
|||||||
case *Const:
|
case *Const:
|
||||||
// The constant may be dot-imported. Mark it as used so that
|
// The constant may be dot-imported. Mark it as used so that
|
||||||
// later we can determine if the corresponding dot-imported
|
// 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
|
// (This code is only used for dot-imports. Without them, we
|
||||||
// would only have to mark Vars.)
|
// would only have to mark Vars.)
|
||||||
obj.used = true
|
obj.used = true
|
||||||
@ -407,15 +407,12 @@ func (check *checker) collectParams(scope *Scope, list *ast.FieldList, variadicO
|
|||||||
return
|
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 {
|
if alt := oset.insert(obj); alt != nil {
|
||||||
check.errorf(pos, "%s redeclared", obj.Name())
|
check.errorf(pos, "%s redeclared", obj.Name())
|
||||||
check.reportAltDecl(alt)
|
check.reportAltDecl(alt)
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
if id != nil {
|
|
||||||
check.recordObject(id, obj)
|
|
||||||
}
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -453,10 +450,15 @@ func (check *checker) interfaceType(ityp *ast.InterfaceType, def *Named, cycleOk
|
|||||||
// Don't type-check signature yet - use an
|
// Don't type-check signature yet - use an
|
||||||
// empty signature now and update it later.
|
// empty signature now and update it later.
|
||||||
m := NewFunc(pos, check.pkg, name.Name, new(Signature))
|
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.methods = append(iface.methods, m)
|
||||||
iface.allMethods = append(iface.allMethods, m)
|
iface.allMethods = append(iface.allMethods, m)
|
||||||
signatures = append(signatures, f.Type)
|
signatures = append(signatures, f.Type)
|
||||||
|
check.recordObject(name, m)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// embedded type
|
// embedded type
|
||||||
@ -507,7 +509,7 @@ func (check *checker) interfaceType(ityp *ast.InterfaceType, def *Named, cycleOk
|
|||||||
iface.types = append(iface.types, named)
|
iface.types = append(iface.types, named)
|
||||||
// collect embedded methods
|
// collect embedded methods
|
||||||
for _, m := range embed.allMethods {
|
for _, m := range embed.allMethods {
|
||||||
if check.declareInSet(&mset, pos, nil, m) {
|
if check.declareInSet(&mset, pos, m) {
|
||||||
iface.allMethods = append(iface.allMethods, 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)
|
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)
|
fields = append(fields, fld)
|
||||||
|
if ident != nil {
|
||||||
|
check.recordObject(ident, fld)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user