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 {
// 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
}
// 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 {
// (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
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
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
// 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
// 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
// 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 {
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 != "":
// 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)
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]
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
}
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)
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}}
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
}
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 {
// 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
}
// 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)
// (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
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
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
// 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
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 {
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
}
}
// 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
// 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 {
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 != "":
// 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)
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]
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
}
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)
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}}
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
}
--- /dev/null
+// -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
+}