mirror of https://github.com/golang/go.git
go/types, types2: instantiated interfaces must be concurrency safe
It is the responsibility of go/types to complete any interface it creates, except for those created by the user using NewInterface. However, this was not being done for interfaces created during instantiation. Fix this by (rather carefully) ensuring that all newly created interfaces are eventually completed. Fixes golang/go#61561 Change-Id: I3926e7c9cf80714838d2c1b5f36a2d3221c60c41 Reviewed-on: https://go-review.googlesource.com/c/go/+/513015 Run-TryBot: Robert Findley <rfindley@google.com> TryBot-Result: Gopher Robot <gobot@golang.org> Auto-Submit: Robert Findley <rfindley@google.com> Reviewed-by: Robert Griesemer <gri@google.com>
This commit is contained in:
parent
8597d052b2
commit
b6898dde3d
|
|
@ -13,6 +13,7 @@ import (
|
|||
"regexp"
|
||||
"sort"
|
||||
"strings"
|
||||
"sync"
|
||||
"testing"
|
||||
|
||||
. "cmd/compile/internal/types2"
|
||||
|
|
@ -2295,6 +2296,60 @@ func TestInstantiate(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestInstantiateConcurrent(t *testing.T) {
|
||||
const src = `package p
|
||||
|
||||
type I[P any] interface {
|
||||
m(P)
|
||||
n() P
|
||||
}
|
||||
|
||||
type J = I[int]
|
||||
|
||||
type Nested[P any] *interface{b(P)}
|
||||
|
||||
type K = Nested[string]
|
||||
`
|
||||
pkg := mustTypecheck(src, nil, nil)
|
||||
|
||||
insts := []*Interface{
|
||||
pkg.Scope().Lookup("J").Type().Underlying().(*Interface),
|
||||
pkg.Scope().Lookup("K").Type().Underlying().(*Pointer).Elem().(*Interface),
|
||||
}
|
||||
|
||||
// Use the interface instances concurrently.
|
||||
for _, inst := range insts {
|
||||
var (
|
||||
counts [2]int // method counts
|
||||
methods [2][]string // method strings
|
||||
)
|
||||
var wg sync.WaitGroup
|
||||
for i := 0; i < 2; i++ {
|
||||
i := i
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
|
||||
counts[i] = inst.NumMethods()
|
||||
for mi := 0; mi < counts[i]; mi++ {
|
||||
methods[i] = append(methods[i], inst.Method(mi).String())
|
||||
}
|
||||
}()
|
||||
}
|
||||
wg.Wait()
|
||||
|
||||
if counts[0] != counts[1] {
|
||||
t.Errorf("mismatching method counts for %s: %d vs %d", inst, counts[0], counts[1])
|
||||
continue
|
||||
}
|
||||
for i := 0; i < counts[0]; i++ {
|
||||
if m0, m1 := methods[0][i], methods[1][i]; m0 != m1 {
|
||||
t.Errorf("mismatching methods for %s: %s vs %s", inst, m0, m1)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestInstantiateErrors(t *testing.T) {
|
||||
tests := []struct {
|
||||
src string // by convention, T must be the type being instantiated
|
||||
|
|
|
|||
|
|
@ -112,6 +112,7 @@ func (t *Interface) String() string { return TypeString(t, nil) }
|
|||
// Implementation
|
||||
|
||||
func (t *Interface) cleanup() {
|
||||
t.typeSet() // any interface that escapes type checking must be safe for concurrent use
|
||||
t.check = nil
|
||||
t.embedPos = nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -633,11 +633,18 @@ func (n *Named) expandUnderlying() Type {
|
|||
old := iface
|
||||
iface = check.newInterface()
|
||||
iface.embeddeds = old.embeddeds
|
||||
assert(old.complete) // otherwise we are copying incomplete data
|
||||
iface.complete = old.complete
|
||||
iface.implicit = old.implicit // should be false but be conservative
|
||||
underlying = iface
|
||||
}
|
||||
iface.methods = methods
|
||||
iface.tset = nil // recompute type set with new methods
|
||||
|
||||
// If check != nil, check.newInterface will have saved the interface for later completion.
|
||||
if check == nil { // golang/go#61561: all newly created interfaces must be fully evaluated
|
||||
iface.typeSet()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -170,6 +170,7 @@ func (subst *subster) typ(typ Type) Type {
|
|||
iface := subst.check.newInterface()
|
||||
iface.embeddeds = embeddeds
|
||||
iface.implicit = t.implicit
|
||||
assert(t.complete) // otherwise we are copying incomplete data
|
||||
iface.complete = t.complete
|
||||
// If we've changed the interface type, we may need to replace its
|
||||
// receiver if the receiver type is the original interface. Receivers of
|
||||
|
|
@ -185,6 +186,11 @@ func (subst *subster) typ(typ Type) Type {
|
|||
// need to create new interface methods to hold the instantiated
|
||||
// receiver. This is handled by Named.expandUnderlying.
|
||||
iface.methods, _ = replaceRecvType(methods, t, iface)
|
||||
|
||||
// If check != nil, check.newInterface will have saved the interface for later completion.
|
||||
if subst.check == nil { // golang/go#61561: all newly created interfaces must be completed
|
||||
iface.typeSet()
|
||||
}
|
||||
return iface
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ import (
|
|||
"regexp"
|
||||
"sort"
|
||||
"strings"
|
||||
"sync"
|
||||
"testing"
|
||||
|
||||
. "go/types"
|
||||
|
|
@ -2300,6 +2301,60 @@ func TestInstantiate(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestInstantiateConcurrent(t *testing.T) {
|
||||
const src = `package p
|
||||
|
||||
type I[P any] interface {
|
||||
m(P)
|
||||
n() P
|
||||
}
|
||||
|
||||
type J = I[int]
|
||||
|
||||
type Nested[P any] *interface{b(P)}
|
||||
|
||||
type K = Nested[string]
|
||||
`
|
||||
pkg := mustTypecheck(src, nil, nil)
|
||||
|
||||
insts := []*Interface{
|
||||
pkg.Scope().Lookup("J").Type().Underlying().(*Interface),
|
||||
pkg.Scope().Lookup("K").Type().Underlying().(*Pointer).Elem().(*Interface),
|
||||
}
|
||||
|
||||
// Use the interface instances concurrently.
|
||||
for _, inst := range insts {
|
||||
var (
|
||||
counts [2]int // method counts
|
||||
methods [2][]string // method strings
|
||||
)
|
||||
var wg sync.WaitGroup
|
||||
for i := 0; i < 2; i++ {
|
||||
i := i
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
|
||||
counts[i] = inst.NumMethods()
|
||||
for mi := 0; mi < counts[i]; mi++ {
|
||||
methods[i] = append(methods[i], inst.Method(mi).String())
|
||||
}
|
||||
}()
|
||||
}
|
||||
wg.Wait()
|
||||
|
||||
if counts[0] != counts[1] {
|
||||
t.Errorf("mismatching method counts for %s: %d vs %d", inst, counts[0], counts[1])
|
||||
continue
|
||||
}
|
||||
for i := 0; i < counts[0]; i++ {
|
||||
if m0, m1 := methods[0][i], methods[1][i]; m0 != m1 {
|
||||
t.Errorf("mismatching methods for %s: %s vs %s", inst, m0, m1)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestInstantiateErrors(t *testing.T) {
|
||||
tests := []struct {
|
||||
src string // by convention, T must be the type being instantiated
|
||||
|
|
|
|||
|
|
@ -151,6 +151,7 @@ func (t *Interface) String() string { return TypeString(t, nil) }
|
|||
// Implementation
|
||||
|
||||
func (t *Interface) cleanup() {
|
||||
t.typeSet() // any interface that escapes type checking must be safe for concurrent use
|
||||
t.check = nil
|
||||
t.embedPos = nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -635,11 +635,18 @@ func (n *Named) expandUnderlying() Type {
|
|||
old := iface
|
||||
iface = check.newInterface()
|
||||
iface.embeddeds = old.embeddeds
|
||||
assert(old.complete) // otherwise we are copying incomplete data
|
||||
iface.complete = old.complete
|
||||
iface.implicit = old.implicit // should be false but be conservative
|
||||
underlying = iface
|
||||
}
|
||||
iface.methods = methods
|
||||
iface.tset = nil // recompute type set with new methods
|
||||
|
||||
// If check != nil, check.newInterface will have saved the interface for later completion.
|
||||
if check == nil { // golang/go#61561: all newly created interfaces must be fully evaluated
|
||||
iface.typeSet()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -172,6 +172,7 @@ func (subst *subster) typ(typ Type) Type {
|
|||
iface := subst.check.newInterface()
|
||||
iface.embeddeds = embeddeds
|
||||
iface.implicit = t.implicit
|
||||
assert(t.complete) // otherwise we are copying incomplete data
|
||||
iface.complete = t.complete
|
||||
// If we've changed the interface type, we may need to replace its
|
||||
// receiver if the receiver type is the original interface. Receivers of
|
||||
|
|
@ -187,6 +188,11 @@ func (subst *subster) typ(typ Type) Type {
|
|||
// need to create new interface methods to hold the instantiated
|
||||
// receiver. This is handled by Named.expandUnderlying.
|
||||
iface.methods, _ = replaceRecvType(methods, t, iface)
|
||||
|
||||
// If check != nil, check.newInterface will have saved the interface for later completion.
|
||||
if subst.check == nil { // golang/go#61561: all newly created interfaces must be completed
|
||||
iface.typeSet()
|
||||
}
|
||||
return iface
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue