]> Cypherpunks repositories - gostls13.git/commitdiff
[dev.typeparams] cmd/compile: handle derived types that are converted to interfaces
authorDan Scales <danscales@google.com>
Tue, 6 Jul 2021 16:38:58 +0000 (09:38 -0700)
committerDan Scales <danscales@google.com>
Wed, 7 Jul 2021 17:40:11 +0000 (17:40 +0000)
Up to this point, we were only handling typeparams that were converted
to empty or non-empty interfaces. But we have a dictionary entry for
each derived type (i.e. type derived from typeparams) as well. So, when
doing a conversion, look for the source type in both the type params and
derived types of the generic info, and then use the appropriate
dictionary entry.

Added some cases to ifaceconv.go (e.g. converting []T to an empty
interface).

Change-Id: I7bbad0128bec20ccccd93ae1d65c1ffd44ca79a0
Reviewed-on: https://go-review.googlesource.com/c/go/+/333011
Trust: Dan Scales <danscales@google.com>
Run-TryBot: Dan Scales <danscales@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Keith Randall <khr@golang.org>
src/cmd/compile/internal/noder/stencil.go
test/typeparam/ifaceconv.go

index 656cab84d17430f715b302c2494f37766db0df7c..ce9dc09bc3b7be41162e70793d5dd5c9ed2d0a8d 100644 (file)
@@ -979,21 +979,14 @@ func getDictionaryEntry(pos src.XPos, dict *ir.Name, i int, size int) ir.Node {
        return r
 }
 
