-
Notifications
You must be signed in to change notification settings - Fork 17.9k
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 inference could be less strict when there are interface arguments #40055
Comments
hi @rogpeppe this: func main() {
} should also work for: func main() {
} |
I am very concerned about avoiding any possible confusion in which type inference does not act as people expect. This is based on the complexity of the C++ equivalent to type inference, which observably confuses people. I would rather say that if there is any confusion, the programmer must explicitly state the desired type. |
I too would like to see better type inference here, but I think this is the sort of refinement that can (and should!) wait until after the initial implementation and release of generics, so that we can gather empirical evidence to weigh the cost (of more difficulty reasoning about inference) against the benefit (of avoiding the verbosity and distraction of “obvious” annotations). |
Updated playground link (new syntax). The idea here in its simple-most form is that if multiple function arguments have the same type parameter type, the "most general" type parameter type should be chosen. In this example, x and y have the same type parameter type T and the argument types are interface{} and int. Since int values can be assigned to interface{} variables, T should be inferred to be interface{}. It gets complicated very quickly (e.g., argument types are different interface types that build a type hierarchy, or channel types with different directions, etc.). In any case, this needs a concrete proposal describing what's in scope. |
If we say that a non-interface type V unifies with an interface type T if V implements T under unification, this code will work. (If V is a type parameter that doesn't have a type inferred for it yet, then we infer T for V, of course.) |
A simple experiment shows that the suggested approach does indeed work for simple cases. But it falls apart in more complex situations. Example: func f[T any](x T, _ ...T) T { return x }
func _() {
var x any
var i int
var s string
x = f(x)
x = f(x, i)
x = f(i, x)
x = f(x, i, s)
x = f(x, s, i)
x = f(i, x, s)
x = f(i, x, x)
x = f(s, x, i)
x = f(s, i, x) // ERROR "type int of i does not match inferred type string for T"
} This code works but for the last assignment: because x (of interface type) is checked at the end (not as first or second argument), the inferred type for T is already string and we get an error when we check i. In all other cases, either we infer the interface right away, or we can correct to the interface. In order to do this truly correctly, unification would have to build a type set whenever we unify two types: unification of two types x, y would need to union x and y. At the end, if we end up with simple types or type sets (interfaces), that a) may be valid Go types (or not => inference fails), b) may satisfy constraints (or not => instantiation fails), or we may end up with the empty set (=> inference fails). We're not set up for computing unions during unification at this point. This will have to wait a bit. |
Quick follow-up: What we can do relatively easily is to check if a union is possible in the first place. For instance, if we try to unify two interfaces, or an interface with a non-interface type, methods that exist with both types must unify. If they don't, a union is not possible (methods with the same name that have different signatures are in conflict). That check will allow the inference of type parameters that occur in the signatures of corresponding methods. |
The following program fails:
The error is:
Although it's true that the types don't match exactly, it seems to me that it might be nice
to allow the type argument to unify to
interface{}
in the same way that we allowa concrete type to be passed to an interface type.
The text was updated successfully, but these errors were encountered: