-
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: type sets are not correctly deduced for type conversion on type params #63827
Comments
The type set of
Given that The original examples I constructed (and hence the point I was trying to make with them) where a bit different from what was given above: Rejected: https://go.dev/play/p/DDn-SFzglUz Note in particular, that the function-instantiation also requires you to explicitly specify that
Intuitively, they should imply the same thing, but I'm honestly losing track of the precise wordings of the spec, when it comes to type parameters, type arguments and type sets. In particular, it is not entirely clear to me what it means for a type argument of type parameter type (sorry, this is probably exciting to parse) to satisfy a constraint. The reason I constructed those examples is that to me, they hint that the problem has a similar structure to the restriction of requiring to mention methods in constraints explicitly. Just like type C interface{ *strings.Reader | *bytes.Reader }
func F[R C](r R) { r.Read(nil) } // r.Read undefined (type R has no field or method Read) is disallowed, but becomes allowed once we intersect the constraint with type C interface { *strings.Reader | *bytes.Reader; io.Reader }
func F[R C](r R) { r.Read(nil) } // fine So too does the example start working, once we intersect the constraint with another interface that should be implied by the union. |
Not a bug by the current spec. A simpler example: func jon[T byte](x string) []T {
return []T(x) // error
} Similarly for assignment: func pet[A, B []byte](x A, y B){
x = y // error: cannot use y as type A in assignment
y = x // error: cannot use x as type B in assignment
} (Just two small examples from here.) The rule might be relaxed later, but I don't know if it is worthy it. |
Good point. I think that statement would have been uncontroversial if the set of operations allowed on a composite type did not depend on the types that it is composed of. And this is mostly true, string-slice conversions is the only exception I can think of off the top of my head. Then I suppose the question is: did the spec intend to leave that difference between type params and concrete types or not? |
CC @griesemer Yes, this is confusing -- it's too late to change the legality of constraints like []elType, but perhaps we can add a vet check for singleton type sets (I'll have to think about this a bit more). |
The question is probably about what do types that are composites of type parameters satisfy? []eltype should satisfy []byte | []rune iff eltype satisfies byte | rune. Probably similar to the logic used in type inference, the slice type constructor could be seen as bijective on the domain of type sets. The conversion here is probably special cased here too and simply not implemented in the general constraint checking algorithm. |
@go101 Just FYI: your assignment example is unrelated to this issue: func pet[A, B []byte](x A, y B){
x = y // error: cannot use y as type A in assignment
y = x // error: cannot use x as type B in assignment
} Type parameters are named types (spec). A named type is always different from any other type (spec). It's never possible to assign a value of one named type ( |
For this issue, it may help to look at really simple examples. In non-generic code, the following is valid: type E byte
func f0(x []E) string { return string(x) } The conversion is valid per the spec because "x is an integer or a slice of bytes or runes and T is a string type", and because The generic variant func f1[T []byte](x T) string { return string(x) } is also accepted because the conversion from func f2[T []E](x T) string { return string(x) } is ok (with The generic variant func f3[E byte](x []E) string { return string(x) } // ERROR cannot convert x (variable of type []E) to type string fails for non-obvious reasons - though I believe this is actually just a bug: note that Similarly, the variant func f4[T []E, E byte](x T) string { return string(x) } will work with the very same change in the implementation. So my inclination is to treat this as a bug: 1) it's not specified precisely enough in the spec, and 2) the implementation is not entirely consistent between generic and non-generic types. I suspect (but I'm not 100% certain yet) we can make this change because it will simply enable code that so far was rejected. Furthermore, we can argue that the code ( Finally, all this doesn't address the issue where |
Follow-up: "slice of bytes" (or "slice of runes") is not explicitly defined in the spec but examples were added in the past to make clear what is meant: type myByte byte
string([]myByte{'w', 'o', 'r', 'l', 'd', '!'}) // "world!" So |
This is also related to #50421. |
What version of Go are you using (
go version
)?Go Playground, 1.21
Does this issue reproduce with the latest release?
Yes
What operating system and processor architecture are you using (
go env
)?N/A, Go Playground
What did you do?
https://go.dev/play/p/Jhx6YGrz21s
What did you expect to see?
Both
convert()
andconvert2()
are accepted by the compiler and are semantically equivalent.What did you see instead?
convert2()
is rejected by the compiler:cannot convert x (variable of type srcType constrained by string) to type dstType: cannot convert string (in srcType) to type []elType (in dstType)
Details
The spec says the following about type conversions of type params:
Consider the following code:
Here
srcType
's type set should be juststring
, anddstType
's type set is[]rune|[]byte
. As per spec,string
can be converted to both[]rune
and[]byte
:So, conversion should be allowed, assuming the compiler is able to correctly deduce type set for
dstType
.The simplest example of the issue I was able to find:
The next step of eliminating elType makes the error disappear:
So it seems the compiler fails to flatten
dstType
's type set with indirection fromelType
.Also, @Merovius was able to construct the following two semantically equivalent examples that hint that the issue is somehow specific to the type conversion operation:
The text was updated successfully, but these errors were encountered: