go/types: don't update the underlying type of an imported type
Updating the underlying type of an imported type (even though
is was set to the same type again) leads to a race condition
if the imported package is imported by separate, concurrently
type-checked packages.
Fixes #31749.
Change-Id: Iabb8e8593eb067eb4816c1df81e545ff52d32c6c
Reviewed-on: https://go-review.googlesource.com/c/go/+/201838
Run-TryBot: Robert Griesemer <gri@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
diff --git a/src/go/types/decl.go b/src/go/types/decl.go
index a4fb2b8..83d4093 100644
--- a/src/go/types/decl.go
+++ b/src/go/types/decl.go
@@ -489,27 +489,34 @@
}
// Otherwise, follow the forward chain.
- seen := map[*Named]int{n0: 0, n: 1}
- path := []Object{n0.obj, n.obj}
+ seen := map[*Named]int{n0: 0}
+ path := []Object{n0.obj}
for {
typ = n.underlying
- n, _ = typ.(*Named)
- if n == nil {
+ n1, _ := typ.(*Named)
+ if n1 == nil {
break // end of chain
}
+ seen[n] = len(seen)
+ path = append(path, n.obj)
+ n = n1
+
if i, ok := seen[n]; ok {
// cycle
check.cycleError(path[i:])
typ = Typ[Invalid]
break
}
-
- seen[n] = len(seen)
- path = append(path, n.obj)
}
for n := range seen {
+ // We should never have to update the underlying type of an imported type;
+ // those underlying types should have been resolved during the import.
+ // Also, doing so would lead to a race condition (was issue #31749).
+ if n.obj.pkg != check.pkg {
+ panic("internal error: imported type with unresolved underlying type")
+ }
n.underlying = typ
}
diff --git a/src/go/types/issues_test.go b/src/go/types/issues_test.go
index 1d0c0cb..f59f905 100644
--- a/src/go/types/issues_test.go
+++ b/src/go/types/issues_test.go
@@ -493,3 +493,33 @@
}
return h.pkg, nil
}
+
+// TestIssue34921 verifies that we don't update an imported type's underlying
+// type when resolving an underlying type. Specifically, when determining the
+// underlying type of b.T (which is the underlying type of a.T, which is int)
+// we must not set the underlying type of a.T again since that would lead to
+// a race condition if package b is imported elsewhere, in a package that is
+// concurrently type-checked.
+func TestIssue34921(t *testing.T) {
+ defer func() {
+ if r := recover(); r != nil {
+ t.Error(r)
+ }
+ }()
+
+ var sources = []string{
+ `package a; type T int`,
+ `package b; import "a"; type T a.T`,
+ }
+
+ var pkg *Package
+ for _, src := range sources {
+ f := mustParse(t, src)
+ conf := Config{Importer: importHelper{pkg}}
+ res, err := conf.Check(f.Name.Name, fset, []*ast.File{f}, nil)
+ if err != nil {
+ t.Errorf("%q failed to typecheck: %v", src, err)
+ }
+ pkg = res // res is imported by the next package in this test
+ }
+}