mirror of https://github.com/golang/go.git
go/types, cmd/compile/internal/types2: use per-file Go version
For #57001, compilers and others tools will need to understand that a different Go version can be used in different files in a program, according to the //go:build lines in those files. Update go/types and cmd/compile/internal/types2 to track and use per-file Go versions. The two must be updated together because of the files in go/types that are generated from files in types2. The effect of the //go:build go1.N line depends on the Go version declared in the 'go 1.M' line in go.mod. If N > M, the file gets go1.N semantics when built with a Go 1.N or later toolchain (when built with an earlier toolchain the //go:build line will keep the file from being built at all). If N < M, then in general we want the file to get go1.N semantics as well, meaning later features are disabled. However, older Go 1.M did not apply this kind of downgrade, so for compatibility, N < M only has an effect when M >= 21, meaning when using semantics from Go 1.21 or later. For #59033. Change-Id: I93cf07e6c687d37bd37a9461dc60cc032bafd01d Reviewed-on: https://go-review.googlesource.com/c/go/+/476278 TryBot-Result: Gopher Robot <gobot@golang.org> Auto-Submit: Russ Cox <rsc@golang.org> Reviewed-by: Robert Griesemer <gri@google.com> Run-TryBot: Russ Cox <rsc@golang.org>
This commit is contained in:
parent
8854be4180
commit
804d786a30
|
|
@ -451,7 +451,7 @@ func AssertableTo(V *Interface, T Type) bool {
|
|||
if T.Underlying() == Typ[Invalid] {
|
||||
return false
|
||||
}
|
||||
return (*Checker)(nil).newAssertableTo(V, T, nil)
|
||||
return (*Checker)(nil).newAssertableTo(nopos, V, T, nil)
|
||||
}
|
||||
|
||||
// AssignableTo reports whether a value of type V is assignable to a variable
|
||||
|
|
@ -489,7 +489,7 @@ func Implements(V Type, T *Interface) bool {
|
|||
if V.Underlying() == Typ[Invalid] {
|
||||
return false
|
||||
}
|
||||
return (*Checker)(nil).implements(V, T, false, nil)
|
||||
return (*Checker)(nil).implements(nopos, V, T, false, nil)
|
||||
}
|
||||
|
||||
// Satisfies reports whether type V satisfies the constraint T.
|
||||
|
|
@ -497,7 +497,7 @@ func Implements(V Type, T *Interface) bool {
|
|||
// The behavior of Satisfies is unspecified if V is Typ[Invalid] or an uninstantiated
|
||||
// generic type.
|
||||
func Satisfies(V Type, T *Interface) bool {
|
||||
return (*Checker)(nil).implements(V, T, true, nil)
|
||||
return (*Checker)(nil).implements(nopos, V, T, true, nil)
|
||||
}
|
||||
|
||||
// Identical reports whether x and y are identical types.
|
||||
|
|
|
|||
|
|
@ -234,7 +234,7 @@ func (check *Checker) builtin(x *operand, call *syntax.CallExpr, id builtinId) (
|
|||
|
||||
case _Clear:
|
||||
// clear(m)
|
||||
if !check.allowVersion(check.pkg, 1, 21) {
|
||||
if !check.allowVersion(check.pkg, call.Pos(), 1, 21) {
|
||||
check.versionErrorf(call.Fun, "go1.21", "clear")
|
||||
return
|
||||
}
|
||||
|
|
@ -626,7 +626,7 @@ func (check *Checker) builtin(x *operand, call *syntax.CallExpr, id builtinId) (
|
|||
|
||||
case _Add:
|
||||
// unsafe.Add(ptr unsafe.Pointer, len IntegerType) unsafe.Pointer
|
||||
if !check.allowVersion(check.pkg, 1, 17) {
|
||||
if !check.allowVersion(check.pkg, call.Pos(), 1, 17) {
|
||||
check.versionErrorf(call.Fun, "go1.17", "unsafe.Add")
|
||||
return
|
||||
}
|
||||
|
|
@ -762,7 +762,7 @@ func (check *Checker) builtin(x *operand, call *syntax.CallExpr, id builtinId) (
|
|||
|
||||
case _Slice:
|
||||
// unsafe.Slice(ptr *T, len IntegerType) []T
|
||||
if !check.allowVersion(check.pkg, 1, 17) {
|
||||
if !check.allowVersion(check.pkg, call.Pos(), 1, 17) {
|
||||
check.versionErrorf(call.Fun, "go1.17", "unsafe.Slice")
|
||||
return
|
||||
}
|
||||
|
|
@ -787,7 +787,7 @@ func (check *Checker) builtin(x *operand, call *syntax.CallExpr, id builtinId) (
|
|||
|
||||
case _SliceData:
|
||||
// unsafe.SliceData(slice []T) *T
|
||||
if !check.allowVersion(check.pkg, 1, 20) {
|
||||
if !check.allowVersion(check.pkg, call.Pos(), 1, 20) {
|
||||
check.versionErrorf(call.Fun, "go1.20", "unsafe.SliceData")
|
||||
return
|
||||
}
|
||||
|
|
@ -806,7 +806,7 @@ func (check *Checker) builtin(x *operand, call *syntax.CallExpr, id builtinId) (
|
|||
|
||||
case _String:
|
||||
// unsafe.String(ptr *byte, len IntegerType) string
|
||||
if !check.allowVersion(check.pkg, 1, 20) {
|
||||
if !check.allowVersion(check.pkg, call.Pos(), 1, 20) {
|
||||
check.versionErrorf(call.Fun, "go1.20", "unsafe.String")
|
||||
return
|
||||
}
|
||||
|
|
@ -830,7 +830,7 @@ func (check *Checker) builtin(x *operand, call *syntax.CallExpr, id builtinId) (
|
|||
|
||||
case _StringData:
|
||||
// unsafe.StringData(str string) *byte
|
||||
if !check.allowVersion(check.pkg, 1, 20) {
|
||||
if !check.allowVersion(check.pkg, call.Pos(), 1, 20) {
|
||||
check.versionErrorf(call.Fun, "go1.20", "unsafe.StringData")
|
||||
return
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ import (
|
|||
func (check *Checker) funcInst(tsig *Signature, pos syntax.Pos, x *operand, inst *syntax.IndexExpr) {
|
||||
assert(tsig != nil || inst != nil)
|
||||
|
||||
if !check.allowVersion(check.pkg, 1, 18) {
|
||||
if !check.allowVersion(check.pkg, pos, 1, 18) {
|
||||
check.versionErrorf(inst.Pos(), "go1.18", "function instantiation")
|
||||
}
|
||||
|
||||
|
|
@ -278,7 +278,7 @@ func (check *Checker) callExpr(x *operand, call *syntax.CallExpr) exprKind {
|
|||
// is an error checking its arguments (for example, if an incorrect number
|
||||
// of arguments is supplied).
|
||||
if got == want && want > 0 {
|
||||
if !check.allowVersion(check.pkg, 1, 18) {
|
||||
if !check.allowVersion(check.pkg, x.Pos(), 1, 18) {
|
||||
check.versionErrorf(inst.Pos(), "go1.18", "function instantiation")
|
||||
}
|
||||
|
||||
|
|
@ -444,7 +444,7 @@ func (check *Checker) arguments(call *syntax.CallExpr, sig *Signature, targs []T
|
|||
|
||||
// infer type arguments and instantiate signature if necessary
|
||||
if sig.TypeParams().Len() > 0 {
|
||||
if !check.allowVersion(check.pkg, 1, 18) {
|
||||
if !check.allowVersion(check.pkg, call.Pos(), 1, 18) {
|
||||
if iexpr, _ := call.Fun.(*syntax.IndexExpr); iexpr != nil {
|
||||
check.versionErrorf(iexpr.Pos(), "go1.18", "function instantiation")
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -116,6 +116,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
|
||||
posVers map[*syntax.PosBase]version // Pos -> Go version mapping
|
||||
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
|
||||
|
|
@ -281,6 +282,32 @@ func (check *Checker) initFiles(files []*syntax.File) {
|
|||
// ignore this file
|
||||
}
|
||||
}
|
||||
|
||||
for _, file := range check.files {
|
||||
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 v.before(check.version) && check.version.before(version{1, 21}) {
|
||||
continue
|
||||
}
|
||||
if check.posVers == nil {
|
||||
check.posVers = make(map[*syntax.PosBase]version)
|
||||
}
|
||||
check.posVers[base(file.Pos())] = v
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// A bailout panic is used for early termination.
|
||||
|
|
|
|||
|
|
@ -183,7 +183,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, 1, 20) {
|
||||
if check == nil || check.allowVersion(check.pkg, x.Pos(), 1, 20) {
|
||||
return true
|
||||
}
|
||||
// check != nil
|
||||
|
|
@ -196,7 +196,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, 1, 17) {
|
||||
if check == nil || check.allowVersion(check.pkg, x.Pos(), 1, 17) {
|
||||
return true
|
||||
}
|
||||
// check != nil
|
||||
|
|
|
|||
|
|
@ -506,7 +506,7 @@ func (check *Checker) typeDecl(obj *TypeName, tdecl *syntax.TypeDecl, def *Named
|
|||
check.validType(t)
|
||||
}
|
||||
// If typ is local, an error was already reported where typ is specified/defined.
|
||||
if check.isImportedConstraint(rhs) && !check.allowVersion(check.pkg, 1, 18) {
|
||||
if check.isImportedConstraint(rhs) && !check.allowVersion(check.pkg, tdecl.Pos(), 1, 18) {
|
||||
check.versionErrorf(tdecl.Type, "go1.18", "using type constraint %s", rhs)
|
||||
}
|
||||
}).describef(obj, "validType(%s)", obj.Name())
|
||||
|
|
@ -521,7 +521,7 @@ func (check *Checker) typeDecl(obj *TypeName, tdecl *syntax.TypeDecl, def *Named
|
|||
|
||||
// alias declaration
|
||||
if alias {
|
||||
if !check.allowVersion(check.pkg, 1, 9) {
|
||||
if !check.allowVersion(check.pkg, tdecl.Pos(), 1, 9) {
|
||||
check.versionErrorf(tdecl, "go1.9", "type aliases")
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -977,7 +977,7 @@ func (check *Checker) shift(x, y *operand, e syntax.Expr, op syntax.Operator) {
|
|||
// Check that RHS is otherwise at least of integer type.
|
||||
switch {
|
||||
case allInteger(y.typ):
|
||||
if !allUnsigned(y.typ) && !check.allowVersion(check.pkg, 1, 13) {
|
||||
if !allUnsigned(y.typ) && !check.allowVersion(check.pkg, x.Pos(), 1, 13) {
|
||||
check.versionErrorf(y, "go1.13", invalidOp+"signed shift count %s", y)
|
||||
x.mode = invalid
|
||||
return
|
||||
|
|
|
|||
|
|
@ -176,7 +176,7 @@ func (check *Checker) verify(pos syntax.Pos, tparams []*TypeParam, targs []Type,
|
|||
// the parameterized type.
|
||||
bound := check.subst(pos, tpar.bound, smap, nil, ctxt)
|
||||
var cause string
|
||||
if !check.implements(targs[i], bound, true, &cause) {
|
||||
if !check.implements(pos, targs[i], bound, true, &cause) {
|
||||
return i, errors.New(cause)
|
||||
}
|
||||
}
|
||||
|
|
@ -189,7 +189,7 @@ func (check *Checker) verify(pos syntax.Pos, tparams []*TypeParam, targs []Type,
|
|||
//
|
||||
// If the provided cause is non-nil, it may be set to an error string
|
||||
// explaining why V does not implement (or satisfy, for constraints) T.
|
||||
func (check *Checker) implements(V, T Type, constraint bool, cause *string) bool {
|
||||
func (check *Checker) implements(pos syntax.Pos, V, T Type, constraint bool, cause *string) bool {
|
||||
Vu := under(V)
|
||||
Tu := under(T)
|
||||
if Vu == Typ[Invalid] || Tu == Typ[Invalid] {
|
||||
|
|
@ -262,7 +262,7 @@ func (check *Checker) implements(V, T Type, constraint bool, cause *string) bool
|
|||
// 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, 1, 20) {
|
||||
if check == nil || check.allowVersion(check.pkg, pos, 1, 20) {
|
||||
return true
|
||||
}
|
||||
if cause != nil {
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ package types2
|
|||
|
||||
import (
|
||||
"bytes"
|
||||
"cmd/compile/internal/syntax"
|
||||
"strings"
|
||||
)
|
||||
|
||||
|
|
@ -505,14 +506,14 @@ func (check *Checker) assertableTo(V, T Type, cause *string) bool {
|
|||
// in constraint position (we have not yet defined that behavior in the spec).
|
||||
// The underlying type of V must be an interface.
|
||||
// If the result is false and cause is not nil, *cause is set to the error cause.
|
||||
func (check *Checker) newAssertableTo(V, T Type, cause *string) bool {
|
||||
func (check *Checker) newAssertableTo(pos syntax.Pos, V, T Type, cause *string) bool {
|
||||
// no static check is required if T is an interface
|
||||
// spec: "If T is an interface type, x.(T) asserts that the
|
||||
// dynamic type of x implements the interface T."
|
||||
if IsInterface(T) {
|
||||
return true
|
||||
}
|
||||
return check.implements(T, V, false, cause)
|
||||
return check.implements(pos, T, V, false, cause)
|
||||
}
|
||||
|
||||
// deref dereferences typ if it is a *Pointer (but not a *Named type
|
||||
|
|
|
|||
|
|
@ -293,7 +293,7 @@ func (x *operand) assignableTo(check *Checker, T Type, cause *string) (bool, Cod
|
|||
// T is an interface type and x implements T and T is not a type parameter.
|
||||
// Also handle the case where T is a pointer to an interface.
|
||||
if _, ok := Tu.(*Interface); ok && Tp == nil || isInterfacePtr(Tu) {
|
||||
if !check.implements(V, T, false, cause) {
|
||||
if !check.implements(x.Pos(), V, T, false, cause) {
|
||||
return false, InvalidIfaceAssign
|
||||
}
|
||||
return true, 0
|
||||
|
|
@ -301,7 +301,7 @@ func (x *operand) assignableTo(check *Checker, T Type, cause *string) (bool, Cod
|
|||
|
||||
// If V is an interface, check if a missing type assertion is the problem.
|
||||
if Vi, _ := Vu.(*Interface); Vi != nil && Vp == nil {
|
||||
if check.implements(T, V, false, nil) {
|
||||
if check.implements(x.Pos(), T, V, false, nil) {
|
||||
// T implements V, so give hint about type assertion.
|
||||
if cause != nil {
|
||||
*cause = "need type assertion"
|
||||
|
|
|
|||
|
|
@ -406,7 +406,7 @@ func (check *Checker) collectObjects() {
|
|||
}
|
||||
|
||||
case *syntax.TypeDecl:
|
||||
if len(s.TParamList) != 0 && !check.allowVersion(pkg, 1, 18) {
|
||||
if len(s.TParamList) != 0 && !check.allowVersion(pkg, s.Pos(), 1, 18) {
|
||||
check.versionErrorf(s.TParamList[0], "go1.18", "type parameter")
|
||||
}
|
||||
obj := NewTypeName(s.Name.Pos(), pkg, s.Name.Value, nil)
|
||||
|
|
@ -455,7 +455,7 @@ func (check *Checker) collectObjects() {
|
|||
}
|
||||
check.recordDef(s.Name, obj)
|
||||
}
|
||||
if len(s.TParamList) != 0 && !check.allowVersion(pkg, 1, 18) && !hasTParamError {
|
||||
if len(s.TParamList) != 0 && !check.allowVersion(pkg, s.Pos(), 1, 18) && !hasTParamError {
|
||||
check.versionErrorf(s.TParamList[0], "go1.18", "type parameter")
|
||||
}
|
||||
info := &declInfo{file: fileScope, fdecl: s}
|
||||
|
|
|
|||
|
|
@ -244,7 +244,7 @@ func computeInterfaceTypeSet(check *Checker, pos syntax.Pos, ityp *Interface) *_
|
|||
}
|
||||
// check != nil
|
||||
check.later(func() {
|
||||
if !check.allowVersion(m.pkg, 1, 14) || !Identical(m.typ, other.Type()) {
|
||||
if !check.allowVersion(m.pkg, pos, 1, 14) || !Identical(m.typ, other.Type()) {
|
||||
var err error_
|
||||
err.code = DuplicateDecl
|
||||
err.errorf(pos, "duplicate method %s", m.name)
|
||||
|
|
@ -278,7 +278,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.allowVersion(check.pkg, 1, 18) {
|
||||
if check != nil && check.isImportedConstraint(typ) && !check.allowVersion(check.pkg, pos, 1, 18) {
|
||||
check.versionErrorf(pos, "go1.18", "embedding constraint interface %s", typ)
|
||||
continue
|
||||
}
|
||||
|
|
@ -288,7 +288,7 @@ func computeInterfaceTypeSet(check *Checker, pos syntax.Pos, ityp *Interface) *_
|
|||
}
|
||||
terms = tset.terms
|
||||
case *Union:
|
||||
if check != nil && !check.allowVersion(check.pkg, 1, 18) {
|
||||
if check != nil && !check.allowVersion(check.pkg, pos, 1, 18) {
|
||||
check.versionErrorf(pos, "go1.18", "embedding interface element %s", u)
|
||||
continue
|
||||
}
|
||||
|
|
@ -303,7 +303,7 @@ func computeInterfaceTypeSet(check *Checker, pos syntax.Pos, ityp *Interface) *_
|
|||
if u == Typ[Invalid] {
|
||||
continue
|
||||
}
|
||||
if check != nil && !check.allowVersion(check.pkg, 1, 18) {
|
||||
if check != nil && !check.allowVersion(check.pkg, pos, 1, 18) {
|
||||
check.versionErrorf(pos, "go1.18", "embedding non-interface type %s", typ)
|
||||
continue
|
||||
}
|
||||
|
|
|
|||
|
|
@ -42,7 +42,7 @@ func (check *Checker) ident(x *operand, e *syntax.Name, def *Named, wantType boo
|
|||
}
|
||||
return
|
||||
case universeAny, universeComparable:
|
||||
if !check.allowVersion(check.pkg, 1, 18) {
|
||||
if !check.allowVersion(check.pkg, e.Pos(), 1, 18) {
|
||||
check.versionErrorf(e, "go1.18", "predeclared %s", e.Value)
|
||||
return // avoid follow-on errors
|
||||
}
|
||||
|
|
@ -272,7 +272,7 @@ func (check *Checker) typInternal(e0 syntax.Expr, def *Named) (T Type) {
|
|||
}
|
||||
|
||||
case *syntax.IndexExpr:
|
||||
if !check.allowVersion(check.pkg, 1, 18) {
|
||||
if !check.allowVersion(check.pkg, e.Pos(), 1, 18) {
|
||||
check.versionErrorf(e.Pos(), "go1.18", "type instantiation")
|
||||
}
|
||||
return check.instantiatedType(e.X, unpackExpr(e.Index), def)
|
||||
|
|
|
|||
|
|
@ -6,9 +6,7 @@ package types2
|
|||
|
||||
import (
|
||||
"cmd/compile/internal/syntax"
|
||||
"fmt"
|
||||
"internal/lazyregexp"
|
||||
"strconv"
|
||||
"errors"
|
||||
"strings"
|
||||
)
|
||||
|
||||
|
|
@ -16,7 +14,7 @@ import (
|
|||
// 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, 1, 13) {
|
||||
if len(s) <= 2 || check.allowVersion(check.pkg, lit.Pos(), 1, 13) {
|
||||
return
|
||||
}
|
||||
// len(s) > 2
|
||||
|
|
@ -43,20 +41,45 @@ func (check *Checker) langCompat(lit *syntax.BasicLit) {
|
|||
|
||||
// allowVersion reports whether the given package
|
||||
// is allowed to use version major.minor.
|
||||
func (check *Checker) allowVersion(pkg *Package, major, minor int) bool {
|
||||
func (check *Checker) allowVersion(pkg *Package, pos syntax.Pos, major, minor int) 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 the source file declares its Go version, use that to decide.
|
||||
if check.posVers != nil {
|
||||
if v, ok := check.posVers[base(pos)]; ok && v.major >= 1 {
|
||||
return v.major > major || v.major == major && v.minor >= minor
|
||||
}
|
||||
}
|
||||
|
||||
// Otherwise fall back to the version in the checker.
|
||||
ma, mi := check.version.major, check.version.minor
|
||||
return ma == 0 && mi == 0 || ma > major || ma == major && mi >= minor
|
||||
}
|
||||
|
||||
// base finds the underlying PosBase of the source file containing pos,
|
||||
// skipping over intermediate PosBase layers created by //line directives.
|
||||
func base(pos syntax.Pos) *syntax.PosBase {
|
||||
b := pos.Base()
|
||||
for {
|
||||
bb := b.Pos().Base()
|
||||
if bb == nil || bb == b {
|
||||
break
|
||||
}
|
||||
b = bb
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
type version struct {
|
||||
major, minor int
|
||||
}
|
||||
|
||||
var errVersionSyntax = errors.New("invalid Go version syntax")
|
||||
|
||||
// parseGoVersion parses a Go version string (such as "go1.12")
|
||||
// and returns the version, or an error. If s is the empty
|
||||
// string, the version is 0.0.
|
||||
|
|
@ -64,18 +87,52 @@ func parseGoVersion(s string) (v version, err error) {
|
|||
if s == "" {
|
||||
return
|
||||
}
|
||||
matches := goVersionRx.FindStringSubmatch(s)
|
||||
if matches == nil {
|
||||
err = fmt.Errorf(`should be something like "go1.12"`)
|
||||
if !strings.HasPrefix(s, "go") {
|
||||
return version{}, errVersionSyntax
|
||||
}
|
||||
s = s[len("go"):]
|
||||
i := 0
|
||||
for ; i < len(s) && '0' <= s[i] && s[i] <= '9'; i++ {
|
||||
if i >= 10 || i == 0 && s[i] == '0' {
|
||||
return version{}, errVersionSyntax
|
||||
}
|
||||
v.major = 10*v.major + int(s[i]) - '0'
|
||||
}
|
||||
if i > 0 && i == len(s) {
|
||||
return
|
||||
}
|
||||
v.major, err = strconv.Atoi(matches[1])
|
||||
if err != nil {
|
||||
if i == 0 || s[i] != '.' {
|
||||
return version{}, errVersionSyntax
|
||||
}
|
||||
s = s[i+1:]
|
||||
if s == "0" {
|
||||
// We really should not accept "go1.0",
|
||||
// but we didn't reject it from the start
|
||||
// and there are now programs that use it.
|
||||
// So accept it.
|
||||
return
|
||||
}
|
||||
v.minor, err = strconv.Atoi(matches[2])
|
||||
return
|
||||
i = 0
|
||||
for ; i < len(s) && '0' <= s[i] && s[i] <= '9'; i++ {
|
||||
if i >= 10 || i == 0 && s[i] == '0' {
|
||||
return version{}, errVersionSyntax
|
||||
}
|
||||
v.minor = 10*v.minor + int(s[i]) - '0'
|
||||
}
|
||||
if i > 0 && i == len(s) {
|
||||
return
|
||||
}
|
||||
return version{}, errVersionSyntax
|
||||
}
|
||||
|
||||
// goVersionRx matches a Go version string, e.g. "go1.12".
|
||||
var goVersionRx = lazyregexp.New(`^go([1-9][0-9]*)\.(0|[1-9][0-9]*)$`)
|
||||
func (v version) equal(u version) bool {
|
||||
return v.major == u.major && v.minor == u.minor
|
||||
}
|
||||
|
||||
func (v version) before(u version) bool {
|
||||
return v.major < u.major || v.major == u.major && v.minor < u.minor
|
||||
}
|
||||
|
||||
func (v version) after(u version) bool {
|
||||
return v.major > u.major || v.major == u.major && v.minor > u.minor
|
||||
}
|
||||
|
|
|
|||
|
|
@ -275,15 +275,20 @@ var depsRules = `
|
|||
< go/printer
|
||||
< go/format;
|
||||
|
||||
go/doc/comment, go/parser, internal/lazyregexp, text/template
|
||||
< go/doc;
|
||||
|
||||
math/big, go/token
|
||||
< go/constant;
|
||||
|
||||
container/heap, go/constant, go/parser, internal/types/errors, internal/lazyregexp
|
||||
container/heap, go/constant, go/parser, internal/types/errors
|
||||
< go/types;
|
||||
|
||||
# The vast majority of standard library packages should not be resorting to regexp.
|
||||
# go/types is a good chokepoint. It shouldn't use regexp, nor should anything
|
||||
# that is low-enough level to be used by go/types.
|
||||
regexp !< go/types;
|
||||
|
||||
go/doc/comment, go/parser, internal/lazyregexp, text/template
|
||||
< go/doc;
|
||||
|
||||
FMT, internal/goexperiment
|
||||
< internal/buildcfg;
|
||||
|
||||
|
|
|
|||
|
|
@ -435,7 +435,7 @@ func AssertableTo(V *Interface, T Type) bool {
|
|||
if T.Underlying() == Typ[Invalid] {
|
||||
return false
|
||||
}
|
||||
return (*Checker)(nil).newAssertableTo(V, T, nil)
|
||||
return (*Checker)(nil).newAssertableTo(nopos, V, T, nil)
|
||||
}
|
||||
|
||||
// AssignableTo reports whether a value of type V is assignable to a variable
|
||||
|
|
@ -473,7 +473,7 @@ func Implements(V Type, T *Interface) bool {
|
|||
if V.Underlying() == Typ[Invalid] {
|
||||
return false
|
||||
}
|
||||
return (*Checker)(nil).implements(V, T, false, nil)
|
||||
return (*Checker)(nil).implements(0, V, T, false, nil)
|
||||
}
|
||||
|
||||
// Satisfies reports whether type V satisfies the constraint T.
|
||||
|
|
@ -481,7 +481,7 @@ func Implements(V Type, T *Interface) bool {
|
|||
// The behavior of Satisfies is unspecified if V is Typ[Invalid] or an uninstantiated
|
||||
// generic type.
|
||||
func Satisfies(V Type, T *Interface) bool {
|
||||
return (*Checker)(nil).implements(V, T, true, nil)
|
||||
return (*Checker)(nil).implements(0, V, T, true, nil)
|
||||
}
|
||||
|
||||
// Identical reports whether x and y are identical types.
|
||||
|
|
|
|||
|
|
@ -235,7 +235,7 @@ func (check *Checker) builtin(x *operand, call *ast.CallExpr, id builtinId) (_ b
|
|||
|
||||
case _Clear:
|
||||
// clear(m)
|
||||
if !check.allowVersion(check.pkg, 1, 21) {
|
||||
if !check.allowVersion(check.pkg, call.Pos(), 1, 21) {
|
||||
check.error(call.Fun, UnsupportedFeature, "clear requires go1.21 or later")
|
||||
return
|
||||
}
|
||||
|
|
@ -627,7 +627,7 @@ func (check *Checker) builtin(x *operand, call *ast.CallExpr, id builtinId) (_ b
|
|||
|
||||
case _Add:
|
||||
// unsafe.Add(ptr unsafe.Pointer, len IntegerType) unsafe.Pointer
|
||||
if !check.allowVersion(check.pkg, 1, 17) {
|
||||
if !check.allowVersion(check.pkg, call.Pos(), 1, 17) {
|
||||
check.error(call.Fun, UnsupportedFeature, "unsafe.Add requires go1.17 or later")
|
||||
return
|
||||
}
|
||||
|
|
@ -763,7 +763,7 @@ func (check *Checker) builtin(x *operand, call *ast.CallExpr, id builtinId) (_ b
|
|||
|
||||
case _Slice:
|
||||
// unsafe.Slice(ptr *T, len IntegerType) []T
|
||||
if !check.allowVersion(check.pkg, 1, 17) {
|
||||
if !check.allowVersion(check.pkg, call.Pos(), 1, 17) {
|
||||
check.error(call.Fun, UnsupportedFeature, "unsafe.Slice requires go1.17 or later")
|
||||
return
|
||||
}
|
||||
|
|
@ -788,7 +788,7 @@ func (check *Checker) builtin(x *operand, call *ast.CallExpr, id builtinId) (_ b
|
|||
|
||||
case _SliceData:
|
||||
// unsafe.SliceData(slice []T) *T
|
||||
if !check.allowVersion(check.pkg, 1, 20) {
|
||||
if !check.allowVersion(check.pkg, call.Pos(), 1, 20) {
|
||||
check.error(call.Fun, UnsupportedFeature, "unsafe.SliceData requires go1.20 or later")
|
||||
return
|
||||
}
|
||||
|
|
@ -807,7 +807,7 @@ func (check *Checker) builtin(x *operand, call *ast.CallExpr, id builtinId) (_ b
|
|||
|
||||
case _String:
|
||||
// unsafe.String(ptr *byte, len IntegerType) string
|
||||
if !check.allowVersion(check.pkg, 1, 20) {
|
||||
if !check.allowVersion(check.pkg, call.Pos(), 1, 20) {
|
||||
check.error(call.Fun, UnsupportedFeature, "unsafe.String requires go1.20 or later")
|
||||
return
|
||||
}
|
||||
|
|
@ -831,7 +831,7 @@ func (check *Checker) builtin(x *operand, call *ast.CallExpr, id builtinId) (_ b
|
|||
|
||||
case _StringData:
|
||||
// unsafe.StringData(str string) *byte
|
||||
if !check.allowVersion(check.pkg, 1, 20) {
|
||||
if !check.allowVersion(check.pkg, call.Pos(), 1, 20) {
|
||||
check.error(call.Fun, UnsupportedFeature, "unsafe.StringData requires go1.20 or later")
|
||||
return
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ import (
|
|||
func (check *Checker) funcInst(tsig *Signature, pos token.Pos, x *operand, ix *typeparams.IndexExpr) {
|
||||
assert(tsig != nil || ix != nil)
|
||||
|
||||
if !check.allowVersion(check.pkg, 1, 18) {
|
||||
if !check.allowVersion(check.pkg, pos, 1, 18) {
|
||||
check.softErrorf(inNode(ix.Orig, ix.Lbrack), UnsupportedFeature, "function instantiation requires go1.18 or later")
|
||||
}
|
||||
|
||||
|
|
@ -283,7 +283,7 @@ func (check *Checker) callExpr(x *operand, call *ast.CallExpr) exprKind {
|
|||
// is an error checking its arguments (for example, if an incorrect number
|
||||
// of arguments is supplied).
|
||||
if got == want && want > 0 {
|
||||
if !check.allowVersion(check.pkg, 1, 18) {
|
||||
if !check.allowVersion(check.pkg, ix.Pos(), 1, 18) {
|
||||
check.softErrorf(inNode(call.Fun, ix.Lbrack), UnsupportedFeature, "function instantiation requires go1.18 or later")
|
||||
}
|
||||
|
||||
|
|
@ -445,7 +445,7 @@ func (check *Checker) arguments(call *ast.CallExpr, sig *Signature, targs []Type
|
|||
|
||||
// infer type arguments and instantiate signature if necessary
|
||||
if sig.TypeParams().Len() > 0 {
|
||||
if !check.allowVersion(check.pkg, 1, 18) {
|
||||
if !check.allowVersion(check.pkg, call.Pos(), 1, 18) {
|
||||
switch call.Fun.(type) {
|
||||
case *ast.IndexExpr, *ast.IndexListExpr:
|
||||
ix := typeparams.UnpackIndexExpr(call.Fun)
|
||||
|
|
|
|||
|
|
@ -118,6 +118,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
|
||||
posVers map[*token.File]version // Pos -> Go version mapping
|
||||
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
|
||||
|
|
@ -284,6 +285,38 @@ func (check *Checker) initFiles(files []*ast.File) {
|
|||
// ignore this file
|
||||
}
|
||||
}
|
||||
|
||||
for _, file := range check.files {
|
||||
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 v.before(check.version) && check.version.before(version{1, 21}) {
|
||||
continue
|
||||
}
|
||||
if check.posVers == nil {
|
||||
check.posVers = make(map[*token.File]version)
|
||||
}
|
||||
check.posVers[check.fset.File(file.FileStart)] = v
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// A posVers records that the file starting at pos declares the Go version vers.
|
||||
type posVers struct {
|
||||
pos token.Pos
|
||||
vers version
|
||||
}
|
||||
|
||||
// A bailout panic is used for early termination.
|
||||
|
|
|
|||
|
|
@ -181,7 +181,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, 1, 20) {
|
||||
if check == nil || check.allowVersion(check.pkg, x.Pos(), 1, 20) {
|
||||
return true
|
||||
}
|
||||
// check != nil
|
||||
|
|
@ -194,7 +194,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, 1, 17) {
|
||||
if check == nil || check.allowVersion(check.pkg, x.Pos(), 1, 17) {
|
||||
return true
|
||||
}
|
||||
// check != nil
|
||||
|
|
|
|||
|
|
@ -561,7 +561,7 @@ func (check *Checker) typeDecl(obj *TypeName, tdecl *ast.TypeSpec, def *Named) {
|
|||
check.validType(t)
|
||||
}
|
||||
// If typ is local, an error was already reported where typ is specified/defined.
|
||||
if check.isImportedConstraint(rhs) && !check.allowVersion(check.pkg, 1, 18) {
|
||||
if check.isImportedConstraint(rhs) && !check.allowVersion(check.pkg, tdecl.Pos(), 1, 18) {
|
||||
check.errorf(tdecl.Type, UnsupportedFeature, "using type constraint %s requires go1.18 or later", rhs)
|
||||
}
|
||||
}).describef(obj, "validType(%s)", obj.Name())
|
||||
|
|
@ -576,7 +576,7 @@ func (check *Checker) typeDecl(obj *TypeName, tdecl *ast.TypeSpec, def *Named) {
|
|||
|
||||
// alias declaration
|
||||
if alias {
|
||||
if !check.allowVersion(check.pkg, 1, 9) {
|
||||
if !check.allowVersion(check.pkg, tdecl.Pos(), 1, 9) {
|
||||
check.error(atPos(tdecl.Assign), UnsupportedFeature, "type aliases requires go1.9 or later")
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -955,7 +955,7 @@ func (check *Checker) shift(x, y *operand, e ast.Expr, op token.Token) {
|
|||
// Check that RHS is otherwise at least of integer type.
|
||||
switch {
|
||||
case allInteger(y.typ):
|
||||
if !allUnsigned(y.typ) && !check.allowVersion(check.pkg, 1, 13) {
|
||||
if !allUnsigned(y.typ) && !check.allowVersion(check.pkg, x.Pos(), 1, 13) {
|
||||
check.errorf(y, UnsupportedFeature, invalidOp+"signed shift count %s requires go1.13 or later", y)
|
||||
x.mode = invalid
|
||||
return
|
||||
|
|
|
|||
|
|
@ -106,7 +106,7 @@ var filemap = map[string]action{
|
|||
// "initorder.go": fixErrErrorfCall, // disabled for now due to unresolved error_ use implications for gopls
|
||||
"instantiate.go": func(f *ast.File) { fixTokenPos(f); fixCheckErrorfCall(f) },
|
||||
"instantiate_test.go": func(f *ast.File) { renameImportPath(f, `"cmd/compile/internal/types2"`, `"go/types"`) },
|
||||
"lookup.go": nil,
|
||||
"lookup.go": func(f *ast.File) { fixTokenPos(f) },
|
||||
"main_test.go": nil,
|
||||
"map.go": nil,
|
||||
"named.go": func(f *ast.File) { fixTokenPos(f); fixTraceSel(f) },
|
||||
|
|
|
|||
|
|
@ -178,7 +178,7 @@ func (check *Checker) verify(pos token.Pos, tparams []*TypeParam, targs []Type,
|
|||
// the parameterized type.
|
||||
bound := check.subst(pos, tpar.bound, smap, nil, ctxt)
|
||||
var cause string
|
||||
if !check.implements(targs[i], bound, true, &cause) {
|
||||
if !check.implements(pos, targs[i], bound, true, &cause) {
|
||||
return i, errors.New(cause)
|
||||
}
|
||||
}
|
||||
|
|
@ -191,7 +191,7 @@ func (check *Checker) verify(pos token.Pos, tparams []*TypeParam, targs []Type,
|
|||
//
|
||||
// If the provided cause is non-nil, it may be set to an error string
|
||||
// explaining why V does not implement (or satisfy, for constraints) T.
|
||||
func (check *Checker) implements(V, T Type, constraint bool, cause *string) bool {
|
||||
func (check *Checker) implements(pos token.Pos, V, T Type, constraint bool, cause *string) bool {
|
||||
Vu := under(V)
|
||||
Tu := under(T)
|
||||
if Vu == Typ[Invalid] || Tu == Typ[Invalid] {
|
||||
|
|
@ -264,7 +264,7 @@ func (check *Checker) implements(V, T Type, constraint bool, cause *string) bool
|
|||
// 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, 1, 20) {
|
||||
if check == nil || check.allowVersion(check.pkg, pos, 1, 20) {
|
||||
return true
|
||||
}
|
||||
if cause != nil {
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ package types
|
|||
|
||||
import (
|
||||
"bytes"
|
||||
"go/token"
|
||||
"strings"
|
||||
)
|
||||
|
||||
|
|
@ -507,14 +508,14 @@ func (check *Checker) assertableTo(V, T Type, cause *string) bool {
|
|||
// in constraint position (we have not yet defined that behavior in the spec).
|
||||
// The underlying type of V must be an interface.
|
||||
// If the result is false and cause is not nil, *cause is set to the error cause.
|
||||
func (check *Checker) newAssertableTo(V, T Type, cause *string) bool {
|
||||
func (check *Checker) newAssertableTo(pos token.Pos, V, T Type, cause *string) bool {
|
||||
// no static check is required if T is an interface
|
||||
// spec: "If T is an interface type, x.(T) asserts that the
|
||||
// dynamic type of x implements the interface T."
|
||||
if IsInterface(T) {
|
||||
return true
|
||||
}
|
||||
return check.implements(T, V, false, cause)
|
||||
return check.implements(pos, T, V, false, cause)
|
||||
}
|
||||
|
||||
// deref dereferences typ if it is a *Pointer (but not a *Named type
|
||||
|
|
|
|||
|
|
@ -282,7 +282,7 @@ func (x *operand) assignableTo(check *Checker, T Type, cause *string) (bool, Cod
|
|||
// T is an interface type and x implements T and T is not a type parameter.
|
||||
// Also handle the case where T is a pointer to an interface.
|
||||
if _, ok := Tu.(*Interface); ok && Tp == nil || isInterfacePtr(Tu) {
|
||||
if !check.implements(V, T, false, cause) {
|
||||
if !check.implements(x.Pos(), V, T, false, cause) {
|
||||
return false, InvalidIfaceAssign
|
||||
}
|
||||
return true, 0
|
||||
|
|
@ -290,7 +290,7 @@ func (x *operand) assignableTo(check *Checker, T Type, cause *string) (bool, Cod
|
|||
|
||||
// If V is an interface, check if a missing type assertion is the problem.
|
||||
if Vi, _ := Vu.(*Interface); Vi != nil && Vp == nil {
|
||||
if check.implements(T, V, false, nil) {
|
||||
if check.implements(x.Pos(), T, V, false, nil) {
|
||||
// T implements V, so give hint about type assertion.
|
||||
if cause != nil {
|
||||
*cause = "need type assertion"
|
||||
|
|
|
|||
|
|
@ -386,7 +386,7 @@ func (check *Checker) collectObjects() {
|
|||
check.declarePkgObj(name, obj, di)
|
||||
}
|
||||
case typeDecl:
|
||||
if d.spec.TypeParams.NumFields() != 0 && !check.allowVersion(pkg, 1, 18) {
|
||||
if d.spec.TypeParams.NumFields() != 0 && !check.allowVersion(pkg, d.spec.Pos(), 1, 18) {
|
||||
check.softErrorf(d.spec.TypeParams.List[0], UnsupportedFeature, "type parameter requires go1.18 or later")
|
||||
}
|
||||
obj := NewTypeName(d.spec.Name.Pos(), pkg, d.spec.Name.Name, nil)
|
||||
|
|
@ -444,7 +444,7 @@ func (check *Checker) collectObjects() {
|
|||
}
|
||||
check.recordDef(d.decl.Name, obj)
|
||||
}
|
||||
if d.decl.Type.TypeParams.NumFields() != 0 && !check.allowVersion(pkg, 1, 18) && !hasTParamError {
|
||||
if d.decl.Type.TypeParams.NumFields() != 0 && !check.allowVersion(pkg, d.decl.Pos(), 1, 18) && !hasTParamError {
|
||||
check.softErrorf(d.decl.Type.TypeParams.List[0], UnsupportedFeature, "type parameter requires go1.18 or later")
|
||||
}
|
||||
info := &declInfo{file: fileScope, fdecl: d.decl}
|
||||
|
|
|
|||
|
|
@ -245,7 +245,7 @@ func computeInterfaceTypeSet(check *Checker, pos token.Pos, ityp *Interface) *_T
|
|||
}
|
||||
// check != nil
|
||||
check.later(func() {
|
||||
if !check.allowVersion(m.pkg, 1, 14) || !Identical(m.typ, other.Type()) {
|
||||
if !check.allowVersion(m.pkg, pos, 1, 14) || !Identical(m.typ, other.Type()) {
|
||||
check.errorf(atPos(pos), DuplicateDecl, "duplicate method %s", m.name)
|
||||
check.errorf(atPos(mpos[other.(*Func)]), DuplicateDecl, "\tother declaration of %s", m.name) // secondary error, \t indented
|
||||
}
|
||||
|
|
@ -276,7 +276,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.allowVersion(check.pkg, 1, 18) {
|
||||
if check != nil && check.isImportedConstraint(typ) && !check.allowVersion(check.pkg, pos, 1, 18) {
|
||||
check.errorf(atPos(pos), UnsupportedFeature, "embedding constraint interface %s requires go1.18 or later", typ)
|
||||
continue
|
||||
}
|
||||
|
|
@ -286,7 +286,7 @@ func computeInterfaceTypeSet(check *Checker, pos token.Pos, ityp *Interface) *_T
|
|||
}
|
||||
terms = tset.terms
|
||||
case *Union:
|
||||
if check != nil && !check.allowVersion(check.pkg, 1, 18) {
|
||||
if check != nil && !check.allowVersion(check.pkg, pos, 1, 18) {
|
||||
check.errorf(atPos(pos), UnsupportedFeature, "embedding interface element %s requires go1.18 or later", u)
|
||||
continue
|
||||
}
|
||||
|
|
@ -301,7 +301,7 @@ func computeInterfaceTypeSet(check *Checker, pos token.Pos, ityp *Interface) *_T
|
|||
if u == Typ[Invalid] {
|
||||
continue
|
||||
}
|
||||
if check != nil && !check.allowVersion(check.pkg, 1, 18) {
|
||||
if check != nil && !check.allowVersion(check.pkg, pos, 1, 18) {
|
||||
check.errorf(atPos(pos), UnsupportedFeature, "embedding non-interface type %s requires go1.18 or later", typ)
|
||||
continue
|
||||
}
|
||||
|
|
|
|||
|
|
@ -43,7 +43,7 @@ func (check *Checker) ident(x *operand, e *ast.Ident, def *Named, wantType bool)
|
|||
}
|
||||
return
|
||||
case universeAny, universeComparable:
|
||||
if !check.allowVersion(check.pkg, 1, 18) {
|
||||
if !check.allowVersion(check.pkg, e.Pos(), 1, 18) {
|
||||
check.versionErrorf(e, "go1.18", "predeclared %s", e.Name)
|
||||
return // avoid follow-on errors
|
||||
}
|
||||
|
|
@ -273,7 +273,7 @@ func (check *Checker) typInternal(e0 ast.Expr, def *Named) (T Type) {
|
|||
|
||||
case *ast.IndexExpr, *ast.IndexListExpr:
|
||||
ix := typeparams.UnpackIndexExpr(e)
|
||||
if !check.allowVersion(check.pkg, 1, 18) {
|
||||
if !check.allowVersion(check.pkg, e.Pos(), 1, 18) {
|
||||
check.softErrorf(inNode(e, ix.Lbrack), UnsupportedFeature, "type instantiation requires go1.18 or later")
|
||||
}
|
||||
return check.instantiatedType(ix, def)
|
||||
|
|
|
|||
|
|
@ -5,12 +5,10 @@
|
|||
package types
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"errors"
|
||||
"go/ast"
|
||||
"go/token"
|
||||
"internal/lazyregexp"
|
||||
. "internal/types/errors"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
|
|
@ -18,7 +16,7 @@ import (
|
|||
// 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, 1, 13) {
|
||||
if len(s) <= 2 || check.allowVersion(check.pkg, lit.Pos(), 1, 13) {
|
||||
return
|
||||
}
|
||||
// len(s) > 2
|
||||
|
|
@ -45,12 +43,21 @@ func (check *Checker) langCompat(lit *ast.BasicLit) {
|
|||
|
||||
// allowVersion reports whether the given package
|
||||
// is allowed to use version major.minor.
|
||||
func (check *Checker) allowVersion(pkg *Package, major, minor int) bool {
|
||||
func (check *Checker) allowVersion(pkg *Package, pos token.Pos, major, minor int) 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 the source file declares its Go version, use that to decide.
|
||||
if check.posVers != nil {
|
||||
if v, ok := check.posVers[check.fset.File(pos)]; ok && v.major >= 1 {
|
||||
return v.major > major || v.major == major && v.minor >= minor
|
||||
}
|
||||
}
|
||||
|
||||
// Otherwise fall back to the version in the checker.
|
||||
ma, mi := check.version.major, check.version.minor
|
||||
return ma == 0 && mi == 0 || ma > major || ma == major && mi >= minor
|
||||
}
|
||||
|
|
@ -59,6 +66,8 @@ type version struct {
|
|||
major, minor int
|
||||
}
|
||||
|
||||
var errVersionSyntax = errors.New("invalid Go version syntax")
|
||||
|
||||
// parseGoVersion parses a Go version string (such as "go1.12")
|
||||
// and returns the version, or an error. If s is the empty
|
||||
// string, the version is 0.0.
|
||||
|
|
@ -66,18 +75,52 @@ func parseGoVersion(s string) (v version, err error) {
|
|||
if s == "" {
|
||||
return
|
||||
}
|
||||
matches := goVersionRx.FindStringSubmatch(s)
|
||||
if matches == nil {
|
||||
err = fmt.Errorf(`should be something like "go1.12"`)
|
||||
if !strings.HasPrefix(s, "go") {
|
||||
return version{}, errVersionSyntax
|
||||
}
|
||||
s = s[len("go"):]
|
||||
i := 0
|
||||
for ; i < len(s) && '0' <= s[i] && s[i] <= '9'; i++ {
|
||||
if i >= 10 || i == 0 && s[i] == '0' {
|
||||
return version{}, errVersionSyntax
|
||||
}
|
||||
v.major = 10*v.major + int(s[i]) - '0'
|
||||
}
|
||||
if i > 0 && i == len(s) {
|
||||
return
|
||||
}
|
||||
v.major, err = strconv.Atoi(matches[1])
|
||||
if err != nil {
|
||||
if i == 0 || s[i] != '.' {
|
||||
return version{}, errVersionSyntax
|
||||
}
|
||||
s = s[i+1:]
|
||||
if s == "0" {
|
||||
// We really should not accept "go1.0",
|
||||
// but we didn't reject it from the start
|
||||
// and there are now programs that use it.
|
||||
// So accept it.
|
||||
return
|
||||
}
|
||||
v.minor, err = strconv.Atoi(matches[2])
|
||||
return
|
||||
i = 0
|
||||
for ; i < len(s) && '0' <= s[i] && s[i] <= '9'; i++ {
|
||||
if i >= 10 || i == 0 && s[i] == '0' {
|
||||
return version{}, errVersionSyntax
|
||||
}
|
||||
v.minor = 10*v.minor + int(s[i]) - '0'
|
||||
}
|
||||
if i > 0 && i == len(s) {
|
||||
return
|
||||
}
|
||||
return version{}, errVersionSyntax
|
||||
}
|
||||
|
||||
// goVersionRx matches a Go version string, e.g. "go1.12".
|
||||
var goVersionRx = lazyregexp.New(`^go([1-9]\d*)\.(0|[1-9]\d*)$`)
|
||||
func (v version) equal(u version) bool {
|
||||
return v.major == u.major && v.minor == u.minor
|
||||
}
|
||||
|
||||
func (v version) before(u version) bool {
|
||||
return v.major < u.major || v.major == u.major && v.minor < u.minor
|
||||
}
|
||||
|
||||
func (v version) after(u version) bool {
|
||||
return v.major > u.major || v.major == u.major && v.minor > u.minor
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,17 @@
|
|||
// -lang=go1.19
|
||||
|
||||
// Copyright 2022 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.
|
||||
|
||||
// Check Go language version-specific errors.
|
||||
|
||||
//go:build go1.20
|
||||
|
||||
package p
|
||||
|
||||
type Slice []byte
|
||||
type Array [8]byte
|
||||
|
||||
var s Slice
|
||||
var p = (Array)(s /* ok */)
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
// -lang=go1.20
|
||||
|
||||
// Copyright 2022 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.
|
||||
|
||||
// Check Go language version-specific errors.
|
||||
|
||||
//go:build go1.19
|
||||
|
||||
package p
|
||||
|
||||
type Slice []byte
|
||||
type Array [8]byte
|
||||
|
||||
var s Slice
|
||||
var p = (Array)(s /* ok because Go 1.20 ignored the //go:build go1.19 */)
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
// -lang=go1.21
|
||||
|
||||
// Copyright 2022 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.
|
||||
|
||||
// Check Go language version-specific errors.
|
||||
|
||||
//go:build go1.19
|
||||
|
||||
package p
|
||||
|
||||
type Slice []byte
|
||||
type Array [8]byte
|
||||
|
||||
var s Slice
|
||||
var p = (Array)(s /* ERROR "requires go1.20 or later" */)
|
||||
Loading…
Reference in New Issue