]> Cypherpunks repositories - gostls13.git/commitdiff
go/types: return an error message from Checker.genericType
authorRobert Findley <rfindley@google.com>
Mon, 15 Nov 2021 15:49:54 +0000 (10:49 -0500)
committerRobert Findley <rfindley@google.com>
Mon, 15 Nov 2021 18:43:13 +0000 (18:43 +0000)
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 <rfindley@google.com>
Run-TryBot: Robert Findley <rfindley@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Robert Griesemer <gri@golang.org>
src/go/types/errorcodes.go
src/go/types/signature.go
src/go/types/testdata/fixedbugs/issue48827.go2 [new file with mode: 0644]
src/go/types/typexpr.go

index cbf00ba0b4bcc630fe0c9f0eff0b1f26afaa9e24..b3796e89194bfd18862c92b8a2cef97c9a431501 100644 (file)
@@ -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
 )
index 306d86c0b75bd3a42d670b82e2f8fce05b7155e0..8f89e931fbb61d5b8583f30200a7f91678ba746c 100644 (file)
@@ -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 (file)
index 0000000..aa1d12a
--- /dev/null
@@ -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" */ ]{})
+}
index c89e69db7b1e37acf3f246cd0e7836f10d1d0e87..d80acbe7d69bd1c83ade555c9c6321f741eb6d12 100644 (file)
@@ -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)
                        }
                }