]> Cypherpunks repositories - gostls13.git/commitdiff
go/types, types2: combine version check with version error reporting
authorRobert Griesemer <gri@golang.org>
Tue, 2 May 2023 21:09:45 +0000 (14:09 -0700)
committerGopher Robot <gobot@golang.org>
Wed, 3 May 2023 19:36:23 +0000 (19:36 +0000)
This removes the duplicate (and possible error-prone) versions
(once for test and once for error message) and simplifies code.

Adjusted multiple go/types call sites to match types2.

Renamed posFor to atPos in types2, for closer match with go/types
and to keep automatic generation of instantiate.go working.

Change-Id: Iff428fc742f305a65bb7d077b7e31b66df3b706d
Reviewed-on: https://go-review.googlesource.com/c/go/+/491715
Reviewed-by: Robert Griesemer <gri@google.com>
Run-TryBot: Robert Griesemer <gri@google.com>
Reviewed-by: Robert Findley <rfindley@google.com>
Auto-Submit: Robert Griesemer <gri@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>

24 files changed:
src/cmd/compile/internal/types2/builtins.go
src/cmd/compile/internal/types2/call.go
src/cmd/compile/internal/types2/check.go
src/cmd/compile/internal/types2/conversions.go
src/cmd/compile/internal/types2/decl.go
src/cmd/compile/internal/types2/errors.go
src/cmd/compile/internal/types2/expr.go
src/cmd/compile/internal/types2/instantiate.go
src/cmd/compile/internal/types2/interface.go
src/cmd/compile/internal/types2/resolver.go
src/cmd/compile/internal/types2/typeset.go
src/cmd/compile/internal/types2/typexpr.go
src/cmd/compile/internal/types2/version.go
src/go/types/builtins.go
src/go/types/call.go
src/go/types/conversions.go
src/go/types/decl.go
src/go/types/expr.go
src/go/types/instantiate.go
src/go/types/resolver.go
src/go/types/typeset.go
src/go/types/typexpr.go
src/go/types/version.go
src/internal/types/testdata/fixedbugs/issue59338a.go

