Skip to content

Commit 17ed215

Browse files
griesemergopherbot
authored andcommitted
go/types, types2: don't panic when instantiating generic alias with wrong number of type arguments
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 <[email protected]> Reviewed-by: Robert Griesemer <[email protected]> LUCI-TryBot-Result: Go LUCI <[email protected]> Auto-Submit: Robert Griesemer <[email protected]>
1 parent c53307c commit 17ed215

File tree

9 files changed

+56
-14
lines changed

9 files changed

+56
-14
lines changed

src/cmd/compile/internal/types2/call.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,9 @@ func (check *Checker) instantiateSignature(pos syntax.Pos, expr syntax.Expr, typ
142142
}()
143143
}
144144

145+
// For signatures, Checker.instance will always succeed because the type argument
146+
// count is correct at this point (see assertion above); hence the type assertion
147+
// to *Signature will always succeed.
145148
inst := check.instance(pos, typ, targs, nil, check.context()).(*Signature)
146149
assert(inst.TypeParams().Len() == 0) // signature is not generic anymore
147150
check.recordInstance(expr, targs, inst)

src/cmd/compile/internal/types2/instantiate.go

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,8 @@ func Instantiate(ctxt *Context, orig Type, targs []Type, validate bool) (Type, e
7474
// instance instantiates the given original (generic) function or type with the
7575
// provided type arguments and returns the resulting instance. If an identical
7676
// instance exists already in the given contexts, it returns that instance,
77-
// otherwise it creates a new one.
77+
// otherwise it creates a new one. If there is an error (such as wrong number
78+
// of type arguments), the result is Typ[Invalid].
7879
//
7980
// If expanding is non-nil, it is the Named instance type currently being
8081
// 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
133134
assert(expanding == nil) // Alias instances cannot be reached from Named types
134135
}
135136

137+
// verify type parameter count (see go.dev/issue/71198 for a test case)
136138
tparams := orig.TypeParams()
137-
// TODO(gri) investigate if this is needed (type argument and parameter count seem to be correct here)
138-
if !check.validateTArgLen(pos, orig.String(), tparams.Len(), len(targs)) {
139+
if !check.validateTArgLen(pos, orig.obj.Name(), tparams.Len(), len(targs)) {
140+
// TODO(gri) Consider returning a valid alias instance with invalid
141+
// underlying (aliased) type to match behavior of *Named
142+
// types. Then this function will never return an invalid
143+
// result.
139144
return Typ[Invalid]
140145
}
141146
if tparams.Len() == 0 {

src/cmd/compile/internal/types2/testdata/manual.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright 2024 The Go Authors. All rights reserved.
1+
// Copyright 2025 The Go Authors. All rights reserved.
22
// Use of this source code is governed by a BSD-style
33
// license that can be found in the LICENSE file.
44

src/cmd/compile/internal/types2/typexpr.go

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -475,9 +475,14 @@ func (check *Checker) instantiatedType(x syntax.Expr, xlist []syntax.Expr, def *
475475
}
476476

477477
// create instance
478-
// The instance is not generic anymore as it has type arguments, but it still
479-
// satisfies the genericType interface because it has type parameters, too.
480-
inst := check.instance(x.Pos(), gtyp, targs, nil, check.context()).(genericType)
478+
// The instance is not generic anymore as it has type arguments, but unless
479+
// instantiation failed, it still satisfies the genericType interface because
480+
// it has type parameters, too.
481+
ityp := check.instance(x.Pos(), gtyp, targs, nil, check.context())
482+
inst, _ := ityp.(genericType)
483+
if inst == nil {
484+
return Typ[Invalid]
485+
}
481486

482487
// For Named types, orig.tparams may not be set up, so we need to do expansion later.
483488
check.later(func() {

src/go/types/call.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,9 @@ func (check *Checker) instantiateSignature(pos token.Pos, expr ast.Expr, typ *Si
143143
}()
144144
}
145145

146+
// For signatures, Checker.instance will always succeed because the type argument
147+
// count is correct at this point (see assertion above); hence the type assertion
148+
// to *Signature will always succeed.
146149
inst := check.instance(pos, typ, targs, nil, check.context()).(*Signature)
147150
assert(inst.TypeParams().Len() == 0) // signature is not generic anymore
148151
check.recordInstance(expr, targs, inst)

src/go/types/instantiate.go

Lines changed: 8 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/go/types/testdata/manual.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright 2024 The Go Authors. All rights reserved.
1+
// Copyright 2025 The Go Authors. All rights reserved.
22
// Use of this source code is governed by a BSD-style
33
// license that can be found in the LICENSE file.
44

src/go/types/typexpr.go

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -471,9 +471,14 @@ func (check *Checker) instantiatedType(ix *indexedExpr, def *TypeName) (res Type
471471
}
472472

473473
// create instance
474-
// The instance is not generic anymore as it has type arguments, but it still
475-
// satisfies the genericType interface because it has type parameters, too.
476-
inst := check.instance(ix.Pos(), gtyp, targs, nil, check.context()).(genericType)
474+
// The instance is not generic anymore as it has type arguments, but unless
475+
// instantiation failed, it still satisfies the genericType interface because
476+
// it has type parameters, too.
477+
ityp := check.instance(ix.Pos(), gtyp, targs, nil, check.context())
478+
inst, _ := ityp.(genericType)
479+
if inst == nil {
480+
return Typ[Invalid]
481+
}
477482

478483
// For Named types, orig.tparams may not be set up, so we need to do expansion later.
479484
check.later(func() {
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// -gotypesalias=1
2+
3+
// Copyright 2025 The Go Authors. All rights reserved.
4+
// Use of this source code is governed by a BSD-style
5+
// license that can be found in the LICENSE file.
6+
7+
package p
8+
9+
type A[_ any] = any
10+
11+
// This must not panic; also the error message must match the style for non-alias types, below.
12+
func _[_ A /* ERROR "too many type arguments for type A: have 2, want 1" */ [int, string]]() {}
13+
14+
type T[_ any] any
15+
16+
func _[_ T /* ERROR "too many type arguments for type T: have 2, want 1" */ [int, string]]() {}

0 commit comments

Comments
 (0)