Skip to content

Commit c730a93

Browse files
committed
go/types: permit embedding of non-defined interfaces via alias type names
Embedded interfaces in interfaces must take the form of a (possibly qualified) type name. Before alias types, a type name always denoted a defined (formerly "named") type. The introduction of alias types enabled embedding of non-defined types via alias type names, as in: type T interface { E } type E interface { m() } Both cmd/compile and gccgo accept this kind of code, and the spec does not prohibit it. There may be code in the wild that makes use of this. go/types was written under the assumption that embedded interfaces were always defined types; and that assumption was even reflected in the go/types API. This change removes this restriction in the implementation (which happens to make it simpler), and in the API (by adding additional functions and deprecating the corresponding older versions). It also replaces uses of NewInterface and Embedded (old API) by NewInterface2 and EmbeddedType (new API) in dependent packages (importers). The old API remains in place for backward compatibility and is marked as deprecated. Fixes #25301. Change-Id: I272acd498754179efaf0590ca49d3eb4eee4348e Reviewed-on: https://go-review.googlesource.com/114317 Reviewed-by: Alan Donovan <[email protected]>
1 parent 29957c5 commit c730a93

File tree

10 files changed

+127
-34
lines changed

10 files changed

+127
-34
lines changed

src/go/internal/gccgoimporter/parser.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -585,13 +585,13 @@ func (p *parser) parseInterfaceType(pkg *types.Package) types.Type {
585585
p.expectKeyword("interface")
586586

587587
var methods []*types.Func
588-
var typs []*types.Named
588+
var embeddeds []types.Type
589589

590590
p.expect('{')
591591
for p.tok != '}' && p.tok != scanner.EOF {
592592
if p.tok == '?' {
593593
p.next()
594-
typs = append(typs, p.parseType(pkg).(*types.Named))
594+
embeddeds = append(embeddeds, p.parseType(pkg))
595595
} else {
596596
method := p.parseFunc(pkg)
597597
methods = append(methods, method)
@@ -600,7 +600,7 @@ func (p *parser) parseInterfaceType(pkg *types.Package) types.Type {
600600
}
601601
p.expect('}')
602602

603-
return types.NewInterface(methods, typs)
603+
return types.NewInterface2(methods, embeddeds)
604604
}
605605

606606
// PointerType = "*" ("any" | Type) .

src/go/internal/gcimporter/bimport.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -529,13 +529,13 @@ func (p *importer) typ(parent *types.Package, tname *types.Named) types.Type {
529529
p.record(nil)
530530
}
531531

532-
var embeddeds []*types.Named
532+
var embeddeds []types.Type
533533
for n := p.int(); n > 0; n-- {
534534
p.pos()
535-
embeddeds = append(embeddeds, p.typ(parent, nil).(*types.Named))
535+
embeddeds = append(embeddeds, p.typ(parent, nil))
536536
}
537537

538-
t := types.NewInterface(p.methodList(parent, tname), embeddeds)
538+
t := types.NewInterface2(p.methodList(parent, tname), embeddeds)
539539
p.interfaceList = append(p.interfaceList, t)
540540
if p.trackAllTypes {
541541
p.typList[n] = t

src/go/internal/gcimporter/gcimporter_test.go

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -286,10 +286,12 @@ func verifyInterfaceMethodRecvs(t *testing.T, named *types.Named, level int) {
286286
}
287287
}
288288

289-
// check embedded interfaces (they are named, too)
289+
// check embedded interfaces (if they are named, too)
290290
for i := 0; i < iface.NumEmbeddeds(); i++ {
291291
// embedding of interfaces cannot have cycles; recursion will terminate
292-
verifyInterfaceMethodRecvs(t, iface.Embedded(i), level+1)
292+
if etype, _ := iface.EmbeddedType(i).(*types.Named); etype != nil {
293+
verifyInterfaceMethodRecvs(t, etype, level+1)
294+
}
293295
}
294296
}
295297

@@ -507,6 +509,26 @@ func TestIssue20046(t *testing.T) {
507509
t.Fatalf("V.M not found (index = %v, indirect = %v)", index, indirect)
508510
}
509511
}
512+
func TestIssue25301(t *testing.T) {
513+
skipSpecialPlatforms(t)
514+
515+
// This package only handles gc export data.
516+
if runtime.Compiler != "gc" {
517+
t.Skipf("gc-built packages not available (compiler = %s)", runtime.Compiler)
518+
}
519+
520+
// On windows, we have to set the -D option for the compiler to avoid having a drive
521+
// letter and an illegal ':' in the import path - just skip it (see also issue #3483).
522+
if runtime.GOOS == "windows" {
523+
t.Skip("avoid dealing with relative paths/drive letters on windows")
524+
}
525+
526+
if f := compile(t, "testdata", "issue25301.go"); f != "" {
527+
defer os.Remove(f)
528+
}
529+
530+
importPkg(t, "./testdata/issue25301")
531+
}
510532

