mirror of
https://github.com/golang/go
synced 2024-09-04 23:44:16 +00:00
[dev.typeparams] cmd/compile/internal/types2: factor out constraint satisfaction check
This is a simple move of a block of inlined code into a function to make instantiation more manageable and easier to understand. There is no change in functionality or behavior. Change-Id: I46e7a9ea03527731e1f0219b3402eb03949627c5 Reviewed-on: https://go-review.googlesource.com/c/go/+/322070 Trust: Robert Griesemer <gri@golang.org> Reviewed-by: Robert Findley <rfindley@google.com>
This commit is contained in:
parent
5770d7a637
commit
155dc0e541
|
@ -119,85 +119,13 @@ func (check *Checker) instantiate(pos syntax.Pos, typ Type, targs []Type, poslis
|
||||||
|
|
||||||
// check bounds
|
// check bounds
|
||||||
for i, tname := range tparams {
|
for i, tname := range tparams {
|
||||||
tpar := tname.typ.(*TypeParam)
|
|
||||||
iface := tpar.Bound()
|
|
||||||
if iface.Empty() {
|
|
||||||
continue // no type bound
|
|
||||||
}
|
|
||||||
|
|
||||||
targ := targs[i]
|
|
||||||
|
|
||||||
// best position for error reporting
|
// best position for error reporting
|
||||||
pos := pos
|
pos := pos
|
||||||
if i < len(poslist) {
|
if i < len(poslist) {
|
||||||
pos = poslist[i]
|
pos = poslist[i]
|
||||||
}
|
}
|
||||||
|
// stop checking bounds after the first failure
|
||||||
// The type parameter bound is parameterized with the same type parameters
|
if !check.satisfies(pos, targs[i], tname.typ.(*TypeParam), smap) {
|
||||||
// as the instantiated type; before we can use it for bounds checking we
|
|
||||||
// need to instantiate it with the type arguments with which we instantiate
|
|
||||||
// the parameterized type.
|
|
||||||
iface = check.subst(pos, iface, smap).(*Interface)
|
|
||||||
|
|
||||||
// targ must implement iface (methods)
|
|
||||||
// - check only if we have methods
|
|
||||||
check.completeInterface(nopos, iface)
|
|
||||||
if len(iface.allMethods) > 0 {
|
|
||||||
// If the type argument is a pointer to a type parameter, the type argument's
|
|
||||||
// method set is empty.
|
|
||||||
// TODO(gri) is this what we want? (spec question)
|
|
||||||
if base, isPtr := deref(targ); isPtr && asTypeParam(base) != nil {
|
|
||||||
check.errorf(pos, "%s has no methods", targ)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
if m, wrong := check.missingMethod(targ, iface, true); m != nil {
|
|
||||||
// TODO(gri) needs to print updated name to avoid major confusion in error message!
|
|
||||||
// (print warning for now)
|
|
||||||
// Old warning:
|
|
||||||
// check.softErrorf(pos, "%s does not satisfy %s (warning: name not updated) = %s (missing method %s)", targ, tpar.bound, iface, m)
|
|
||||||
if m.name == "==" {
|
|
||||||
// We don't want to report "missing method ==".
|
|
||||||
check.softErrorf(pos, "%s does not satisfy comparable", targ)
|
|
||||||
} else if wrong != nil {
|
|
||||||
// TODO(gri) This can still report uninstantiated types which makes the error message
|
|
||||||
// more difficult to read then necessary.
|
|
||||||
check.softErrorf(pos,
|
|
||||||
"%s does not satisfy %s: wrong method signature\n\tgot %s\n\twant %s",
|
|
||||||
targ, tpar.bound, wrong, m,
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
check.softErrorf(pos, "%s does not satisfy %s (missing method %s)", targ, tpar.bound, m.name)
|
|
||||||
}
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// targ's underlying type must also be one of the interface types listed, if any
|
|
||||||
if iface.allTypes == nil {
|
|
||||||
continue // nothing to do
|
|
||||||
}
|
|
||||||
|
|
||||||
// If targ is itself a type parameter, each of its possible types, but at least one, must be in the
|
|
||||||
// list of iface types (i.e., the targ type list must be a non-empty subset of the iface types).
|
|
||||||
if targ := asTypeParam(targ); targ != nil {
|
|
||||||
targBound := targ.Bound()
|
|
||||||
if targBound.allTypes == nil {
|
|
||||||
check.softErrorf(pos, "%s does not satisfy %s (%s has no type constraints)", targ, tpar.bound, targ)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
for _, t := range unpack(targBound.allTypes) {
|
|
||||||
if !iface.isSatisfiedBy(t) {
|
|
||||||
// TODO(gri) match this error message with the one below (or vice versa)
|
|
||||||
check.softErrorf(pos, "%s does not satisfy %s (%s type constraint %s not found in %s)", targ, tpar.bound, targ, t, iface.allTypes)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
// Otherwise, targ's type or underlying type must also be one of the interface types listed, if any.
|
|
||||||
if !iface.isSatisfiedBy(targ) {
|
|
||||||
check.softErrorf(pos, "%s does not satisfy %s (%s not found in %s)", targ, tpar.bound, under(targ), iface.allTypes)
|
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -205,6 +133,86 @@ func (check *Checker) instantiate(pos syntax.Pos, typ Type, targs []Type, poslis
|
||||||
return check.subst(pos, typ, smap)
|
return check.subst(pos, typ, smap)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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.
|
||||||
|
func (check *Checker) satisfies(pos syntax.Pos, targ Type, tpar *TypeParam, smap *substMap) bool {
|
||||||
|
iface := tpar.Bound()
|
||||||
|
if iface.Empty() {
|
||||||
|
return true // no type bound
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 instantiate
|
||||||
|
// the parameterized type.
|
||||||
|
iface = check.subst(pos, iface, smap).(*Interface)
|
||||||
|
|
||||||
|
// targ must implement iface (methods)
|
||||||
|
// - check only if we have methods
|
||||||
|
check.completeInterface(nopos, iface)
|
||||||
|
if len(iface.allMethods) > 0 {
|
||||||
|
// If the type argument is a pointer to a type parameter, the type argument's
|
||||||
|
// method set is empty.
|
||||||
|
// TODO(gri) is this what we want? (spec question)
|
||||||
|
if base, isPtr := deref(targ); isPtr && asTypeParam(base) != nil {
|
||||||
|
check.errorf(pos, "%s has no methods", targ)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if m, wrong := check.missingMethod(targ, iface, true); m != nil {
|
||||||
|
// TODO(gri) needs to print updated name to avoid major confusion in error message!
|
||||||
|
// (print warning for now)
|
||||||
|
// Old warning:
|
||||||
|
// check.softErrorf(pos, "%s does not satisfy %s (warning: name not updated) = %s (missing method %s)", targ, tpar.bound, iface, m)
|
||||||
|
if m.name == "==" {
|
||||||
|
// We don't want to report "missing method ==".
|
||||||
|
check.softErrorf(pos, "%s does not satisfy comparable", targ)
|
||||||
|
} else if wrong != nil {
|
||||||
|
// TODO(gri) This can still report uninstantiated types which makes the error message
|
||||||
|
// more difficult to read then necessary.
|
||||||
|
check.softErrorf(pos,
|
||||||
|
"%s does not satisfy %s: wrong method signature\n\tgot %s\n\twant %s",
|
||||||
|
targ, tpar.bound, wrong, m,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
check.softErrorf(pos, "%s does not satisfy %s (missing method %s)", targ, tpar.bound, m.name)
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// targ's underlying type must also be one of the interface types listed, if any
|
||||||
|
if iface.allTypes == nil {
|
||||||
|
return true // nothing to do
|
||||||
|
}
|
||||||
|
|
||||||
|
// If targ is itself a type parameter, each of its possible types, but at least one, must be in the
|
||||||
|
// list of iface types (i.e., the targ type list must be a non-empty subset of the iface types).
|
||||||
|
if targ := asTypeParam(targ); targ != nil {
|
||||||
|
targBound := targ.Bound()
|
||||||
|
if targBound.allTypes == nil {
|
||||||
|
check.softErrorf(pos, "%s does not satisfy %s (%s has no type constraints)", targ, tpar.bound, targ)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
for _, t := range unpack(targBound.allTypes) {
|
||||||
|
if !iface.isSatisfiedBy(t) {
|
||||||
|
// TODO(gri) match this error message with the one below (or vice versa)
|
||||||
|
check.softErrorf(pos, "%s does not satisfy %s (%s type constraint %s not found in %s)", targ, tpar.bound, targ, t, iface.allTypes)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Otherwise, targ's type or underlying type must also be one of the interface types listed, if any.
|
||||||
|
if !iface.isSatisfiedBy(targ) {
|
||||||
|
check.softErrorf(pos, "%s does not satisfy %s (%s not found in %s)", targ, tpar.bound, under(targ), iface.allTypes)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
// subst returns the type typ with its type parameters tpars replaced by
|
// subst returns the type typ with its type parameters tpars replaced by
|
||||||
// the corresponding type arguments targs, recursively.
|
// the corresponding type arguments targs, recursively.
|
||||||
// subst is functional in the sense that it doesn't modify the incoming
|
// subst is functional in the sense that it doesn't modify the incoming
|
||||||
|
|
Loading…
Reference in a new issue