cmd/compile/internal/types2: better error message for missing ~ in constraint
If a constraint could be satisfied if one of its type elements
had a ~, provide this information in the error message.
Fixes #49179.
Change-Id: I59f1a855a0646ad7254a978420b0334f1f52ec22
Reviewed-on: https://go-review.googlesource.com/c/go/+/366758
Trust: Robert Griesemer <gri@golang.org>
Reviewed-by: Robert Findley <rfindley@google.com>
diff --git a/src/cmd/compile/internal/types2/instantiate.go b/src/cmd/compile/internal/types2/instantiate.go
index 3f5fc56..f9423dd 100644
--- a/src/cmd/compile/internal/types2/instantiate.go
+++ b/src/cmd/compile/internal/types2/instantiate.go
@@ -238,9 +238,28 @@
}
// Otherwise, V's type must be included in the iface type set.
- if !Ti.typeSet().includes(V) {
- // TODO(gri) report which type is missing
- return errorf("%s does not implement %s", V, T)
+ var alt Type
+ if Ti.typeSet().is(func(t *term) bool {
+ if !t.includes(V) {
+ // If V ∉ t.typ but V ∈ ~t.typ then remember this type
+ // so we can suggest it as an alternative in the error
+ // message.
+ if alt == nil && !t.tilde && Identical(t.typ, under(t.typ)) {
+ tt := *t
+ tt.tilde = true
+ if tt.includes(V) {
+ alt = t.typ
+ }
+ }
+ return true
+ }
+ return false
+ }) {
+ if alt != nil {
+ return errorf("%s does not implement %s (possibly missing ~ for %s in constraint %s)", V, T, alt, T)
+ } else {
+ return errorf("%s does not implement %s", V, T)
+ }
}
return nil
diff --git a/src/cmd/compile/internal/types2/testdata/fixedbugs/issue49179.go2 b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue49179.go2
index 7cba52a..75bea18 100644
--- a/src/cmd/compile/internal/types2/testdata/fixedbugs/issue49179.go2
+++ b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue49179.go2
@@ -4,6 +4,24 @@
package p
+func f1[P int | string]() {}
+func f2[P ~int | string | float64]() {}
+func f3[P int](x P) {}
+
+type myInt int
+type myFloat float64
+
+func _() {
+ _ = f1[int]
+ _ = f1[myInt /* ERROR possibly missing ~ for int in constraint int\|string */]
+ _ = f2[myInt]
+ _ = f2[myFloat /* ERROR possibly missing ~ for float64 in constraint int\|string|float64 */]
+ var x myInt
+ f3( /* ERROR myInt does not implement int \(possibly missing ~ for int in constraint int\) */ x)
+}
+
+// test case from the issue
+
type SliceConstraint[T any] interface {
[]T
}
@@ -15,5 +33,5 @@
type MySlice []int
func f(s MySlice) {
- Map[MySlice /* ERROR MySlice does not implement SliceConstraint\[int\] */, int](s, nil)
+ Map[MySlice /* ERROR MySlice does not implement SliceConstraint\[int\] \(possibly missing ~ for \[\]int in constraint SliceConstraint\[int\]\) */, int](s, nil)
}
diff --git a/src/cmd/compile/internal/types2/typeset.go b/src/cmd/compile/internal/types2/typeset.go
index a55e9d1..eaf614d 100644
--- a/src/cmd/compile/internal/types2/typeset.go
+++ b/src/cmd/compile/internal/types2/typeset.go
@@ -108,6 +108,8 @@
func (s *_TypeSet) singleType() Type { return s.terms.singleType() }
// includes reports whether t ∈ s.
+// TODO(gri) This function is not used anywhere anymore. Remove once we
+// are clear that we don't need it elsewhere in the future.
func (s *_TypeSet) includes(t Type) bool { return s.terms.includes(t) }
// subsetOf reports whether s1 ⊆ s2.