go/types, types2: add Environment to Config

Port to types2 and adjust compiler accordingly.

Change-Id: I2e72b151ef834977dca64cb2e62cedcac4e46062
Reviewed-on: https://go-review.googlesource.com/c/go/+/348578
Trust: Robert Griesemer <gri@golang.org>
Run-TryBot: Robert Griesemer <gri@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
Reviewed-by: Robert Findley <rfindley@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
This commit is contained in:
Robert Griesemer 2021-09-08 16:03:57 -07:00
parent f5f8a911d8
commit a1f6208e56
16 changed files with 43 additions and 28 deletions

View file

@ -43,12 +43,12 @@ var haveLegacyImports = false
// for an imported package by overloading writeNewExportFunc, then
// that payload will be mapped into memory and passed to
// newReadImportFunc.
var newReadImportFunc = func(data string, pkg1 *types.Pkg, check *types2.Checker, packages map[string]*types2.Package) (pkg2 *types2.Package, err error) {
var newReadImportFunc = func(data string, pkg1 *types.Pkg, env *types2.Environment, packages map[string]*types2.Package) (pkg2 *types2.Package, err error) {
panic("unexpected new export data payload")
}
type gcimports struct {
check *types2.Checker
env *types2.Environment
packages map[string]*types2.Package
}
@ -61,7 +61,7 @@ func (m *gcimports) ImportFrom(path, srcDir string, mode types2.ImportMode) (*ty
panic("mode must be 0")
}
_, pkg, err := readImportFile(path, typecheck.Target, m.check, m.packages)
_, pkg, err := readImportFile(path, typecheck.Target, m.env, m.packages)
return pkg, err
}
@ -224,7 +224,7 @@ func parseImportPath(pathLit *syntax.BasicLit) (string, error) {
// readImportFile reads the import file for the given package path and
// returns its types.Pkg representation. If packages is non-nil, the
// types2.Package representation is also returned.
func readImportFile(path string, target *ir.Package, check *types2.Checker, packages map[string]*types2.Package) (pkg1 *types.Pkg, pkg2 *types2.Package, err error) {
func readImportFile(path string, target *ir.Package, env *types2.Environment, packages map[string]*types2.Package) (pkg1 *types.Pkg, pkg2 *types2.Package, err error) {
path, err = resolveImportPath(path)
if err != nil {
return
@ -279,7 +279,7 @@ func readImportFile(path string, target *ir.Package, check *types2.Checker, pack
return
}
pkg2, err = newReadImportFunc(data, pkg1, check, packages)
pkg2, err = newReadImportFunc(data, pkg1, env, packages)
} else {
// We only have old data. Oh well, fall back to the legacy importers.
haveLegacyImports = true

View file

@ -34,10 +34,13 @@ func checkFiles(noders []*noder) (posMap, *types2.Package, *types2.Info) {
}
// typechecking
env := types2.NewEnvironment()
importer := gcimports{
env: env,
packages: map[string]*types2.Package{"unsafe": types2.Unsafe},
}
conf := types2.Config{
Environment: env,
GoVersion: base.Flag.Lang,
IgnoreLabels: true, // parser already checked via syntax.CheckBranches mode
CompilerErrorMessages: true, // use error strings matching existing compiler errors
@ -60,9 +63,7 @@ func checkFiles(noders []*noder) (posMap, *types2.Package, *types2.Info) {
// expand as needed
}
pkg := types2.NewPackage(base.Ctxt.Pkgpath, "")
importer.check = types2.NewChecker(&conf, pkg, info)
err := importer.check.Files(files)
pkg, err := conf.Check(base.Ctxt.Pkgpath, files, info)
base.ExitIfErrors()
if err != nil {

View file

@ -18,7 +18,7 @@ import (
type pkgReader2 struct {
pkgDecoder
check *types2.Checker
env *types2.Environment
imports map[string]*types2.Package
posBases []*syntax.PosBase
@ -26,11 +26,11 @@ type pkgReader2 struct {
typs []types2.Type
}
func readPackage2(check *types2.Checker, imports map[string]*types2.Package, input pkgDecoder) *types2.Package {
func readPackage2(env *types2.Environment, imports map[string]*types2.Package, input pkgDecoder) *types2.Package {
pr := pkgReader2{
pkgDecoder: input,
check: check,
env: env,
imports: imports,
posBases: make([]*syntax.PosBase, input.numElems(relocPosBase)),
@ -233,9 +233,7 @@ func (r *reader2) doTyp() (res types2.Type) {
obj, targs := r.obj()
name := obj.(*types2.TypeName)
if len(targs) != 0 {
// TODO(mdempsky) should use a single shared environment here
// (before, this used a shared checker)
t, _ := types2.Instantiate(types2.NewEnvironment(), name.Type(), targs, false)
t, _ := types2.Instantiate(r.p.env, name.Type(), targs, false)
return t
}
return name.Type()

View file

@ -78,12 +78,12 @@ func unified(noders []*noder) {
base.Errorf("cannot use -G and -d=quirksmode together")
}
newReadImportFunc = func(data string, pkg1 *types.Pkg, check *types2.Checker, packages map[string]*types2.Package) (pkg2 *types2.Package, err error) {
newReadImportFunc = func(data string, pkg1 *types.Pkg, env *types2.Environment, packages map[string]*types2.Package) (pkg2 *types2.Package, err error) {
pr := newPkgDecoder(pkg1.Path, data)
// Read package descriptors for both types2 and compiler backend.
readPackage(newPkgReader(pr), pkg1)
pkg2 = readPackage2(check, packages, pr)
pkg2 = readPackage2(env, packages, pr)
return
}

View file

@ -108,6 +108,11 @@ type ImporterFrom interface {
// A Config specifies the configuration for type checking.
// The zero value for Config is a ready-to-use default configuration.
type Config struct {
// Environment is the environment used for resolving global
// identifiers. If nil, the type checker will initialize this
// field with a newly created environment.
Environment *Environment
// GoVersion describes the accepted Go language version. The string
// must follow the format "go%d.%d" (e.g. "go1.12") or ist must be
// empty; an empty string indicates the latest language version.

View file

@ -86,7 +86,6 @@ type Checker struct {
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
impMap map[importKey]*Package // maps (import path, source directory) to (complete or fake) package
env *Environment // for deduplicating identical instances
// pkgPathMap maps package names to the set of distinct import paths we've
// seen for that name, anywhere in the import graph. It is used for
@ -171,6 +170,11 @@ func NewChecker(conf *Config, pkg *Package, info *Info) *Checker {
conf = new(Config)
}
// make sure we have an environment
if conf.Environment == nil {
conf.Environment = NewEnvironment()
}
// make sure we have an info struct
if info == nil {
info = new(Info)
@ -188,7 +192,6 @@ func NewChecker(conf *Config, pkg *Package, info *Info) *Checker {
version: version,
objMap: make(map[Object]*declInfo),
impMap: make(map[importKey]*Package),
env: NewEnvironment(),
}
}

View file

@ -317,7 +317,7 @@ func (check *Checker) validType(typ Type, path []Object) typeInfo {
}
case *Named:
t.expand(check.env)
t.expand(check.conf.Environment)
// don't touch the type if it is from a different package or the Universe scope
// (doing so would lead to a race condition - was issue #35049)

View file

@ -71,7 +71,7 @@ func (check *Checker) instantiate(pos syntax.Pos, typ Type, targs []Type, posLis
}()
}
inst := check.instance(pos, typ, targs, check.env)
inst := check.instance(pos, typ, targs, check.conf.Environment)
assert(len(posList) <= len(targs))
check.later(func() {

View file

@ -254,7 +254,7 @@ func (n *Named) expand(env *Environment) *Named {
// in subst) feels overly complicated. Can we simplify?
if env == nil {
if n.check != nil {
env = n.check.env
env = n.check.conf.Environment
} else {
// If we're instantiating lazily, we might be outside the scope of a
// type-checking pass. In that case we won't have a pre-existing

View file

@ -59,7 +59,7 @@ func (check *Checker) subst(pos syntax.Pos, typ Type, smap substMap, env *Enviro
if check != nil {
subst.check = check
if env == nil {
env = check.env
env = check.conf.Environment
}
}
if env == nil {

View file

@ -115,6 +115,11 @@ type ImporterFrom interface {
// A Config specifies the configuration for type checking.
// The zero value for Config is a ready-to-use default configuration.
type Config struct {
// Environment is the environment used for resolving global
// identifiers. If nil, the type checker will initialize this
// field with a newly created environment.
Environment *Environment
// GoVersion describes the accepted Go language version. The string
// must follow the format "go%d.%d" (e.g. "go1.12") or it must be
// empty; an empty string indicates the latest language version.

View file

@ -89,7 +89,6 @@ type Checker struct {
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
impMap map[importKey]*Package // maps (import path, source directory) to (complete or fake) package
env *Environment // for deduplicating identical instances
// pkgPathMap maps package names to the set of distinct import paths we've
// seen for that name, anywhere in the import graph. It is used for
@ -174,6 +173,11 @@ func NewChecker(conf *Config, fset *token.FileSet, pkg *Package, info *Info) *Ch
conf = new(Config)
}
// make sure we have an environment
if conf.Environment == nil {
conf.Environment = NewEnvironment()
}
// make sure we have an info struct
if info == nil {
info = new(Info)
@ -192,7 +196,6 @@ func NewChecker(conf *Config, fset *token.FileSet, pkg *Package, info *Info) *Ch
version: version,
objMap: make(map[Object]*declInfo),
impMap: make(map[importKey]*Package),
env: NewEnvironment(),
}
}

View file

@ -316,7 +316,7 @@ func (check *Checker) validType(typ Type, path []Object) typeInfo {
}
case *Named:
t.expand(check.env)
t.expand(check.conf.Environment)
// don't touch the type if it is from a different package or the Universe scope
// (doing so would lead to a race condition - was issue #35049)
if t.obj.pkg != check.pkg {

View file

@ -71,7 +71,7 @@ func (check *Checker) instantiate(pos token.Pos, typ Type, targs []Type, posList
}()
}
inst := check.instance(pos, typ, targs, check.env)
inst := check.instance(pos, typ, targs, check.conf.Environment)
assert(len(posList) <= len(targs))
check.later(func() {

View file

@ -254,7 +254,7 @@ func (n *Named) expand(env *Environment) *Named {
// in subst) feels overly complicated. Can we simplify?
if env == nil {
if n.check != nil {
env = n.check.env
env = n.check.conf.Environment
} else {
// If we're instantiating lazily, we might be outside the scope of a
// type-checking pass. In that case we won't have a pre-existing

View file

@ -62,7 +62,7 @@ func (check *Checker) subst(pos token.Pos, typ Type, smap substMap, env *Environ
if check != nil {
subst.check = check
if env == nil {
env = check.env
env = check.conf.Environment
}
}
if env == nil {