-
Notifications
You must be signed in to change notification settings - Fork 17.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
cmd/compile: inconsistent error messages based on type constraints #53692
Comments
Here's another example, which seems to have the opposite behavior. When a function is given as a type constraint, it's not inferred, but when it's declared as a parameter, it is inferred properly: https://go.dev/play/p/qOpMvvHt8On package main
// FetchFunc is a function that is invoked when a cached value is not found.
type FetchFunc[V any] func() (V, error)
// Cache is a generic cache implementation.
type Cache[K comparable, V any] interface {
Get(K)
Set(K, V)
}
// LRU is a cache.
type LRU[K comparable, V any] struct{}
func (l *LRU[K, V]) Get(key K) {}
func (l *LRU[K, V]) Set(key K, val V) {}
func Fetch1[K comparable, V any, C Cache[K, V]](cache C, key K, fn FetchFunc[V]) {}
func Fetch2[K comparable, V any](cache Cache[K, V], key K, fn FetchFunc[V]) {}
func Fetch3[K comparable, V any, C Cache[K, V], F FetchFunc[V]](cache C, key K, fn F) {}
func main() {
var lru LRU[string, int]
Fetch1(&lru, "foo", func() (int, error) { return 0, nil }) // ok
Fetch2[string, int](&lru, "foo", func() (int, error) { return 0, nil }) // ok
Fetch2(&lru, "foo", func() (int, error) { return 0, nil }) // nok: cannot infer K and V
Fetch3(&lru, "foo", FetchFunc[int](func() (int, error) { return 0, nil })) // ok
Fetch3(&lru, "foo", func() (int, error) { return 0, nil }) // nok: does not implement FetchFunc[int]
} |
These cases are in fact different. The only error message I see that looks clearly unhelpful is WithLocking2[string](&lru) // nok: does not match Cache[string, V] for which the full error is
The other cases seem to be saying the right thing. It seems normal to me to get different kinds of errors based on different kinds of type inference. Maybe I'm missing something. |
CC @griesemer |
It would also be helpful to understand why the functions in this comment are all subtly different. I've read through all the generics documentation, and I think I'm missing something. |
The error cases in the second comment can only work if the compiler uses the argument to infer the type arguments of the generic type of the parameter. There is no type inference rule that will do that. The type inference is as described at https://tip.golang.org/ref/spec#Type_inference. Rules that are not listed there are not applied, even if they may be obvious to the human reader. |
I agree with @ianlancetaylor here. All these cases are slightly different and result in slightly different inference mechanisms, resulting to slightly different errors. This is not to say that we cannot do better - improving inference and related error messages was simply not the highest priority so far. With respect to "but there seems to be multiple ways to define generics, all of which are subtly different with unknown consequences" I respectfully disagree: there's exactly one way to define generic types and functions, by declaring type parameters and constraints. We allow - as syntactic sugar - the leaving away of the But there are different ways to invoke/instantiate a generic function: with or without type inference, and when using type inference, with partial type argument lists. Leaving this open for now, as a reference for when we work on better type inference and/or error messages. |
Simplified code for the case that could use a better error message (playground link): package main
type Cache[K comparable, V any] interface{}
type LRU[K comparable, V any] struct{}
func WithLocking2[K comparable, V any](Cache[K, V]) {}
func main() {
WithLocking2[string](LRU[string, int]{})
} At the moment the error is:
The reason it doesn't match is because |
Change https://go.dev/cl/474197 mentions this issue: |
With the above CL, the error message for the above example is now:
making it clearer that |
I like it. |
Factor out check for identical origin. Match unification code with type identity check. Add a test case for #53692. Change-Id: I1238b28297a5ac549e99261c8a085dd46f3dd65f Reviewed-on: https://go-review.googlesource.com/c/go/+/474197 Run-TryBot: Robert Griesemer <gri@google.com> Reviewed-by: Robert Findley <rfindley@google.com> Reviewed-by: Robert Griesemer <gri@google.com> TryBot-Result: Gopher Robot <gobot@golang.org> Auto-Submit: Robert Griesemer <gri@google.com>
The above CL has now landed. Given the variety of calls and type parameters, a variety of error messages is expected. The one that's been most confusing is fixed with the CL. There's more opportunity for better error messages. Ok to file issues for such cases, ideally one issue per case. Closing this as fixed for now. |
What version of Go are you using (
go version
)?Does this issue reproduce with the latest release?
Yes, including tip as of today
What operating system and processor architecture are you using (
go env
)?go env
OutputWhat did you do?
I suspect the root cause of my actual issue is #41176, but it would be great if @ianlancetaylor can confirm for me 😄.
Playground link for the code: https://go.dev/play/p/RqzqQxNapkw. Note this code does not compile.
Code:
What did you expect to see?
As shown in the comments, the error messages are all subtly different depending on the type declarations on the function. I would expect the same error messages here.
What did you see instead?
Different error messages.
Aside
As an aside, it's unclear to me what the "correct" authorship is here. I really appreciate Go's "do one thing", but there seems to be multiple ways to define generics, all of which are subtly different with unknown consequences.
The text was updated successfully, but these errors were encountered: