From b6342a02ad901e015e1c4eb9f862824029efb7b7 Mon Sep 17 00:00:00 2001 From: Robert Findley Date: Mon, 15 Nov 2021 10:49:54 -0500 Subject: [PATCH] go/types: return an error message from Checker.genericType The bare error message "%s is not a generic type" is probably never sufficient, so change the signature of genericType to instead return an message that may be formatted as additional context in errors. Along the way, refactor instantiatedType to have access to the entire index expression. Fixes #48827 Change-Id: I0c455c1ce46ac3f1ef2990c997da19e5fc6c4eae Reviewed-on: https://go-review.googlesource.com/c/go/+/363994 Trust: Robert Findley Run-TryBot: Robert Findley TryBot-Result: Go Bot Reviewed-by: Robert Griesemer --- src/go/types/errorcodes.go | 13 +++-- src/go/types/signature.go | 2 +- .../types/testdata/fixedbugs/issue48827.go2 | 19 ++++++++ src/go/types/typexpr.go | 47 +++++++++++-------- 4 files changed, 56 insertions(+), 25 deletions(-) create mode 100644 src/go/types/testdata/fixedbugs/issue48827.go2 diff --git a/src/go/types/errorcodes.go b/src/go/types/errorcodes.go index cbf00ba0b4..b3796e8919 100644 --- a/src/go/types/errorcodes.go +++ b/src/go/types/errorcodes.go @@ -1307,6 +1307,15 @@ const ( // supported at this Go version. _UnsupportedFeature + // _NotAGenericType occurs when a non-generic type is used where a generic + // type is expected: in type or function instantiation. + // + // Example: + // type T int + // + // var _ T[int] + _NotAGenericType + // _WrongTypeArgCount occurs when a type or function is instantiated with an // incorrent number of type arguments, including when a generic type or // function is used without instantiation. @@ -1391,8 +1400,4 @@ const ( // Example: // type T[P any] struct{ *P } _MisplacedTypeParam - - // _Todo is a placeholder for error codes that have not been decided. - // TODO(rFindley) remove this error code after deciding on errors for generics code. - _Todo ) diff --git a/src/go/types/signature.go b/src/go/types/signature.go index 306d86c0b7..8f89e931fb 100644 --- a/src/go/types/signature.go +++ b/src/go/types/signature.go @@ -137,7 +137,7 @@ func (check *Checker) funcType(sig *Signature, recvPar *ast.FieldList, ftyp *ast // Also: Don't report an error via genericType since it will be reported // again when we type-check the signature. // TODO(gri) maybe the receiver should be marked as invalid instead? - if recv, _ := check.genericType(rname, false).(*Named); recv != nil { + if recv, _ := check.genericType(rname, nil).(*Named); recv != nil { recvTParams = recv.TypeParams().list() } } diff --git a/src/go/types/testdata/fixedbugs/issue48827.go2 b/src/go/types/testdata/fixedbugs/issue48827.go2 new file mode 100644 index 0000000000..aa1d12aaf5 --- /dev/null +++ b/src/go/types/testdata/fixedbugs/issue48827.go2 @@ -0,0 +1,19 @@ +// 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. + +package p + +type G[P any] int + +type ( + _ G[int] + _ G[G /* ERROR "cannot use.*without instantiation" */] + _ bool /* ERROR "invalid operation: bool\[int\] \(bool is not a generic type\)" */ [int] + _ bool /* ERROR "invalid operation: bool\[G\] \(bool is not a generic type\)" */[G] +) + +// The example from the issue. +func _() { + _ = &([10]bool /* ERROR "invalid operation.*bool is not a generic type" */ [1 /* ERROR "expected type" */ ]{}) +} diff --git a/src/go/types/typexpr.go b/src/go/types/typexpr.go index c89e69db7b..d80acbe7d6 100644 --- a/src/go/types/typexpr.go +++ b/src/go/types/typexpr.go @@ -176,13 +176,15 @@ func (check *Checker) definedType(e ast.Expr, def *Named) Type { return typ } -// genericType is like typ but the type must be an (uninstantiated) generic type. -func (check *Checker) genericType(e ast.Expr, reportErr bool) Type { +// genericType is like typ but the type must be an (uninstantiated) generic +// type. If reason is non-nil and the type expression was a valid type but not +// generic, reason will be populated with a message describing the error. +func (check *Checker) genericType(e ast.Expr, reason *string) Type { typ := check.typInternal(e, nil) assert(isTyped(typ)) if typ != Typ[Invalid] && !isGeneric(typ) { - if reportErr { - check.errorf(e, _Todo, "%s is not a generic type", typ) + if reason != nil { + *reason = check.sprintf("%s is not a generic type", typ) } typ = Typ[Invalid] } @@ -263,7 +265,7 @@ func (check *Checker) typInternal(e0 ast.Expr, def *Named) (T Type) { if !check.allowVersion(check.pkg, 1, 18) { check.softErrorf(inNode(e, ix.Lbrack), _UnsupportedFeature, "type instantiation requires go1.18 or later") } - return check.instantiatedType(ix.X, ix.Indices, def) + return check.instantiatedType(ix, def) case *ast.ParenExpr: // Generic types must be instantiated before they can be used in any form. @@ -374,29 +376,34 @@ func (check *Checker) typInternal(e0 ast.Expr, def *Named) (T Type) { return typ } -func (check *Checker) instantiatedType(x ast.Expr, targsx []ast.Expr, def *Named) (res Type) { +func (check *Checker) instantiatedType(ix *typeparams.IndexExpr, def *Named) (res Type) { + pos := ix.X.Pos() if trace { - check.trace(x.Pos(), "-- instantiating %s with %s", x, targsx) + check.trace(pos, "-- instantiating %s with %s", ix.X, ix.Indices) check.indent++ defer func() { check.indent-- // Don't format the underlying here. It will always be nil. - check.trace(x.Pos(), "=> %s", res) + check.trace(pos, "=> %s", res) }() } - gtyp := check.genericType(x, true) + var reason string + gtyp := check.genericType(ix.X, &reason) + if reason != "" { + check.invalidOp(ix.Orig, _NotAGenericType, "%s (%s)", ix.Orig, reason) + } if gtyp == Typ[Invalid] { return gtyp // error already reported } orig, _ := gtyp.(*Named) if orig == nil { - panic(fmt.Sprintf("%v: cannot instantiate %v", x.Pos(), gtyp)) + panic(fmt.Sprintf("%v: cannot instantiate %v", ix.Pos(), gtyp)) } // evaluate arguments - targs := check.typeList(targsx) + targs := check.typeList(ix.Indices) if targs == nil { def.setUnderlying(Typ[Invalid]) // avoid later errors due to lazy instantiation return Typ[Invalid] @@ -404,7 +411,7 @@ func (check *Checker) instantiatedType(x ast.Expr, targsx []ast.Expr, def *Named // determine argument positions posList := make([]token.Pos, len(targs)) - for i, arg := range targsx { + for i, arg := range ix.Indices { posList[i] = arg.Pos() } @@ -418,7 +425,7 @@ func (check *Checker) instantiatedType(x ast.Expr, targsx []ast.Expr, def *Named // validation below. Ensure that the validation (and resulting errors) runs // for each instantiated type in the source. if inst == nil { - tname := NewTypeName(x.Pos(), orig.obj.pkg, orig.obj.name, nil) + tname := NewTypeName(ix.X.Pos(), orig.obj.pkg, orig.obj.name, nil) inst = check.newNamed(tname, orig, nil, nil, nil) // underlying, methods and tparams are set when named is resolved inst.targs = NewTypeList(targs) inst = ctxt.update(h, orig, targs, inst).(*Named) @@ -432,14 +439,14 @@ func (check *Checker) instantiatedType(x ast.Expr, targsx []ast.Expr, def *Named if len(targs) < len(tparams) { // If inference fails, len(inferred) will be 0, and inst.underlying will // be set to Typ[Invalid] in expandNamed. - inferred = check.infer(x, tparams, targs, nil, nil) + inferred = check.infer(ix.Orig, tparams, targs, nil, nil) if len(inferred) > len(targs) { inst.targs = NewTypeList(inferred) } } - check.recordInstance(x, inferred, inst) - return expandNamed(ctxt, n, x.Pos()) + check.recordInstance(ix.Orig, inferred, inst) + return expandNamed(ctxt, n, pos) } // origin.tparams may not be set up, so we need to do expansion later. @@ -452,16 +459,16 @@ func (check *Checker) instantiatedType(x ast.Expr, targsx []ast.Expr, def *Named // frees some memory. inst.resolver = nil - if check.validateTArgLen(x.Pos(), inst.tparams.Len(), inst.targs.Len()) { - if i, err := check.verify(x.Pos(), inst.tparams.list(), inst.targs.list()); err != nil { + if check.validateTArgLen(pos, inst.tparams.Len(), inst.targs.Len()) { + if i, err := check.verify(pos, inst.tparams.list(), inst.targs.list()); err != nil { // best position for error reporting - pos := x.Pos() + pos := ix.Pos() if i < len(posList) { pos = posList[i] } check.softErrorf(atPos(pos), _InvalidTypeArg, err.Error()) } else { - check.mono.recordInstance(check.pkg, x.Pos(), inst.tparams.list(), inst.targs.list(), posList) + check.mono.recordInstance(check.pkg, pos, inst.tparams.list(), inst.targs.list(), posList) } } -- 2.48.1