]> Cypherpunks repositories - gostls13.git/commitdiff
[dev.typeparams] cmd/compile: use dictionary entries for more conversion cases
authorKeith Randall <khr@golang.org>
Tue, 8 Jun 2021 22:58:16 +0000 (15:58 -0700)
committerKeith Randall <khr@golang.org>
Tue, 29 Jun 2021 21:50:53 +0000 (21:50 +0000)
This CL handles I(x) where I is an interface type and x has
typeparam type.

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

index 49781ddc074d96c227b7aa436f03f5550f8f7893..29ee863a71fcfc9de42de6945aebfe0c24882809 100644 (file)
@@ -838,7 +838,15 @@ func (subst *subster) node(n ir.Node) ir.Node {
                        case ir.OTYPE:
                                // Transform the conversion, now that we know the
                                // type argument.
-                               m = transformConvCall(m.(*ir.CallExpr))
+                               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.
+                                               c := m.(*ir.ConvExpr)
+                                               m = subst.convertUsingDictionary(c.Pos(), c.X, c.Type(), srcType)
+                                       }
+                               }
 
                        case ir.OMETHVALUE:
                                // Redo the transformation of OXDOT, now that we
@@ -919,30 +927,10 @@ func (subst *subster) node(n ir.Node) ir.Node {
 
                case ir.OCONVIFACE:
                        x := x.(*ir.ConvExpr)
-                       // TODO: handle converting from derived types. For now, just from naked
-                       // type parameters.
-                       if x.X.Type().IsTypeParam() {
-                               // Load the actual runtime._type of the type parameter from the dictionary.
-                               rt := subst.getDictionaryType(m.Pos(), x.X.Type())
-
-                               // At this point, m is an interface type with a data word we want.
-                               // But the type word represents a gcshape type, which we don't want.
-                               // Replace with the instantiated type loaded from the dictionary.
-                               m = ir.NewUnaryExpr(m.Pos(), ir.OIDATA, m)
-                               typed(types.Types[types.TUNSAFEPTR], m)
-                               m = ir.NewBinaryExpr(m.Pos(), ir.OEFACE, rt, m)
-                               if !x.Type().IsEmptyInterface() {
-                                       // We just built an empty interface{}. Type it as such,
-                                       // then assert it to the required non-empty interface.
-                                       typed(types.NewInterface(types.LocalPkg, nil), m)
-                                       m = ir.NewTypeAssertExpr(m.Pos(), m, nil)
-                               }
-                               typed(x.Type(), m)
-                               // TODO: we're throwing away the type word of the original version
-                               // of m here (it would be OITAB(m)), which probably took some
-                               // work to generate. Can we avoid generating it at all?
-                               // (The linker will throw them away if not needed, so it would just
-                               // save toolchain work, not binary size.)
+                       // 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)
                        }
                }
                return m
@@ -951,6 +939,47 @@ 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)
+       }
+       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)
+
+       // Convert value to an interface type, so the data field is what we want.
+       if !v.Type().IsInterface() {
+               v = ir.NewConvExpr(v.Pos(), ir.OCONVIFACE, nil, v)
+               typed(types.NewInterface(types.LocalPkg, nil), v)
+       }
+
+       // At this point, v is an interface type with a data word we want.
+       // But the type word represents a gcshape type, which we don't want.
+       // Replace with the instantiated type loaded from the dictionary.
+       data := ir.NewUnaryExpr(pos, ir.OIDATA, v)
+       typed(types.Types[types.TUNSAFEPTR], data)
+       var i ir.Node = ir.NewBinaryExpr(pos, ir.OEFACE, rt, data)
+       if !dst.IsEmptyInterface() {
+               // We just built an empty interface{}. Type it as such,
+               // then assert it to the required non-empty interface.
+               typed(types.NewInterface(types.LocalPkg, nil), i)
+               i = ir.NewTypeAssertExpr(pos, i, nil)
+       }
+       typed(dst, i)
+       // TODO: we're throwing away the type word of the original version
+       // of m here (it would be OITAB(m)), which probably took some
+       // work to generate. Can we avoid generating it at all?
+       // (The linker will throw them away if not needed, so it would just
+       // save toolchain work, not binary size.)
+       return i
+
+}
+
 func (subst *subster) namelist(l []*ir.Name) []*ir.Name {
        s := make([]*ir.Name, len(l))
        for i, n := range l {
index 0b0776815c96ceb3874334f8ccbd276747ddcf90..32c2dbe7c288776db00f2dffa3debb683b81167f 100644 (file)
@@ -38,10 +38,14 @@ func h[T C](x T) interface{foo() int} {
        return i
 }
 func i[T C](x T) C {
-       var i C = x
+       var i C = x // conversion in assignment
        return i
 }
 
+func j[T C](t T) C {
+       return C(t) // explicit conversion
+}
+
 func main() {
        if got, want := f[int](7), 7; got != want {
                panic(fmt.Sprintf("got %d want %d", got, want))
@@ -55,4 +59,7 @@ func main() {
        if got, want := i[myInt](7).foo(), 8; got != want {
                panic(fmt.Sprintf("got %d want %d", got, want))
        }
+       if got, want := j[myInt](7).foo(), 8; got != want {
+               panic(fmt.Sprintf("got %d want %d", got, want))
+       }
 }