p.expectKeyword("interface")
var methods []*types.Func
- var typs []*types.Named
+ var embeddeds []types.Type
p.expect('{')
for p.tok != '}' && p.tok != scanner.EOF {
if p.tok == '?' {
p.next()
- typs = append(typs, p.parseType(pkg).(*types.Named))
+ embeddeds = append(embeddeds, p.parseType(pkg))
} else {
method := p.parseFunc(pkg)
methods = append(methods, method)
}
p.expect('}')
- return types.NewInterface(methods, typs)
+ return types.NewInterface2(methods, embeddeds)
}
// PointerType = "*" ("any" | Type) .
p.record(nil)
}
- var embeddeds []*types.Named
+ var embeddeds []types.Type
for n := p.int(); n > 0; n-- {
p.pos()
- embeddeds = append(embeddeds, p.typ(parent, nil).(*types.Named))
+ embeddeds = append(embeddeds, p.typ(parent, nil))
}
- t := types.NewInterface(p.methodList(parent, tname), embeddeds)
+ t := types.NewInterface2(p.methodList(parent, tname), embeddeds)
p.interfaceList = append(p.interfaceList, t)
if p.trackAllTypes {
p.typList[n] = t
}
}
- // check embedded interfaces (they are named, too)
+ // check embedded interfaces (if they are named, too)
for i := 0; i < iface.NumEmbeddeds(); i++ {
// embedding of interfaces cannot have cycles; recursion will terminate
- verifyInterfaceMethodRecvs(t, iface.Embedded(i), level+1)
+ if etype, _ := iface.EmbeddedType(i).(*types.Named); etype != nil {
+ verifyInterfaceMethodRecvs(t, etype, level+1)
+ }
}
}
t.Fatalf("V.M not found (index = %v, indirect = %v)", index, indirect)
}
}
+func TestIssue25301(t *testing.T) {
+ skipSpecialPlatforms(t)
+
+ // This package only handles gc export data.
+ if runtime.Compiler != "gc" {
+ t.Skipf("gc-built packages not available (compiler = %s)", runtime.Compiler)
+ }
+
+ // On windows, we have to set the -D option for the compiler to avoid having a drive
+ // letter and an illegal ':' in the import path - just skip it (see also issue #3483).
+ if runtime.GOOS == "windows" {
+ t.Skip("avoid dealing with relative paths/drive letters on windows")
+ }
+
+ if f := compile(t, "testdata", "issue25301.go"); f != "" {
+ defer os.Remove(f)
+ }
+
+ importPkg(t, "./testdata/issue25301")
+}
func importPkg(t *testing.T, path string) *types.Package {
pkg, err := Import(make(map[string]*types.Package), path, ".", nil)
case interfaceType:
r.currPkg = r.pkg()
- embeddeds := make([]*types.Named, r.uint64())
+ embeddeds := make([]types.Type, r.uint64())
for i := range embeddeds {
_ = r.pos()
- embeddeds[i] = r.typ().(*types.Named)
+ embeddeds[i] = r.typ()
}
methods := make([]*types.Func, r.uint64())
methods[i] = types.NewFunc(mpos, r.currPkg, mname, msig)
}
- typ := types.NewInterface(methods, embeddeds)
+ typ := types.NewInterface2(methods, embeddeds)
r.p.interfaceList = append(r.p.interfaceList, typ)
return typ
}
--- /dev/null
+// Copyright 2018 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 issue25301
+
+type (
+ A = interface {
+ M()
+ }
+ T interface {
+ A
+ }
+ S struct{}
+)
+
+func (S) M() { println("m") }
if
{ /* ERROR missing condition */ }
}
+
+// Test that we can embed alias type names in interfaces.
+type issue25301 interface {
+ E
+}
+
+type E = interface {
+ m()
+}
+
+// Test case from issue. Eventually we may disallow this due
+// to the cycle via the alias type name. But for now we make
+// sure this is accepted.
+type issue25301b = interface {
+ m() interface{ issue25301b }
+}
+
+type issue25301c interface {
+ notE // ERROR struct\{\} is not an interface
+}
+
+type notE = struct{}
// An Interface represents an interface type.
type Interface struct {
- methods []*Func // ordered list of explicitly declared methods
- embeddeds []*Named // ordered list of explicitly embedded types
+ methods []*Func // ordered list of explicitly declared methods
+ embeddeds []Type // ordered list of explicitly embedded types
allMethods []*Func // ordered list of methods declared with or embedded in this interface (TODO(gri): replace with mset)
}
var markComplete = make([]*Func, 0)
// NewInterface returns a new (incomplete) interface for the given methods and embedded types.
+// Each embedded type must have an underlying type of interface type.
// NewInterface takes ownership of the provided methods and may modify their types by setting
// missing receivers. To compute the method set of the interface, Complete must be called.
+//
+// Deprecated: Use NewInterface2 instead which allows any (even non-defined) interface types
+// to be embedded. This is necessary for interfaces that embed alias type names referring to
+// non-defined (literal) interface types.
func NewInterface(methods []*Func, embeddeds []*Named) *Interface {
+ var tnames []Type
+ if len(embeddeds) > 0 {
+ tnames := make([]Type, len(embeddeds))
+ for i, t := range embeddeds {
+ tnames[i] = t
+ }
+ }
+ return NewInterface2(methods, tnames)
+}
+
+// NewInterface2 returns a new (incomplete) interface for the given methods and embedded types.
+// Each embedded type must have an underlying type of interface type.
+// NewInterface2 takes ownership of the provided methods and may modify their types by setting
+// missing receivers. To compute the method set of the interface, Complete must be called.
+func NewInterface2(methods []*Func, embeddeds []Type) *Interface {
typ := new(Interface)
if len(methods) == 0 && len(embeddeds) == 0 {
}
sort.Sort(byUniqueMethodName(methods))
- if embeddeds != nil {
- sort.Sort(byUniqueTypeName(embeddeds))
+ if len(embeddeds) > 0 {
+ for _, t := range embeddeds {
+ if !IsInterface(t) {
+ panic("embedded type is not an interface")
+ }
+ }
+ sort.Stable(byUniqueTypeName(embeddeds))
}
typ.methods = methods
// NumEmbeddeds returns the number of embedded types in interface t.
func (t *Interface) NumEmbeddeds() int { return len(t.embeddeds) }
-// Embedded returns the i'th embedded type of interface t for 0 <= i < t.NumEmbeddeds().
-// The types are ordered by the corresponding TypeName's unique Id.
-func (t *Interface) Embedded(i int) *Named { return t.embeddeds[i] }
+// Embedded returns the i'th embedded defined (*Named) type of interface t for 0 <= i < t.NumEmbeddeds().
+// The result is nil if the i'th embedded type is not a defined type.
+//
+// Deprecated: Use EmbeddedType which is not restricted to defined (*Named) types.
+func (t *Interface) Embedded(i int) *Named { tname, _ := t.embeddeds[i].(*Named); return tname }
+
+// EmbeddedType returns the i'th embedded type of interface t for 0 <= i < t.NumEmbeddeds().
+func (t *Interface) EmbeddedType(i int) Type { return t.embeddeds[i] }
// NumMethods returns the total number of methods of interface t.
func (t *Interface) NumMethods() int { return len(t.allMethods) }
}{
{new(Interface), "interface{/* incomplete */}"},
{new(Interface).Complete(), "interface{}"},
- {NewInterface(nil, nil), "interface{/* incomplete */}"},
- {NewInterface(nil, nil).Complete(), "interface{}"},
- {NewInterface([]*Func{NewFunc(token.NoPos, nil, "m", sig)}, nil), "interface{m() /* incomplete */}"},
- {NewInterface([]*Func{NewFunc(token.NoPos, nil, "m", sig)}, nil).Complete(), "interface{m()}"},
+ {NewInterface2(nil, nil), "interface{/* incomplete */}"},
+ {NewInterface2(nil, nil).Complete(), "interface{}"},
+ {NewInterface2([]*Func{NewFunc(token.NoPos, nil, "m", sig)}, nil), "interface{m() /* incomplete */}"},
+ {NewInterface2([]*Func{NewFunc(token.NoPos, nil, "m", sig)}, nil).Complete(), "interface{m()}"},
} {
got := test.typ.String()
if got != test.want {
if typ == Typ[Invalid] {
continue // error reported before
}
- if !isNamed(typ) {
- check.invalidAST(f.Type.Pos(), "%s is not a named type", f.Type)
- continue
- }
embed, _ := typ.Underlying().(*Interface)
if embed == nil {
check.errorf(f.Type.Pos(), "%s is not an interface", typ)
unreachable()
}
// collect interface
- // (at this point we know that typ must be a named, non-basic type)
- ityp.embeddeds = append(ityp.embeddeds, typ.(*Named))
+ ityp.embeddeds = append(ityp.embeddeds, typ)
}
}
- // sort to match NewInterface
+ // sort to match NewInterface/NewInterface2
// TODO(gri) we may be able to switch to source order
- sort.Sort(byUniqueTypeName(ityp.embeddeds))
+ sort.Stable(byUniqueTypeName(ityp.embeddeds))
})
// compute method set
}
check.context = savedContext
- // sort to match NewInterface
+ // sort to match NewInterface/NewInterface2
// TODO(gri) we may be able to switch to source order
sort.Sort(byUniqueMethodName(ityp.methods))
}
// byUniqueTypeName named type lists can be sorted by their unique type names.
-type byUniqueTypeName []*Named
+type byUniqueTypeName []Type
func (a byUniqueTypeName) Len() int { return len(a) }
-func (a byUniqueTypeName) Less(i, j int) bool { return a[i].obj.Id() < a[j].obj.Id() }
+func (a byUniqueTypeName) Less(i, j int) bool { return sortName(a[i]) < sortName(a[j]) }
func (a byUniqueTypeName) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
+func sortName(t Type) string {
+ if named, _ := t.(*Named); named != nil {
+ return named.obj.Id()
+ }
+ return ""
+}
+
// byUniqueMethodName method lists can be sorted by their unique method names.
type byUniqueMethodName []*Func
res := NewVar(token.NoPos, nil, "", Typ[String])
sig := &Signature{results: NewTuple(res)}
err := NewFunc(token.NoPos, nil, "Error", sig)
- typ := &Named{underlying: NewInterface([]*Func{err}, nil).Complete()}
+ typ := &Named{underlying: NewInterface2([]*Func{err}, nil).Complete()}
sig.recv = NewVar(token.NoPos, nil, "", typ)
def(NewTypeName(token.NoPos, nil, "error", typ))
}