]> Cypherpunks repositories - gostls13.git/commitdiff
cmd/compile: fix naming of types inside instantiations
authorDan Scales <danscales@google.com>
Sun, 22 Aug 2021 15:58:24 +0000 (08:58 -0700)
committerDan Scales <danscales@google.com>
Tue, 24 Aug 2021 18:30:13 +0000 (18:30 +0000)
Issues 47713 and 47877 were both due to problems with the names used for
instantiated functions/methods, which must be in sync with the names
used by types2.

 - Switched to using NameString() for writing out type arguments in
   instantiation names. This ensures that we are always adding the
   package to type names even for the local package. Previously, we were
   explicitly adding the package name for local packages, but that
   doesn't handle the case when the local type is embedded inside a
   pointer or slice type. By switching to NameString(), we fix #47713.

 - types1 and types2 write out 'interface {' differently (vs.
   'interface{') and we were already handling that. But we needed to add
   similar code to handle 'struct {' vs 'struct{'. This fixes issue
   #47877.

While fixing these bugs, I also moved some duplicated code (which
include some of the changes above) into a common function addTargs(). I
also moved InstType() name to subr.go, and renamed: MakeInstName ->
MakeFuncInstSym and MakeDictName -> MakeDictSym.

Also removed a couple of ".inst..inst." prefix checks which are
irrelvant now, since we don't add ".inst." anymore to function
instantiations.

Fixes #47713
Fixes #47877
Fixes #47922

Change-Id: I19e9a073451f3ababd8ec31b6608cd79ba8cba36
Reviewed-on: https://go-review.googlesource.com/c/go/+/344613
Trust: Dan Scales <danscales@google.com>
Reviewed-by: Keith Randall <khr@golang.org>
src/cmd/compile/internal/noder/stencil.go
src/cmd/compile/internal/reflectdata/reflect.go
src/cmd/compile/internal/typecheck/iimport.go
src/cmd/compile/internal/typecheck/subr.go
test/typeparam/issue47713.go [new file with mode: 0644]
test/typeparam/issue47713.out [new file with mode: 0644]
test/typeparam/issue47877.go [new file with mode: 0644]

index 570dec999045467b39d42f5e9b1873a23cda038c..602e88c102c249f34993b6c3d52e22fcab898c58 100644 (file)
@@ -590,7 +590,7 @@ func (g *irgen) getInstantiation(nameNode *ir.Name, shapes []*types.Type, isMeth
                shapes = s1
        }
 
-       sym := typecheck.MakeInstName(nameNode.Sym(), shapes, isMeth)
+       sym := typecheck.MakeFuncInstSym(nameNode.Sym(), shapes, isMeth)
        info := g.instInfoMap[sym]
        if info == nil {
                // If instantiation doesn't exist yet, create it and add
@@ -1372,7 +1372,7 @@ func (g *irgen) getDictionarySym(gf *ir.Name, targs []*types.Type, isMeth bool)
        }
 
        // Get a symbol representing the dictionary.
-       sym := typecheck.MakeDictName(gf.Sym(), targs, isMeth)
+       sym := typecheck.MakeDictSym(gf.Sym(), targs, isMeth)
 
        // Initialize the dictionary, if we haven't yet already.
        lsym := sym.Linksym()
index a95c76ff268f0c071ca7e208ab98ba1a31768afd..9b9efe04a2e1309ed8bb167fca4d55374942fc77 100644 (file)
@@ -1897,10 +1897,6 @@ func methodWrapper(rcvr *types.Type, method *types.Field, forItab bool) *obj.LSy
                        } else {
                                targs = rcvr.RParams()
                        }
-                       if strings.HasPrefix(ir.MethodSym(orig, method.Sym).Name, ".inst.") {
-                               fmt.Printf("%s\n", ir.MethodSym(orig, method.Sym).Name)
-                               panic("multiple .inst.")
-                       }
                        // The wrapper for an auto-generated pointer/non-pointer
                        // receiver method should share the same dictionary as the
                        // corresponding original (user-written) method.
@@ -1929,7 +1925,7 @@ func methodWrapper(rcvr *types.Type, method *types.Field, forItab bool) *obj.LSy
                        }
                        targs = targs2
 
-                       sym := typecheck.MakeInstName(ir.MethodSym(methodrcvr, method.Sym), targs, true)
+                       sym := typecheck.MakeFuncInstSym(ir.MethodSym(methodrcvr, method.Sym), targs, true)
                        if sym.Def == nil {
                                // Currently we make sure that we have all the instantiations
                                // we need by generating them all in ../noder/stencil.go:instantiateMethods
@@ -2040,7 +2036,7 @@ func getDictionary(gf *types.Sym, targs []*types.Type) ir.Node {
                }
        }
 
