]> Cypherpunks repositories - gostls13.git/commitdiff
go/types, types2: don't do version checks for embedded types of imported interfaces
authorRobert Griesemer <gri@golang.org>
Tue, 12 Mar 2024 16:31:06 +0000 (09:31 -0700)
committerGopher Robot <gobot@golang.org>
Thu, 14 Mar 2024 23:12:40 +0000 (23:12 +0000)
This is a cherry-pick of CL 571075 combined with adjustments for 1.23:

Imported interfaces don't have position information for embedded types.
When computing the type set of such interfaces, doing a version check
may fail because it will rely on the Go version of the current package.

We must not do a version check for features of types from imported
packages - those types have already been typechecked and are "correct".
The version check code does look at packages to avoid such incorrect
version checks, but we don't have the package information available
in an interface type (divorced from its object).

Instead, rely on the fact that imported interfaces don't have position
information for embedded types: if the position is unknown, don't do a
version check.

In Checker.allowVersion, still allow for unknown positions and resort
to the module version in that case (source code may be generated by
tools and not contain position information). Also, remove the *Package
argument as it was always check.pkg except in one case, and that case
may in fact be incorrect; treat that case separately for now.

Fixes #66064.

Change-Id: I773d57e5410c3d4a911ab3e018b3233c2972b3c9
Reviewed-on: https://go-review.googlesource.com/c/go/+/571075
Reviewed-by: Robert Findley <rfindley@google.com>
Auto-Submit: Robert Griesemer <gri@google.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Robert Griesemer <gri@google.com>
Reviewed-on: https://go-review.googlesource.com/c/go/+/571137

20 files changed:
src/cmd/compile/internal/types2/call.go
src/cmd/compile/internal/types2/check.go
src/cmd/compile/internal/types2/conversions.go
src/cmd/compile/internal/types2/expr.go
src/cmd/compile/internal/types2/infer.go
src/cmd/compile/internal/types2/instantiate.go
src/cmd/compile/internal/types2/stmt.go
src/cmd/compile/internal/types2/typeset.go
src/cmd/compile/internal/types2/version.go
src/go/types/call.go
src/go/types/check.go
src/go/types/conversions.go
src/go/types/expr.go
src/go/types/generate_test.go
src/go/types/infer.go
src/go/types/instantiate.go
src/go/types/stmt.go
src/go/types/typeset.go
src/go/types/version.go
src/internal/types/testdata/fixedbugs/issue66064.go [new file with mode: 0644]

