]> Cypherpunks repositories - gostls13.git/commitdiff
go/types, types2: don't panic when instantiating generic alias with wrong number...
authorRobert Griesemer <gri@golang.org>
Thu, 9 Jan 2025 23:01:03 +0000 (15:01 -0800)
committerGopher Robot <gobot@golang.org>
Mon, 13 Jan 2025 18:30:43 +0000 (10:30 -0800)
The existing code assumed the type argument count check in
Checker.instance couldn't fail for generic alias types
(matching the code for generic signatures), but it actually
can.

Adjust the code accordingly and document that the result of
Checker.instance may be invalid.

Review all call sites of Checker.instance and make sure we
handle the failure case, or document the code accordingly
(in the case of generic signatures).

When reporting an type argument count error, use the alias
name rather than the alias string representation to match
the error we get for a non-alias type.

While at it, update the manual.go template for ease of use.

Fixes #71198.

Change-Id: I6d19ec6418440e9b49574a2d7dd9825e0af6c2fc
Reviewed-on: https://go-review.googlesource.com/c/go/+/641857
Reviewed-by: Robert Findley <rfindley@google.com>
Reviewed-by: Robert Griesemer <gri@google.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Auto-Submit: Robert Griesemer <gri@google.com>

src/cmd/compile/internal/types2/call.go
src/cmd/compile/internal/types2/instantiate.go
src/cmd/compile/internal/types2/testdata/manual.go
src/cmd/compile/internal/types2/typexpr.go
src/go/types/call.go
src/go/types/instantiate.go
src/go/types/testdata/manual.go
src/go/types/typexpr.go
src/internal/types/testdata/fixedbugs/issue71198.go [new file with mode: 0644]

index ae2ab5f9849bc96cd8a2a0502aeefdf84ee046f2..897c846d8ff98de099b76b01ef1837d01fb85a53 100644 (file)
@@ -142,6 +142,9 @@ func (check *Checker) instantiateSignature(pos syntax.Pos, expr syntax.Expr, typ
                }()
        }
 
+       // For signatures, Checker.instance will always succeed because the type argument
+       // count is correct at this point (see assertion above); hence the type assertion
+       // to *Signature will always succeed.
        inst := check.instance(pos, typ, targs, nil, check.context()).(*Signature)
        assert(inst.TypeParams().Len() == 0) // signature is not generic anymore
        check.recordInstance(expr, targs, inst)
