From fb4a306e3a3e5f5ae6e32ed796157e6e1fe0b6db Mon Sep 17 00:00:00 2001 From: Robert Griesemer Date: Tue, 2 May 2023 14:09:45 -0700 Subject: [PATCH] go/types, types2: combine version check with version error reporting 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 Run-TryBot: Robert Griesemer Reviewed-by: Robert Findley Auto-Submit: Robert Griesemer TryBot-Result: Gopher Robot --- src/cmd/compile/internal/types2/builtins.go | 22 +++++--------- src/cmd/compile/internal/types2/call.go | 26 ++++++---------- src/cmd/compile/internal/types2/check.go | 2 +- .../compile/internal/types2/conversions.go | 4 +-- src/cmd/compile/internal/types2/decl.go | 9 ++---- src/cmd/compile/internal/types2/errors.go | 8 ++--- src/cmd/compile/internal/types2/expr.go | 7 ++--- .../compile/internal/types2/instantiate.go | 2 +- src/cmd/compile/internal/types2/interface.go | 2 +- src/cmd/compile/internal/types2/resolver.go | 8 ++--- src/cmd/compile/internal/types2/typeset.go | 9 ++---- src/cmd/compile/internal/types2/typexpr.go | 7 ++--- src/cmd/compile/internal/types2/version.go | 15 ++++++++-- src/go/types/builtins.go | 18 ++++------- src/go/types/call.go | 30 +++++++------------ src/go/types/conversions.go | 4 +-- src/go/types/decl.go | 9 ++---- src/go/types/expr.go | 3 +- src/go/types/instantiate.go | 2 +- src/go/types/resolver.go | 8 ++--- src/go/types/typeset.go | 11 +++---- src/go/types/typexpr.go | 7 ++--- src/go/types/version.go | 26 +++++++++++----- .../types/testdata/fixedbugs/issue59338a.go | 4 +-- 24 files changed, 102 insertions(+), 141 deletions(-) diff --git a/src/cmd/compile/internal/types2/builtins.go b/src/cmd/compile/internal/types2/builtins.go index 915eb2db9e..51a3023bc5 100644 --- a/src/cmd/compile/internal/types2/builtins.go +++ b/src/cmd/compile/internal/types2/builtins.go @@ -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 diff --git a/src/cmd/compile/internal/types2/call.go b/src/cmd/compile/internal/types2/call.go index 8ad7744ab4..50529cd0ee 100644 --- a/src/cmd/compile/internal/types2/call.go +++ b/src/cmd/compile/internal/types2/call.go @@ -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 diff --git a/src/cmd/compile/internal/types2/check.go b/src/cmd/compile/internal/types2/check.go index 550fb1cafd..b2a9eb0dbc 100644 --- a/src/cmd/compile/internal/types2/check.go +++ b/src/cmd/compile/internal/types2/check.go @@ -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) diff --git a/src/cmd/compile/internal/types2/conversions.go b/src/cmd/compile/internal/types2/conversions.go index 7cb7d490be..57c54f1ef2 100644 --- a/src/cmd/compile/internal/types2/conversions.go +++ b/src/cmd/compile/internal/types2/conversions.go @@ -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 diff --git a/src/cmd/compile/internal/types2/decl.go b/src/cmd/compile/internal/types2/decl.go index dd39c42037..7760f17008 100644 --- a/src/cmd/compile/internal/types2/decl.go +++ b/src/cmd/compile/internal/types2/decl.go @@ -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) diff --git a/src/cmd/compile/internal/types2/errors.go b/src/cmd/compile/internal/types2/errors.go index bbe4cc3fea..3501b213ed 100644 --- a/src/cmd/compile/internal/types2/errors.go +++ b/src/cmd/compile/internal/types2/errors.go @@ -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 { diff --git a/src/cmd/compile/internal/types2/expr.go b/src/cmd/compile/internal/types2/expr.go index 51b944eead..b240dae558 100644 --- a/src/cmd/compile/internal/types2/expr.go +++ b/src/cmd/compile/internal/types2/expr.go @@ -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 diff --git a/src/cmd/compile/internal/types2/instantiate.go b/src/cmd/compile/internal/types2/instantiate.go index 7329fffc86..fdce74ef21 100644 --- a/src/cmd/compile/internal/types2/instantiate.go +++ b/src/cmd/compile/internal/types2/instantiate.go @@ -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 { diff --git a/src/cmd/compile/internal/types2/interface.go b/src/cmd/compile/internal/types2/interface.go index 0978989424..872a3217c2 100644 --- a/src/cmd/compile/internal/types2/interface.go +++ b/src/cmd/compile/internal/types2/interface.go @@ -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 diff --git a/src/cmd/compile/internal/types2/resolver.go b/src/cmd/compile/internal/types2/resolver.go index cfaca2b665..a4de484ed8 100644 --- a/src/cmd/compile/internal/types2/resolver.go +++ b/src/cmd/compile/internal/types2/resolver.go @@ -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 diff --git a/src/cmd/compile/internal/types2/typeset.go b/src/cmd/compile/internal/types2/typeset.go index 26c20cb380..eaeb126ec2 100644 --- a/src/cmd/compile/internal/types2/typeset.go +++ b/src/cmd/compile/internal/types2/typeset.go @@ -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}} diff --git a/src/cmd/compile/internal/types2/typexpr.go b/src/cmd/compile/internal/types2/typexpr.go index f973454645..99b6daf90e 100644 --- a/src/cmd/compile/internal/types2/typexpr.go +++ b/src/cmd/compile/internal/types2/typexpr.go @@ -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: diff --git a/src/cmd/compile/internal/types2/version.go b/src/cmd/compile/internal/types2/version.go index 37e86a2bb4..2c70fa11a1 100644 --- a/src/cmd/compile/internal/types2/version.go +++ b/src/cmd/compile/internal/types2/version.go @@ -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 { diff --git a/src/go/types/builtins.go b/src/go/types/builtins.go index eb9b3ed3b2..0e8f843468 100644 --- a/src/go/types/builtins.go +++ b/src/go/types/builtins.go @@ -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 } diff --git a/src/go/types/call.go b/src/go/types/call.go index 02b6038ccc..c65ef8ceac 100644 --- a/src/go/types/call.go +++ b/src/go/types/call.go @@ -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 diff --git a/src/go/types/conversions.go b/src/go/types/conversions.go index 92ae8196c5..c9a941fa26 100644 --- a/src/go/types/conversions.go +++ b/src/go/types/conversions.go @@ -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 diff --git a/src/go/types/decl.go b/src/go/types/decl.go index c18c7c1ae1..47421ca7f2 100644 --- a/src/go/types/decl.go +++ b/src/go/types/decl.go @@ -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) diff --git a/src/go/types/expr.go b/src/go/types/expr.go index 891153ba8d..cede9f566c 100644 --- a/src/go/types/expr.go +++ b/src/go/types/expr.go @@ -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 } diff --git a/src/go/types/instantiate.go b/src/go/types/instantiate.go index 652511ffd8..8f9f3f52bf 100644 --- a/src/go/types/instantiate.go +++ b/src/go/types/instantiate.go @@ -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 { diff --git a/src/go/types/resolver.go b/src/go/types/resolver.go index d0875f5961..56c26abc11 100644 --- a/src/go/types/resolver.go +++ b/src/go/types/resolver.go @@ -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 diff --git a/src/go/types/typeset.go b/src/go/types/typeset.go index 3f0b81419a..17ef710cd1 100644 --- a/src/go/types/typeset.go +++ b/src/go/types/typeset.go @@ -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}} diff --git a/src/go/types/typexpr.go b/src/go/types/typexpr.go index d01289a9d1..5f254167ec 100644 --- a/src/go/types/typexpr.go +++ b/src/go/types/typexpr.go @@ -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: diff --git a/src/go/types/version.go b/src/go/types/version.go index e02074cf01..893e85ca2a 100644 --- a/src/go/types/version.go +++ b/src/go/types/version.go @@ -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 } diff --git a/src/internal/types/testdata/fixedbugs/issue59338a.go b/src/internal/types/testdata/fixedbugs/issue59338a.go index fd37586cfb..f927813e10 100644 --- a/src/internal/types/testdata/fixedbugs/issue59338a.go +++ b/src/internal/types/testdata/fixedbugs/issue59338a.go @@ -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" */) } -- 2.48.1