index 915eb2db9e6ebb95e4a36538921ffdfb78d7118d..51a3023bc5a8c90e9bcc830202ef5d61f4de260d 100644 (file)
@@ -234,8 +234,7 @@ func (check *Checker) builtin(x *operand, call *syntax.CallExpr, id builtinId) (
 
        case _Clear:
                // clear(m)
-               if !check.allowVersion(check.pkg, call.Pos(), 1, 21) {
-                       check.versionErrorf(call.Fun, "go1.21", "clear")
+               if !check.allowVersionf(check.pkg, call.Fun, 1, 21, "clear") {
                        return
                }
 
@@ -626,8 +625,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, call.Pos(), 1, 17) {
-                       check.versionErrorf(call.Fun, "go1.17", "unsafe.Add")
+               if !check.allowVersionf(check.pkg, call.Fun, 1, 17, "unsafe.Add") {
                        return
                }
 
@@ -762,8 +760,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, call.Pos(), 1, 17) {
-                       check.versionErrorf(call.Fun, "go1.17", "unsafe.Slice")
+               if !check.allowVersionf(check.pkg, call.Fun, 1, 17, "unsafe.Slice") {
                        return
                }
 
@@ -787,8 +784,7 @@ func (check *Checker) builtin(x *operand, call *syntax.CallExpr, id builtinId) (
 
        case _SliceData:
                // unsafe.SliceData(slice []T) *T
-               if !check.allowVersion(check.pkg, call.Pos(), 1, 20) {
-                       check.versionErrorf(call.Fun, "go1.20", "unsafe.SliceData")
+               if !check.allowVersionf(check.pkg, call.Fun, 1, 20, "unsafe.SliceData") {
                        return
                }
 
@@ -806,8 +802,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, call.Pos(), 1, 20) {
-                       check.versionErrorf(call.Fun, "go1.20", "unsafe.String")
+               if !check.allowVersionf(check.pkg, call.Fun, 1, 20, "unsafe.String") {
                        return
                }
 
@@ -830,8 +825,7 @@ func (check *Checker) builtin(x *operand, call *syntax.CallExpr, id builtinId) (
 
        case _StringData:
                // unsafe.StringData(str string) *byte
-               if !check.allowVersion(check.pkg, call.Pos(), 1, 20) {
-                       check.versionErrorf(call.Fun, "go1.20", "unsafe.StringData")
+               if !check.allowVersionf(check.pkg, call.Fun, 1, 20, "unsafe.StringData") {
                        return
                }
 
@@ -871,7 +865,7 @@ func (check *Checker) builtin(x *operand, call *syntax.CallExpr, id builtinId) (
                // Note: trace is only available in self-test mode.
                // (no argument evaluated yet)
                if nargs == 0 {
-                       check.dump("%v: trace() without arguments", posFor(call))
+                       check.dump("%v: trace() without arguments", atPos(call))
                        x.mode = novalue
                        break
                }
@@ -879,7 +873,7 @@ func (check *Checker) builtin(x *operand, call *syntax.CallExpr, id builtinId) (
                x1 := x
                for _, arg := range call.ArgList {
                        check.rawExpr(nil, x1, arg, nil, false) // permit trace for types, e.g.: new(trace(T))
-                       check.dump("%v: %s", posFor(x1), x1)
+                       check.dump("%v: %s", atPos(x1), x1)
                        x1 = &t // use incoming x only for first argument
                }
                // trace is only available in test mode - no need to record signature
index 8ad7744ab4afdb916c5e66c1e6bf91428fc59932..50529cd0ee940a55c6234a9650c02bb979e8c790 100644 (file)
@@ -23,17 +23,13 @@ import (
 func (check *Checker) funcInst(tsig *Signature, pos syntax.Pos, x *operand, inst *syntax.IndexExpr) {
        assert(tsig != nil || inst != nil)
 
-       var versionErr bool  // set if version error was reported
-       var instErrPos poser // position for instantion error
+       var instErrPos poser
        if inst != nil {
                instErrPos = inst.Pos()
        } else {
                instErrPos = pos
        }
-       if !check.allowVersion(check.pkg, pos, 1, 18) {
-               check.versionErrorf(instErrPos, "go1.18", "function instantiation")
-               versionErr = true
-       }
+       versionErr := !check.allowVersionf(check.pkg, instErrPos, 1, 18, "function instantiation")
 
        // targs and xlist are the type arguments and corresponding type expressions, or nil.
        var targs []Type
@@ -74,7 +70,7 @@ func (check *Checker) funcInst(tsig *Signature, pos syntax.Pos, x *operand, inst
                        // of a synthetic function f where f's parameters are the parameters and results
                        // of x and where the arguments to the call of f are values of the parameter and
                        // result types of x.
-                       if !versionErr && !check.allowVersion(check.pkg, pos, 1, 21) {
+                       if !versionErr && !check.allowVersion(check.pkg, instErrPos, 1, 21) {
                                if inst != nil {
                                        check.versionErrorf(instErrPos, "go1.21", "partially instantiated function in assignment")
                                } else {
@@ -296,9 +292,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, x.Pos(), 1, 18) {
-                               check.versionErrorf(inst.Pos(), "go1.18", "function instantiation")
-                       }
+                       check.allowVersionf(check.pkg, inst, 1, 18, "function instantiation")
 
                        sig = check.instantiateSignature(inst.Pos(), sig, targs, xlist)
                        assert(sig.TypeParams().Len() == 0) // signature is not generic anymore
@@ -489,9 +483,9 @@ func (check *Checker) arguments(call *syntax.CallExpr, sig *Signature, targs []T
        if n > 0 {
                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")
+                               check.versionErrorf(iexpr, "go1.18", "function instantiation")
                        } else {
-                               check.versionErrorf(call.Pos(), "go1.18", "implicit function instantiation")
+                               check.versionErrorf(call, "go1.18", "implicit function instantiation")
                        }
                }
                // rename type parameters to avoid problems with recursive calls
@@ -510,10 +504,8 @@ func (check *Checker) arguments(call *syntax.CallExpr, sig *Signature, targs []T
                        }
                }
        }
-       if len(genericArgs) > 0 && !check.allowVersion(check.pkg, call.Pos(), 1, 21) {
-               // at the moment we only support implicit instantiations of argument functions
-               check.versionErrorf(args[genericArgs[0]].Pos(), "go1.21", "implicitly instantiated function as argument")
-       }
+       // at the moment we only support implicit instantiations of argument functions
+       _ = len(genericArgs) > 0 && check.allowVersionf(check.pkg, args[genericArgs[0]], 1, 21, "implicitly instantiated function as argument")
 
        // tparams holds the type parameters of the callee and generic function arguments, if any:
        // the first n type parameters belong to the callee, followed by mi type parameters for each
@@ -673,7 +665,7 @@ func (check *Checker) selector(x *operand, e *syntax.SelectorExpr, def *Named, w
                                x.typ = exp.typ
                                x.id = exp.id
                        default:
-                               check.dump("%v: unexpected object %v", posFor(e.Sel), exp)
+                               check.dump("%v: unexpected object %v", atPos(e.Sel), exp)
                                unreachable()
                        }
                        x.expr = e
index 550fb1cafdaa046b3317bf665816288485204bae..b2a9eb0dbc4c1de5d78f5f77edca0db76e267f51 100644 (file)
@@ -463,7 +463,7 @@ func (check *Checker) recordUntyped() {
 
        for x, info := range check.untyped {
                if debug && isTyped(info.typ) {
-                       check.dump("%v: %s (type %s) is typed", posFor(x), x, info.typ)
+                       check.dump("%v: %s (type %s) is typed", atPos(x), x, info.typ)
                        unreachable()
                }
                check.recordTypeAndValue(x, info.mode, info.typ, info.val)
index 7cb7d490be3250f1e00791e091b8e19059af3190..57c54f1ef20a00b8ab6fadc38eb50d72e317d5d4 100644 (file)
@@ -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, x.Pos(), 1, 20) {
+                               if check == nil || check.allowVersion(check.pkg, x, 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, x.Pos(), 1, 17) {
+                                       if check == nil || check.allowVersion(check.pkg, x, 1, 17) {
                                                return true
                                        }
                                        // check != nil
index dd39c42037f949f0bb0116d810ddd5720ae33c62..7760f17008c170371e46cf44d692ee1ef029576a 100644 (file)
@@ -492,9 +492,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, tdecl.Pos(), 1, 18) {
-                       check.versionErrorf(tdecl.Type, "go1.18", "using type constraint %s", rhs)
-               }
+               _ = check.isImportedConstraint(rhs) && check.allowVersionf(check.pkg, tdecl.Type, 1, 18, "using type constraint %s", rhs)
        }).describef(obj, "validType(%s)", obj.Name())
 
        alias := tdecl.Alias
@@ -507,10 +505,7 @@ func (check *Checker) typeDecl(obj *TypeName, tdecl *syntax.TypeDecl, def *Named
 
        // alias declaration
        if alias {
-               if !check.allowVersion(check.pkg, tdecl.Pos(), 1, 9) {
-                       check.versionErrorf(tdecl, "go1.9", "type aliases")
-               }
-
+               check.allowVersionf(check.pkg, tdecl, 1, 9, "type aliases")
                check.brokenAlias(obj)
                rhs = check.typ(tdecl.Type)
                check.validAlias(obj, rhs)
index bbe4cc3feaa63adfe5f8156af460fec796e71530..3501b213edcf37e56df13e8cd1581943236a384f 100644 (file)
@@ -87,7 +87,7 @@ func (err *error_) String() string {
 // errorf adds formatted error information to err.
 // It may be called multiple times to provide additional information.
 func (err *error_) errorf(at poser, format string, args ...interface{}) {
-       err.desc = append(err.desc, errorDesc{posFor(at), format, args})
+       err.desc = append(err.desc, errorDesc{atPos(at), format, args})
 }
 
 func sprintf(qf Qualifier, tpSubscripts bool, format string, args ...interface{}) string {
@@ -237,7 +237,7 @@ func (check *Checker) err(at poser, code Code, msg string, soft bool) {
                return
        }
 
-       pos := posFor(at)
+       pos := atPos(at)
 
        // If we are encountering an error while evaluating an inherited
        // constant initialization expression, pos is the position of in
@@ -293,8 +293,8 @@ func (check *Checker) versionErrorf(at poser, goVersion string, format string, a
        check.err(at, UnsupportedFeature, msg, true)
 }
 
-// posFor reports the left (= start) position of at.
-func posFor(at poser) syntax.Pos {
+// atPos reports the left (= start) position of at.
+func atPos(at poser) syntax.Pos {
        switch x := at.(type) {
        case *operand:
                if x.expr != nil {
index 51b944eeade7d51beeeb2eb197d8fb2cb632782d..b240dae5587932d58d20e58fc31826122901620d 100644 (file)
@@ -538,7 +538,7 @@ func (check *Checker) updateExprType0(parent, x syntax.Expr, typ Type, final boo
                // The respective sub-expressions got their final types
                // upon assignment or use.
                if debug {
-                       check.dump("%v: found old type(%s): %s (new: %s)", posFor(x), x, old.typ, typ)
+                       check.dump("%v: found old type(%s): %s (new: %s)", atPos(x), x, old.typ, typ)
                        unreachable()
                }
                return
@@ -977,8 +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, x.Pos(), 1, 13) {
-                               check.versionErrorf(y, "go1.13", invalidOp+"signed shift count %s", y)
+                       if !allUnsigned(y.typ) && !check.allowVersionf(check.pkg, y, 1, 13, invalidOp+"signed shift count %s", y) {
                                x.mode = invalid
                                return
                        }
@@ -1761,7 +1760,7 @@ func (check *Checker) exprInternal(T Type, x *operand, e syntax.Expr, hint Type)
                // types, which are comparatively rare.
 
        default:
-               panic(fmt.Sprintf("%s: unknown expression type %T", posFor(e), e))
+               panic(fmt.Sprintf("%s: unknown expression type %T", atPos(e), e))
        }
 
        // everything went well
index 7329fffc867ebcf9e084859598a864c7f7f3ae93..fdce74ef213b83406b718440f11595f71fc9a9d2 100644 (file)
@@ -262,7 +262,7 @@ func (check *Checker) implements(pos syntax.Pos, V, T Type, constraint bool, cau
                // so that ordinary, non-type parameter interfaces implement comparable.
                if constraint && comparable(V, true /* spec comparability */, nil, nil) {
                        // V is comparable if we are at Go 1.20 or higher.
-                       if check == nil || check.allowVersion(check.pkg, pos, 1, 20) {
+                       if check == nil || check.allowVersion(check.pkg, atPos(pos), 1, 20) { // atPos needed so that go/types generate passes
                                return true
                        }
                        if cause != nil {
index 0978989424e854f07ebc4a9f74036cc72d00edce..872a3217c22d1bde37907ae258030d8f13f12554 100644 (file)
@@ -127,7 +127,7 @@ func (check *Checker) interfaceType(ityp *Interface, iface *syntax.InterfaceType
 
        for _, f := range iface.MethodList {
                if f.Name == nil {
-                       addEmbedded(posFor(f.Type), parseUnion(check, f.Type))
+                       addEmbedded(atPos(f.Type), parseUnion(check, f.Type))
                        continue
                }
                // f.Name != nil
index cfaca2b6659821d17b8299352cc15171e7088772..a4de484ed818cb19c899287c330f605e975cc4be 100644 (file)
@@ -406,9 +406,7 @@ func (check *Checker) collectObjects() {
                                }
 
                        case *syntax.TypeDecl:
-                               if len(s.TParamList) != 0 && !check.allowVersion(pkg, s.Pos(), 1, 18) {
-                                       check.versionErrorf(s.TParamList[0], "go1.18", "type parameter")
-                               }
+                               _ = len(s.TParamList) != 0 && check.allowVersionf(pkg, s.TParamList[0], 1, 18, "type parameter")
                                obj := NewTypeName(s.Name.Pos(), pkg, s.Name.Value, nil)
                                check.declarePkgObj(s.Name, obj, &declInfo{file: fileScope, tdecl: s})
 
@@ -455,9 +453,7 @@ func (check *Checker) collectObjects() {
                                        }
                                        check.recordDef(s.Name, obj)
                                }
-                               if len(s.TParamList) != 0 && !check.allowVersion(pkg, s.Pos(), 1, 18) && !hasTParamError {
-                                       check.versionErrorf(s.TParamList[0], "go1.18", "type parameter")
-                               }
+                               _ = len(s.TParamList) != 0 && !hasTParamError && check.allowVersionf(pkg, s.TParamList[0], 1, 18, "type parameter")
                                info := &declInfo{file: fileScope, fdecl: s}
                                // Methods are not package-level objects but we still track them in the
                                // object map so that we can handle them like regular functions (if the
index 26c20cb38028cd4f0da79c87afb25a8eac9eb5af..eaeb126ec210776fd0743e85a313dca4329e400c 100644 (file)
@@ -278,8 +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, pos, 1, 18) {
-                               check.versionErrorf(pos, "go1.18", "embedding constraint interface %s", typ)
+                       if check != nil && check.isImportedConstraint(typ) && !check.allowVersionf(check.pkg, pos, 1, 18, "embedding constraint interface %s", typ) {
                                continue
                        }
                        comparable = tset.comparable
@@ -288,8 +287,7 @@ func computeInterfaceTypeSet(check *Checker, pos syntax.Pos, ityp *Interface) *_
                        }
                        terms = tset.terms
                case *Union:
-                       if check != nil && !check.allowVersion(check.pkg, pos, 1, 18) {
-                               check.versionErrorf(pos, "go1.18", "embedding interface element %s", u)
+                       if check != nil && !check.allowVersionf(check.pkg, pos, 1, 18, "embedding interface element %s", u) {
                                continue
                        }
                        tset := computeUnionTypeSet(check, unionSets, pos, u)
@@ -303,8 +301,7 @@ func computeInterfaceTypeSet(check *Checker, pos syntax.Pos, ityp *Interface) *_
                        if u == Typ[Invalid] {
                                continue
                        }
-                       if check != nil && !check.allowVersion(check.pkg, pos, 1, 18) {
-                               check.versionErrorf(pos, "go1.18", "embedding non-interface type %s", typ)
+                       if check != nil && !check.allowVersionf(check.pkg, pos, 1, 18, "embedding non-interface type %s", typ) {
                                continue
                        }
                        terms = termlist{{false, typ}}
index f9734546455c282283486d038c94a0c48b745ac2..99b6daf90eb44621ad89f5f6fdda1423ae812c80 100644 (file)
@@ -42,8 +42,7 @@ func (check *Checker) ident(x *operand, e *syntax.Name, def *Named, wantType boo
                }
                return
        case universeAny, universeComparable:
-               if !check.allowVersion(check.pkg, e.Pos(), 1, 18) {
-                       check.versionErrorf(e, "go1.18", "predeclared %s", e.Value)
+               if !check.allowVersionf(check.pkg, e, 1, 18, "predeclared %s", e.Value) {
                        return // avoid follow-on errors
                }
        }
@@ -272,9 +271,7 @@ func (check *Checker) typInternal(e0 syntax.Expr, def *Named) (T Type) {
                }
 
        case *syntax.IndexExpr:
-               if !check.allowVersion(check.pkg, e.Pos(), 1, 18) {
-                       check.versionErrorf(e.Pos(), "go1.18", "type instantiation")
-               }
+               check.allowVersionf(check.pkg, e, 1, 18, "type instantiation")
                return check.instantiatedType(e.X, unpackExpr(e.Index), def)
 
        case *syntax.ParenExpr:
index 37e86a2bb4c4e4dd78c7cfd4177133e0420638e5..2c70fa11a11b788a3540dec7a0d288084115bc4a 100644 (file)
@@ -7,6 +7,7 @@ package types2
 import (
        "cmd/compile/internal/syntax"
        "errors"
+       "fmt"
        "strings"
 )
 
@@ -41,7 +42,7 @@ 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, pos syntax.Pos, major, minor int) bool {
+func (check *Checker) allowVersion(pkg *Package, at poser, 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 {
@@ -50,7 +51,7 @@ func (check *Checker) allowVersion(pkg *Package, pos syntax.Pos, major, minor in
 
        // 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 {
+               if v, ok := check.posVers[base(at.Pos())]; ok && v.major >= 1 {
                        return v.major > major || v.major == major && v.minor >= minor
                }
        }
@@ -60,6 +61,16 @@ func (check *Checker) allowVersion(pkg *Package, pos syntax.Pos, major, minor in
        return ma == 0 && mi == 0 || ma > major || ma == major && mi >= minor
 }
 
+// allowVersionf is like allowVersion but also accepts a format string and arguments
+// which are used to report a version error if allowVersion returns false.
+func (check *Checker) allowVersionf(pkg *Package, at poser, major, minor int, format string, args ...interface{}) bool {
+       if !check.allowVersion(pkg, at, major, minor) {
+               check.versionErrorf(at, fmt.Sprintf("go%d.%d", major, minor), format, args...)
+               return false
+       }
+       return true
+}
+
 // 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 {
index eb9b3ed3b2f0917abe3ab9ef2e38432be531b721..0e8f843468d148a4743162619ac52614e6f6eddc 100644 (file)
@@ -235,8 +235,7 @@ func (check *Checker) builtin(x *operand, call *ast.CallExpr, id builtinId) (_ b
 
        case _Clear:
                // clear(m)
-               if !check.allowVersion(check.pkg, call.Pos(), 1, 21) {
-                       check.error(call.Fun, UnsupportedFeature, "clear requires go1.21 or later")
+               if !check.allowVersionf(check.pkg, call.Fun, 1, 21, "clear") {
                        return
                }
 
@@ -627,8 +626,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, call.Pos(), 1, 17) {
-                       check.error(call.Fun, UnsupportedFeature, "unsafe.Add requires go1.17 or later")
+               if !check.allowVersionf(check.pkg, call.Fun, 1, 17, "unsafe.Add") {
                        return
                }
 
@@ -763,8 +761,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, call.Pos(), 1, 17) {
-                       check.error(call.Fun, UnsupportedFeature, "unsafe.Slice requires go1.17 or later")
+               if !check.allowVersionf(check.pkg, call.Fun, 1, 17, "unsafe.Slice") {
                        return
                }
 
@@ -788,8 +785,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, call.Pos(), 1, 20) {
-                       check.error(call.Fun, UnsupportedFeature, "unsafe.SliceData requires go1.20 or later")
+               if !check.allowVersionf(check.pkg, call.Fun, 1, 20, "unsafe.SliceData") {
                        return
                }
 
@@ -807,8 +803,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, call.Pos(), 1, 20) {
-                       check.error(call.Fun, UnsupportedFeature, "unsafe.String requires go1.20 or later")
+               if !check.allowVersionf(check.pkg, call.Fun, 1, 20, "unsafe.String") {
                        return
                }
 
@@ -831,8 +826,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, call.Pos(), 1, 20) {
-                       check.error(call.Fun, UnsupportedFeature, "unsafe.StringData requires go1.20 or later")
+               if !check.allowVersionf(check.pkg, call.Fun, 1, 20, "unsafe.StringData") {
                        return
                }
 
index 02b6038cccf23260d4a8104575abac2dea49fec0..c65ef8ceac77bd73f58f2ffc6ff0cabf7e328d01 100644 (file)
@@ -25,17 +25,13 @@ import (
 func (check *Checker) funcInst(tsig *Signature, pos token.Pos, x *operand, ix *typeparams.IndexExpr) {
        assert(tsig != nil || ix != nil)
 
-       var versionErr bool       // set if version error was reported
-       var instErrPos positioner // position for instantion error
+       var instErrPos positioner
        if ix != nil {
                instErrPos = inNode(ix.Orig, ix.Lbrack)
        } else {
                instErrPos = atPos(pos)
        }
-       if !check.allowVersion(check.pkg, pos, 1, 18) {
-               check.softErrorf(instErrPos, UnsupportedFeature, "function instantiation requires go1.18 or later")
-               versionErr = true
-       }
+       versionErr := !check.allowVersionf(check.pkg, instErrPos, 1, 18, "function instantiation")
 
        // targs and xlist are the type arguments and corresponding type expressions, or nil.
        var targs []Type
@@ -76,11 +72,11 @@ func (check *Checker) funcInst(tsig *Signature, pos token.Pos, x *operand, ix *t
                        // of a synthetic function f where f's parameters are the parameters and results
                        // of x and where the arguments to the call of f are values of the parameter and
                        // result types of x.
-                       if !versionErr && !check.allowVersion(check.pkg, pos, 1, 21) {
+                       if !versionErr && !check.allowVersion(check.pkg, instErrPos, 1, 21) {
                                if ix != nil {
-                                       check.softErrorf(instErrPos, UnsupportedFeature, "partially instantiated function in assignment requires go1.21 or later")
+                                       check.versionErrorf(instErrPos, "go1.21", "partially instantiated function in assignment")
                                } else {
-                                       check.softErrorf(instErrPos, UnsupportedFeature, "implicitly instantiated function in assignment requires go1.21 or later")
+                                       check.versionErrorf(instErrPos, "go1.21", "implicitly instantiated function in assignment")
                                }
                        }
                        n := tsig.params.Len()
@@ -301,9 +297,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, ix.Pos(), 1, 18) {
-                               check.softErrorf(inNode(call.Fun, ix.Lbrack), UnsupportedFeature, "function instantiation requires go1.18 or later")
-                       }
+                       check.allowVersionf(check.pkg, atPos(ix.Lbrack), 1, 18, "function instantiation")
 
                        sig = check.instantiateSignature(ix.Pos(), sig, targs, xlist)
                        assert(sig.TypeParams().Len() == 0) // signature is not generic anymore
@@ -488,13 +482,13 @@ func (check *Checker) arguments(call *ast.CallExpr, sig *Signature, targs []Type
        // collect type parameters of callee
        n := sig.TypeParams().Len()
        if n > 0 {
-               if !check.allowVersion(check.pkg, call.Pos(), 1, 18) {
+               if !check.allowVersion(check.pkg, call, 1, 18) {
                        switch call.Fun.(type) {
                        case *ast.IndexExpr, *ast.IndexListExpr:
                                ix := typeparams.UnpackIndexExpr(call.Fun)
-                               check.softErrorf(inNode(call.Fun, ix.Lbrack), UnsupportedFeature, "function instantiation requires go1.18 or later")
+                               check.versionErrorf(inNode(call.Fun, ix.Lbrack), "go1.18", "function instantiation")
                        default:
-                               check.softErrorf(inNode(call, call.Lparen), UnsupportedFeature, "implicit function instantiation requires go1.18 or later")
+                               check.versionErrorf(inNode(call, call.Lparen), "go1.18", "implicit function instantiation")
                        }
                }
                // rename type parameters to avoid problems with recursive calls
@@ -513,10 +507,8 @@ func (check *Checker) arguments(call *ast.CallExpr, sig *Signature, targs []Type
                        }
                }
        }
-       if len(genericArgs) > 0 && !check.allowVersion(check.pkg, call.Pos(), 1, 21) {
-               // at the moment we only support implicit instantiations of argument functions
-               check.softErrorf(inNode(call, call.Lparen), UnsupportedFeature, "implicitly instantiated function as argument requires go1.21 or later")
-       }
+       // at the moment we only support implicit instantiations of argument functions
+       _ = len(genericArgs) > 0 && check.allowVersionf(check.pkg, args[genericArgs[0]], 1, 21, "implicitly instantiated function as argument")
 
        // tparams holds the type parameters of the callee and generic function arguments, if any:
        // the first n type parameters belong to the callee, followed by mi type parameters for each
index 92ae8196c5a447a29acf91b683d68cc56f7b5943..c9a941fa26697481b5f9bf9ddf9825275b93ab3b 100644 (file)
@@ -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, x.Pos(), 1, 20) {
+                               if check == nil || check.allowVersion(check.pkg, x, 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, x.Pos(), 1, 17) {
+                                       if check == nil || check.allowVersion(check.pkg, x, 1, 17) {
                                                return true
                                        }
                                        // check != nil
index c18c7c1ae106cbe5266bb3fe355797b16d07bb66..47421ca7f22a3096d0b905e5d1f3b2f0037308e5 100644 (file)
@@ -561,9 +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, tdecl.Pos(), 1, 18) {
-                       check.errorf(tdecl.Type, UnsupportedFeature, "using type constraint %s requires go1.18 or later", rhs)
-               }
+               _ = check.isImportedConstraint(rhs) && check.allowVersionf(check.pkg, tdecl.Type, 1, 18, "using type constraint %s", rhs)
        }).describef(obj, "validType(%s)", obj.Name())
 
        alias := tdecl.Assign.IsValid()
@@ -576,10 +574,7 @@ func (check *Checker) typeDecl(obj *TypeName, tdecl *ast.TypeSpec, def *Named) {
 
        // alias declaration
        if alias {
-               if !check.allowVersion(check.pkg, tdecl.Pos(), 1, 9) {
-                       check.error(atPos(tdecl.Assign), UnsupportedFeature, "type aliases requires go1.9 or later")
-               }
-
+               check.allowVersionf(check.pkg, atPos(tdecl.Assign), 1, 9, "type aliases")
                check.brokenAlias(obj)
                rhs = check.typ(tdecl.Type)
                check.validAlias(obj, rhs)
index 891153ba8d1a672a20c5b92a4ecb2b2f238de943..cede9f566c42774ef213a251f37cd2f0164bf7a6 100644 (file)
@@ -955,8 +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, x.Pos(), 1, 13) {
-                               check.errorf(y, UnsupportedFeature, invalidOp+"signed shift count %s requires go1.13 or later", y)
+                       if !allUnsigned(y.typ) && !check.allowVersionf(check.pkg, y, 1, 13, invalidOp+"signed shift count %s", y) {
                                x.mode = invalid
                                return
                        }
index 652511ffd882a8d40ea4257df616ebbde619d358..8f9f3f52bf0dc24a07b4d75811f486ada6b724ba 100644 (file)
@@ -264,7 +264,7 @@ func (check *Checker) implements(pos token.Pos, V, T Type, constraint bool, caus
                // so that ordinary, non-type parameter interfaces implement comparable.
                if constraint && comparable(V, true /* spec comparability */, nil, nil) {
                        // V is comparable if we are at Go 1.20 or higher.
-                       if check == nil || check.allowVersion(check.pkg, pos, 1, 20) {
+                       if check == nil || check.allowVersion(check.pkg, atPos(pos), 1, 20) { // atPos needed so that go/types generate passes
                                return true
                        }
                        if cause != nil {
index d0875f59611712e38f9db2aedf3e5a38c4f8af91..56c26abc11deb1af5c019517716662352274f585 100644 (file)
@@ -386,9 +386,7 @@ func (check *Checker) collectObjects() {
                                        check.declarePkgObj(name, obj, di)
                                }
                        case typeDecl:
-                               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")
-                               }
+                               _ = d.spec.TypeParams.NumFields() != 0 && check.allowVersionf(pkg, d.spec.TypeParams.List[0], 1, 18, "type parameter")
                                obj := NewTypeName(d.spec.Name.Pos(), pkg, d.spec.Name.Name, nil)
                                check.declarePkgObj(d.spec.Name, obj, &declInfo{file: fileScope, tdecl: d.spec})
                        case funcDecl:
@@ -444,9 +442,7 @@ func (check *Checker) collectObjects() {
                                        }
                                        check.recordDef(d.decl.Name, obj)
                                }
-                               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")
-                               }
+                               _ = d.decl.Type.TypeParams.NumFields() != 0 && !hasTParamError && check.allowVersionf(pkg, d.decl.Type.TypeParams.List[0], 1, 18, "type parameter")
                                info := &declInfo{file: fileScope, fdecl: d.decl}
                                // Methods are not package-level objects but we still track them in the
                                // object map so that we can handle them like regular functions (if the
index 3f0b81419afcdae7349805f5ac883c5494f6564a..17ef710cd18efbc65d4459ac0e8040ffceebda4d 100644 (file)
@@ -245,7 +245,7 @@ func computeInterfaceTypeSet(check *Checker, pos token.Pos, ityp *Interface) *_T
                        }
                        // check != nil
                        check.later(func() {
-                               if !check.allowVersion(m.pkg, pos, 1, 14) || !Identical(m.typ, other.Type()) {
+                               if !check.allowVersion(m.pkg, atPos(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,8 +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, pos, 1, 18) {
-                               check.errorf(atPos(pos), UnsupportedFeature, "embedding constraint interface %s requires go1.18 or later", typ)
+                       if check != nil && check.isImportedConstraint(typ) && !check.allowVersionf(check.pkg, atPos(pos), 1, 18, "embedding constraint interface %s", typ) {
                                continue
                        }
                        comparable = tset.comparable
@@ -286,8 +285,7 @@ func computeInterfaceTypeSet(check *Checker, pos token.Pos, ityp *Interface) *_T
                        }
                        terms = tset.terms
                case *Union:
-                       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)
+                       if check != nil && !check.allowVersionf(check.pkg, atPos(pos), 1, 18, "embedding interface element %s", u) {
                                continue
                        }
                        tset := computeUnionTypeSet(check, unionSets, pos, u)
@@ -301,8 +299,7 @@ func computeInterfaceTypeSet(check *Checker, pos token.Pos, ityp *Interface) *_T
                        if u == Typ[Invalid] {
                                continue
                        }
-                       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)
+                       if check != nil && !check.allowVersionf(check.pkg, atPos(pos), 1, 18, "embedding non-interface type %s", typ) {
                                continue
                        }
                        terms = termlist{{false, typ}}
index d01289a9d1b4d51bb9097b5fc43094a8d61d5ba4..5f254167ecf2b47c1312a620fe07ca55313c0975 100644 (file)
@@ -43,8 +43,7 @@ func (check *Checker) ident(x *operand, e *ast.Ident, def *Named, wantType bool)
                }
                return
        case universeAny, universeComparable:
-               if !check.allowVersion(check.pkg, e.Pos(), 1, 18) {
-                       check.versionErrorf(e, "go1.18", "predeclared %s", e.Name)
+               if !check.allowVersionf(check.pkg, e, 1, 18, "predeclared %s", e.Name) {
                        return // avoid follow-on errors
                }
        }
@@ -273,9 +272,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, e.Pos(), 1, 18) {
-                       check.softErrorf(inNode(e, ix.Lbrack), UnsupportedFeature, "type instantiation requires go1.18 or later")
-               }
+               check.allowVersionf(check.pkg, inNode(e, ix.Lbrack), 1, 18, "type instantiation")
                return check.instantiatedType(ix, def)
 
        case *ast.ParenExpr:
index e02074cf010fb7d18482698ba813c45b494cb1c0..893e85ca2a7c31a0c3ce2e9192ed75e51289e043 100644 (file)
@@ -6,9 +6,9 @@ package types
 
 import (
        "errors"
+       "fmt"
        "go/ast"
        "go/token"
-       . "internal/types/errors"
        "strings"
 )
 
@@ -16,12 +16,12 @@ 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, lit.Pos(), 1, 13) {
+       if len(s) <= 2 || check.allowVersion(check.pkg, lit, 1, 13) {
                return
        }
        // len(s) > 2
        if strings.Contains(s, "_") {
-               check.error(lit, UnsupportedFeature, "underscores in numeric literals requires go1.13 or later")
+               check.versionErrorf(lit, "go1.13", "underscores in numeric literals")
                return
        }
        if s[0] != '0' {
@@ -29,21 +29,21 @@ func (check *Checker) langCompat(lit *ast.BasicLit) {
        }
        radix := s[1]
        if radix == 'b' || radix == 'B' {
-               check.error(lit, UnsupportedFeature, "binary literals requires go1.13 or later")
+               check.versionErrorf(lit, "go1.13", "binary literals")
                return
        }
        if radix == 'o' || radix == 'O' {
-               check.error(lit, UnsupportedFeature, "0o/0O-style octal literals requires go1.13 or later")
+               check.versionErrorf(lit, "go1.13", "0o/0O-style octal literals")
                return
        }
        if lit.Kind != token.INT && (radix == 'x' || radix == 'X') {
-               check.error(lit, UnsupportedFeature, "hexadecimal floating-point literals requires go1.13 or later")
+               check.versionErrorf(lit, "go1.13", "hexadecimal floating-point literals")
        }
 }
 
 // allowVersion reports whether the given package
 // is allowed to use version major.minor.
-func (check *Checker) allowVersion(pkg *Package, pos token.Pos, major, minor int) bool {
+func (check *Checker) allowVersion(pkg *Package, at positioner, 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 {
@@ -52,7 +52,7 @@ func (check *Checker) allowVersion(pkg *Package, pos token.Pos, major, minor int
 
        // 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 {
+               if v, ok := check.posVers[check.fset.File(at.Pos())]; ok && v.major >= 1 {
                        return v.major > major || v.major == major && v.minor >= minor
                }
        }
@@ -62,6 +62,16 @@ func (check *Checker) allowVersion(pkg *Package, pos token.Pos, major, minor int
        return ma == 0 && mi == 0 || ma > major || ma == major && mi >= minor
 }
 
+// allowVersionf is like allowVersion but also accepts a format string and arguments
+// which are used to report a version error if allowVersion returns false.
+func (check *Checker) allowVersionf(pkg *Package, at positioner, major, minor int, format string, args ...interface{}) bool {
+       if !check.allowVersion(pkg, at, major, minor) {
+               check.versionErrorf(at, fmt.Sprintf("go%d.%d", major, minor), format, args...)
+               return false
+       }
+       return true
+}
+
 type version struct {
        major, minor int
 }
index fd37586cfb54afd61ad0502e70f5177164420467..f927813e102ddba784239463d4c600c519f85c38 100644 (file)
@@ -16,6 +16,6 @@ func f1(func(int))      {}
 func f2(int, func(int)) {}
 
 func _() {
-       f1( /* ERROR "implicitly instantiated function as argument requires go1.21 or later" */ g)
-       f2( /* ERROR "implicitly instantiated function as argument requires go1.21 or later" */ 0, g)
+       f1(g /* ERROR "implicitly instantiated function as argument requires go1.21 or later" */)
+       f2(0, g /* ERROR "implicitly instantiated function as argument requires go1.21 or later" */)
 }