Skip to content

Commit ea012d1

Browse files
committed
go/importer: use named receiver types for methods of named interfaces
R=go1.11 Once approved, this change must be ported to golang.org/x/tools/go/gcimporter15. Fixes #13829. Change-Id: I26a0094d2bfd38b97f2b64bae84b9f428fc9cdf1 Reviewed-on: https://go-review.googlesource.com/85318 Reviewed-by: Alan Donovan <[email protected]>
1 parent 01b8f5d commit ea012d1

File tree

4 files changed

+134
-32
lines changed

4 files changed

+134
-32
lines changed

src/go/internal/gcimporter/bimport.go

Lines changed: 39 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -257,24 +257,24 @@ func (p *importer) obj(tag int) {
257257
case constTag:
258258
pos := p.pos()
259259
pkg, name := p.qualifiedName()
260-
typ := p.typ(nil)
260+
typ := p.typ(nil, nil)
261261
val := p.value()
262262
p.declare(types.NewConst(pos, pkg, name, typ, val))
263263

264264
case aliasTag:
265265
// TODO(gri) verify type alias hookup is correct
266266
pos := p.pos()
267267
pkg, name := p.qualifiedName()
268-
typ := p.typ(nil)
268+
typ := p.typ(nil, nil)
269269
p.declare(types.NewTypeName(pos, pkg, name, typ))
270270

271271
case typeTag:
272-
p.typ(nil)
272+
p.typ(nil, nil)
273273

274274
case varTag:
275275
pos := p.pos()
276276
pkg, name := p.qualifiedName()
277-
typ := p.typ(nil)
277+
typ := p.typ(nil, nil)
278278
p.declare(types.NewVar(pos, pkg, name, typ))
279279

280280
case funcTag:
@@ -379,7 +379,11 @@ func (t *dddSlice) String() string { return "..." + t.elem.String() }
379379
// the package currently imported. The parent package is needed for
380380
// exported struct fields and interface methods which don't contain
381381
// explicit package information in the export data.
382-
func (p *importer) typ(parent *types.Package) types.Type {
382+
//
383+
// A non-nil tname is used as the "owner" of the result type; i.e.,
384+
// the result type is the underlying type of tname. tname is used
385+
// to give interface methods a named receiver type where possible.
386+
func (p *importer) typ(parent *types.Package, tname *types.Named) types.Type {
383387
// if the type was seen before, i is its index (>= 0)
384388
i := p.tagOrIndex()
385389
if i >= 0 {
@@ -409,15 +413,15 @@ func (p *importer) typ(parent *types.Package) types.Type {
409413
t0 := types.NewNamed(obj.(*types.TypeName), nil, nil)
410414

411415
// but record the existing type, if any
412-
t := obj.Type().(*types.Named)
413-
p.record(t)
416+
tname := obj.Type().(*types.Named) // tname is either t0 or the existing type
417+
p.record(tname)
414418

415419
// read underlying type
416-
t0.SetUnderlying(p.typ(parent))
420+
t0.SetUnderlying(p.typ(parent, t0))
417421

418422
// interfaces don't have associated methods
419423
if types.IsInterface(t0) {
420-
return t
424+
return tname
421425
}
422426

423427
// read associated methods
@@ -438,7 +442,7 @@ func (p *importer) typ(parent *types.Package) types.Type {
438442
t0.AddMethod(types.NewFunc(pos, parent, name, sig))
439443
}
440444

441-
return t
445+
return tname
442446

443447
case arrayTag:
444448
t := new(types.Array)
@@ -447,7 +451,7 @@ func (p *importer) typ(parent *types.Package) types.Type {
447451
}
448452

449453
n := p.int64()
450-
*t = *types.NewArray(p.typ(parent), n)
454+
*t = *types.NewArray(p.typ(parent, nil), n)
451455
return t
452456

453457
case sliceTag:
@@ -456,7 +460,7 @@ func (p *importer) typ(parent *types.Package) types.Type {
456460
p.record(t)
457461
}
458462

459-
*t = *types.NewSlice(p.typ(parent))
463+
*t = *types.NewSlice(p.typ(parent, nil))
460464
return t
461465

462466
case dddTag:
@@ -465,7 +469,7 @@ func (p *importer) typ(parent *types.Package) types.Type {
465469
p.record(t)
466470
}
467471

468-
t.elem = p.typ(parent)
472+
t.elem = p.typ(parent, nil)
469473
return t
470474

471475
case structTag:
@@ -483,7 +487,7 @@ func (p *importer) typ(parent *types.Package) types.Type {
483487
p.record(t)
484488
}
485489

486-
*t = *types.NewPointer(p.typ(parent))
490+
*t = *types.NewPointer(p.typ(parent, nil))
487491
return t
488492

489493
case signatureTag:
@@ -502,6 +506,8 @@ func (p *importer) typ(parent *types.Package) types.Type {
502506
// cannot expect the interface type to appear in a cycle, as any
503507
// such cycle must contain a named type which would have been
504508
// first defined earlier.
509+
// TODO(gri) Is this still true now that we have type aliases?
510+
// See issue #23225.
505511
n := len(p.typList)
506512
if p.trackAllTypes {
507513
p.record(nil)
@@ -510,10 +516,10 @@ func (p *importer) typ(parent *types.Package) types.Type {
510516
var embeddeds []*types.Named
511517
for n := p.int(); n > 0; n-- {
512518
p.pos()
513-
embeddeds = append(embeddeds, p.typ(parent).(*types.Named))
519+
embeddeds = append(embeddeds, p.typ(parent, nil).(*types.Named))
514520
}
515521

516-
t := types.NewInterface(p.methodList(parent), embeddeds)
522+
t := types.NewInterface(p.methodList(parent, tname), embeddeds)
517523
p.interfaceList = append(p.interfaceList, t)
518524
if p.trackAllTypes {
519525
p.typList[n] = t
@@ -526,8 +532,8 @@ func (p *importer) typ(parent *types.Package) types.Type {
526532
p.record(t)
527533
}
528534

529-
key := p.typ(parent)
530-
val := p.typ(parent)
535+
key := p.typ(parent, nil)
536+
val := p.typ(parent, nil)
531537
*t = *types.NewMap(key, val)
532538
return t
533539

@@ -549,7 +555,7 @@ func (p *importer) typ(parent *types.Package) types.Type {
549555
default:
550556
errorf("unexpected channel dir %d", d)
551557
}
552-
val := p.typ(parent)
558+
val := p.typ(parent, nil)
553559
*t = *types.NewChan(dir, val)
554560
return t
555561

@@ -573,7 +579,7 @@ func (p *importer) fieldList(parent *types.Package) (fields []*types.Var, tags [
573579
func (p *importer) field(parent *types.Package) (*types.Var, string) {
574580
pos := p.pos()
575581
pkg, name, alias := p.fieldName(parent)
576-
typ := p.typ(parent)
582+
typ := p.typ(parent, nil)
577583
tag := p.string()
578584

579585
anonymous := false
@@ -597,22 +603,30 @@ func (p *importer) field(parent *types.Package) (*types.Var, string) {
597603
return types.NewField(pos, pkg, name, typ, anonymous), tag
598604
}
599605

600-
func (p *importer) methodList(parent *types.Package) (methods []*types.Func) {
606+
func (p *importer) methodList(parent *types.Package, baseType *types.Named) (methods []*types.Func) {
601607
if n := p.int(); n > 0 {
602608
methods = make([]*types.Func, n)
603609
for i := range methods {
604-
methods[i] = p.method(parent)
610+
methods[i] = p.method(parent, baseType)
605611
}
606612
}
607613
return
608614
}
609615

610-
func (p *importer) method(parent *types.Package) *types.Func {
616+
func (p *importer) method(parent *types.Package, baseType *types.Named) *types.Func {
611617
pos := p.pos()
612618
pkg, name, _ := p.fieldName(parent)
619+
// If we don't have a baseType, use a nil receiver.
620+
// A receiver using the actual interface type (which
621+
// we don't know yet) will be filled in when we call
622+
// types.Interface.Complete.
623+
var recv *types.Var
624+
if baseType != nil {
625+
recv = types.NewVar(token.NoPos, parent, "", baseType)
626+
}
613627
params, isddd := p.paramList()
614628
result, _ := p.paramList()
615-
sig := types.NewSignature(nil, params, result, isddd)
629+
sig := types.NewSignature(recv, params, result, isddd)
616630
return types.NewFunc(pos, pkg, name, sig)
617631
}
618632

@@ -668,7 +682,7 @@ func (p *importer) paramList() (*types.Tuple, bool) {
668682
}
669683

670684
func (p *importer) param(named bool) (*types.Var, bool) {
671-
t := p.typ(nil)
685+
t := p.typ(nil, nil)
672686
td, isddd := t.(*dddSlice)
673687
if isddd {
674688
t = types.NewSlice(td.elem)

src/go/internal/gcimporter/gcimporter_test.go

Lines changed: 51 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -200,11 +200,22 @@ var importedObjectTests = []struct {
200200
name string
201201
want string
202202
}{
203+
// non-interfaces
204+
{"crypto.Hash", "type Hash uint"},
205+
{"go/ast.ObjKind", "type ObjKind int"},
206+
{"go/types.Qualifier", "type Qualifier func(*Package) string"},
207+
{"go/types.Comparable", "func Comparable(T Type) bool"},
203208
{"math.Pi", "const Pi untyped float"},
209+
{"math.Sin", "func Sin(x float64) float64"},
210+
211+
// interfaces
212+
{"context.Context", "type Context interface{Deadline() (deadline time.Time, ok bool); Done() <-chan struct{}; Err() error; Value(key interface{}) interface{}}"},
213+
{"crypto.Decrypter", "type Decrypter interface{Decrypt(rand io.Reader, msg []byte, opts DecrypterOpts) (plaintext []byte, err error); Public() PublicKey}"},
214+
{"encoding.BinaryMarshaler", "type BinaryMarshaler interface{MarshalBinary() (data []byte, err error)}"},
204215
{"io.Reader", "type Reader interface{Read(p []byte) (n int, err error)}"},
205216
{"io.ReadWriter", "type ReadWriter interface{Reader; Writer}"},
206-
{"math.Sin", "func Sin(x float64) float64"},
207-
// TODO(gri) add more tests
217+
{"go/ast.Node", "type Node interface{End() go/token.Pos; Pos() go/token.Pos}"},
218+
{"go/types.Type", "type Type interface{String() string; Underlying() Type}"},
208219
}
209220

210221
func TestImportedTypes(t *testing.T) {
@@ -239,6 +250,44 @@ func TestImportedTypes(t *testing.T) {
239250
if got != test.want {
240251
t.Errorf("%s: got %q; want %q", test.name, got, test.want)
241252
}
253+
254+
if named, _ := obj.Type().(*types.Named); named != nil {
255+
verifyInterfaceMethodRecvs(t, named, 0)
256+
}
257+
}
258+
}
259+
260+
// verifyInterfaceMethodRecvs verifies that method receiver types
261+
// are named if the methods belong to a named interface type.
262+
func verifyInterfaceMethodRecvs(t *testing.T, named *types.Named, level int) {
263+
// avoid endless recursion in case of an embedding bug that lead to a cycle
264+
if level > 10 {
265+
t.Errorf("%s: embeds itself", named)
266+
return
267+
}
268+
269+
iface, _ := named.Underlying().(*types.Interface)
270+
if iface == nil {
271+
return // not an interface
272+
}
273+
274+
// check explicitly declared methods
275+
for i := 0; i < iface.NumExplicitMethods(); i++ {
276+
m := iface.ExplicitMethod(i)
277+
recv := m.Type().(*types.Signature).Recv()
278+
if recv == nil {
279+
t.Errorf("%s: missing receiver type", m)
280+
continue
281+
}
282+
if recv.Type() != named {
283+
t.Errorf("%s: got recv type %s; want %s", m, recv.Type(), named)
284+
}
285+
}
286+
287+
// check embedded interfaces (they are named, too)
288+
for i := 0; i < iface.NumEmbeddeds(); i++ {
289+
// embedding of interfaces cannot have cycles; recursion will terminate
290+
verifyInterfaceMethodRecvs(t, iface.Embedded(i), level+1)
242291
}
243292
}
244293

src/go/internal/srcimporter/srcimporter_test.go

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,44 @@ func TestImportedTypes(t *testing.T) {
130130
if got != test.want {
131131
t.Errorf("%s: got %q; want %q", test.name, got, test.want)
132132
}
133+
134+
if named, _ := obj.Type().(*types.Named); named != nil {
135+
verifyInterfaceMethodRecvs(t, named, 0)
136+
}
137+
}
138+
}
139+
140+
// verifyInterfaceMethodRecvs verifies that method receiver types
141+
// are named if the methods belong to a named interface type.
142+
func verifyInterfaceMethodRecvs(t *testing.T, named *types.Named, level int) {
143+
// avoid endless recursion in case of an embedding bug that lead to a cycle
144+
if level > 10 {
145+
t.Errorf("%s: embeds itself", named)
146+
return
147+
}
148+
149+
iface, _ := named.Underlying().(*types.Interface)
150+
if iface == nil {
151+
return // not an interface
152+
}
153+
154+
// check explicitly declared methods
155+
for i := 0; i < iface.NumExplicitMethods(); i++ {
156+
m := iface.ExplicitMethod(i)
157+
recv := m.Type().(*types.Signature).Recv()
158+
if recv == nil {
159+
t.Errorf("%s: missing receiver type", m)
160+
continue
161+
}
162+
if recv.Type() != named {
163+
t.Errorf("%s: got recv type %s; want %s", m, recv.Type(), named)
164+
}
165+
}
166+
167+
// check embedded interfaces (they are named, too)
168+
for i := 0; i < iface.NumEmbeddeds(); i++ {
169+
// embedding of interfaces cannot have cycles; recursion will terminate
170+
verifyInterfaceMethodRecvs(t, iface.Embedded(i), level+1)
133171
}
134172
}
135173

src/go/types/type.go

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -254,7 +254,8 @@ var emptyInterface = Interface{allMethods: markComplete}
254254
var markComplete = make([]*Func, 0)
255255

256256
// NewInterface returns a new (incomplete) interface for the given methods and embedded types.
257-
// To compute the method set of the interface, Complete must be called.
257+
// NewInterface takes ownership of the provided methods and may modify their types by setting
258+
// missing receivers. To compute the method set of the interface, Complete must be called.
258259
func NewInterface(methods []*Func, embeddeds []*Named) *Interface {
259260
typ := new(Interface)
260261

@@ -267,10 +268,10 @@ func NewInterface(methods []*Func, embeddeds []*Named) *Interface {
267268
if mset.insert(m) != nil {
268269
panic("multiple methods with the same name")
269270
}
270-
// set receiver
271-
// TODO(gri) Ideally, we should use a named type here instead of
272-
// typ, for less verbose printing of interface method signatures.
273-
m.typ.(*Signature).recv = NewVar(m.pos, m.pkg, "", typ)
271+
// set receiver if we don't have one
272+
if sig := m.typ.(*Signature); sig.recv == nil {
273+
sig.recv = NewVar(m.pos, m.pkg, "", typ)
274+
}
274275
}
275276
sort.Sort(byUniqueMethodName(methods))
276277

0 commit comments

Comments
 (0)