-       sym := typecheck.MakeDictName(gf, targs, true)
+       sym := typecheck.MakeDictSym(gf, targs, true)
 
        // Initialize the dictionary, if we haven't yet already.
        if lsym := sym.Linksym(); len(lsym.P) == 0 {
index 2e3fdbc1bc99b365bc8524a1742f95a23ae75f04..a1a3ac3e8ad7617cfaafb9676a691219b0f844c3 100644 (file)
@@ -8,7 +8,6 @@
 package typecheck
 
 import (
-       "bytes"
        "encoding/binary"
        "fmt"
        "go/constant"
@@ -1751,32 +1750,6 @@ func builtinCall(pos src.XPos, op ir.Op) *ir.CallExpr {
        return ir.NewCallExpr(pos, ir.OCALL, ir.NewIdent(base.Pos, types.BuiltinPkg.Lookup(ir.OpNames[op])), nil)
 }
 
-// InstTypeName creates a name for an instantiated type, based on the name of the
-// generic type and the type args.
-func InstTypeName(name string, targs []*types.Type) string {
-       b := bytes.NewBufferString(name)
-       b.WriteByte('[')
-       for i, targ := range targs {
-               if i > 0 {
-                       b.WriteByte(',')
-               }
-               // WriteString() does not include the package name for the local
-               // package, but we want it to make sure type arguments (including
-               // type params) are uniquely specified.
-               if targ.Sym() != nil && targ.Sym().Pkg == types.LocalPkg {
-                       b.WriteString(targ.Sym().Pkg.Name)
-                       b.WriteByte('.')
-               }
-               // types1 uses "interface {" and types2 uses "interface{" - convert
-               // to consistent types2 format.
-               tstring := targ.String()
-               tstring = strings.Replace(tstring, "interface {", "interface{", -1)
-               b.WriteString(tstring)
-       }
-       b.WriteByte(']')
-       return b.String()
-}
-
 // NewIncompleteNamedType returns a TFORW type t with name specified by sym, such
 // that t.nod and sym.Def are set correctly.
 func NewIncompleteNamedType(pos src.XPos, sym *types.Sym) *types.Type {
@@ -1879,7 +1852,7 @@ func substInstType(t *types.Type, baseType *types.Type, targs []*types.Type) {
                }
                t2 := msubst.Typ(f.Type)
                oldsym := f.Nname.Sym()
-               newsym := MakeInstName(oldsym, targs, true)
+               newsym := MakeFuncInstSym(oldsym, targs, true)
                var nname *ir.Name
                if newsym.Def != nil {
                        nname = newsym.Def.(*ir.Name)
index c7a3718b31cea4a962a2c067fe5c15ee57a2f95b..7ae10ef40652d6e5116b7cc3890ccdfc435f7d73 100644 (file)
@@ -900,6 +900,35 @@ func TypesOf(x []ir.Node) []*types.Type {
        return r
 }
 
+// addTargs writes out the targs to buffer b as a comma-separated list enclosed by
+// brackets.
+func addTargs(b *bytes.Buffer, targs []*types.Type) {
+       b.WriteByte('[')
+       for i, targ := range targs {
+               if i > 0 {
+                       b.WriteByte(',')
+               }
+               // Use NameString(), which includes the package name for the local
+               // package, to make sure that type arguments (including type params),
+               // are uniquely specified.
+               tstring := targ.NameString()
+               // types1 uses "interface {" and types2 uses "interface{" - convert
+               // to consistent types2 format.  Same for "struct {"
+               tstring = strings.Replace(tstring, "interface {", "interface{", -1)
+               tstring = strings.Replace(tstring, "struct {", "struct{", -1)
+               b.WriteString(tstring)
+       }
+       b.WriteString("]")
+}
+
+// InstTypeName creates a name for an instantiated type, based on the name of the
+// generic type and the type args.
+func InstTypeName(name string, targs []*types.Type) string {
+       b := bytes.NewBufferString(name)
+       addTargs(b, targs)
+       return b.String()
+}
+
 // makeInstName1 returns the name of the generic function instantiated with the
 // given types, which can have type params or shapes, or be concrete types. name is
 // the name of the generic function or method.
@@ -912,36 +941,16 @@ func makeInstName1(name string, targs []*types.Type, hasBrackets bool) string {
        } else {
                b.WriteString(name)
        }
-       b.WriteString("[")
-       for i, targ := range targs {
-               if i > 0 {
-                       b.WriteString(",")
-               }
-               // WriteString() does not include the package name for the local
-               // package, but we want it for uniqueness.
-               if targ.Sym() != nil && targ.Sym().Pkg == types.LocalPkg {
-                       b.WriteString(targ.Sym().Pkg.Name)
-                       b.WriteByte('.')
-               }
-               // types1 uses "interface {" and types2 uses "interface{" - convert
-               // to consistent types2 format.
-               tstring := targ.String()
-               tstring = strings.Replace(tstring, "interface {", "interface{", -1)
-               b.WriteString(tstring)
-       }
-       b.WriteString("]")
+       addTargs(b, targs)
        if i >= 0 {
                i2 := strings.LastIndex(name[i:], "]")
                assert(i2 >= 0)
                b.WriteString(name[i+i2+1:])
        }
-       if strings.HasPrefix(b.String(), ".inst..inst.") {
-               panic(fmt.Sprintf("multiple .inst. prefix in %s", b.String()))
-       }
        return b.String()
 }
 
-// MakeInstName makes the unique name for a stenciled generic function or method,
+// MakeFuncInstSym makes the unique sym for a stenciled generic function or method,
 // based on the name of the function fnsym and the targs. It replaces any
 // existing bracket type list in the name. MakeInstName asserts that fnsym has
 // brackets in its name if and only if hasBrackets is true.
@@ -953,11 +962,11 @@ func makeInstName1(name string, targs []*types.Type, hasBrackets bool) string {
 //
 // The standard naming is something like: 'genFn[int,bool]' for functions and
 // '(*genType[int,bool]).methodName' for methods
-func MakeInstName(gf *types.Sym, targs []*types.Type, hasBrackets bool) *types.Sym {
+func MakeFuncInstSym(gf *types.Sym, targs []*types.Type, hasBrackets bool) *types.Sym {
        return gf.Pkg.Lookup(makeInstName1(gf.Name, targs, hasBrackets))
 }
 
-func MakeDictName(gf *types.Sym, targs []*types.Type, hasBrackets bool) *types.Sym {
+func MakeDictSym(gf *types.Sym, targs []*types.Type, hasBrackets bool) *types.Sym {
        for _, targ := range targs {
                if targ.HasTParam() {
                        fmt.Printf("FUNCTION %s\n", gf.Name)
@@ -1222,7 +1231,7 @@ func (ts *Tsubster) Typ(t *types.Type) *types.Type {
                for i, f := range t.Methods().Slice() {
                        t2 := ts.Typ(f.Type)
                        oldsym := f.Nname.Sym()
-                       newsym := MakeInstName(oldsym, ts.Targs, true)
+                       newsym := MakeFuncInstSym(oldsym, ts.Targs, true)
                        var nname *ir.Name
                        if newsym.Def != nil {
                                nname = newsym.Def.(*ir.Name)
diff --git a/test/typeparam/issue47713.go b/test/typeparam/issue47713.go
new file mode 100644 (file)
index 0000000..a38eea1
--- /dev/null
@@ -0,0 +1,52 @@
+// run -gcflags=-G=3
+
+// Copyright 2021 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 main
+
+import (
+       "encoding"
+       "fmt"
+)
+
+type Seralizable interface {
+       encoding.BinaryMarshaler
+       encoding.BinaryUnmarshaler
+}
+
+type SerDeString string
+
+func (s *SerDeString) UnmarshalBinary(in []byte) error {
+       *s = SerDeString(in)
+       return nil
+}
+
+func (s SerDeString) MarshalBinary() ([]byte, error) {
+       return []byte(s), nil
+}
+
+
+type GenericSerializable[T Seralizable] struct {
+       Key string
+       Value T
+}
+
+func (g GenericSerializable[T]) Send() {
+       out, err := g.Value.MarshalBinary()
+       if err != nil {
+               panic("bad")
+       }
+       var newval SerDeString
+       newval.UnmarshalBinary(out)
+       fmt.Printf("Sent %s\n", newval)
+}
+
+func main() {
+       val := SerDeString("asdf")
+       x := GenericSerializable[*SerDeString]{
+               Value: &val,
+       }
+       x.Send()
+}
diff --git a/test/typeparam/issue47713.out b/test/typeparam/issue47713.out
new file mode 100644 (file)
index 0000000..a6e7719
--- /dev/null
@@ -0,0 +1 @@
+Sent asdf
diff --git a/test/typeparam/issue47877.go b/test/typeparam/issue47877.go
new file mode 100644 (file)
index 0000000..0a83459
--- /dev/null
@@ -0,0 +1,23 @@
+// run -gcflags=-G=3
+
+// Copyright 2021 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 main
+
+type Map[K comparable, V any] struct {
+        m map[K]V
+}
+
+func NewMap[K comparable, V any]() Map[K, V] {
+        return Map[K, V]{m: map[K]V{}}
+}
+
+func (m Map[K, V]) Get(key K) V {
+        return m.m[key]
+}
+
+func main() {
+        _ = NewMap[int, struct{}]()
+}