diff options
| author | Keith Randall <khr@golang.org> | 2021-08-24 14:50:05 -0700 |
|---|---|---|
| committer | Keith Randall <khr@golang.org> | 2021-08-27 15:03:09 +0000 |
| commit | 4f0dedca7141afafbc01be96097570de2da2bdcc (patch) | |
| tree | 2fec8be25a9065d2bd7038ba62645d2c5ae2085e | |
| parent | 39eb1cc3f4a7d620d3faae139f83e88e652f5d25 (diff) | |
| download | go-4f0dedca7141afafbc01be96097570de2da2bdcc.tar.xz | |
cmd/compile: fix parameterized interfaces
type I[T any] interface{}
This is an interface, but it has a type parameter.
We need to distinguish that from an interface that is not parameterized.
That means when doing type substitution on an interface with
parameters, we need to make a new one.
Same for non-empty interfaces. Even if the type parameter is not
used in any method, we sill need to make a new type.
Similar case to tstruct, above.
Change-Id: I23ad9f21d2c4ef675bf3f7d84899d9e4919d05e7
Reviewed-on: https://go-review.googlesource.com/c/go/+/344578
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>
| -rw-r--r-- | src/cmd/compile/internal/typecheck/subr.go | 15 | ||||
| -rw-r--r-- | test/typeparam/eface.go | 71 |
2 files changed, 83 insertions, 3 deletions
diff --git a/src/cmd/compile/internal/typecheck/subr.go b/src/cmd/compile/internal/typecheck/subr.go index 8d05356543..73f83f65e4 100644 --- a/src/cmd/compile/internal/typecheck/subr.go +++ b/src/cmd/compile/internal/typecheck/subr.go @@ -1163,8 +1163,8 @@ func (ts *Tsubster) typ1(t *types.Type) *types.Type { } case types.TINTER: - newt = ts.tinter(t) - if newt == t && !targsChanged { + newt = ts.tinter(t, targsChanged) + if newt == t { newt = nil } @@ -1324,11 +1324,20 @@ func (ts *Tsubster) tstruct(t *types.Type, force bool) *types.Type { } // tinter substitutes type params in types of the methods of an interface type. -func (ts *Tsubster) tinter(t *types.Type) *types.Type { +func (ts *Tsubster) tinter(t *types.Type, force bool) *types.Type { if t.Methods().Len() == 0 { + if t.HasTParam() { + // For an empty interface, we need to return a new type, + // since it may now be fully instantiated (HasTParam + // becomes false). + return types.NewInterface(t.Pkg(), nil) + } return t } var newfields []*types.Field + if force { + newfields = make([]*types.Field, t.Methods().Len()) + } for i, f := range t.Methods().Slice() { t2 := ts.typ1(f.Type) if (t2 != f.Type || f.Nname != nil) && newfields == nil { diff --git a/test/typeparam/eface.go b/test/typeparam/eface.go new file mode 100644 index 0000000000..e8147ef081 --- /dev/null +++ b/test/typeparam/eface.go @@ -0,0 +1,71 @@ +// 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. + +// Make sure we handle instantiated empty interfaces. + +package main + +type E[T any] interface { +} + +//go:noinline +func f[T any](x E[T]) interface{} { + return x +} + +//go:noinline +func g[T any](x interface{}) E[T] { + return x +} + +type I[T any] interface { + foo() +} + +type myint int + +func (x myint) foo() {} + +//go:noinline +func h[T any](x I[T]) interface{ foo() } { + return x +} + +//go:noinline +func i[T any](x interface{ foo() }) I[T] { + return x +} + +func main() { + if f[int](1) != 1 { + println("test 1 failed") + } + if f[int](2) != (interface{})(2) { + println("test 2 failed") + } + if g[int](3) != 3 { + println("test 3 failed") + } + if g[int](4) != (E[int])(4) { + println("test 4 failed") + } + if h[int](myint(5)) != myint(5) { + // TODO: disabled + //println("test 5 failed") + } + if h[int](myint(6)) != interface{ foo() }(myint(6)) { + // TODO: disabled + //println("test 6 failed") + } + if i[int](myint(7)) != myint(7) { + // TODO: This happens to work, but not for the right reasons. + println("test 7 failed") + } + if i[int](myint(8)) != I[int](myint(8)) { + // TODO: disabled + //println("test 8 failed") + } +} |
