mirror of https://github.com/golang/go.git
go/types, types2: do not mutate arguments in NewChecker
CL 507975 resulted in new data races (as reported in #61212), because the pkg argument to NewChecker was mutated. Fix this by deferring the recording of the goVersion in pkg until type checking is actually initiated via a call to Checker.Files. Additionally, modify types2/check.go to bring it in sync with the changes in go/types/check.go, and generate the new version_test.go from the types2 file. Also move parsing the version into checkFiles, for simplicity. Fixes #61212 Change-Id: I15edb6c2cff3085622fe7c6a3b0dab531d27bd04 Reviewed-on: https://go-review.googlesource.com/c/go/+/508439 Run-TryBot: Robert Findley <rfindley@google.com> TryBot-Result: Gopher Robot <gobot@golang.org> Reviewed-by: Alan Donovan <adonovan@google.com>
This commit is contained in:
parent
158d11196f
commit
ac9137f8d3
|
|
@ -11,6 +11,7 @@ import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"go/constant"
|
"go/constant"
|
||||||
|
"internal/goversion"
|
||||||
. "internal/types/errors"
|
. "internal/types/errors"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -231,17 +232,17 @@ func NewChecker(conf *Config, pkg *Package, info *Info) *Checker {
|
||||||
info = new(Info)
|
info = new(Info)
|
||||||
}
|
}
|
||||||
|
|
||||||
version, err := parseGoVersion(conf.GoVersion)
|
// Note: clients may call NewChecker with the Unsafe package, which is
|
||||||
if err != nil {
|
// globally shared and must not be mutated. Therefore NewChecker must not
|
||||||
panic(fmt.Sprintf("invalid Go version %q (%v)", conf.GoVersion, err))
|
// mutate *pkg.
|
||||||
}
|
//
|
||||||
|
// (previously, pkg.goVersion was mutated here: go.dev/issue/61212)
|
||||||
|
|
||||||
return &Checker{
|
return &Checker{
|
||||||
conf: conf,
|
conf: conf,
|
||||||
ctxt: conf.Context,
|
ctxt: conf.Context,
|
||||||
pkg: pkg,
|
pkg: pkg,
|
||||||
Info: info,
|
Info: info,
|
||||||
version: version,
|
|
||||||
objMap: make(map[Object]*declInfo),
|
objMap: make(map[Object]*declInfo),
|
||||||
impMap: make(map[importKey]*Package),
|
impMap: make(map[importKey]*Package),
|
||||||
}
|
}
|
||||||
|
|
@ -333,6 +334,20 @@ func (check *Checker) Files(files []*syntax.File) error { return check.checkFile
|
||||||
var errBadCgo = errors.New("cannot use FakeImportC and go115UsesCgo together")
|
var errBadCgo = errors.New("cannot use FakeImportC and go115UsesCgo together")
|
||||||
|
|
||||||
func (check *Checker) checkFiles(files []*syntax.File) (err error) {
|
func (check *Checker) checkFiles(files []*syntax.File) (err error) {
|
||||||
|
if check.pkg == Unsafe {
|
||||||
|
// Defensive handling for Unsafe, which cannot be type checked, and must
|
||||||
|
// not be mutated. See https://go.dev/issue/61212 for an example of where
|
||||||
|
// Unsafe is passed to NewChecker.
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
check.version, err = parseGoVersion(check.conf.GoVersion)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if check.version.after(version{1, goversion.Version}) {
|
||||||
|
return fmt.Errorf("package requires newer Go version %v", check.version)
|
||||||
|
}
|
||||||
if check.conf.FakeImportC && check.conf.go115UsesCgo {
|
if check.conf.FakeImportC && check.conf.go115UsesCgo {
|
||||||
return errBadCgo
|
return errBadCgo
|
||||||
}
|
}
|
||||||
|
|
@ -377,6 +392,7 @@ func (check *Checker) checkFiles(files []*syntax.File) (err error) {
|
||||||
check.monomorph()
|
check.monomorph()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
check.pkg.goVersion = check.conf.GoVersion
|
||||||
check.pkg.complete = true
|
check.pkg.complete = true
|
||||||
|
|
||||||
// no longer needed - release memory
|
// no longer needed - release memory
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,24 @@
|
||||||
|
// Copyright 2023 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 types2
|
||||||
|
|
||||||
|
import "testing"
|
||||||
|
|
||||||
|
var parseGoVersionTests = []struct {
|
||||||
|
in string
|
||||||
|
out version
|
||||||
|
}{
|
||||||
|
{"go1.21", version{1, 21}},
|
||||||
|
{"go1.21.0", version{1, 21}},
|
||||||
|
{"go1.21rc2", version{1, 21}},
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestParseGoVersion(t *testing.T) {
|
||||||
|
for _, tt := range parseGoVersionTests {
|
||||||
|
if out, err := parseGoVersion(tt.in); out != tt.out || err != nil {
|
||||||
|
t.Errorf("parseGoVersion(%q) = %v, %v, want %v, nil", tt.in, out, err, tt.out)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -100,7 +100,6 @@ type Checker struct {
|
||||||
pkg *Package
|
pkg *Package
|
||||||
*Info
|
*Info
|
||||||
version version // accepted language version
|
version version // accepted language version
|
||||||
versionErr error // version error, delayed from NewChecker
|
|
||||||
nextID uint64 // unique Id for type parameters (first valid Id is 1)
|
nextID uint64 // unique Id for type parameters (first valid Id is 1)
|
||||||
objMap map[Object]*declInfo // maps package-level objects and (non-interface) methods to declaration info
|
objMap map[Object]*declInfo // maps package-level objects and (non-interface) methods to declaration info
|
||||||
impMap map[importKey]*Package // maps (import path, source directory) to (complete or fake) package
|
impMap map[importKey]*Package // maps (import path, source directory) to (complete or fake) package
|
||||||
|
|
@ -235,10 +234,11 @@ func NewChecker(conf *Config, fset *token.FileSet, pkg *Package, info *Info) *Ch
|
||||||
info = new(Info)
|
info = new(Info)
|
||||||
}
|
}
|
||||||
|
|
||||||
version, versionErr := parseGoVersion(conf.GoVersion)
|
// Note: clients may call NewChecker with the Unsafe package, which is
|
||||||
if pkg != nil {
|
// globally shared and must not be mutated. Therefore NewChecker must not
|
||||||
pkg.goVersion = conf.GoVersion
|
// mutate *pkg.
|
||||||
}
|
//
|
||||||
|
// (previously, pkg.goVersion was mutated here: go.dev/issue/61212)
|
||||||
|
|
||||||
return &Checker{
|
return &Checker{
|
||||||
conf: conf,
|
conf: conf,
|
||||||
|
|
@ -246,8 +246,6 @@ func NewChecker(conf *Config, fset *token.FileSet, pkg *Package, info *Info) *Ch
|
||||||
fset: fset,
|
fset: fset,
|
||||||
pkg: pkg,
|
pkg: pkg,
|
||||||
Info: info,
|
Info: info,
|
||||||
version: version,
|
|
||||||
versionErr: versionErr,
|
|
||||||
objMap: make(map[Object]*declInfo),
|
objMap: make(map[Object]*declInfo),
|
||||||
impMap: make(map[importKey]*Package),
|
impMap: make(map[importKey]*Package),
|
||||||
}
|
}
|
||||||
|
|
@ -345,8 +343,16 @@ func (check *Checker) Files(files []*ast.File) error { return check.checkFiles(f
|
||||||
var errBadCgo = errors.New("cannot use FakeImportC and go115UsesCgo together")
|
var errBadCgo = errors.New("cannot use FakeImportC and go115UsesCgo together")
|
||||||
|
|
||||||
func (check *Checker) checkFiles(files []*ast.File) (err error) {
|
func (check *Checker) checkFiles(files []*ast.File) (err error) {
|
||||||
if check.versionErr != nil {
|
if check.pkg == Unsafe {
|
||||||
return check.versionErr
|
// Defensive handling for Unsafe, which cannot be type checked, and must
|
||||||
|
// not be mutated. See https://go.dev/issue/61212 for an example of where
|
||||||
|
// Unsafe is passed to NewChecker.
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
check.version, err = parseGoVersion(check.conf.GoVersion)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
if check.version.after(version{1, goversion.Version}) {
|
if check.version.after(version{1, goversion.Version}) {
|
||||||
return fmt.Errorf("package requires newer Go version %v", check.version)
|
return fmt.Errorf("package requires newer Go version %v", check.version)
|
||||||
|
|
@ -395,6 +401,7 @@ func (check *Checker) checkFiles(files []*ast.File) (err error) {
|
||||||
check.monomorph()
|
check.monomorph()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
check.pkg.goVersion = check.conf.GoVersion
|
||||||
check.pkg.complete = true
|
check.pkg.complete = true
|
||||||
|
|
||||||
// no longer needed - release memory
|
// no longer needed - release memory
|
||||||
|
|
|
||||||
|
|
@ -141,6 +141,7 @@ var filemap = map[string]action{
|
||||||
"universe.go": fixGlobalTypVarDecl,
|
"universe.go": fixGlobalTypVarDecl,
|
||||||
"util_test.go": fixTokenPos,
|
"util_test.go": fixTokenPos,
|
||||||
"validtype.go": nil,
|
"validtype.go": nil,
|
||||||
|
"version_test.go": nil,
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO(gri) We should be able to make these rewriters more configurable/composable.
|
// TODO(gri) We should be able to make these rewriters more configurable/composable.
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,5 @@
|
||||||
|
// Code generated by "go test -run=Generate -write=all"; DO NOT EDIT.
|
||||||
|
|
||||||
// Copyright 2023 The Go Authors. All rights reserved.
|
// Copyright 2023 The Go Authors. All rights reserved.
|
||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by a BSD-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue