Skip to content

Commit 80963bc

Browse files
committed
go/internal/gcimporter: support unique naming for blank type parameters
As described in golang/go#50481, in the existing export data schema blank type parameters do not have unique names, and therefore types with multiple blank (receiver) type parameters cannot be correctly imported. This CL implements the fix proposed in that issue, using the schema <prefix>.$<index> as the exported name of a blank type parameter, where <prefix> is the qualifying prefix and <index> is the index of the type parameter in its type parameter list. The importer is backwards compatible with the old schema: it will continue to import <prefix>._ as long as there are not multiple blank type parameters. I considered not making the exporter change simultaneously with the importer change, so that we interleave the corresponding changes in the standard library. However, that made it harder to test the importer, and updating both seems unlikely to cause problems. For golang/go#50481 Change-Id: Id24428c6ea2b256312156894f9f76fa8e9ee38d4 Reviewed-on: https://go-review.googlesource.com/c/tools/+/379855 Trust: Robert Findley <rfindley@google.com> Run-TryBot: Robert Findley <rfindley@google.com> Trust: Dan Scales <danscales@google.com> Reviewed-by: Dan Scales <danscales@google.com> Reviewed-by: Robert Griesemer <gri@golang.org> gopls-CI: kokoro <noreply+kokoro@google.com> TryBot-Result: Gopher Robot <gobot@golang.org>
1 parent 25e1ac4 commit 80963bc

File tree

3 files changed

+74
-30
lines changed

3 files changed

+74
-30
lines changed

go/internal/gcimporter/iexport.go

Lines changed: 60 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import (
2020
"math/big"
2121
"reflect"
2222
"sort"
23+
"strconv"
2324
"strings"
2425

2526
"golang.org/x/tools/internal/typeparams"
@@ -158,7 +159,7 @@ func (w *exportWriter) writeIndex(index map[types.Object]uint64) {
158159
}
159160

160161
for obj := range index {
161-
name := w.p.indexName(obj)
162+
name := w.p.exportName(obj)
162163
pkgObjs[obj.Pkg()] = append(pkgObjs[obj.Pkg()], pkgObj{obj, name})
163164
}
164165

@@ -190,10 +191,9 @@ func (w *exportWriter) writeIndex(index map[types.Object]uint64) {
190191
}
191192
}
192193

