]> Cypherpunks repositories - gostls13.git/commitdiff
go/types: better error message for missing ~ in constraint
authorRobert Griesemer <gri@golang.org>
Fri, 26 Nov 2021 21:27:12 +0000 (13:27 -0800)
committerRobert Griesemer <gri@golang.org>
Mon, 29 Nov 2021 22:02:51 +0000 (22:02 +0000)
This is a port of CL 366758 from types2 to go/types.

For #49179.

Change-Id: I7e1c6ffb392d5c535cf901004b7acbe8c3be9b0f
Reviewed-on: https://go-review.googlesource.com/c/go/+/367199
Trust: Robert Griesemer <gri@golang.org>
Run-TryBot: Robert Griesemer <gri@golang.org>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Robert Findley <rfindley@google.com>
src/go/types/instantiate.go
src/go/types/testdata/fixedbugs/issue49179.go2
src/go/types/typeset.go

index 011fb8e540cbc177209e33ed28751c6a072e75eb..597a6da624f62fb9ebb844e8ffbc442217c81f63 100644 (file)
@@ -239,9 +239,28 @@ func (check *Checker) implements(V, T Type, qf Qualifier) error {
        }
 
        // 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
index 7cba52aa252d658aaa9aa5b77cd5d82631b8a36e..d4c8a897c65a4004a6dacbdc836846aca99b43ca 100644 (file)
@@ -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 @@ func Map[S SliceConstraint[E], E any](s S, f func(E) E) S {
 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)
 }
index 29283687351adafc2d5914d91a58f83d53459357..d39483f2549642d72254872e028bbf6246c259a3 100644 (file)
@@ -106,6 +106,8 @@ func (s *_TypeSet) hasTerms() bool { return !s.terms.isEmpty() && !s.terms.isAll
 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.