go/types, types2: ensure we have an interface before checking constraints

Due to delayed computation of constraint type sets it is possible that
a type argument is checked against a constraint before that constraint
has been wrapped into an implicit interface (if needed).

When the type checker is about to check whether a type argument
implements a constraint, it's always safe to force wrapping of
a constraint in an implicit interface (if necessary) before doing
the implements check.

Also, use a better position for tracing output related to constraint
type set computation.

Fixes #51048.

Change-Id: I52fecbf57814f09b62762452d7e17c2a230fdd59
Reviewed-on: https://go-review.googlesource.com/c/go/+/383834
Trust: Robert Griesemer <gri@golang.org>
Reviewed-by: Robert Findley <rfindley@google.com>
This commit is contained in:
Robert Griesemer 2022-02-07 11:28:14 -08:00
parent 49030c87e0
commit 7db75b368d
6 changed files with 32 additions and 5 deletions

View File

@ -135,6 +135,8 @@ func (check *Checker) validateTArgLen(pos syntax.Pos, ntparams, ntargs int) bool
func (check *Checker) verify(pos syntax.Pos, tparams []*TypeParam, targs []Type) (int, error) {
smap := makeSubstMap(tparams, targs)
for i, tpar := range tparams {
// Ensure that we have a (possibly implicit) interface as type bound (issue #51048).
tpar.iface()
// The type parameter bound is parameterized with the same type parameters
// as the instantiated type; before we can use it for bounds checking we
// need to instantiate it with the type arguments with which we instantiated

View File

@ -0,0 +1,11 @@
// Copyright 2022 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
func _[P int]() {
_ = f[P]
}
func f[T int]() {}

View File

@ -124,8 +124,9 @@ func (t *TypeParam) iface() *Interface {
// compute type set if necessary
if ityp.tset == nil {
// use the (original) type bound position if we have one
pos := nopos
// pos is used for tracing output; start with the type parameter position.
pos := t.obj.pos
// use the (original or possibly instantiated) type bound position if we have one
if n, _ := bound.(*Named); n != nil {
pos = n.obj.pos
}

View File

@ -135,6 +135,8 @@ func (check *Checker) validateTArgLen(pos token.Pos, ntparams, ntargs int) bool
func (check *Checker) verify(pos token.Pos, tparams []*TypeParam, targs []Type) (int, error) {
smap := makeSubstMap(tparams, targs)
for i, tpar := range tparams {
// Ensure that we have a (possibly implicit) interface as type bound (issue #51048).
tpar.iface()
// The type parameter bound is parameterized with the same type parameters
// as the instantiated type; before we can use it for bounds checking we
// need to instantiate it with the type arguments with which we instantiated

View File

@ -0,0 +1,11 @@
// Copyright 2022 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
func _[P int]() {
_ = f[P]
}
func f[T int]() {}

View File

@ -5,7 +5,6 @@
package types
import (
"go/token"
"sync/atomic"
)
@ -127,8 +126,9 @@ func (t *TypeParam) iface() *Interface {
// compute type set if necessary
if ityp.tset == nil {
// use the (original) type bound position if we have one
pos := token.NoPos
// pos is used for tracing output; start with the type parameter position.
pos := t.obj.pos
// use the (original or possibly instantiated) type bound position if we have one
if n, _ := bound.(*Named); n != nil {
pos = n.obj.pos
}