index e51cf18de67fd675cde2212328b998c854e283cf..03c490a3866df2123eb3e935aa6c855b983e992c 100644 (file)
@@ -74,7 +74,8 @@ func Instantiate(ctxt *Context, orig Type, targs []Type, validate bool) (Type, e
 // instance instantiates the given original (generic) function or type with the
 // provided type arguments and returns the resulting instance. If an identical
 // instance exists already in the given contexts, it returns that instance,
-// otherwise it creates a new one.
+// otherwise it creates a new one. If there is an error (such as wrong number
+// of type arguments), the result is Typ[Invalid].
 //
 // If expanding is non-nil, it is the Named instance type currently being
 // expanded. If ctxt is non-nil, it is the context associated with the current
@@ -133,9 +134,13 @@ func (check *Checker) instance(pos syntax.Pos, orig genericType, targs []Type, e
                        assert(expanding == nil) // Alias instances cannot be reached from Named types
                }
 
+               // verify type parameter count (see go.dev/issue/71198 for a test case)
                tparams := orig.TypeParams()
-               // TODO(gri) investigate if this is needed (type argument and parameter count seem to be correct here)
-               if !check.validateTArgLen(pos, orig.String(), tparams.Len(), len(targs)) {
+               if !check.validateTArgLen(pos, orig.obj.Name(), tparams.Len(), len(targs)) {
+                       // TODO(gri) Consider returning a valid alias instance with invalid
+                       //           underlying (aliased) type to match behavior of *Named
+                       //           types. Then this function will never return an invalid
+                       //           result.
                        return Typ[Invalid]
                }
                if tparams.Len() == 0 {
index d8f312f61d58c50e54cb8e70f9f80dc0a98c05a1..825ab50f92de4907d91e17bd872c0a198e13c840 100644 (file)
@@ -1,4 +1,4 @@
-// Copyright 2024 The Go Authors. All rights reserved.
+// Copyright 2025 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.
 
index d955654fc9af3760b69ea084c79f2cee249ea55e..fa6a6f622af7cfd8ce892b7ffc3a01dd3a919fc9 100644 (file)
@@ -475,9 +475,14 @@ func (check *Checker) instantiatedType(x syntax.Expr, xlist []syntax.Expr, def *
        }
 
        // create instance
-       // The instance is not generic anymore as it has type arguments, but it still
-       // satisfies the genericType interface because it has type parameters, too.
-       inst := check.instance(x.Pos(), gtyp, targs, nil, check.context()).(genericType)
+       // The instance is not generic anymore as it has type arguments, but unless
+       // instantiation failed, it still satisfies the genericType interface because
+       // it has type parameters, too.
+       ityp := check.instance(x.Pos(), gtyp, targs, nil, check.context())
+       inst, _ := ityp.(genericType)
+       if inst == nil {
+               return Typ[Invalid]
+       }
 
        // For Named types, orig.tparams may not be set up, so we need to do expansion later.
        check.later(func() {
index 200068b17600547a7eff643458360a776d3f7153..4e8dfc0d6bdc9d09e439643502f7d3e38c6edf8c 100644 (file)
@@ -143,6 +143,9 @@ func (check *Checker) instantiateSignature(pos token.Pos, expr ast.Expr, typ *Si
                }()
        }
 
+       // For signatures, Checker.instance will always succeed because the type argument
+       // count is correct at this point (see assertion above); hence the type assertion
+       // to *Signature will always succeed.
        inst := check.instance(pos, typ, targs, nil, check.context()).(*Signature)
        assert(inst.TypeParams().Len() == 0) // signature is not generic anymore
        check.recordInstance(expr, targs, inst)
index 48eef7ca76c18733d2e45feb6d66ba81bdd9a4c7..4b36312f96e75e8a2864008a55e8c4b44421a763 100644 (file)
@@ -77,7 +77,8 @@ func Instantiate(ctxt *Context, orig Type, targs []Type, validate bool) (Type, e
 // instance instantiates the given original (generic) function or type with the
 // provided type arguments and returns the resulting instance. If an identical
 // instance exists already in the given contexts, it returns that instance,
-// otherwise it creates a new one.
+// otherwise it creates a new one. If there is an error (such as wrong number
+// of type arguments), the result is Typ[Invalid].
 //
 // If expanding is non-nil, it is the Named instance type currently being
 // expanded. If ctxt is non-nil, it is the context associated with the current
@@ -136,9 +137,13 @@ func (check *Checker) instance(pos token.Pos, orig genericType, targs []Type, ex
                        assert(expanding == nil) // Alias instances cannot be reached from Named types
                }
 
+               // verify type parameter count (see go.dev/issue/71198 for a test case)
                tparams := orig.TypeParams()
-               // TODO(gri) investigate if this is needed (type argument and parameter count seem to be correct here)
-               if !check.validateTArgLen(pos, orig.String(), tparams.Len(), len(targs)) {
+               if !check.validateTArgLen(pos, orig.obj.Name(), tparams.Len(), len(targs)) {
+                       // TODO(gri) Consider returning a valid alias instance with invalid
+                       //           underlying (aliased) type to match behavior of *Named
+                       //           types. Then this function will never return an invalid
+                       //           result.
                        return Typ[Invalid]
                }
                if tparams.Len() == 0 {
index d8f312f61d58c50e54cb8e70f9f80dc0a98c05a1..825ab50f92de4907d91e17bd872c0a198e13c840 100644 (file)
@@ -1,4 +1,4 @@
-// Copyright 2024 The Go Authors. All rights reserved.
+// Copyright 2025 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.
 
index 5bcbc2d1d3b1143e2ee1756c66a7bc1a2c9a69d1..e560f2c131bd37db9e57ee8015a3fe7a7f9daa0f 100644 (file)
@@ -471,9 +471,14 @@ func (check *Checker) instantiatedType(ix *indexedExpr, def *TypeName) (res Type
        }
 
        // create instance
-       // The instance is not generic anymore as it has type arguments, but it still
-       // satisfies the genericType interface because it has type parameters, too.
-       inst := check.instance(ix.Pos(), gtyp, targs, nil, check.context()).(genericType)
+       // The instance is not generic anymore as it has type arguments, but unless
+       // instantiation failed, it still satisfies the genericType interface because
+       // it has type parameters, too.
+       ityp := check.instance(ix.Pos(), gtyp, targs, nil, check.context())
+       inst, _ := ityp.(genericType)
+       if inst == nil {
+               return Typ[Invalid]
+       }
 
        // For Named types, orig.tparams may not be set up, so we need to do expansion later.
        check.later(func() {
diff --git a/src/internal/types/testdata/fixedbugs/issue71198.go b/src/internal/types/testdata/fixedbugs/issue71198.go
new file mode 100644 (file)
index 0000000..479f8e2
--- /dev/null
@@ -0,0 +1,16 @@
+// -gotypesalias=1
+
+// Copyright 2025 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 A[_ any] = any
+
+// This must not panic; also the error message must match the style for non-alias types, below.
+func _[_ A /* ERROR "too many type arguments for type A: have 2, want 1" */ [int, string]]() {}
+
+type T[_ any] any
+
+func _[_ T /* ERROR "too many type arguments for type T: have 2, want 1" */ [int, string]]() {}