Skip to content

Commit 07bfcd4

Browse files
committed
internal/gcimporter: another workaround for race to interface type set
When importing, it is also possible to produce an incomplete interface instance, which can be racy (as reproduced in the included test). Partially avoid this by completing interface underlyings of instances. For golang/go#61561 Change-Id: I8ca2bdc2d03fa1a46179bbd8596d7ef9baf51600 Reviewed-on: https://go-review.googlesource.com/c/tools/+/512955 gopls-CI: kokoro <[email protected]> Run-TryBot: Robert Findley <[email protected]> Reviewed-by: Alan Donovan <[email protected]> TryBot-Result: Gopher Robot <[email protected]>
1 parent db5406b commit 07bfcd4

File tree

2 files changed

+83
-0
lines changed

2 files changed

+83
-0
lines changed

internal/gcimporter/gcimporter_test.go

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import (
2525
"runtime"
2626
"sort"
2727
"strings"
28+
"sync"
2829
"testing"
2930
"time"
3031

@@ -769,6 +770,72 @@ func TestIssue51836(t *testing.T) {
769770
_ = importPkg(t, "./testdata/aa", tmpdir)
770771
}
771772

773+
func TestIssue61561(t *testing.T) {
774+
testenv.NeedsGo1Point(t, 18) // requires generics
775+
776+
const src = `package p
777+
778+
type I[P any] interface {
779+
m(P)
780+
n() P
781+
}
782+
783+
type J = I[int]
784+
785+
type StillBad[P any] *interface{b(P)}
786+
787+
type K = StillBad[string]
788+
`
789+
fset := token.NewFileSet()
790+
f, err := goparser.ParseFile(fset, "p.go", src, 0)
791+
if f == nil {
792+
// Some test cases may have parse errors, but we must always have a
793+
// file.
794+
t.Fatalf("ParseFile returned nil file. Err: %v", err)
795+
}
796+
797+
config := &types.Config{}
798+
pkg1, err := config.Check("p", fset, []*ast.File{f}, nil)
799+
if err != nil {
800+
t.Fatal(err)
801+
}
802+
803+
// Export it. (Shallowness isn't important here.)
804+
data, err := IExportShallow(fset, pkg1, nil)
805+
if err != nil {
806+
t.Fatalf("export: %v", err) // any failure to export is a bug
807+
}
808+
809+
// Re-import it.
810+
imports := make(map[string]*types.Package)
811+
pkg2, err := IImportShallow(fset, GetPackagesFromMap(imports), data, "p", nil)
812+
if err != nil {
813+
t.Fatalf("import: %v", err) // any failure of IExport+IImport is a bug.
814+
}
815+
816+
insts := []types.Type{
817+
pkg2.Scope().Lookup("J").Type(),
818+
// This test is still racy, because the incomplete interface is contained
819+
// within a nested type expression.
820+
//
821+
// Uncomment this once golang/go#61561 is fixed.
822+
// pkg2.Scope().Lookup("K").Type().Underlying().(*types.Pointer).Elem(),
823+
}
824+
825+
// Use the interface instances concurrently.
826+
for _, inst := range insts {
827+
var wg sync.WaitGroup
828+
for i := 0; i < 2; i++ {
829+
wg.Add(1)
830+
go func() {
831+
defer wg.Done()
832+
_ = types.NewMethodSet(inst)
833+
}()
834+
}
835+
wg.Wait()
836+
}
837+
}
838+
772839
func TestIssue57015(t *testing.T) {
773840
testenv.NeedsGo1Point(t, 18) // requires generics
774841

internal/gcimporter/iimport.go

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -328,6 +328,13 @@ func iimportCommon(fset *token.FileSet, getPackages GetPackagesFunc, data []byte
328328
typ.Complete()
329329
}
330330

331+
// Workaround for golang/go#61561. See the doc for instanceList for details.
332+
for _, typ := range p.instanceList {
333+
if iface, _ := typ.Underlying().(*types.Interface); iface != nil {
334+
iface.Complete()
335+
}
336+
}
337+
331338
return pkgs, nil
332339
}
333340

@@ -358,6 +365,12 @@ type iimporter struct {
358365
fake fakeFileSet
359366
interfaceList []*types.Interface
360367

368+
// Workaround for the go/types bug golang/go#61561: instances produced during
369+
// instantiation may contain incomplete interfaces. Here we only complete the
370+
// underlying type of the instance, which is the most common case but doesn't
371+
// handle parameterized interface literals defined deeper in the type.
372+
instanceList []types.Type // instances for later completion (see golang/go#61561)
373+
361374
// Arguments for calls to SetConstraint that are deferred due to recursive types
362375
later []setConstraintArgs
363376

@@ -954,6 +967,9 @@ func (r *importReader) doType(base *types.Named) (res types.Type) {
954967
// we must always use the methods of the base (orig) type.
955968
// TODO provide a non-nil *Environment
956969
t, _ := typeparams.Instantiate(nil, baseType, targs, false)
970+
971+
// Workaround for golang/go#61561. See the doc for instanceList for details.
972+
r.p.instanceList = append(r.p.instanceList, t)
957973
return t
958974

959975
case unionType:

0 commit comments

Comments
 (0)