1
0
mirror of https://github.com/golang/go synced 2024-11-17 07:54:41 -07:00

[dev.typeparams] go/types: trigger verification while resolving instance

The refactoring of CL 335929 to merge the instance and Named types
resulted in type instances only being evaluated once. As a side effect,
we only verified constraints once per unique instantiation expression.

This can be confusing if type instantations are occurring far apart in
the code. Resolve this by lifting up the verification logic into
Instantiate and InstantiateLazy.

Change-Id: Icd5a482d097d983073955c62931441edfd92f5c2
Reviewed-on: https://go-review.googlesource.com/c/go/+/335978
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>
This commit is contained in:
Rob Findley 2021-07-20 13:07:50 -04:00 committed by Robert Findley
parent 61f69d2559
commit b7149b781f
3 changed files with 33 additions and 25 deletions

View File

@ -24,7 +24,7 @@ type instance struct {
func (n *Named) complete() { func (n *Named) complete() {
if n.instance != nil && len(n.targs) > 0 && n.underlying == nil { if n.instance != nil && len(n.targs) > 0 && n.underlying == nil {
check := n.instance.check check := n.instance.check
inst := check.instantiate(n.instance.pos, n.orig.underlying, n.tparams, n.targs, n.instance.posList, n.instance.verify) inst := check.instantiate(n.instance.pos, n.orig.underlying, n.tparams, n.targs, n.instance.posList)
n.underlying = inst n.underlying = inst
n.fromRHS = inst n.fromRHS = inst
n.methods = n.orig.methods n.methods = n.orig.methods

View File

@ -54,10 +54,14 @@ func (check *Checker) Instantiate(pos token.Pos, typ Type, targs []Type, posList
panic(fmt.Sprintf("%v: cannot instantiate %v", pos, typ)) panic(fmt.Sprintf("%v: cannot instantiate %v", pos, typ))
} }
return check.instantiate(pos, typ, tparams, targs, posList, verify) inst := check.instantiate(pos, typ, tparams, targs, posList)
if verify && len(tparams) == len(targs) {
check.verify(pos, tparams, targs, posList)
}
return inst
} }
func (check *Checker) instantiate(pos token.Pos, typ Type, tparams []*TypeName, targs []Type, posList []token.Pos, verify bool) (res Type) { func (check *Checker) instantiate(pos token.Pos, typ Type, tparams []*TypeName, targs []Type, posList []token.Pos) (res Type) {
// the number of supplied types must match the number of type parameters // the number of supplied types must match the number of type parameters
if len(targs) != len(tparams) { if len(targs) != len(tparams) {
// TODO(gri) provide better error message // TODO(gri) provide better error message
@ -67,9 +71,6 @@ func (check *Checker) instantiate(pos token.Pos, typ Type, tparams []*TypeName,
} }
panic(fmt.Sprintf("%v: got %d arguments but %d type parameters", pos, len(targs), len(tparams))) panic(fmt.Sprintf("%v: got %d arguments but %d type parameters", pos, len(targs), len(tparams)))
} }
if verify && check == nil {
panic("cannot have nil receiver if verify is set")
}
if check != nil && trace { if check != nil && trace {
check.trace(pos, "-- instantiating %s with %s", typ, typeListString(targs)) check.trace(pos, "-- instantiating %s with %s", typ, typeListString(targs))
@ -97,22 +98,6 @@ func (check *Checker) instantiate(pos token.Pos, typ Type, tparams []*TypeName,
smap := makeSubstMap(tparams, targs) smap := makeSubstMap(tparams, targs)
// check bounds
if verify {
for i, tname := range tparams {
// best position for error reporting
pos := pos
if i < len(posList) {
pos = posList[i]
}
// stop checking bounds after the first failure
if !check.satisfies(pos, targs[i], tname.typ.(*TypeParam), smap) {
break
}
}
}
return check.subst(pos, typ, smap) return check.subst(pos, typ, smap)
} }
@ -124,6 +109,11 @@ func (check *Checker) InstantiateLazy(pos token.Pos, typ Type, targs []Type, pos
if base == nil { if base == nil {
panic(fmt.Sprintf("%v: cannot instantiate %v", pos, typ)) panic(fmt.Sprintf("%v: cannot instantiate %v", pos, typ))
} }
if verify && len(base.tparams) == len(targs) {
check.later(func() {
check.verify(pos, base.tparams, targs, posList)
})
}
h := instantiatedHash(base, targs) h := instantiatedHash(base, targs)
if check != nil { if check != nil {
if named := check.typMap[h]; named != nil { if named := check.typMap[h]; named != nil {
@ -146,6 +136,26 @@ func (check *Checker) InstantiateLazy(pos token.Pos, typ Type, targs []Type, pos
return named return named
} }
func (check *Checker) verify(pos token.Pos, tparams []*TypeName, targs []Type, posList []token.Pos) {
if check == nil {
panic("cannot have nil Checker if verifying constraints")
}
smap := makeSubstMap(tparams, targs)
for i, tname := range tparams {
// best position for error reporting
pos := pos
if i < len(posList) {
pos = posList[i]
}
// stop checking bounds after the first failure
if !check.satisfies(pos, targs[i], tname.typ.(*TypeParam), smap) {
break
}
}
}
// satisfies reports whether the type argument targ satisfies the constraint of type parameter // satisfies reports whether the type argument targ satisfies the constraint of type parameter
// parameter tpar (after any of its type parameters have been substituted through smap). // parameter tpar (after any of its type parameters have been substituted through smap).
// A suitable error is reported if the result is false. // A suitable error is reported if the result is false.

View File

@ -81,10 +81,8 @@ func (u T2[U]) Add1() U {
return u.s + 1 return u.s + 1
} }
// TODO(rfindley): we should probably report an error here as well, not
// just when the type is first instantiated.
func NewT2[U any]() T2[U /* ERROR U has no type constraints */ ] { func NewT2[U any]() T2[U /* ERROR U has no type constraints */ ] {
return T2[U]{} return T2[U /* ERROR U has no type constraints */ ]{}
} }
func _() { func _() {