Skip to content

Commit

Permalink
go/internal/gcimporter: support unique naming for blank type parameters
Browse files Browse the repository at this point in the history
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 <[email protected]>
Run-TryBot: Robert Findley <[email protected]>
Trust: Dan Scales <[email protected]>
Reviewed-by: Dan Scales <[email protected]>
Reviewed-by: Robert Griesemer <[email protected]>
gopls-CI: kokoro <[email protected]>
TryBot-Result: Gopher Robot <[email protected]>
  • Loading branch information
findleyr committed Jan 20, 2022
1 parent 25e1ac4 commit 80963bc
Show file tree
Hide file tree
Showing 3 changed files with 74 additions and 30 deletions.
84 changes: 60 additions & 24 deletions go/internal/gcimporter/iexport.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import (
"math/big"
"reflect"
"sort"
"strconv"
"strings"

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

for obj := range index {
name := w.p.indexName(obj)
name := w.p.exportName(obj)
pkgObjs[obj.Pkg()] = append(pkgObjs[obj.Pkg()], pkgObj{obj, name})
}

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

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

data0 intWriter
declIndex map[types.Object]uint64
tparamNames map[types.Object]string // typeparam->qualified name
tparamNames map[types.Object]string // typeparam->exported name
typIndex map[types.Type]uint64

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

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

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

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

// Receiver type parameters are type arguments of the receiver type, so
// their name must be qualified before exporting recv.
rparams := typeparams.RecvTypeParams(sig)
for i := 0; i < rparams.Len(); i++ {
rparam := rparams.At(i)
name := obj.Name() + "." + m.Name() + "." + rparam.Obj().Name()
w.p.tparamNames[rparam.Obj()] = name
if rparams := typeparams.RecvTypeParams(sig); rparams.Len() > 0 {
prefix := obj.Name() + "." + m.Name()
for i := 0; i < rparams.Len(); i++ {
rparam := rparams.At(i)
name := tparamExportName(prefix, rparam)
w.p.tparamNames[rparam.Obj()] = name
}
}
w.param(sig.Recv())
w.signature(sig)
Expand Down Expand Up @@ -490,7 +495,7 @@ func (w *exportWriter) pkg(pkg *types.Package) {
}

func (w *exportWriter) qualifiedIdent(obj types.Object) {
name := w.p.indexName(obj)
name := w.p.exportName(obj)

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

func (w *exportWriter) tparamList(owner types.Object, list *typeparams.TypeParamList, pkg *types.Package) {
func (w *exportWriter) tparamList(prefix string, list *typeparams.TypeParamList, pkg *types.Package) {
ll := uint64(list.Len())
w.uint64(ll)
for i := 0; i < list.Len(); i++ {
tparam := list.At(i)
// Qualify the type parameter name before exporting its type.
name := owner.Name() + "." + tparam.Obj().Name()
w.p.tparamNames[tparam.Obj()] = name
// Set the type parameter exportName before exporting its type.
exportName := tparamExportName(prefix, tparam)
w.p.tparamNames[tparam.Obj()] = exportName
w.typ(list.At(i), pkg)
}
}

const blankMarker = "$"

// tparamExportName returns the 'exported' name of a type parameter, which
// differs from its actual object name: it is prefixed with a qualifier, and
// blank type parameter names are disambiguated by their index in the type
// parameter list.
func tparamExportName(prefix string, tparam *typeparams.TypeParam) string {
assert(prefix != "")
name := tparam.Obj().Name()
if name == "_" {
name = blankMarker + strconv.Itoa(tparam.Index())
}
return prefix + "." + name
}

// tparamName returns the real name of a type parameter, after stripping its
// qualifying prefix and reverting blank-name encoding. See tparamExportName
// for details.
func tparamName(exportName string) string {
// Remove the "path" from the type param name that makes it unique.
ix := strings.LastIndex(exportName, ".")
if ix < 0 {
errorf("malformed type parameter export name %s: missing prefix", exportName)
}
name := exportName[ix+1:]
if strings.HasPrefix(name, blankMarker) {
return "_"
}
return name
}

func (w *exportWriter) paramList(tup *types.Tuple) {
n := tup.Len()
w.uint64(uint64(n))
Expand Down
13 changes: 13 additions & 0 deletions go/internal/gcimporter/iexport_go118_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ type Any any
type T[A, B any] struct { Left A; Right B }
func (T[P, Q]) m() {}
var X T[int, string] = T[int, string]{1, "hi"}
func ToInt[P interface{ ~int }](p P) int { return int(p) }
Expand All @@ -48,6 +50,17 @@ type ImplicitType[T ~int] int
const C1 = 42
const C2 int = 42
const C3 float64 = 42
type Constraint[T any] interface {
m(T)
}
// TODO(rfindley): revert to multiple blanks once the restriction on multiple
// blanks is removed from the type checker.
// type Blanks[_ any, _ Constraint[int]] int
// func (Blanks[_, _]) m() {}
type Blanks[_ any] int
func (Blanks[_]) m() {}
`
testExportSrc(t, []byte(src))
}
Expand Down
7 changes: 1 addition & 6 deletions go/internal/gcimporter/iimport.go
Original file line number Diff line number Diff line change
Expand Up @@ -438,12 +438,7 @@ func (r *importReader) obj(name string) {
if r.p.version < iexportVersionGenerics {
errorf("unexpected type param type")
}
// Remove the "path" from the type param name that makes it unique
ix := strings.LastIndex(name, ".")
if ix < 0 {
errorf("missing path for type param")
}
name0 := name[ix+1:]
name0 := tparamName(name)
tn := types.NewTypeName(pos, r.currPkg, name0, nil)
t := typeparams.NewTypeParam(tn, nil)

Expand Down

0 comments on commit 80963bc

Please sign in to comment.