]> Cypherpunks.ru repositories - gostls13.git/blobdiff - src/cmd/compile/internal/types2/check.go
go/types, types2: remove local version processing in favor of go/version
[gostls13.git] / src / cmd / compile / internal / types2 / check.go
index 381ccd8dcfae81cafba5863181925576596a7f72..058236708329df1a435c4923a329b9067f2513b6 100644 (file)
@@ -12,7 +12,6 @@ import (
        "fmt"
        "go/constant"
        "internal/godebug"
-       "internal/goversion"
        . "internal/types/errors"
 )
 
@@ -107,12 +106,11 @@ type Checker struct {
        ctxt *Context // context for de-duplicating instances
        pkg  *Package
        *Info
-       version version                     // accepted language version
-       posVers map[*syntax.PosBase]version // maps file PosBases to versions (may be nil)
-       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
-       valids  instanceLookup              // valid *Named (incl. instantiated) types per the validType check
+       version goVersion              // accepted language version
+       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
+       valids  instanceLookup         // valid *Named (incl. instantiated) types per the validType check
 
        // 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
@@ -128,6 +126,7 @@ type Checker struct {
        // (initialized by Files, valid only for the duration of check.Files;
        // maps and lists are allocated on demand)
        files         []*syntax.File              // list of package files
+       versions      map[*syntax.PosBase]string  // maps file bases to version strings (each file has an entry)
        imports       []*PkgName                  // list of imported packages
        dotImportMap  map[dotImportKey]*PkgName   // maps dot-imported objects to the package they were dot-imported through
        recvTParamMap map[*syntax.Name]*TypeParam // maps blank receiver type parameters to their type
@@ -261,6 +260,7 @@ func NewChecker(conf *Config, pkg *Package, info *Info) *Checker {
                ctxt:        conf.Context,
                pkg:         pkg,
                Info:        info,
+               version:     asGoVersion(conf.GoVersion),
                objMap:      make(map[Object]*declInfo),
                impMap:      make(map[importKey]*Package),
        }
@@ -302,36 +302,51 @@ func (check *Checker) initFiles(files []*syntax.File) {
                }
        }
 
+       // reuse Info.FileVersions if provided
+       versions := check.Info.FileVersions
+       if versions == nil {
+               versions = make(map[*syntax.PosBase]string)
+       }
+       check.versions = versions
+
+       pkgVersionOk := check.version.isValid()
+       downgradeOk := check.version.cmp(go1_21) >= 0
+
+       // determine Go version for each file
        for _, file := range check.files {
-               fbase := base(file.Pos())                            // fbase may be nil for tests
-               check.recordFileVersion(fbase, check.conf.GoVersion) // record package version (possibly zero version)
-               v, _ := parseGoVersion(file.GoVersion)
-               if v.major > 0 {
-                       if v.equal(check.version) {
-                               continue
-                       }
-                       // Go 1.21 introduced the feature of setting the go.mod
-                       // go line to an early version of Go and allowing //go:build lines
-                       // to “upgrade” the Go version in a given file.
-                       // We can do that backwards compatibly.
-                       // Go 1.21 also introduced the feature of allowing //go:build lines
-                       // to “downgrade” the Go version in a given file.
-                       // That can't be done compatibly in general, since before the
-                       // build lines were ignored and code got the module's Go version.
-                       // To work around this, downgrades are only allowed when the
-                       // module's Go version is Go 1.21 or later.
-                       // If there is no check.version, then we don't really know what Go version to apply.
-                       // Legacy tools may do this, and they historically have accepted everything.
-                       // Preserve that behavior by ignoring //go:build constraints entirely in that case.
-                       if (v.before(check.version) && check.version.before(go1_21)) || check.version.equal(go0_0) {
-                               continue
-                       }
-                       if check.posVers == nil {
-                               check.posVers = make(map[*syntax.PosBase]version)
+               // use unaltered Config.GoVersion by default
+               // (This version string may contain dot-release numbers as in go1.20.1,
+               // unlike file versions which are Go language versions only, if valid.)
+               v := check.conf.GoVersion
+               // use the file version, if applicable
+               // (file versions are either the empty string or of the form go1.dd)
+               if pkgVersionOk {
+                       fileVersion := asGoVersion(file.GoVersion)
+                       if fileVersion.isValid() {
+                               cmp := fileVersion.cmp(check.version)
+                               // Go 1.21 introduced the feature of setting the go.mod
+                               // go line to an early version of Go and allowing //go:build lines
+                               // to “upgrade” (cmp > 0) the Go version in a given file.
+                               // We can do that backwards compatibly.
+                               //
+                               // Go 1.21 also introduced the feature of allowing //go:build lines
+                               // to “downgrade” (cmp < 0) the Go version in a given file.
+                               // That can't be done compatibly in general, since before the
+                               // build lines were ignored and code got the module's Go version.
+                               // To work around this, downgrades are only allowed when the
+                               // module's Go version is Go 1.21 or later.
+                               //
+                               // If there is no valid check.version, then we don't really know what
+                               // Go version to apply.
+                               // Legacy tools may do this, and they historically have accepted everything.
+                               // Preserve that behavior by ignoring //go:build constraints entirely in that
+                               // case (!pkgVersionOk).
+                               if cmp > 0 || cmp < 0 && downgradeOk {
+                                       v = file.GoVersion
+                               }
                        }
-                       check.posVers[fbase] = v
-                       check.recordFileVersion(fbase, file.GoVersion) // overwrite package version
                }
+               versions[base(file.Pos())] = v // base(file.Pos()) may be nil for tests
        }
 }
 
@@ -362,15 +377,8 @@ func (check *Checker) checkFiles(files []*syntax.File) (err error) {
                return nil
        }
 
-       // Note: parseGoVersion and the subsequent checks should happen once,
-       //       when we create a new Checker, not for each batch of files.
-       //       We can't change it at this point because NewChecker doesn't
-       //       return an error.
-       check.version, err = parseGoVersion(check.conf.GoVersion)
-       if err != nil {
-               return err
-       }
-       if check.version.after(version{1, goversion.Version}) {
+       // Note: NewChecker doesn't return an error, so we need to check the version here.
+       if check.version.cmp(go_current) > 0 {
                return fmt.Errorf("package requires newer Go version %v", check.version)
        }
        if check.conf.FakeImportC && check.conf.go115UsesCgo {
@@ -694,9 +702,3 @@ func (check *Checker) recordScope(node syntax.Node, scope *Scope) {
                m[node] = scope
        }
 }
-
-func (check *Checker) recordFileVersion(fbase *syntax.PosBase, version string) {
-       if m := check.FileVersions; m != nil {
-               m[fbase] = version
-       }
-}