Skip to content
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 does not match inferred type" even though it compiles when the inferred type is explicitly provided as type argument #53389

Closed
arvidfm opened this issue Jun 15, 2022 · 7 comments
Assignees
Labels
early-in-cycle A change that should be done early in the 3 month dev cycle. FrozenDueToAge NeedsDecision Feedback is required from experts, contributors, and/or the community before a change can be made. Thinking TypeInference Issue is related to generic type inference
Milestone

Comments

@arvidfm
Copy link

arvidfm commented Jun 15, 2022

What version of Go are you using (go version)?

$ go version
go version go1.18.2 linux/amd64

Does this issue reproduce with the latest release?

Yes.

What operating system and processor architecture are you using (go env)?

go env Output
$ go env
GO111MODULE=""
GOARCH="amd64"
GOBIN=""
GOCACHE="/home/user/.cache/go-build"
GOENV="/home/user/.config/go/env"
GOEXE=""
GOEXPERIMENT=""
GOFLAGS=""
GOHOSTARCH="amd64"
GOHOSTOS="linux"
GOINSECURE=""
GOMODCACHE="/home/user/go/pkg/mod"
GONOPROXY=""
GONOSUMDB=""
GOOS="linux"
GOPATH="/home/user/go"
GOPRIVATE=""
GOPROXY="https://proxy.golang.org,direct"
GOROOT="/usr/lib/go"
GOSUMDB="sum.golang.org"
GOTMPDIR=""
GOTOOLDIR="/usr/lib/go/pkg/tool/linux_amd64"
GOVCS=""
GOVERSION="go1.18.2"
GCCGO="gccgo"
GOAMD64="v1"
AR="ar"
CC="gcc"
CXX="g++"
CGO_ENABLED="1"
GOMOD="/dev/null"
GOWORK=""
CGO_CFLAGS="-g -O2"
CGO_CPPFLAGS=""
CGO_CXXFLAGS="-g -O2"
CGO_FFLAGS="-g -O2"
CGO_LDFLAGS="-g -O2"
PKG_CONFIG="pkg-config"
GOGCCFLAGS="-fPIC -m64 -pthread -fmessage-length=0 -fdebug-prefix-map=/tmp/go-build3440234987=/tmp/go-build -gno-record-gcc-switches"

What did you do?

I tried compiling the below program.

https://go.dev/play/p/QcWycysXf-F?v=gotip

package main

type X[T any] struct{}

type Y struct{}

func F[T any](x X[T], y T) {}

func main() {
	x := X[any]{}
	F(x, any(Y{})) // works
	F[any](x, Y{}) // works
	F(x, Y{})      // error: type Y of Y{} does not match inferred type any for T
}

What did you expect to see?

I expected F(x, Y{}) to be equivalent to F[any](x, Y{}), and thus compile without issue.

What did you see instead?

F(x, Y{}) failed to compile with the following error:

type Y of Y{} does not match inferred type any for T
@seankhliao seankhliao added the NeedsInvestigation Someone must examine and confirm this is a valid issue and not a duplicate of an existing one. label Jun 15, 2022
@seankhliao
Copy link
Member

cc @randall77 @griesemer

@griesemer griesemer self-assigned this Jun 15, 2022
@griesemer griesemer added NeedsDecision Feedback is required from experts, contributors, and/or the community before a change can be made. and removed NeedsInvestigation Someone must examine and confirm this is a valid issue and not a duplicate of an existing one. labels Jun 15, 2022
@griesemer griesemer added this to the Go1.20 milestone Jun 15, 2022
@griesemer
Copy link
Contributor

Thanks for reporting this. Currently, type inference tries to matches each argument/parameter pair. So in this case, it infers the type any for T but any is not equal to the type Y, hence the failure.

If inference would stop as soon as all type arguments are known, this specific case would work. But we also want type inference to be independent of the ordering of the parameters/arguments. Consider this case:

package p

type X[T any] struct{}
type Y struct{}

func F1[T any](X[T], T) {}
func F2[T any](T, X[T]) {}

func _() {
	var x X[any]
	var y Y
	F1(x, y)
	F2(y, x)
}

where we'd expect no difference in behavior between the calls to F1 and F2 since parameters and arguments are swapped symmetrically. With the current implementation we get two errors:

x.go:12:8: type Y of y does not match inferred type any for T
x.go:13:8: type X[any] of x does not match inferred type X[Y] 

If inference would stop as soon as all type parameters are known we get one error (the call to F1 works):

x.go:13:8: cannot use x (variable of type X[any]) as X[Y] value in argument to F2

The current (implemented) behavior ensures that when ordering of parameters/arguments changes the outcome, one needs to provide explicit type arguments. This makes the code robust in the presence of such re-orderings. These calls:

	F1[any](x, y)
	F2[any](y, x)

work independent of parameter/argument ordering.

Thus, this is working correctly as designed, but perhaps not as expected. Leaving open for now so that we don't lose sight of this as we refine type inference over time.

@ianlancetaylor ianlancetaylor added the TypeInference Issue is related to generic type inference label Jun 15, 2022
@beoran
Copy link

beoran commented Jun 23, 2022

@griesemer it seems to me that this could be solved by assigning a priority to each parameter/type parameter pair, where the highest priority is for type arguments of the lowest complexity (i.e just T, U, V etc) and then the higher order type arguments later (T[U], etc ). Then the order of the parameters does not matter either.

@ianlancetaylor
Copy link
Contributor

@beoran I think it's extremely important that type inference be very easy to understand and completely predictable for anybody reading the code. I'm not sure your suggestion fits that model.

@beoran
Copy link

beoran commented Jun 23, 2022

@ianlancetaylor Well, yes, the current rule had the benefit of being much more easy to explain and understand. But it has the downside that in some cases it is not so easy to use. I'm not sure how the balance should be in this case.

@griesemer griesemer added the early-in-cycle A change that should be done early in the 3 month dev cycle. label Nov 15, 2022
@griesemer griesemer modified the milestones: Go1.20, Go1.21 Nov 15, 2022
@gopherbot
Copy link
Contributor

This issue is currently labeled as early-in-cycle for Go 1.21.
That time is now, so a friendly reminder to look at it again.

@griesemer
Copy link
Contributor

Simpler reproducer (playground link):

package main

func f[P any](P, P) {}

func _(x any, y int) {
	f[any](x, y) // ok
	f(x, y)      // ERROR: type int of y does not match inferred type any for P
}

Type inference uses unification to make types "equivalent" (essentially identical, but with slightly less stringent rules). Specifically, we make use of the fact that type identity (and thus type equivalence and unification) are symmetric: if x and y can be unified, then can y and x, and in both cases we get the same result.

In order to make this example work, type inference would need to take into account assignment rules, which are nor symmetric: if x can be assigned to y, it's not a given that y can be assigned to x. This would likely make unification much more complicated. It would require that we take assignability into account. Perhaps it is possible, while retaining the property that it doesn't matter in which order type parameters are declared or function arguments are provided, but it's bound to be very complicated to implement and even more complicated to explain.

Type inference is already pretty complicated; if anything we want to reduce complexity, not add more.

In this specific case, and all cases where assignability matters, one can easily provide explicit type arguments and make the code compile.

We're not going to make type inference work in cases like these. Closing as not planned.

@golang golang locked and limited conversation to collaborators Mar 6, 2024
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
early-in-cycle A change that should be done early in the 3 month dev cycle. FrozenDueToAge NeedsDecision Feedback is required from experts, contributors, and/or the community before a change can be made. Thinking TypeInference Issue is related to generic type inference
Projects
None yet
Development

No branches or pull requests

6 participants