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: different results between inferred generic type argument and explicit generic type argument #61741

Closed
kylelemons opened this issue Aug 3, 2023 · 4 comments
Assignees
Labels
compiler/runtime Issues related to the Go compiler and/or runtime. Thinking TypeInference Issue is related to generic type inference
Milestone

Comments

@kylelemons
Copy link
Contributor

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

$ go version
go version go1.21rc3 darwin/arm64

Does this issue reproduce with the latest release?

It is currently reproducing on the latest gotipplay

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

go env Output
$ go env
GO111MODULE='on'
GOARCH='arm64'
GOBIN=''
GOCACHE='/Users/kyle.lemons/Library/Caches/go-build'
GOENV='/Users/kyle.lemons/Library/Application Support/go/env'
GOEXE=''
GOEXPERIMENT=''
GOFLAGS=''
GOHOSTARCH='arm64'
GOHOSTOS='darwin'
GOINSECURE=''
GOMODCACHE='/Users/kyle.lemons/go/pkg/mod'
GONOPROXY=''
GONOSUMDB=''
GOOS='darwin'
GOPATH='/Users/kyle.lemons/go'
GOPRIVATE=''
GOPROXY='https://proxy.golang.org,direct'
GOROOT='/Users/kyle.lemons/sdk/go1.21rc3'
GOSUMDB='sum.golang.org'
GOTMPDIR=''
GOTOOLCHAIN='auto'
GOTOOLDIR='/Users/kyle.lemons/sdk/go1.21rc3/pkg/tool/darwin_arm64'
GOVCS=''
GOVERSION='go1.21rc3'
GCCGO='gccgo'
AR='ar'
CC='clang'
CXX='clang++'
CGO_ENABLED='1'
GOMOD='/Users/kyle.lemons/dev//infrared-generator/go.mod'
GOWORK=''
CGO_CFLAGS='-O2 -g'
CGO_CPPFLAGS=''
CGO_CXXFLAGS='-O2 -g'
CGO_FFLAGS='-O2 -g'
CGO_LDFLAGS='-O2 -g'
PKG_CONFIG='pkg-config'
GOGCCFLAGS='-fPIC -arch arm64 -pthread -fno-caret-diagnostics -Qunused-arguments -fmessage-length=0 -ffile-prefix-map=/var/folders/t8/cjh0whg91cz4xv7fqwhx374r0000gp/T/go-build2237738850=/tmp/go-build -gno-record-gcc-switches -fno-common'
GOROOT/bin/go version: go version go1.21rc3 darwin/arm64
GOROOT/bin/go tool compile -V: compile version go1.21rc3
uname -v: Darwin Kernel Version 22.5.0: Thu Jun  8 22:22:20 PDT 2023; root:xnu-8796.121.3~7/RELEASE_ARM64_T6000
ProductName:		macOS
ProductVersion:		13.4.1
BuildVersion:		22F82
lldb --version: lldb-1403.0.17.67
Apple Swift version 5.8.1 (swiftlang-5.8.0.124.5 clang-1403.0.22.11.100)

What did you do?

https://gotipplay.golang.org/p/sUrlIpN1rLa

Given a generic interface+function pair:

type FileCodec[T any] interface {
	Encode(w io.Writer, data T) error
}

func WriteFile[T any](path string, codec FileCodec[T], data T) error

and a valid implementation of the above interface:

type RuntimeObject interface {
	GroupVersionKind() string
}

type KubeCodec struct{}

func (KubeCodec) Encode(w io.Writer, data RuntimeObject) error

and the two calls below:

type Object struct{}

func (Object) GroupVersionKind() string { return "object" }

func main() {
	WriteFile[RuntimeObject]("works", KubeCodec{}, Object{})
	WriteFile("fails", KubeCodec{}, Object{}) // type Object of Object{} does not match inferred type RuntimeObject for T
}

The generic type inference can correctly infer the type for WriteFile in the second case, and it matches the explicit type argument from the first call, but it still gives an error.

What did you expect to see?

I expect both of these lines to work:

	WriteFile[RuntimeObject]("works", KubeCodec{}, Object{})
	WriteFile("fails", KubeCodec{}, Object{})

What did you see instead?

type Object of Object{} does not match inferred type RuntimeObject for T
@ianlancetaylor
Copy link
Contributor

CC @griesemer

@ianlancetaylor ianlancetaylor added TypeInference Issue is related to generic type inference compiler/runtime Issues related to the Go compiler and/or runtime. labels Aug 3, 2023
@ianlancetaylor ianlancetaylor changed the title Different results between inferred generic type argument and explicit generic type argument cmd/compile: different results between inferred generic type argument and explicit generic type argument Aug 3, 2023
@ianlancetaylor
Copy link
Contributor

This does look like a type-checking bug to me. The type inference code checks that it inferred the expected type, but doesn't consider the case of the inferred type being an inference type.

But this doesn't seem like a major bug, and we should consider postponing a fix until 1.22 and 1.21.1.

@griesemer
Copy link
Contributor

Simplified repro:

package p

func f[T any](interface{ Encode(T) }, T) {}

type RuntimeObject interface{}

type KubeCodec struct{}

func (KubeCodec) Encode(RuntimeObject) {}

type Object struct{}

func _() {
	f[RuntimeObject](KubeCodec{}, Object{})
	f(KubeCodec{}, Object{}) // type Object of Object{} does not match inferred type RuntimeObject for T
}

Inference trace:

== infer : [T₂](interface{Encode(T₂)}, T₂) ➞ [<nil>]
== function parameters: (interface{Encode(T₂)}, T₂)
-- function arguments : [KubeCodec{} (value of type p.KubeCodec) Object{} (value of type p.Object)]
.  interface{Encode(T₂)} ≡ p.KubeCodec  // assign
.  .  func(T₂) ≡ func(p.RuntimeObject)  // assign, exact
.  .  .  (T₂) ≡ (p.RuntimeObject)       // assign, exact
.  .  .  .  T₂ ≡ p.RuntimeObject        // assign, exact
.  .  .  .  T₂ ➞ p.RuntimeObject
.  .  .  () ≡ ()        // assign, exact
.  T₂ ≡ p.Object        // assign
.  .  p.RuntimeObject ≡ p.Object        // assign
.  .  p.Object ≡ p.RuntimeObject        // swap
.  T₂ ≢ p.Object
=> [T₂] ➞ []

This is a (known) limitation of the current implementation: T₂ has been correctly inferred to be a RuntimeObject. But at the moment we require both types to be interfaces to avoid an order dependency. Related to #60933.

@griesemer griesemer self-assigned this Aug 3, 2023
@griesemer griesemer added this to the Go1.22 milestone Aug 3, 2023
@griesemer
Copy link
Contributor

Even simpler reproducer:

package p

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

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

We can't address this with the current, relatively simple (but still plenty complex!) type inference approach. It's not clear that we should. There's always the option to provide an explicit type argument.

Cases like these appear when the type argument is an interface. In many such cases, not using generic code in the first place may be the right choice (we already have polymorphism through the interface).

Closing as not planned.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
compiler/runtime Issues related to the Go compiler and/or runtime. Thinking TypeInference Issue is related to generic type inference
Projects
None yet
Development

No branches or pull requests

3 participants