]> Cypherpunks repositories - gostls13.git/commitdiff
[dev.typeparams] cmd/compile: allow conversions from type parameter to interface
authorKeith Randall <khr@golang.org>
Sat, 5 Jun 2021 05:54:08 +0000 (22:54 -0700)
committerKeith Randall <khr@golang.org>
Mon, 7 Jun 2021 19:53:38 +0000 (19:53 +0000)
When converting from a type param to an interface, allow it if
the type bound implements that interface.

Query: some conversions go through this path, some use another path?
The test does

   var i interface{foo()int} = x

but

   i := (interface{foo()int})(x)

works at tip.

Change-Id: I84d497e5228c0e1d1c9d76ffebaedce09dc45e8e
Reviewed-on: https://go-review.googlesource.com/c/go/+/325409
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/transform.go
src/cmd/compile/internal/typecheck/subr.go
test/typeparam/ifaceconv.go [new file with mode: 0644]

index a084f0b7be2ddf31da3a1bf4f6ef689fe3cadb2d..946d335f0763452b0ea58a87021dc0ef10882401 100644 (file)
@@ -437,7 +437,10 @@ func assignconvfn(n ir.Node, t *types.Type) ir.Node {
                return n
        }
 
-       op, _ := typecheck.Assignop(n.Type(), t)
+       op, why := typecheck.Assignop(n.Type(), t)
+       if op == ir.OXXX {
+               base.Fatalf("found illegal assignment %+v -> %+v; %s", n.Type(), t, why)
+       }
 
        r := ir.NewConvExpr(base.Pos, op, t, n)
        r.SetTypecheck(1)
index e9a9a57126955d9b3a29c568357be78ef81ae355..0e306eaea83f791ddb45aa6be38f88a2ea243b14 100644 (file)
@@ -723,13 +723,23 @@ func ifacelookdot(s *types.Sym, t *types.Type, ignorecase bool) (m *types.Field,
        return m, followptr
 }
 
+// implements reports whether t implements the interface iface. t can be
+// an interface, a type parameter, or a concrete type. If implements returns
+// false, it stores a method of iface that is not implemented in *m. If the
+// method name matches but the type is wrong, it additionally stores the type
+// of the method (on t) in *samename.
 func implements(t, iface *types.Type, m, samename **types.Field, ptr *int) bool {
        t0 := t
        if t == nil {
                return false
        }
 
-       if t.IsInterface() {
+       if t.IsInterface() || t.IsTypeParam() {
+               if t.IsTypeParam() {
+                       // A typeparam satisfies an interface if its type bound
+                       // has all the methods of that interface.
+                       t = t.Bound()
+               }
                i := 0
                tms := t.AllMethods().Slice()
                for _, im := range iface.AllMethods().Slice() {
diff --git a/test/typeparam/ifaceconv.go b/test/typeparam/ifaceconv.go
new file mode 100644 (file)
index 0000000..0b07768
--- /dev/null
@@ -0,0 +1,58 @@
+// 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.
+
+// Test that we can convert type parameters to both empty
+// and nonempty interfaces, and named and nonnamed versions
+// thereof.
+
+package main
+
+import "fmt"
+
+type E interface{}
+
+func f[T any](x T) interface{} {
+       var i interface{} = x
+       return i
+}
+func g[T any](x T) E {
+       var i E = x
+       return i
+}
+
+type C interface {
+       foo() int
+}
+
+type myInt int
+
+func (x myInt) foo() int {
+       return int(x+1)
+}
+
+func h[T C](x T) interface{foo() int} {
+       var i interface{foo()int} = x
+       return i
+}
+func i[T C](x T) C {
+       var i C = x
+       return i
+}
+
+func main() {
+       if got, want := f[int](7), 7; got != want {
+               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))
+       }
+       if got, want := h[myInt](7).foo(), 8; got != want {
+               panic(fmt.Sprintf("got %d want %d", got, want))
+       }
+       if got, want := i[myInt](7).foo(), 8; got != want {
+               panic(fmt.Sprintf("got %d want %d", got, want))
+       }
+}