From b7149b781fda907078b9312d301ea384e91482ef Mon Sep 17 00:00:00 2001 From: Rob Findley Date: Tue, 20 Jul 2021 13:07:50 -0400 Subject: [PATCH] [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 Run-TryBot: Robert Findley TryBot-Result: Go Bot Reviewed-by: Robert Griesemer --- src/go/types/instance.go | 2 +- src/go/types/instantiate.go | 52 +++++++++++++++----------- src/go/types/testdata/check/issues.go2 | 4 +- 3 files changed, 33 insertions(+), 25 deletions(-) diff --git a/src/go/types/instance.go b/src/go/types/instance.go index 205cb470464..9d31b42690a 100644 --- a/src/go/types/instance.go +++ b/src/go/types/instance.go @@ -24,7 +24,7 @@ type instance struct { func (n *Named) complete() { if n.instance != nil && len(n.targs) > 0 && n.underlying == nil { 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.fromRHS = inst n.methods = n.orig.methods diff --git a/src/go/types/instantiate.go b/src/go/types/instantiate.go index 270652149f6..14bbf2b12b1 100644 --- a/src/go/types/instantiate.go +++ b/src/go/types/instantiate.go @@ -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)) } - 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 if len(targs) != len(tparams) { // 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))) } - if verify && check == nil { - panic("cannot have nil receiver if verify is set") - } if check != nil && trace { 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) - // 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) } @@ -124,6 +109,11 @@ func (check *Checker) InstantiateLazy(pos token.Pos, typ Type, targs []Type, pos if base == nil { 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) if check != 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 } +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 // parameter tpar (after any of its type parameters have been substituted through smap). // A suitable error is reported if the result is false. diff --git a/src/go/types/testdata/check/issues.go2 b/src/go/types/testdata/check/issues.go2 index ce0d6082163..c57f0023034 100644 --- a/src/go/types/testdata/check/issues.go2 +++ b/src/go/types/testdata/check/issues.go2 @@ -81,10 +81,8 @@ func (u T2[U]) Add1() U { 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 */ ] { - return T2[U]{} + return T2[U /* ERROR U has no type constraints */ ]{} } func _() {