cmd/compile: better error message for when a type is in a constraint but not the type set

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>
This commit is contained in:
Robert Griesemer 2023-01-03 17:05:53 -08:00 committed by Heschi Kreinick
parent 76d39ae349
commit 6ad27161f8
6 changed files with 88 additions and 14 deletions

View 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
}

View 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
}

View 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\)*/]
}

View 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)
}

View 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]
}

View File

@ -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]
}