From b77f5f9667c6e5c2081d94163dd7d11c03fa2b8e Mon Sep 17 00:00:00 2001 From: Robert Griesemer Date: Tue, 23 Nov 2021 15:55:11 -0800 Subject: [PATCH] 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 Reviewed-by: Robert Findley --- .../compile/internal/types2/instantiate.go | 25 ++++++++++++++++--- .../types2/testdata/fixedbugs/issue49179.go2 | 20 ++++++++++++++- src/cmd/compile/internal/types2/typeset.go | 2 ++ 3 files changed, 43 insertions(+), 4 deletions(-) diff --git a/src/cmd/compile/internal/types2/instantiate.go b/src/cmd/compile/internal/types2/instantiate.go index 3f5fc56f5d..f9423dd70e 100644 --- a/src/cmd/compile/internal/types2/instantiate.go +++ b/src/cmd/compile/internal/types2/instantiate.go @@ -238,9 +238,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 diff --git a/src/cmd/compile/internal/types2/testdata/fixedbugs/issue49179.go2 b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue49179.go2 index 7cba52aa25..75bea18072 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 @@ 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) } diff --git a/src/cmd/compile/internal/types2/typeset.go b/src/cmd/compile/internal/types2/typeset.go index a55e9d1d63..eaf614da64 100644 --- a/src/cmd/compile/internal/types2/typeset.go +++ b/src/cmd/compile/internal/types2/typeset.go @@ -108,6 +108,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.