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
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.
// 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.
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
}
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
}
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
}
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
}
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
}
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
}
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")
}
// 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")
}
// 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 {
// (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
// 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.
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
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
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())
// 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")
}
// 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
// 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)
}
}
//
// 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] {
// 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 {
import (
"bytes"
+ "cmd/compile/internal/syntax"
"strings"
)
// 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
// 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
// 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"
}
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)
}
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}
}
// 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)
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
}
}
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
}
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
}
}
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
}
}
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)
import (
"cmd/compile/internal/syntax"
- "fmt"
- "internal/lazyregexp"
- "strconv"
+ "errors"
"strings"
)
// 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
// 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.
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
+ }
+ 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.major, err = strconv.Atoi(matches[1])
- if err != nil {
+ 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
}
- v.minor, err = strconv.Atoi(matches[2])
- 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
+}
< 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;
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
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.
// 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.
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
}
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
}
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
}
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
}
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
}
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
}
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")
}
// 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")
}
// 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)
// (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
// 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.
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
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
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())
// 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")
}
// 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
// "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) },
// 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)
}
}
//
// 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] {
// 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 {
import (
"bytes"
+ "go/token"
"strings"
)
// 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
// 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
// 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"
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)
}
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}
}
// 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
}
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
}
}
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
}
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
}
}
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
}
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)
package types
import (
- "fmt"
+ "errors"
"go/ast"
"go/token"
- "internal/lazyregexp"
. "internal/types/errors"
- "strconv"
"strings"
)
// 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
// 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
}
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.
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
+}
+
+func (v version) equal(u version) bool {
+ return v.major == u.major && v.minor == u.minor
}
-// 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) 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
+}
--- /dev/null
+// -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 */)
--- /dev/null
+// -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 */)
--- /dev/null
+// -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" */)