]> Cypherpunks repositories - gostls13.git/commitdiff
cmd/compile: better error message for when a type is in a constraint but not the...
authorRobert Griesemer <gri@golang.org>
Wed, 4 Jan 2023 01:05:53 +0000 (17:05 -0800)
committerHeschi Kreinick <heschi@google.com>
Wed, 11 Jan 2023 17:10:19 +0000 (17:10 +0000)
While at it, also remove the word "constraint" in the detail explanation
of an unsatisfied constraint.

Fixes #57500.

Change-Id: I55dae1694de2cfdb434aeba9d4a3530af7aca8f5
Reviewed-on: https://go-review.googlesource.com/c/go/+/460455
Reviewed-by: Robert Findley <rfindley@google.com>
Reviewed-by: Ian Lance Taylor <iant@google.com>
Run-TryBot: Robert Griesemer <gri@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
Reviewed-by: Robert Griesemer <gri@google.com>
src/cmd/compile/internal/types2/instantiate.go
src/go/types/instantiate.go
src/internal/types/testdata/fixedbugs/issue40350.go
src/internal/types/testdata/fixedbugs/issue49179.go
src/internal/types/testdata/fixedbugs/issue57486.go
src/internal/types/testdata/fixedbugs/issue57500.go [new file with mode: 0644]

index f028161118cc3cc2ac1426453ef4aa2651c63dae..819368299386078856154283b68f5e210cc07ffd 100644 (file)
@@ -324,14 +324,43 @@ func (check *Checker) implements(V, T Type, constraint bool, cause *string) bool
                return false
        }) {
                if cause != nil {
-                       if alt != nil {
-                               *cause = check.sprintf("%s does not %s %s (possibly missing ~ for %s in constraint %s)", V, verb, T, alt, T)
-                       } else {
-                               *cause = check.sprintf("%s does not %s %s (%s missing in %s)", V, verb, T, V, Ti.typeSet().terms)
+                       var detail string
+                       switch {
+                       case alt != nil:
+                               detail = check.sprintf("possibly missing ~ for %s in %s", alt, T)
+                       case mentions(Ti, V):
+                               detail = check.sprintf("%s mentions %s, but %s is not in the type set of %s", T, V, V, T)
+                       default:
+                               detail = check.sprintf("%s missing in %s", V, Ti.typeSet().terms)
                        }
+                       *cause = check.sprintf("%s does not %s %s (%s)", V, verb, T, detail)
                }
                return false
        }
 
        return checkComparability()
 }
+
+// mentions reports whether type T "mentions" typ in an (embedded) element or term
+// of T (whether typ is in the type set of T or not). For better error messages.
+func mentions(T, typ Type) bool {
+       switch T := T.(type) {
+       case *Interface:
+               for _, e := range T.embeddeds {
+                       if mentions(e, typ) {
+                               return true
+                       }
+               }
+       case *Union:
+               for _, t := range T.terms {
+                       if mentions(t.typ, typ) {
+                               return true
+                       }
+               }
+       default:
+               if Identical(T, typ) {
+                       return true
+               }
+       }
+       return false
+}
index 9f565c326ba6fc4873f28b92ec2dc1ca13870783..2cf48c17d298dcc1b39fb95ea634b6afb1e30e86 100644 (file)
@@ -324,14 +324,43 @@ func (check *Checker) implements(V, T Type, constraint bool, cause *string) bool
                return false
        }) {
                if cause != nil {
-                       if alt != nil {
-                               *cause = check.sprintf("%s does not %s %s (possibly missing ~ for %s in constraint %s)", V, verb, T, alt, T)
-                       } else {
-                               *cause = check.sprintf("%s does not %s %s (%s missing in %s)", V, verb, T, V, Ti.typeSet().terms)
+                       var detail string
+                       switch {
+                       case alt != nil:
+                               detail = check.sprintf("possibly missing ~ for %s in %s", alt, T)
+                       case mentions(Ti, V):
+                               detail = check.sprintf("%s mentions %s, but %s is not in the type set of %s", T, V, V, T)
+                       default:
+                               detail = check.sprintf("%s missing in %s", V, Ti.typeSet().terms)
                        }
+                       *cause = check.sprintf("%s does not %s %s (%s)", V, verb, T, detail)
                }
                return false
        }
 
        return checkComparability()
 }
