"regexp"
"sort"
"strings"
+ "sync"
"testing"
. "cmd/compile/internal/types2"
}
}
+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
// 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
}
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()
+ }
}
}
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
// 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
}
"regexp"
"sort"
"strings"
+ "sync"
"testing"
. "go/types"
}
}
+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
// 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
}
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()
+ }
}
}
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
// 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
}