511533
func importPkg(t *testing.T, path string) *types.Package {
512534
pkg, err := Import(make(map[string]*types.Package), path, ".", nil)

src/go/internal/gcimporter/iimport.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -500,10 +500,10 @@ func (r *importReader) doType(base *types.Named) types.Type {
500500
case interfaceType:
501501
r.currPkg = r.pkg()
502502

503-
embeddeds := make([]*types.Named, r.uint64())
503+
embeddeds := make([]types.Type, r.uint64())
504504
for i := range embeddeds {
505505
_ = r.pos()
506-
embeddeds[i] = r.typ().(*types.Named)
506+
embeddeds[i] = r.typ()
507507
}
508508

509509
methods := make([]*types.Func, r.uint64())
@@ -522,7 +522,7 @@ func (r *importReader) doType(base *types.Named) types.Type {
522522
methods[i] = types.NewFunc(mpos, r.currPkg, mname, msig)
523523
}
524524

525-
typ := types.NewInterface(methods, embeddeds)
525+
typ := types.NewInterface2(methods, embeddeds)
526526
r.p.interfaceList = append(r.p.interfaceList, typ)
527527
return typ
528528
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
// Copyright 2018 The Go Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style
3+
// license that can be found in the LICENSE file.
4+
5+
package issue25301
6+
7+
type (
8+
A = interface {
9+
M()
10+
}
11+
T interface {
12+
A
13+
}
14+
S struct{}
15+
)
16+
17+
func (S) M() { println("m") }

src/go/types/testdata/issues.src

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -248,3 +248,25 @@ func issue25438() {
248248
if
249249
{ /* ERROR missing condition */ }
250250
}
251+
252+
// Test that we can embed alias type names in interfaces.
253+
type issue25301 interface {
254+
E
255+
}
256+
257+
type E = interface {
258+
m()
259+
}
260+
261+
// Test case from issue. Eventually we may disallow this due
262+
// to the cycle via the alias type name. But for now we make
263+
// sure this is accepted.
264+
type issue25301b = interface {
265+
m() interface{ issue25301b }
266+
}
267+
268+
type issue25301c interface {
269+
notE // ERROR struct\{\} is not an interface
270+
}
271+
272+
type notE = struct{}

src/go/types/type.go

Lines changed: 37 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -242,8 +242,8 @@ func (s *Signature) Variadic() bool { return s.variadic }
242242

243243
// An Interface represents an interface type.
244244
type Interface struct {
245-
methods []*Func // ordered list of explicitly declared methods
246-
embeddeds []*Named // ordered list of explicitly embedded types
245+
methods []*Func // ordered list of explicitly declared methods
246+
embeddeds []Type // ordered list of explicitly embedded types
247247

248248
allMethods []*Func // ordered list of methods declared with or embedded in this interface (TODO(gri): replace with mset)
249249
}
@@ -256,9 +256,29 @@ var emptyInterface = Interface{allMethods: markComplete}
256256
var markComplete = make([]*Func, 0)
257257

258258
// NewInterface returns a new (incomplete) interface for the given methods and embedded types.
259+
// Each embedded type must have an underlying type of interface type.
259260
// NewInterface takes ownership of the provided methods and may modify their types by setting
260261
// missing receivers. To compute the method set of the interface, Complete must be called.
262+
//
263+
// Deprecated: Use NewInterface2 instead which allows any (even non-defined) interface types
264+
// to be embedded. This is necessary for interfaces that embed alias type names referring to
265+
// non-defined (literal) interface types.
261266
func NewInterface(methods []*Func, embeddeds []*Named) *Interface {
267+
var tnames []Type
268+
if len(embeddeds) > 0 {
269+
tnames := make([]Type, len(embeddeds))
270+
for i, t := range embeddeds {
271+
tnames[i] = t
272+
}
273+
}
274+
return NewInterface2(methods, tnames)
275+
}
276+
277+
// NewInterface2 returns a new (incomplete) interface for the given methods and embedded types.
278+
// Each embedded type must have an underlying type of interface type.
279+
// NewInterface2 takes ownership of the provided methods and may modify their types by setting
280+
// missing receivers. To compute the method set of the interface, Complete must be called.
281+
func NewInterface2(methods []*Func, embeddeds []Type) *Interface {
262282
typ := new(Interface)
263283

264284
if len(methods) == 0 && len(embeddeds) == 0 {
@@ -277,8 +297,13 @@ func NewInterface(methods []*Func, embeddeds []*Named) *Interface {
277297
}
278298
sort.Sort(byUniqueMethodName(methods))
279299

280-
if embeddeds != nil {
281-
sort.Sort(byUniqueTypeName(embeddeds))
300+
if len(embeddeds) > 0 {
301+
for _, t := range embeddeds {
302+
if !IsInterface(t) {
303+
panic("embedded type is not an interface")
304+
}
305+
}
306+
sort.Stable(byUniqueTypeName(embeddeds))
282307
}
283308

284309
typ.methods = methods
@@ -296,9 +321,14 @@ func (t *Interface) ExplicitMethod(i int) *Func { return t.methods[i] }
296321
// NumEmbeddeds returns the number of embedded types in interface t.
297322
func (t *Interface) NumEmbeddeds() int { return len(t.embeddeds) }
298323

299-
// Embedded returns the i'th embedded type of interface t for 0 <= i < t.NumEmbeddeds().
300-
// The types are ordered by the corresponding TypeName's unique Id.
301-
func (t *Interface) Embedded(i int) *Named { return t.embeddeds[i] }
324+
// Embedded returns the i'th embedded defined (*Named) type of interface t for 0 <= i < t.NumEmbeddeds().
325+
// The result is nil if the i'th embedded type is not a defined type.
326+
//
327+
// Deprecated: Use EmbeddedType which is not restricted to defined (*Named) types.
328+
func (t *Interface) Embedded(i int) *Named { tname, _ := t.embeddeds[i].(*Named); return tname }
329+
330+
// EmbeddedType returns the i'th embedded type of interface t for 0 <= i < t.NumEmbeddeds().
331+
func (t *Interface) EmbeddedType(i int) Type { return t.embeddeds[i] }
302332

303333
// NumMethods returns the total number of methods of interface t.
304334
func (t *Interface) NumMethods() int { return len(t.allMethods) }

src/go/types/typestring_test.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -146,10 +146,10 @@ func TestIncompleteInterfaces(t *testing.T) {
146146
}{
147147
{new(Interface), "interface{/* incomplete */}"},
148148
{new(Interface).Complete(), "interface{}"},
149-
{NewInterface(nil, nil), "interface{/* incomplete */}"},
150-
{NewInterface(nil, nil).Complete(), "interface{}"},
151-
{NewInterface([]*Func{NewFunc(token.NoPos, nil, "m", sig)}, nil), "interface{m() /* incomplete */}"},
152-
{NewInterface([]*Func{NewFunc(token.NoPos, nil, "m", sig)}, nil).Complete(), "interface{m()}"},
149+
{NewInterface2(nil, nil), "interface{/* incomplete */}"},
150+
{NewInterface2(nil, nil).Complete(), "interface{}"},
151+
{NewInterface2([]*Func{NewFunc(token.NoPos, nil, "m", sig)}, nil), "interface{m() /* incomplete */}"},
152+
{NewInterface2([]*Func{NewFunc(token.NoPos, nil, "m", sig)}, nil).Complete(), "interface{m()}"},
153153
} {
154154
got := test.typ.String()
155155
if got != test.want {

src/go/types/typexpr.go

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -511,10 +511,6 @@ func (check *Checker) interfaceType(ityp *Interface, iface *ast.InterfaceType, d
511511
if typ == Typ[Invalid] {
512512
continue // error reported before
513513
}
514-
if !isNamed(typ) {
515-
check.invalidAST(f.Type.Pos(), "%s is not a named type", f.Type)
516-
continue
517-
}
518514
embed, _ := typ.Underlying().(*Interface)
519515
if embed == nil {
520516
check.errorf(f.Type.Pos(), "%s is not an interface", typ)
@@ -528,13 +524,12 @@ func (check *Checker) interfaceType(ityp *Interface, iface *ast.InterfaceType, d
528524
unreachable()
529525
}
530526
// collect interface
531-
// (at this point we know that typ must be a named, non-basic type)
532-
ityp.embeddeds = append(ityp.embeddeds, typ.(*Named))
527+
ityp.embeddeds = append(ityp.embeddeds, typ)
533528
}
534529
}
535-
// sort to match NewInterface
530+
// sort to match NewInterface/NewInterface2
536531
// TODO(gri) we may be able to switch to source order
537-
sort.Sort(byUniqueTypeName(ityp.embeddeds))
532+
sort.Stable(byUniqueTypeName(ityp.embeddeds))
538533
})
539534

540535
// compute method set
@@ -605,7 +600,7 @@ func (check *Checker) interfaceType(ityp *Interface, iface *ast.InterfaceType, d
605600
}
606601
check.context = savedContext
607602

608-
// sort to match NewInterface
603+
// sort to match NewInterface/NewInterface2
609604
// TODO(gri) we may be able to switch to source order
610605
sort.Sort(byUniqueMethodName(ityp.methods))
611606

@@ -617,12 +612,19 @@ func (check *Checker) interfaceType(ityp *Interface, iface *ast.InterfaceType, d
617612
}
618613

619614
// byUniqueTypeName named type lists can be sorted by their unique type names.
620-
type byUniqueTypeName []*Named
615+
type byUniqueTypeName []Type
621616

622617
func (a byUniqueTypeName) Len() int { return len(a) }
623-
func (a byUniqueTypeName) Less(i, j int) bool { return a[i].obj.Id() < a[j].obj.Id() }
618+
func (a byUniqueTypeName) Less(i, j int) bool { return sortName(a[i]) < sortName(a[j]) }
624619
func (a byUniqueTypeName) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
625620

621+
func sortName(t Type) string {
622+
if named, _ := t.(*Named); named != nil {
623+
return named.obj.Id()
624+
}
625+
return ""
626+
}
627+
626628
// byUniqueMethodName method lists can be sorted by their unique method names.
627629
type byUniqueMethodName []*Func
628630

src/go/types/universe.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ func defPredeclaredTypes() {
8080
res := NewVar(token.NoPos, nil, "", Typ[String])
8181
sig := &Signature{results: NewTuple(res)}
8282
err := NewFunc(token.NoPos, nil, "Error", sig)
83-
typ := &Named{underlying: NewInterface([]*Func{err}, nil).Complete()}
83+
typ := &Named{underlying: NewInterface2([]*Func{err}, nil).Complete()}
8484
sig.recv = NewVar(token.NoPos, nil, "", typ)
8585
def(NewTypeName(token.NoPos, nil, "error", typ))
8686
}

0 commit comments

Comments
 (0)