+
+// mentions reports whether type T "mentions" typ in an (embedded) element or term
+// of T (whether typ is in the type set of T or not). For better error messages.
+func mentions(T, typ Type) bool {
+       switch T := T.(type) {
+       case *Interface:
+               for _, e := range T.embeddeds {
+                       if mentions(e, typ) {
+                               return true
+                       }
+               }
+       case *Union:
+               for _, t := range T.terms {
+                       if mentions(t.typ, typ) {
+                               return true
+                       }
+               }
+       default:
+               if Identical(T, typ) {
+                       return true
+               }
+       }
+       return false
+}
index 96ad1678d407d74f25079198e4cc53f364006ba9..08eb42641085197d483d081d31ce638214713fc6 100644 (file)
@@ -12,5 +12,5 @@ type number interface {
 func f[T number]() {}
 
 func _() {
-       _ = f[int /* ERROR int does not satisfy number \(int missing in float64 \| ~int32\)*/]
+       _ = f[int /* ERROR int does not satisfy number \(number mentions int, but int is not in the type set of number\)*/]
 }
index 468d83edbeaed4c0c001c105aa2ac7ebc19d8f97..2ddfa3312d27733b8924cb567bbc08ceed96a74f 100644 (file)
@@ -13,11 +13,11 @@ type myFloat float64
 
 func _() {
        _ = f1[int]
-       _ = f1[myInt /* ERROR possibly missing ~ for int in constraint int \| string */]
+       _ = f1[myInt /* ERROR possibly missing ~ for int in int \| string */]
        _ = f2[myInt]
-       _ = f2[myFloat /* ERROR possibly missing ~ for float64 in constraint ~int \| string \| float64 */]
+       _ = f2[myFloat /* ERROR possibly missing ~ for float64 in ~int \| string \| float64 */]
        var x myInt
-       f3 /* ERROR myInt does not satisfy int \(possibly missing ~ for int in constraint int\) */ (x)
+       f3 /* ERROR myInt does not satisfy int \(possibly missing ~ for int in int\) */ (x)
 }
 
 // test case from the issue
@@ -33,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 satisfy SliceConstraint\[int\] \(possibly missing ~ for \[\]int in constraint SliceConstraint\[int\]\) */, int](s, nil)
+       Map[MySlice /* ERROR MySlice does not satisfy SliceConstraint\[int\] \(possibly missing ~ for \[\]int in SliceConstraint\[int\]\) */, int](s, nil)
 }
index f6ba1b60b8e9c48833fa4607c4b5fa94c6d795d9..43ba1b044009968f344f8e93d1782cd31276b2f5 100644 (file)
@@ -24,6 +24,6 @@ func F1[V [2]any](v V) {
 
 func F2[V [2]any](v V) {
        _ = G2[V /* ERROR "V does not satisfy C2" */]
-       _ = G2[[ /* ERROR "\[2\]any does not satisfy C2 \(\[2\]any missing in int\)" */ 2]any]
+       _ = G2[[ /* ERROR "\[2\]any does not satisfy C2 \(C2 mentions \[2\]any, but \[2\]any is not in the type set of C2\)" */ 2]any]
        _ = G2[int]
 }
diff --git a/src/internal/types/testdata/fixedbugs/issue57500.go b/src/internal/types/testdata/fixedbugs/issue57500.go
new file mode 100644 (file)
index 0000000..abdcb5e
--- /dev/null
@@ -0,0 +1,16 @@
+// Copyright 2023 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package p
+
+type C interface {
+       comparable
+       [2]any | int
+}
+
+func f[T C]() {}
+
+func _() {
+       _ = f[[ /* ERROR \[2\]any does not satisfy C \(C mentions \[2\]any, but \[2\]any is not in the type set of C\) */ 2]any]
+}