index fe5b71d965c488e9daf2829e9f7c355260851d94..0a5de6667e18426efeec30b3cc12810a473afc5a 100644 (file)
@@ -87,7 +87,7 @@ func (check *Checker) funcInst(T *target, pos syntax.Pos, x *operand, inst *synt
                var params []*Var
                var reverse bool
                if T != nil && sig.tparams != nil {
-                       if !versionErr && !check.allowVersion(check.pkg, instErrPos, go1_21) {
+                       if !versionErr && !check.allowVersion(instErrPos, go1_21) {
                                if inst != nil {
                                        check.versionErrorf(instErrPos, go1_21, "partially instantiated function in assignment")
                                } else {
@@ -371,7 +371,7 @@ func (check *Checker) genericExprList(elist []syntax.Expr) (resList []*operand,
        // nor permitted. Checker.funcInst must infer missing type arguments in that case.
        infer := true // for -lang < go1.21
        n := len(elist)
-       if n > 0 && check.allowVersion(check.pkg, elist[0], go1_21) {
+       if n > 0 && check.allowVersion(elist[0], go1_21) {
                infer = false
        }
 
@@ -541,7 +541,7 @@ func (check *Checker) arguments(call *syntax.CallExpr, sig *Signature, targs []T
        // collect type parameters of callee
        n := sig.TypeParams().Len()
        if n > 0 {
-               if !check.allowVersion(check.pkg, call.Pos(), go1_18) {
+               if !check.allowVersion(call.Pos(), go1_18) {
                        if iexpr, _ := call.Fun.(*syntax.IndexExpr); iexpr != nil {
                                check.versionErrorf(iexpr, go1_18, "function instantiation")
                        } else {
index f36dff3d4a7dd6b0ef0a664676ffcf7f4dfe25ae..bf613fd28b277c18154b5a6da6ed31f21c1b49e2 100644 (file)
@@ -127,7 +127,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)
+       versions      map[*syntax.PosBase]string  // maps files to version strings (each file has an entry); shared with Info.FileVersions if present
        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
index d9ed0b3c1b6a35cccb4923c3c50a8b8b38e2e1c9..b8d8f6e1507fe77e45d5b012ddb0d1881b9339d6 100644 (file)
@@ -197,7 +197,7 @@ func (x *operand) convertibleTo(check *Checker, T Type, cause *string) bool {
                switch a := Tu.(type) {
                case *Array:
                        if Identical(s.Elem(), a.Elem()) {
-                               if check == nil || check.allowVersion(check.pkg, x, go1_20) {
+                               if check == nil || check.allowVersion(x, go1_20) {
                                        return true
                                }
                                // check != nil
@@ -210,7 +210,7 @@ func (x *operand) convertibleTo(check *Checker, T Type, cause *string) bool {
                case *Pointer:
                        if a, _ := under(a.Elem()).(*Array); a != nil {
                                if Identical(s.Elem(), a.Elem()) {
-                                       if check == nil || check.allowVersion(check.pkg, x, go1_17) {
+                                       if check == nil || check.allowVersion(x, go1_17) {
                                                return true
                                        }
                                        // check != nil
index 2f9d544a4b2fcd8b01862f1a6afc644243506473..b2ff262762acd17a5b630ba16e635fe774e221e9 100644 (file)
@@ -1036,7 +1036,7 @@ func (check *Checker) nonGeneric(T *target, x *operand) {
 // literal is not compatible with the current language version.
 func (check *Checker) langCompat(lit *syntax.BasicLit) {
        s := lit.Value
-       if len(s) <= 2 || check.allowVersion(check.pkg, lit, go1_13) {
+       if len(s) <= 2 || check.allowVersion(lit, go1_13) {
                return
        }
        // len(s) > 2
index b3f0f47c22dd87ee6015e5fcc96d462cfcad5b44..1cdc4e79a2a0c7288b03d14605f2cafd07451b04 100644 (file)
@@ -110,7 +110,7 @@ func (check *Checker) infer(pos syntax.Pos, tparams []*TypeParam, targs []Type,
        // Unify parameter and argument types for generic parameters with typed arguments
        // and collect the indices of generic parameters with untyped arguments.
        // Terminology: generic parameter = function parameter with a type-parameterized type
-       u := newUnifier(tparams, targs, check.allowVersion(check.pkg, pos, go1_21))
+       u := newUnifier(tparams, targs, check.allowVersion(pos, go1_21))
 
        errorf := func(tpar, targ Type, arg *operand) {
                // provide a better error message if we can
index e33d4b41c2716b238fc224f3092c4a3520dcb287..a25cb141eca0d18ce479e2ce06af3925b6d9a217 100644 (file)
@@ -271,7 +271,7 @@ func (check *Checker) implements(pos syntax.Pos, V, T Type, constraint bool, cau
                // so that ordinary, non-type parameter interfaces implement comparable.
                if constraint && comparable(V, true /* spec comparability */, nil, nil) {
                        // V is comparable if we are at Go 1.20 or higher.
-                       if check == nil || check.allowVersion(check.pkg, atPos(pos), go1_20) { // atPos needed so that go/types generate passes
+                       if check == nil || check.allowVersion(atPos(pos), go1_20) { // atPos needed so that go/types generate passes
                                return true
                        }
                        if cause != nil {
index e79e4cd586b6a03d4efe7a1b9922c52d1884703c..1a6ec4ffc1c88ab108edf7589a4caab1b59a62fd 100644 (file)
@@ -858,7 +858,7 @@ func (check *Checker) rangeStmt(inner stmtContext, s *syntax.ForStmt, rclause *s
        if x.mode != invalid {
                // Ranging over a type parameter is permitted if it has a core type.
                k, v, cause, isFunc, ok := rangeKeyVal(x.typ, func(v goVersion) bool {
-                       return check.allowVersion(check.pkg, x.expr, v)
+                       return check.allowVersion(x.expr, v)
                })
                switch {
                case !ok && cause != "":
index 778809e42e2c778249e6ce46ee1222b6ba842bf5..a7dddc308d53a33f9f61052996218585d078a80f 100644 (file)
@@ -238,7 +238,9 @@ func computeInterfaceTypeSet(check *Checker, pos syntax.Pos, ityp *Interface) *_
                        // error message.
                        if check != nil {
                                check.later(func() {
-                                       if !check.allowVersion(m.pkg, atPos(pos), go1_14) || !Identical(m.typ, other.Type()) {
+                                       // ignore version check if method is from a different package
+                                       // TODO(gri) this seems incorrect - see go.dev/issue/66285
+                                       if check.pkg == m.pkg && pos.IsKnown() && !check.allowVersion(atPos(pos), go1_14) || !Identical(m.typ, other.Type()) {
                                                err := check.newError(DuplicateDecl)
                                                err.addf(atPos(pos), "duplicate method %s", m.name)
                                                err.addf(atPos(mpos[other.(*Func)]), "other declaration of %s", m.name)
@@ -257,9 +259,8 @@ func computeInterfaceTypeSet(check *Checker, pos syntax.Pos, ityp *Interface) *_
        allTerms := allTermlist
        allComparable := false
        for i, typ := range ityp.embeddeds {
-               // The embedding position is nil for imported interfaces
-               // and also for interface copies after substitution (but
-               // in that case we don't need to report errors again).
+               // The embedding position is nil for imported interfaces.
+               // We don't need to do version checks in those cases.
                var pos syntax.Pos // embedding position
                if ityp.embedPos != nil {
                        pos = (*ityp.embedPos)[i]
@@ -272,7 +273,7 @@ func computeInterfaceTypeSet(check *Checker, pos syntax.Pos, ityp *Interface) *_
                        assert(!isTypeParam(typ))
                        tset := computeInterfaceTypeSet(check, pos, u)
                        // If typ is local, an error was already reported where typ is specified/defined.
-                       if check != nil && check.isImportedConstraint(typ) && !check.verifyVersionf(atPos(pos), go1_18, "embedding constraint interface %s", typ) {
+                       if pos.IsKnown() && check != nil && check.isImportedConstraint(typ) && !check.verifyVersionf(atPos(pos), go1_18, "embedding constraint interface %s", typ) {
                                continue
                        }
                        comparable = tset.comparable
@@ -281,7 +282,7 @@ func computeInterfaceTypeSet(check *Checker, pos syntax.Pos, ityp *Interface) *_
                        }
                        terms = tset.terms
                case *Union:
-                       if check != nil && !check.verifyVersionf(atPos(pos), go1_18, "embedding interface element %s", u) {
+                       if pos.IsKnown() && check != nil && !check.verifyVersionf(atPos(pos), go1_18, "embedding interface element %s", u) {
                                continue
                        }
                        tset := computeUnionTypeSet(check, unionSets, pos, u)
@@ -295,7 +296,7 @@ func computeInterfaceTypeSet(check *Checker, pos syntax.Pos, ityp *Interface) *_
                        if !isValid(u) {
                                continue
                        }
-                       if check != nil && !check.verifyVersionf(atPos(pos), go1_18, "embedding non-interface type %s", typ) {
+                       if pos.IsKnown() && check != nil && !check.verifyVersionf(atPos(pos), go1_18, "embedding non-interface type %s", typ) {
                                continue
                        }
                        terms = termlist{{false, typ}}
index bcd47fbb7e44be63ff04011538675453def5228c..241b10d3e637289d59953ec1a4fdac641d419d8d 100644 (file)
@@ -49,31 +49,28 @@ var (
        go_current = asGoVersion(fmt.Sprintf("go1.%d", goversion.Version))
 )
 
-// allowVersion reports whether the given package is allowed to use version v.
-func (check *Checker) allowVersion(pkg *Package, at poser, v goVersion) bool {
-       // We assume that imported packages have all been checked,
-       // so we only have to check for the local package.
-       if pkg != check.pkg {
-               return true
-       }
-
-       // If no explicit file version is specified,
-       // fileVersion corresponds to the module version.
-       var fileVersion goVersion
+// allowVersion reports whether the current package at the given position
+// is allowed to use version v. If the position is unknown, the specified
+// module version (Config.GoVersion) is used. If that version is invalid,
+// allowVersion returns true.
+func (check *Checker) allowVersion(at poser, v goVersion) bool {
+       fileVersion := check.conf.GoVersion
        if pos := at.Pos(); pos.IsKnown() {
-               // We need version.Lang below because file versions
-               // can be (unaltered) Config.GoVersion strings that
-               // may contain dot-release information.
-               fileVersion = asGoVersion(check.versions[base(pos)])
+               fileVersion = check.versions[base(pos)]
        }
-       return !fileVersion.isValid() || fileVersion.cmp(v) >= 0
+
+       // We need asGoVersion (which calls version.Lang) below
+       // because fileVersion may be the (unaltered) Config.GoVersion
+       // string which may contain dot-release information.
+       version := asGoVersion(fileVersion)
+
+       return !version.isValid() || version.cmp(v) >= 0
 }
 
 // verifyVersionf is like allowVersion but also accepts a format string and arguments
-// which are used to report a version error if allowVersion returns false. It uses the
-// current package.
+// which are used to report a version error if allowVersion returns false.
 func (check *Checker) verifyVersionf(at poser, v goVersion, format string, args ...interface{}) bool {
-       if !check.allowVersion(check.pkg, at, v) {
+       if !check.allowVersion(at, v) {
                check.versionErrorf(at, v, format, args...)
                return false
        }
index 42ef5b6f861d9820a85f7e11403e1b392f6a8dca..b4f155a501169574ff9e87dd2e2ea7992fd2adc2 100644 (file)
@@ -89,7 +89,7 @@ func (check *Checker) funcInst(T *target, pos token.Pos, x *operand, ix *typepar
                var params []*Var
                var reverse bool
                if T != nil && sig.tparams != nil {
-                       if !versionErr && !check.allowVersion(check.pkg, instErrPos, go1_21) {
+                       if !versionErr && !check.allowVersion(instErrPos, go1_21) {
                                if ix != nil {
                                        check.versionErrorf(instErrPos, go1_21, "partially instantiated function in assignment")
                                } else {
@@ -374,7 +374,7 @@ func (check *Checker) genericExprList(elist []ast.Expr) (resList []*operand, tar
        // nor permitted. Checker.funcInst must infer missing type arguments in that case.
        infer := true // for -lang < go1.21
        n := len(elist)
-       if n > 0 && check.allowVersion(check.pkg, elist[0], go1_21) {
+       if n > 0 && check.allowVersion(elist[0], go1_21) {
                infer = false
        }
 
@@ -542,7 +542,7 @@ func (check *Checker) arguments(call *ast.CallExpr, sig *Signature, targs []Type
        // collect type parameters of callee
        n := sig.TypeParams().Len()
        if n > 0 {
-               if !check.allowVersion(check.pkg, call, go1_18) {
+               if !check.allowVersion(call, go1_18) {
                        switch call.Fun.(type) {
                        case *ast.IndexExpr, *ast.IndexListExpr:
                                ix := typeparams.UnpackIndexExpr(call.Fun)
index d9c290066b86070a820a277d0a979932e767bc9a..6da42e63f67c2d95a8975d7369bcb4589ea1252e 100644 (file)
@@ -131,7 +131,7 @@ type Checker struct {
        // (initialized by Files, valid only for the duration of check.Files;
        // maps and lists are allocated on demand)
        files         []*ast.File               // package files
-       versions      map[*ast.File]string      // maps files to version strings (each file has an entry)
+       versions      map[*ast.File]string      // maps files to version strings (each file has an entry); shared with Info.FileVersions if present
        imports       []*PkgName                // list of imported packages
        dotImportMap  map[dotImportKey]*PkgName // maps dot-imported objects to the package they were dot-imported through
        recvTParamMap map[*ast.Ident]*TypeParam // maps blank receiver type parameters to their type
index f5834cd86da27d5c9faea80a85cfada1aa80fedb..16585671149dff952aeb8adb5cf16e3eba0ef885 100644 (file)
@@ -199,7 +199,7 @@ func (x *operand) convertibleTo(check *Checker, T Type, cause *string) bool {
                switch a := Tu.(type) {
                case *Array:
                        if Identical(s.Elem(), a.Elem()) {
-                               if check == nil || check.allowVersion(check.pkg, x, go1_20) {
+                               if check == nil || check.allowVersion(x, go1_20) {
                                        return true
                                }
                                // check != nil
@@ -212,7 +212,7 @@ func (x *operand) convertibleTo(check *Checker, T Type, cause *string) bool {
                case *Pointer:
                        if a, _ := under(a.Elem()).(*Array); a != nil {
                                if Identical(s.Elem(), a.Elem()) {
-                                       if check == nil || check.allowVersion(check.pkg, x, go1_17) {
+                                       if check == nil || check.allowVersion(x, go1_17) {
                                                return true
                                        }
                                        // check != nil
index 22904cb1b520c689bcbc425a10f61c33bbed2fe1..ef61e2cc40dec6b60d9f1948a893d151963a69d3 100644 (file)
@@ -1021,7 +1021,7 @@ func (check *Checker) nonGeneric(T *target, x *operand) {
 // literal is not compatible with the current language version.
 func (check *Checker) langCompat(lit *ast.BasicLit) {
        s := lit.Value
-       if len(s) <= 2 || check.allowVersion(check.pkg, lit, go1_13) {
+       if len(s) <= 2 || check.allowVersion(lit, go1_13) {
                return
        }
        // len(s) > 2
index f7ba479c3bb1682a1cbdc101af43d9cd9772d6b5..22ace576fe9ab08f694bcfc547a18f704eda3534 100644 (file)
@@ -305,7 +305,6 @@ func fixTokenPos(f *ast.File) {
                case *ast.SelectorExpr:
                        // rewrite syntax.Pos to token.Pos
                        m.renameSel(n)
-                       return false
                case *ast.CallExpr:
                        // rewrite x.IsKnown() to x.IsValid()
                        if fun, _ := n.Fun.(*ast.SelectorExpr); fun != nil && len(n.Args) == 0 {
@@ -370,11 +369,11 @@ func fixInferSig(f *ast.File) {
                                                return false
                                        }
                                case "allowVersion":
-                                       // rewrite check.allowVersion(..., pos, ...) to check.allowVersion(..., posn, ...)
-                                       if isIdent(n.Args[1], "pos") {
-                                               pos := n.Args[1].Pos()
+                                       // rewrite check.allowVersion(pos, ...) to check.allowVersion(posn, ...)
+                                       if isIdent(n.Args[0], "pos") {
+                                               pos := n.Args[0].Pos()
                                                arg := newIdent(pos, "posn")
-                                               n.Args[1] = arg
+                                               n.Args[0] = arg
                                                return false
                                        }
                                }
index 39215d88d545d9f288b9c27029772a4e165db149..3a3e5de4dd6df751000ceabe0d42cf639977a51d 100644 (file)
@@ -112,7 +112,7 @@ func (check *Checker) infer(posn positioner, tparams []*TypeParam, targs []Type,
        // Unify parameter and argument types for generic parameters with typed arguments
        // and collect the indices of generic parameters with untyped arguments.
        // Terminology: generic parameter = function parameter with a type-parameterized type
-       u := newUnifier(tparams, targs, check.allowVersion(check.pkg, posn, go1_21))
+       u := newUnifier(tparams, targs, check.allowVersion(posn, go1_21))
 
        errorf := func(tpar, targ Type, arg *operand) {
                // provide a better error message if we can
index bf7ecc531690c6a17e9e392b50ad48bae771d588..c7ea9e1c7891bc1509fa6a074c6651e01770de34 100644 (file)
@@ -273,7 +273,7 @@ func (check *Checker) implements(pos token.Pos, V, T Type, constraint bool, caus
                // so that ordinary, non-type parameter interfaces implement comparable.
                if constraint && comparable(V, true /* spec comparability */, nil, nil) {
                        // V is comparable if we are at Go 1.20 or higher.
-                       if check == nil || check.allowVersion(check.pkg, atPos(pos), go1_20) { // atPos needed so that go/types generate passes
+                       if check == nil || check.allowVersion(atPos(pos), go1_20) { // atPos needed so that go/types generate passes
                                return true
                        }
                        if cause != nil {
index 9788fc8142f3650d1968a024fa4ff22d754ec552..4fd37a68f019fa2f2ea87097a208d0120fcce160 100644 (file)
@@ -858,7 +858,7 @@ func (check *Checker) rangeStmt(inner stmtContext, s *ast.RangeStmt) {
        if x.mode != invalid {
                // Ranging over a type parameter is permitted if it has a core type.
                k, v, cause, isFunc, ok := rangeKeyVal(x.typ, func(v goVersion) bool {
-                       return check.allowVersion(check.pkg, x.expr, v)
+                       return check.allowVersion(x.expr, v)
                })
                switch {
                case !ok && cause != "":
index 16bc62cc19fa3747f5d038323a50e67120796012..29bd7183356c1015d84148c32553571212c17312 100644 (file)
@@ -240,7 +240,9 @@ func computeInterfaceTypeSet(check *Checker, pos token.Pos, ityp *Interface) *_T
                        // error message.
                        if check != nil {
                                check.later(func() {
-                                       if !check.allowVersion(m.pkg, atPos(pos), go1_14) || !Identical(m.typ, other.Type()) {
+                                       // ignore version check if method is from a different package
+                                       // TODO(gri) this seems incorrect - see go.dev/issue/66285
+                                       if check.pkg == m.pkg && pos.IsValid() && !check.allowVersion(atPos(pos), go1_14) || !Identical(m.typ, other.Type()) {
                                                err := check.newError(DuplicateDecl)
                                                err.addf(atPos(pos), "duplicate method %s", m.name)
                                                err.addf(atPos(mpos[other.(*Func)]), "other declaration of %s", m.name)
@@ -259,9 +261,8 @@ func computeInterfaceTypeSet(check *Checker, pos token.Pos, ityp *Interface) *_T
        allTerms := allTermlist
        allComparable := false
        for i, typ := range ityp.embeddeds {
-               // The embedding position is nil for imported interfaces
-               // and also for interface copies after substitution (but
-               // in that case we don't need to report errors again).
+               // The embedding position is nil for imported interfaces.
+               // We don't need to do version checks in those cases.
                var pos token.Pos // embedding position
                if ityp.embedPos != nil {
                        pos = (*ityp.embedPos)[i]
@@ -274,7 +275,7 @@ func computeInterfaceTypeSet(check *Checker, pos token.Pos, ityp *Interface) *_T
                        assert(!isTypeParam(typ))
                        tset := computeInterfaceTypeSet(check, pos, u)
                        // If typ is local, an error was already reported where typ is specified/defined.
-                       if check != nil && check.isImportedConstraint(typ) && !check.verifyVersionf(atPos(pos), go1_18, "embedding constraint interface %s", typ) {
+                       if pos.IsValid() && check != nil && check.isImportedConstraint(typ) && !check.verifyVersionf(atPos(pos), go1_18, "embedding constraint interface %s", typ) {
                                continue
                        }
                        comparable = tset.comparable
@@ -283,7 +284,7 @@ func computeInterfaceTypeSet(check *Checker, pos token.Pos, ityp *Interface) *_T
                        }
                        terms = tset.terms
                case *Union:
-                       if check != nil && !check.verifyVersionf(atPos(pos), go1_18, "embedding interface element %s", u) {
+                       if pos.IsValid() && check != nil && !check.verifyVersionf(atPos(pos), go1_18, "embedding interface element %s", u) {
                                continue
                        }
                        tset := computeUnionTypeSet(check, unionSets, pos, u)
@@ -297,7 +298,7 @@ func computeInterfaceTypeSet(check *Checker, pos token.Pos, ityp *Interface) *_T
                        if !isValid(u) {
                                continue
                        }
-                       if check != nil && !check.verifyVersionf(atPos(pos), go1_18, "embedding non-interface type %s", typ) {
+                       if pos.IsValid() && check != nil && !check.verifyVersionf(atPos(pos), go1_18, "embedding non-interface type %s", typ) {
                                continue
                        }
                        terms = termlist{{false, typ}}
index 565183de048886512701e52b975289fb34c5f93e..669ca66a3972167a58a6023a01c4e3720dee7605 100644 (file)
@@ -50,31 +50,29 @@ var (
        go_current = asGoVersion(fmt.Sprintf("go1.%d", goversion.Version))
 )
 
-// allowVersion reports whether the given package is allowed to use version v.
-func (check *Checker) allowVersion(pkg *Package, at positioner, v goVersion) bool {
-       // We assume that imported packages have all been checked,
-       // so we only have to check for the local package.
-       if pkg != check.pkg {
-               return true
-       }
-
-       // If no explicit file version is specified,
-       // fileVersion corresponds to the module version.
-       var fileVersion goVersion
+// allowVersion reports whether the current package at the given position
+// is allowed to use version v. If the position is unknown, the specified
+// module version (Config.GoVersion) is used. If that version is invalid,
+// allowVersion returns true.
+func (check *Checker) allowVersion(at positioner, v goVersion) bool {
+       fileVersion := check.conf.GoVersion
        if pos := at.Pos(); pos.IsValid() {
-               // We need version.Lang below because file versions
-               // can be (unaltered) Config.GoVersion strings that
-               // may contain dot-release information.
-               fileVersion = asGoVersion(check.versions[check.fileFor(pos)])
+               fileVersion = check.versions[check.fileFor(pos)]
        }
-       return !fileVersion.isValid() || fileVersion.cmp(v) >= 0
+
+       // We need asGoVersion (which calls version.Lang) below
+       // because fileVersion may be the (unaltered) Config.GoVersion
+       // string which may contain dot-release information.
+       version := asGoVersion(fileVersion)
+
+       return !version.isValid() || version.cmp(v) >= 0
 }
 
 // verifyVersionf is like allowVersion but also accepts a format string and arguments
 // which are used to report a version error if allowVersion returns false. It uses the
 // current package.
 func (check *Checker) verifyVersionf(at positioner, v goVersion, format string, args ...interface{}) bool {
-       if !check.allowVersion(check.pkg, at, v) {
+       if !check.allowVersion(at, v) {
                check.versionErrorf(at, v, format, args...)
                return false
        }
diff --git a/src/internal/types/testdata/fixedbugs/issue66064.go b/src/internal/types/testdata/fixedbugs/issue66064.go
new file mode 100644 (file)
index 0000000..d4a3617
--- /dev/null
@@ -0,0 +1,15 @@
+// -lang=go1.16
+
+// 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.
+
+//go:build go1.21
+
+package main
+
+import "slices"
+
+func main() {
+       _ = slices.Clone([]string{}) // no error should be reported here
+}