-// getDictionaryType returns a *runtime._type from the dictionary corresponding to the input type.
-// The input type must be a type parameter (TODO: or a local derived type).
-func (subst *subster) getDictionaryType(pos src.XPos, t *types.Type) ir.Node {
-       tparams := subst.ts.Tparams
-       var i = 0
-       for i = range tparams {
-               if t == tparams[i] {
-                       break
-               }
-       }
-       if i == len(tparams) {
-               base.Fatalf(fmt.Sprintf("couldn't find type param %+v", t))
+// getDictionaryType returns a *runtime._type from the dictionary entry i
+// (which refers to a type param or a derived type that uses type params).
+func (subst *subster) getDictionaryType(pos src.XPos, i int) ir.Node {
+       if i < 0 || i >= subst.info.startSubDict {
+               base.Fatalf(fmt.Sprintf("bad dict index %d", i))
        }
 
-       r := getDictionaryEntry(pos, subst.info.dictParam, i, len(tparams))
+       r := getDictionaryEntry(pos, subst.info.dictParam, i, subst.info.startSubDict)
        // change type of retrieved dictionary entry to *byte, which is the
        // standard typing of a *runtime._type in the compiler
        typed(types.Types[types.TUINT8].PtrTo(), r)
@@ -1134,11 +1127,12 @@ func (subst *subster) node(n ir.Node) ir.Node {
                                // type argument.
                                m = transformConvCall(call)
                                if m.Op() == ir.OCONVIFACE {
-                                       if srcType := x.(*ir.CallExpr).Args[0].Type(); srcType.IsTypeParam() { // TODO: or derived type
-                                               // Note: srcType uses x.Args[0], not m.X or call.Args[0], because
-                                               // we need the type before the type parameter -> type argument substitution.
+                                       // Note: srcType uses x.Args[0], not m.X or call.Args[0], because
+                                       // we need the type before the type parameter -> type argument substitution.
+                                       srcType := x.(*ir.CallExpr).Args[0].Type()
+                                       if ix := subst.findDictType(srcType); ix >= 0 {
                                                c := m.(*ir.ConvExpr)
-                                               m = subst.convertUsingDictionary(c.Pos(), c.X, c.Type(), srcType)
+                                               m = subst.convertUsingDictionary(c.Pos(), c.X, c.Type(), srcType, ix)
                                        }
                                }
 
@@ -1240,8 +1234,9 @@ func (subst *subster) node(n ir.Node) ir.Node {
                        x := x.(*ir.ConvExpr)
                        // Note: x's argument is still typed as a type parameter.
                        // m's argument now has an instantiated type.
-                       if t := x.X.Type(); t.IsTypeParam() {
-                               m = subst.convertUsingDictionary(x.Pos(), m.(*ir.ConvExpr).X, m.Type(), t)
+                       t := x.X.Type()
+                       if ix := subst.findDictType(t); ix >= 0 {
+                               m = subst.convertUsingDictionary(x.Pos(), m.(*ir.ConvExpr).X, m.Type(), t, ix)
                        }
                }
                return m
@@ -1250,18 +1245,31 @@ func (subst *subster) node(n ir.Node) ir.Node {
        return edit(n)
 }
 
-// convertUsingDictionary converts value v from generic type src to an interface type dst.
-func (subst *subster) convertUsingDictionary(pos src.XPos, v ir.Node, dst, src *types.Type) ir.Node {
-       // TODO: handle converting from derived types. For now, just from naked
-       // type parameters.
-       if !src.IsTypeParam() {
-               base.Fatalf("source must be a type parameter %+v", src)
+// findDictType looks for type t in the typeparams or derived types in the generic
+// function info subst.info.gfInfo. This will indicate the dictionary entry with the
+// correct concrete type for the associated instantiated function.
+func (subst *subster) findDictType(t *types.Type) int {
+       for i, dt := range subst.info.gfInfo.tparams {
+               if dt == t {
+                       return i
+               }
        }
+       for i, dt := range subst.info.gfInfo.derivedTypes {
+               if types.Identical(dt, t) {
+                       return i + len(subst.info.gfInfo.tparams)
+               }
+       }
+       return -1
+}
+
+// convertUsingDictionary converts value v from instantiated type src (which is index
+// 'ix' in the instantiation's dictionary) to an interface type dst.
+func (subst *subster) convertUsingDictionary(pos src.XPos, v ir.Node, dst, src *types.Type, ix int) ir.Node {
        if !dst.IsInterface() {
                base.Fatalf("can only convert type parameters to interfaces %+v -> %+v", src, dst)
        }
        // Load the actual runtime._type of the type parameter from the dictionary.
-       rt := subst.getDictionaryType(pos, src)
+       rt := subst.getDictionaryType(pos, ix)
 
        // Convert value to an interface type, so the data field is what we want.
        if !v.Type().IsInterface() {
index 32c2dbe7c288776db00f2dffa3debb683b81167f..f4023366b9e55860ab4ba795e67e882659265a1e 100644 (file)
@@ -18,6 +18,13 @@ func f[T any](x T) interface{} {
        var i interface{} = x
        return i
 }
+
+func fs[T any](x T) interface{} {
+       y := []T{x}
+       var i interface{} = y
+       return i
+}
+
 func g[T any](x T) E {
        var i E = x
        return i
@@ -46,10 +53,18 @@ func j[T C](t T) C {
        return C(t) // explicit conversion
 }
 
+func js[T any](x T) interface{} {
+       y := []T{x}
+       return interface{}(y)
+}
+
 func main() {
        if got, want := f[int](7), 7; got != want {
                panic(fmt.Sprintf("got %d want %d", got, want))
        }
+       if got, want := fs[int](7), []int{7}; got.([]int)[0] != want[0] {
+               panic(fmt.Sprintf("got %d want %d", got, want))
+       }
        if got, want := g[int](7), 7; got != want {
                panic(fmt.Sprintf("got %d want %d", got, want))
        }
@@ -62,4 +77,7 @@ func main() {
        if got, want := j[myInt](7).foo(), 8; got != want {
                panic(fmt.Sprintf("got %d want %d", got, want))
        }
+       if got, want := js[int](7), []int{7}; got.([]int)[0] != want[0] {
+               panic(fmt.Sprintf("got %d want %d", got, want))
+       }
 }