193-
// indexName returns the 'indexed' name of an object. It differs from
194-
// obj.Name() only for type parameter names, where the name is qualified by
195-
// owner.
196-
func (p *iexporter) indexName(obj types.Object) (res string) {
194+
// exportName returns the 'exported' name of an object. It differs from
195+
// obj.Name() only for type parameters (see tparamExportName for details).
196+
func (p *iexporter) exportName(obj types.Object) (res string) {
197197
if name := p.tparamNames[obj]; name != "" {
198198
return name
199199
}
@@ -219,7 +219,7 @@ type iexporter struct {
219219

220220
data0 intWriter
221221
declIndex map[types.Object]uint64
222-
tparamNames map[types.Object]string // typeparam->qualified name
222+
tparamNames map[types.Object]string // typeparam->exported name
223223
typIndex map[types.Type]uint64
224224

225225
indent int // for tracing support
@@ -310,14 +310,15 @@ func (p *iexporter) doDecl(obj types.Object) {
310310
w.tag('G')
311311
}
312312
w.pos(obj.Pos())
313-
// The tparam list of the function type is the
314-
// declaration of the type params. So, write out the type
315-
// params right now. Then those type params will be
316-
// referenced via their type offset (via typOff) in all
317-
// other places in the signature and function that they
318-
// are used.
313+
// The tparam list of the function type is the declaration of the type
314+
// params. So, write out the type params right now. Then those type params
315+
// will be referenced via their type offset (via typOff) in all other
316+
// places in the signature and function where they are used.
317+
//
318+
// While importing the type parameters, tparamList computes and records
319+
// their export name, so that it can be later used when writing the index.
319320
if tparams := typeparams.ForSignature(sig); tparams.Len() > 0 {
320-
w.tparamList(obj, tparams, obj.Pkg())
321+
w.tparamList(obj.Name(), tparams, obj.Pkg())
321322
}
322323
w.signature(sig)
323324

@@ -365,7 +366,9 @@ func (p *iexporter) doDecl(obj types.Object) {
365366
w.pos(obj.Pos())
366367

367368
if typeparams.ForNamed(named).Len() > 0 {
368-
w.tparamList(obj, typeparams.ForNamed(named), obj.Pkg())
369+
// While importing the type parameters, tparamList computes and records
370+
// their export name, so that it can be later used when writing the index.
371+
w.tparamList(obj.Name(), typeparams.ForNamed(named), obj.Pkg())
369372
}
370373

371374
underlying := obj.Type().Underlying()
@@ -385,11 +388,13 @@ func (p *iexporter) doDecl(obj types.Object) {
385388

386389
// Receiver type parameters are type arguments of the receiver type, so
387390
// their name must be qualified before exporting recv.
388-
rparams := typeparams.RecvTypeParams(sig)
389-
for i := 0; i < rparams.Len(); i++ {
390-
rparam := rparams.At(i)
391-
name := obj.Name() + "." + m.Name() + "." + rparam.Obj().Name()
392-
w.p.tparamNames[rparam.Obj()] = name
391+
if rparams := typeparams.RecvTypeParams(sig); rparams.Len() > 0 {
392+
prefix := obj.Name() + "." + m.Name()
393+
for i := 0; i < rparams.Len(); i++ {
394+
rparam := rparams.At(i)
395+
name := tparamExportName(prefix, rparam)
396+
w.p.tparamNames[rparam.Obj()] = name
397+
}
393398
}
394399
w.param(sig.Recv())
395400
w.signature(sig)
@@ -490,7 +495,7 @@ func (w *exportWriter) pkg(pkg *types.Package) {
490495
}
491496

492497
func (w *exportWriter) qualifiedIdent(obj types.Object) {
493-
name := w.p.indexName(obj)
498+
name := w.p.exportName(obj)
494499

495500
// Ensure any referenced declarations are written out too.
496501
w.p.pushDecl(obj)
@@ -672,18 +677,49 @@ func (w *exportWriter) typeList(ts *typeparams.TypeList, pkg *types.Package) {
672677
}
673678
}
674679

675-
func (w *exportWriter) tparamList(owner types.Object, list *typeparams.TypeParamList, pkg *types.Package) {
680+
func (w *exportWriter) tparamList(prefix string, list *typeparams.TypeParamList, pkg *types.Package) {
676681
ll := uint64(list.Len())
677682
w.uint64(ll)
678683
for i := 0; i < list.Len(); i++ {
679684
tparam := list.At(i)
680-
// Qualify the type parameter name before exporting its type.
681-
name := owner.Name() + "." + tparam.Obj().Name()
682-
w.p.tparamNames[tparam.Obj()] = name
685+
// Set the type parameter exportName before exporting its type.
686+
exportName := tparamExportName(prefix, tparam)
687+
w.p.tparamNames[tparam.Obj()] = exportName
683688
w.typ(list.At(i), pkg)
684689
}
685690
}
686691

692+
const blankMarker = "$"
693+
694+
// tparamExportName returns the 'exported' name of a type parameter, which
695+
// differs from its actual object name: it is prefixed with a qualifier, and
696+
// blank type parameter names are disambiguated by their index in the type
697+
// parameter list.
698+
func tparamExportName(prefix string, tparam *typeparams.TypeParam) string {
699+
assert(prefix != "")
700+
name := tparam.Obj().Name()
701+
if name == "_" {
702+
name = blankMarker + strconv.Itoa(tparam.Index())
703+
}
704+
return prefix + "." + name
705+
}
706+
707+
// tparamName returns the real name of a type parameter, after stripping its
708+
// qualifying prefix and reverting blank-name encoding. See tparamExportName
709+
// for details.
710+
func tparamName(exportName string) string {
711+
// Remove the "path" from the type param name that makes it unique.
712+
ix := strings.LastIndex(exportName, ".")
713+
if ix < 0 {
714+
errorf("malformed type parameter export name %s: missing prefix", exportName)
715+
}
716+
name := exportName[ix+1:]
717+
if strings.HasPrefix(name, blankMarker) {
718+
return "_"
719+
}
720+
return name
721+
}
722+
687723
func (w *exportWriter) paramList(tup *types.Tuple) {
688724
n := tup.Len()
689725
w.uint64(uint64(n))

go/internal/gcimporter/iexport_go118_test.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@ type Any any
3232
3333
type T[A, B any] struct { Left A; Right B }
3434
35+
func (T[P, Q]) m() {}
36+
3537
var X T[int, string] = T[int, string]{1, "hi"}
3638
3739
func ToInt[P interface{ ~int }](p P) int { return int(p) }
@@ -48,6 +50,17 @@ type ImplicitType[T ~int] int
4850
const C1 = 42
4951
const C2 int = 42
5052
const C3 float64 = 42
53+
54+
type Constraint[T any] interface {
55+
m(T)
56+
}
57+
58+
// TODO(rfindley): revert to multiple blanks once the restriction on multiple
59+
// blanks is removed from the type checker.
60+
// type Blanks[_ any, _ Constraint[int]] int
61+
// func (Blanks[_, _]) m() {}
62+
type Blanks[_ any] int
63+
func (Blanks[_]) m() {}
5164
`
5265
testExportSrc(t, []byte(src))
5366
}

go/internal/gcimporter/iimport.go

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -438,12 +438,7 @@ func (r *importReader) obj(name string) {
438438
if r.p.version < iexportVersionGenerics {
439439
errorf("unexpected type param type")
440440
}
441-
// Remove the "path" from the type param name that makes it unique
442-
ix := strings.LastIndex(name, ".")
443-
if ix < 0 {
444-
errorf("missing path for type param")
445-
}
446-
name0 := name[ix+1:]
441+
name0 := tparamName(name)
447442
tn := types.NewTypeName(pos, r.currPkg, name0, nil)
448443
t := typeparams.NewTypeParam(tn, nil)
449444

0 commit comments

Comments
 (0)