mirror of https://github.com/golang/go.git
go/types: add error reporting for 1.18 syntax if GoVersion is below 1.18
This is a port of CL 344871 to go/types. Unlike the compiler, go/parser is already always producing 1.18 syntax, so the effect of this CL is to add some additional errors when Config.GoVersion is below 1.18. This is a non-trivial port, both due to different error reporting APIs and due to interacting with declaration syntax nodes, which differ between go/ast and cmd/compile/internal/syntax. Change-Id: I8003a014e6eec5e554c24e9a6cfc0548ec534834 Reviewed-on: https://go-review.googlesource.com/c/go/+/346433 Trust: Robert Findley <rfindley@google.com> Run-TryBot: Robert Findley <rfindley@google.com> TryBot-Result: Go Bot <gobot@golang.org> Reviewed-by: Robert Griesemer <gri@golang.org>
This commit is contained in:
parent
aed59d172a
commit
b93581e47d
|
|
@ -17,6 +17,10 @@ import (
|
||||||
// funcInst type-checks a function instantiation inst and returns the result in x.
|
// funcInst type-checks a function instantiation inst and returns the result in x.
|
||||||
// The operand x must be the evaluation of inst.X and its type must be a signature.
|
// The operand x must be the evaluation of inst.X and its type must be a signature.
|
||||||
func (check *Checker) funcInst(x *operand, ix *typeparams.IndexExpr) {
|
func (check *Checker) funcInst(x *operand, ix *typeparams.IndexExpr) {
|
||||||
|
if !check.allowVersion(check.pkg, 1, 18) {
|
||||||
|
check.softErrorf(inNode(ix.Orig, ix.Lbrack), _Todo, "function instantiation requires go1.18 or later")
|
||||||
|
}
|
||||||
|
|
||||||
targs := check.typeList(ix.Indices)
|
targs := check.typeList(ix.Indices)
|
||||||
if targs == nil {
|
if targs == nil {
|
||||||
x.mode = invalid
|
x.mode = invalid
|
||||||
|
|
@ -324,6 +328,15 @@ func (check *Checker) arguments(call *ast.CallExpr, sig *Signature, targs []Type
|
||||||
|
|
||||||
// infer type arguments and instantiate signature if necessary
|
// infer type arguments and instantiate signature if necessary
|
||||||
if sig.TParams().Len() > 0 {
|
if sig.TParams().Len() > 0 {
|
||||||
|
if !check.allowVersion(check.pkg, 1, 18) {
|
||||||
|
switch call.Fun.(type) {
|
||||||
|
case *ast.IndexExpr, *ast.MultiIndexExpr:
|
||||||
|
ix := typeparams.UnpackIndexExpr(call.Fun)
|
||||||
|
check.softErrorf(inNode(call.Fun, ix.Lbrack), _Todo, "function instantiation requires go1.18 or later")
|
||||||
|
default:
|
||||||
|
check.softErrorf(inNode(call, call.Lparen), _Todo, "implicit function instantiation requires go1.18 or later")
|
||||||
|
}
|
||||||
|
}
|
||||||
// TODO(gri) provide position information for targs so we can feed
|
// TODO(gri) provide position information for targs so we can feed
|
||||||
// it to the instantiate call for better error reporting
|
// it to the instantiate call for better error reporting
|
||||||
targs := check.infer(call, sig.TParams().list(), targs, sigParams, args, true)
|
targs := check.infer(call, sig.TParams().list(), targs, sigParams, args, true)
|
||||||
|
|
|
||||||
|
|
@ -567,11 +567,26 @@ func (check *Checker) varDecl(obj *Var, lhs []*Var, typ, init ast.Expr) {
|
||||||
check.initVars(lhs, []ast.Expr{init}, token.NoPos)
|
check.initVars(lhs, []ast.Expr{init}, token.NoPos)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// isImportedConstraint reports whether typ is an imported type constraint.
|
||||||
|
func (check *Checker) isImportedConstraint(typ Type) bool {
|
||||||
|
named, _ := typ.(*Named)
|
||||||
|
if named == nil || named.obj.pkg == check.pkg || named.obj.pkg == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
u, _ := named.under().(*Interface)
|
||||||
|
return u != nil && u.IsConstraint()
|
||||||
|
}
|
||||||
|
|
||||||
func (check *Checker) typeDecl(obj *TypeName, tdecl *ast.TypeSpec, def *Named) {
|
func (check *Checker) typeDecl(obj *TypeName, tdecl *ast.TypeSpec, def *Named) {
|
||||||
assert(obj.typ == nil)
|
assert(obj.typ == nil)
|
||||||
|
|
||||||
|
var rhs Type
|
||||||
check.later(func() {
|
check.later(func() {
|
||||||
check.validType(obj.typ, nil)
|
check.validType(obj.typ, nil)
|
||||||
|
// If typ is local, an error was already reported where typ is specified/defined.
|
||||||
|
if check.isImportedConstraint(rhs) && !check.allowVersion(check.pkg, 1, 18) {
|
||||||
|
check.errorf(tdecl.Type, _Todo, "using type constraint %s requires go1.18 or later", rhs)
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
alias := tdecl.Assign.IsValid()
|
alias := tdecl.Assign.IsValid()
|
||||||
|
|
@ -589,7 +604,8 @@ func (check *Checker) typeDecl(obj *TypeName, tdecl *ast.TypeSpec, def *Named) {
|
||||||
}
|
}
|
||||||
|
|
||||||
obj.typ = Typ[Invalid]
|
obj.typ = Typ[Invalid]
|
||||||
obj.typ = check.anyType(tdecl.Type)
|
rhs = check.anyType(tdecl.Type)
|
||||||
|
obj.typ = rhs
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -604,8 +620,9 @@ func (check *Checker) typeDecl(obj *TypeName, tdecl *ast.TypeSpec, def *Named) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// determine underlying type of named
|
// determine underlying type of named
|
||||||
named.fromRHS = check.definedType(tdecl.Type, named)
|
rhs = check.definedType(tdecl.Type, named)
|
||||||
assert(named.fromRHS != nil)
|
assert(rhs != nil)
|
||||||
|
named.fromRHS = rhs
|
||||||
|
|
||||||
// The underlying type of named may be itself a named type that is
|
// The underlying type of named may be itself a named type that is
|
||||||
// incomplete:
|
// incomplete:
|
||||||
|
|
|
||||||
|
|
@ -381,12 +381,15 @@ func (check *Checker) collectObjects() {
|
||||||
check.declarePkgObj(name, obj, di)
|
check.declarePkgObj(name, obj, di)
|
||||||
}
|
}
|
||||||
case typeDecl:
|
case typeDecl:
|
||||||
|
if d.spec.TParams.NumFields() != 0 && !check.allowVersion(pkg, 1, 18) {
|
||||||
|
check.softErrorf(d.spec.TParams.List[0], _Todo, "type parameters require go1.18 or later")
|
||||||
|
}
|
||||||
obj := NewTypeName(d.spec.Name.Pos(), pkg, d.spec.Name.Name, nil)
|
obj := NewTypeName(d.spec.Name.Pos(), pkg, d.spec.Name.Name, nil)
|
||||||
check.declarePkgObj(d.spec.Name, obj, &declInfo{file: fileScope, tdecl: d.spec})
|
check.declarePkgObj(d.spec.Name, obj, &declInfo{file: fileScope, tdecl: d.spec})
|
||||||
case funcDecl:
|
case funcDecl:
|
||||||
info := &declInfo{file: fileScope, fdecl: d.decl}
|
|
||||||
name := d.decl.Name.Name
|
name := d.decl.Name.Name
|
||||||
obj := NewFunc(d.decl.Name.Pos(), pkg, name, nil)
|
obj := NewFunc(d.decl.Name.Pos(), pkg, name, nil)
|
||||||
|
hasTParamError := false // avoid duplicate type parameter errors
|
||||||
if d.decl.Recv.NumFields() == 0 {
|
if d.decl.Recv.NumFields() == 0 {
|
||||||
// regular function
|
// regular function
|
||||||
if d.decl.Recv != nil {
|
if d.decl.Recv != nil {
|
||||||
|
|
@ -398,8 +401,9 @@ func (check *Checker) collectObjects() {
|
||||||
if name == "main" {
|
if name == "main" {
|
||||||
code = _InvalidMainDecl
|
code = _InvalidMainDecl
|
||||||
}
|
}
|
||||||
if tparams := typeparams.Get(d.decl.Type); tparams != nil {
|
if d.decl.Type.TParams.NumFields() != 0 {
|
||||||
check.softErrorf(tparams, code, "func %s must have no type parameters", name)
|
check.softErrorf(d.decl.Type.TParams.List[0], code, "func %s must have no type parameters", name)
|
||||||
|
hasTParamError = true
|
||||||
}
|
}
|
||||||
if t := d.decl.Type; t.Params.NumFields() != 0 || t.Results != nil {
|
if t := d.decl.Type; t.Params.NumFields() != 0 || t.Results != nil {
|
||||||
// TODO(rFindley) Should this be a hard error?
|
// TODO(rFindley) Should this be a hard error?
|
||||||
|
|
@ -435,6 +439,10 @@ func (check *Checker) collectObjects() {
|
||||||
}
|
}
|
||||||
check.recordDef(d.decl.Name, obj)
|
check.recordDef(d.decl.Name, obj)
|
||||||
}
|
}
|
||||||
|
if d.decl.Type.TParams.NumFields() != 0 && !check.allowVersion(pkg, 1, 18) && !hasTParamError {
|
||||||
|
check.softErrorf(d.decl.Type.TParams.List[0], _Todo, "type parameters require go1.18 or later")
|
||||||
|
}
|
||||||
|
info := &declInfo{file: fileScope, fdecl: d.decl}
|
||||||
// Methods are not package-level objects but we still track them in the
|
// 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
|
// object map so that we can handle them like regular functions (if the
|
||||||
// receiver is invalid); also we need their fdecl info when associating
|
// receiver is invalid); also we need their fdecl info when associating
|
||||||
|
|
|
||||||
|
|
@ -41,7 +41,7 @@ func (m substMap) lookup(tpar *TypeParam) Type {
|
||||||
// subst returns the type typ with its type parameters tpars replaced by the
|
// subst returns the type typ with its type parameters tpars replaced by the
|
||||||
// corresponding type arguments targs, recursively. subst is pure in the sense
|
// corresponding type arguments targs, recursively. subst is pure in the sense
|
||||||
// that it doesn't modify the incoming type. If a substitution took place, the
|
// that it doesn't modify the incoming type. If a substitution took place, the
|
||||||
// result type is different from from the incoming type.
|
// result type is different from the incoming type.
|
||||||
//
|
//
|
||||||
// If the given typMap is non-nil, it is used in lieu of check.typMap.
|
// If the given typMap is non-nil, it is used in lieu of check.typMap.
|
||||||
func (check *Checker) subst(pos token.Pos, typ Type, smap substMap, typMap map[string]*Named) Type {
|
func (check *Checker) subst(pos token.Pos, typ Type, smap substMap, typMap map[string]*Named) Type {
|
||||||
|
|
|
||||||
|
|
@ -146,7 +146,7 @@ type (
|
||||||
m1(I5)
|
m1(I5)
|
||||||
}
|
}
|
||||||
I6 interface {
|
I6 interface {
|
||||||
S0 /* ERROR "not an interface" */
|
S0 /* ERROR "non-interface type S0" */
|
||||||
}
|
}
|
||||||
I7 interface {
|
I7 interface {
|
||||||
I1
|
I1
|
||||||
|
|
|
||||||
|
|
@ -79,11 +79,11 @@ func issue9473(a []int, b ...int) {
|
||||||
// Check that embedding a non-interface type in an interface results in a good error message.
|
// Check that embedding a non-interface type in an interface results in a good error message.
|
||||||
func issue10979() {
|
func issue10979() {
|
||||||
type _ interface {
|
type _ interface {
|
||||||
int /* ERROR int is not an interface */
|
int /* ERROR non-interface type int */
|
||||||
}
|
}
|
||||||
type T struct{}
|
type T struct{}
|
||||||
type _ interface {
|
type _ interface {
|
||||||
T /* ERROR T is not an interface */
|
T /* ERROR non-interface type T */
|
||||||
}
|
}
|
||||||
type _ interface {
|
type _ interface {
|
||||||
nosuchtype /* ERROR undeclared name: nosuchtype */
|
nosuchtype /* ERROR undeclared name: nosuchtype */
|
||||||
|
|
@ -280,7 +280,7 @@ type issue25301b /* ERROR cycle */ = interface {
|
||||||
}
|
}
|
||||||
|
|
||||||
type issue25301c interface {
|
type issue25301c interface {
|
||||||
notE // ERROR struct\{\} is not an interface
|
notE // ERROR non-interface type struct\{\}
|
||||||
}
|
}
|
||||||
|
|
||||||
type notE = struct{}
|
type notE = struct{}
|
||||||
|
|
|
||||||
|
|
@ -4,4 +4,4 @@
|
||||||
|
|
||||||
package main
|
package main
|
||||||
|
|
||||||
func main[ /* ERROR "func main must have no type parameters" */ T any]() {}
|
func main[T /* ERROR "func main must have no type parameters" */ any]() {}
|
||||||
|
|
|
||||||
|
|
@ -304,8 +304,8 @@ var _ = f8[int, float64](0, 0, nil...) // test case for #18268
|
||||||
// init functions cannot have type parameters
|
// init functions cannot have type parameters
|
||||||
|
|
||||||
func init() {}
|
func init() {}
|
||||||
func init[/* ERROR func init must have no type parameters */ _ any]() {}
|
func init[_ /* ERROR func init must have no type parameters */ any]() {}
|
||||||
func init[/* ERROR func init must have no type parameters */ P any]() {}
|
func init[P /* ERROR func init must have no type parameters */ any]() {}
|
||||||
|
|
||||||
type T struct {}
|
type T struct {}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,59 @@
|
||||||
|
// Copyright 2021 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.
|
||||||
|
|
||||||
|
// Parser accepts type parameters but the type checker
|
||||||
|
// needs to report any operations that are not permitted
|
||||||
|
// before Go 1.18.
|
||||||
|
|
||||||
|
package go1_17
|
||||||
|
|
||||||
|
type T[P /* ERROR type parameters require go1\.18 or later */ any] struct{}
|
||||||
|
|
||||||
|
// for init (and main, but we're not in package main) we should only get one error
|
||||||
|
func init[P /* ERROR func init must have no type parameters */ any]() {}
|
||||||
|
func main[P /* ERROR type parameters require go1\.18 or later */ any]() {}
|
||||||
|
|
||||||
|
func f[P /* ERROR type parameters require go1\.18 or later */ any](x P) {
|
||||||
|
var _ T[ /* ERROR type instantiation requires go1\.18 or later */ int]
|
||||||
|
var _ (T[ /* ERROR type instantiation requires go1\.18 or later */ int])
|
||||||
|
_ = T[ /* ERROR type instantiation requires go1\.18 or later */ int]{}
|
||||||
|
_ = T[ /* ERROR type instantiation requires go1\.18 or later */ int](struct{}{})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (T[ /* ERROR type instantiation requires go1\.18 or later */ P]) g(x int) {
|
||||||
|
f[ /* ERROR function instantiation requires go1\.18 or later */ int](0) // explicit instantiation
|
||||||
|
(f[ /* ERROR function instantiation requires go1\.18 or later */ int])(0) // parentheses (different code path)
|
||||||
|
f( /* ERROR implicit function instantiation requires go1\.18 or later */ x) // implicit instantiation
|
||||||
|
}
|
||||||
|
|
||||||
|
type C1 interface {
|
||||||
|
comparable // ERROR undeclared name: comparable \(requires version go1\.18 or later\)
|
||||||
|
}
|
||||||
|
|
||||||
|
type C2 interface {
|
||||||
|
comparable // ERROR undeclared name: comparable \(requires version go1\.18 or later\)
|
||||||
|
int // ERROR embedding non-interface type int requires go1\.18 or later
|
||||||
|
~ /* ERROR embedding interface element ~int requires go1\.18 or later */ int
|
||||||
|
int /* ERROR embedding interface element int\|~string requires go1\.18 or later */ | ~string
|
||||||
|
}
|
||||||
|
|
||||||
|
type _ interface {
|
||||||
|
// errors for these were reported with their declaration
|
||||||
|
C1
|
||||||
|
C2
|
||||||
|
}
|
||||||
|
|
||||||
|
type (
|
||||||
|
_ comparable // ERROR undeclared name: comparable \(requires version go1\.18 or later\)
|
||||||
|
// errors for these were reported with their declaration
|
||||||
|
_ C1
|
||||||
|
_ C2
|
||||||
|
|
||||||
|
_ = comparable // ERROR undeclared name: comparable \(requires version go1\.18 or later\)
|
||||||
|
// errors for these were reported with their declaration
|
||||||
|
_ = C1
|
||||||
|
_ = C2
|
||||||
|
)
|
||||||
|
|
||||||
|
// TODO(gri) need test cases for imported constraint types (see also issue #47967)
|
||||||
|
|
@ -269,6 +269,11 @@ func computeInterfaceTypeSet(check *Checker, pos token.Pos, ityp *Interface) *_T
|
||||||
switch u := under(typ).(type) {
|
switch u := under(typ).(type) {
|
||||||
case *Interface:
|
case *Interface:
|
||||||
tset := computeInterfaceTypeSet(check, pos, u)
|
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) {
|
||||||
|
check.errorf(atPos(pos), _Todo, "embedding constraint interface %s requires go1.18 or later", typ)
|
||||||
|
continue
|
||||||
|
}
|
||||||
if tset.comparable {
|
if tset.comparable {
|
||||||
ityp.tset.comparable = true
|
ityp.tset.comparable = true
|
||||||
}
|
}
|
||||||
|
|
@ -277,6 +282,10 @@ func computeInterfaceTypeSet(check *Checker, pos token.Pos, ityp *Interface) *_T
|
||||||
}
|
}
|
||||||
terms = tset.terms
|
terms = tset.terms
|
||||||
case *Union:
|
case *Union:
|
||||||
|
if check != nil && !check.allowVersion(check.pkg, 1, 18) {
|
||||||
|
check.errorf(atPos(pos), _Todo, "embedding interface element %s requires go1.18 or later", u)
|
||||||
|
continue
|
||||||
|
}
|
||||||
tset := computeUnionTypeSet(check, pos, u)
|
tset := computeUnionTypeSet(check, pos, u)
|
||||||
if tset == &invalidTypeSet {
|
if tset == &invalidTypeSet {
|
||||||
continue // ignore invalid unions
|
continue // ignore invalid unions
|
||||||
|
|
@ -291,7 +300,7 @@ func computeInterfaceTypeSet(check *Checker, pos token.Pos, ityp *Interface) *_T
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if check != nil && !check.allowVersion(check.pkg, 1, 18) {
|
if check != nil && !check.allowVersion(check.pkg, 1, 18) {
|
||||||
check.errorf(atPos(pos), _InvalidIfaceEmbed, "%s is not an interface", typ)
|
check.errorf(atPos(pos), _InvalidIfaceEmbed, "embedding non-interface type %s requires go1.18 or later", typ)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
terms = termlist{{false, typ}}
|
terms = termlist{{false, typ}}
|
||||||
|
|
|
||||||
|
|
@ -36,14 +36,12 @@ func (check *Checker) ident(x *operand, e *ast.Ident, def *Named, wantType bool)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
case universeAny, universeComparable:
|
case universeAny, universeComparable:
|
||||||
|
// complain if necessary but keep going
|
||||||
if !check.allowVersion(check.pkg, 1, 18) {
|
if !check.allowVersion(check.pkg, 1, 18) {
|
||||||
check.errorf(e, _UndeclaredName, "undeclared name: %s (requires version go1.18 or later)", e.Name)
|
check.softErrorf(e, _UndeclaredName, "undeclared name: %s (requires version go1.18 or later)", e.Name)
|
||||||
return
|
} else if obj == universeAny {
|
||||||
}
|
// If we allow "any" for general use, this if-statement can be removed (issue #33232).
|
||||||
// If we allow "any" for general use, this if-statement can be removed (issue #33232).
|
check.softErrorf(e, _Todo, "cannot use any outside constraint position")
|
||||||
if obj == universeAny {
|
|
||||||
check.error(e, _Todo, "cannot use any outside constraint position")
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
check.recordUse(e, obj)
|
check.recordUse(e, obj)
|
||||||
|
|
@ -273,6 +271,9 @@ func (check *Checker) typInternal(e0 ast.Expr, def *Named) (T Type) {
|
||||||
|
|
||||||
case *ast.IndexExpr, *ast.MultiIndexExpr:
|
case *ast.IndexExpr, *ast.MultiIndexExpr:
|
||||||
ix := typeparams.UnpackIndexExpr(e)
|
ix := typeparams.UnpackIndexExpr(e)
|
||||||
|
if !check.allowVersion(check.pkg, 1, 18) {
|
||||||
|
check.softErrorf(inNode(e, ix.Lbrack), _Todo, "type instantiation requires go1.18 or later")
|
||||||
|
}
|
||||||
// TODO(rfindley): type instantiation should require go1.18
|
// TODO(rfindley): type instantiation should require go1.18
|
||||||
return check.instantiatedType(ix.X, ix.Indices, def)
|
return check.instantiatedType(ix.X, ix.Indices, def)
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue