import (
"bytes"
+ "cmd/compile/internal/syntax"
"cmd/compile/internal/types2"
"fmt"
"go/build"
"path/filepath"
"runtime"
"strings"
+ "sync"
"testing"
"time"
)
t.Fatalf("%s not found", name)
return nil
}
+
+// importMap implements the types2.Importer interface.
+type importMap map[string]*types2.Package
+
+func (m importMap) Import(path string) (*types2.Package, error) { return m[path], nil }
+
+func TestIssue69912(t *testing.T) {
+ testenv.MustHaveGoBuild(t)
+
+ // This package only handles gc export data.
+ if runtime.Compiler != "gc" {
+ t.Skipf("gc-built packages not available (compiler = %s)", runtime.Compiler)
+ }
+
+ tmpdir := t.TempDir()
+ testoutdir := filepath.Join(tmpdir, "testdata")
+ if err := os.Mkdir(testoutdir, 0700); err != nil {
+ t.Fatalf("making output dir: %v", err)
+ }
+
+ compile(t, "testdata", "issue69912.go", testoutdir, nil)
+
+ issue69912, err := Import(make(map[string]*types2.Package), "./testdata/issue69912", tmpdir, nil)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ check := func(pkgname, src string, imports importMap) (*types2.Package, error) {
+ f, err := syntax.Parse(syntax.NewFileBase(pkgname), strings.NewReader(src), nil, nil, 0)
+ if err != nil {
+ return nil, err
+ }
+ config := &types2.Config{
+ Importer: imports,
+ }
+ return config.Check(pkgname, []*syntax.File{f}, nil)
+ }
+
+ // Use the resulting package concurrently, via dot-imports, to exercise the
+ // race of issue #69912.
+ const pSrc = `package p
+
+import . "issue69912"
+
+type S struct {
+ f T
+}
+`
+ importer := importMap{
+ "issue69912": issue69912,
+ }
+ var wg sync.WaitGroup
+ for range 10 {
+ wg.Add(1)
+ go func() {
+ defer wg.Done()
+ if _, err := check("p", pSrc, importer); err != nil {
+ t.Errorf("Check failed: %v", err)
+ }
+ }()
+ }
+ wg.Wait()
+}
--- /dev/null
+// Copyright 2024 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.
+
+package issue69912
+
+// Define an arbitrary type name, which will be used to demonstrate
+// the race of issue #69912.
+type T int
// Type-check the object.
// Only call Checker.objDecl if the object doesn't have a type yet
// (in which case we must actually determine it) or the object is a
- // TypeName and we also want a type (in which case we might detect
- // a cycle which needs to be reported). Otherwise we can skip the
- // call and avoid a possible cycle error in favor of the more
- // informative "not a type/value" error that this function's caller
- // will issue (see go.dev/issue/25790).
+ // TypeName from the current package and we also want a type (in which case
+ // we might detect a cycle which needs to be reported). Otherwise we can skip
+ // the call and avoid a possible cycle error in favor of the more informative
+ // "not a type/value" error that this function's caller will issue (see
+ // go.dev/issue/25790).
+ //
+ // Note that it is important to avoid calling objDecl on objects from other
+ // packages, to avoid races: see issue #69912.
typ := obj.Type()
- if typ == nil || gotType && wantType {
+ if typ == nil || (gotType && wantType && obj.Pkg() == check.pkg) {
check.objDecl(obj, def)
typ = obj.Type() // type must have been assigned by Checker.objDecl
}
"path/filepath"
"runtime"
"strings"
+ "sync"
"testing"
"time"
t.Fatalf("%s not found", name)
return nil
}
+
+// importMap implements the types.Importer interface.
+type importMap map[string]*types.Package
+
+func (m importMap) Import(path string) (*types.Package, error) { return m[path], nil }
+
+func TestIssue69912(t *testing.T) {
+ testenv.MustHaveGoBuild(t)
+
+ // This package only handles gc export data.
+ if runtime.Compiler != "gc" {
+ t.Skipf("gc-built packages not available (compiler = %s)", runtime.Compiler)
+ }
+
+ tmpdir := t.TempDir()
+ testoutdir := filepath.Join(tmpdir, "testdata")
+ if err := os.Mkdir(testoutdir, 0700); err != nil {
+ t.Fatalf("making output dir: %v", err)
+ }
+
+ compile(t, "testdata", "issue69912.go", testoutdir, nil)
+
+ fset := token.NewFileSet()
+
+ issue69912, err := Import(fset, make(map[string]*types.Package), "./testdata/issue69912", tmpdir, nil)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ check := func(pkgname, src string, imports importMap) (*types.Package, error) {
+ f, err := parser.ParseFile(fset, "a.go", src, 0)
+ if err != nil {
+ return nil, err
+ }
+ config := &types.Config{
+ Importer: imports,
+ }
+ return config.Check(pkgname, fset, []*ast.File{f}, nil)
+ }
+
+ // Use the resulting package concurrently, via dot-imports, to exercise the
+ // race of issue #69912.
+ const pSrc = `package p
+
+import . "issue69912"
+
+type S struct {
+ f T
+}
+`
+ importer := importMap{
+ "issue69912": issue69912,
+ }
+ var wg sync.WaitGroup
+ for range 10 {
+ wg.Add(1)
+ go func() {
+ defer wg.Done()
+ if _, err := check("p", pSrc, importer); err != nil {
+ t.Errorf("Check failed: %v", err)
+ }
+ }()
+ }
+ wg.Wait()
+}
--- /dev/null
+// Copyright 2024 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.
+
+package issue69912
+
+// Define an arbitrary type name, which will be used to demonstrate
+// the race of issue #69912.
+type T int
// Type-check the object.
// Only call Checker.objDecl if the object doesn't have a type yet
// (in which case we must actually determine it) or the object is a
- // TypeName and we also want a type (in which case we might detect
- // a cycle which needs to be reported). Otherwise we can skip the
- // call and avoid a possible cycle error in favor of the more
- // informative "not a type/value" error that this function's caller
- // will issue (see go.dev/issue/25790).
+ // TypeName from the current package and we also want a type (in which case
+ // we might detect a cycle which needs to be reported). Otherwise we can skip
+ // the call and avoid a possible cycle error in favor of the more informative
+ // "not a type/value" error that this function's caller will issue (see
+ // go.dev/issue/25790).
+ //
+ // Note that it is important to avoid calling objDecl on objects from other
+ // packages, to avoid races: see issue #69912.
typ := obj.Type()
- if typ == nil || gotType && wantType {
+ if typ == nil || (gotType && wantType && obj.Pkg() == check.pkg) {
check.objDecl(obj, def)
typ = obj.Type() // type must have been assigned by Checker.objDecl
}