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

spec: interfaces that have different-size method sets never unify (but they possibly could) #57192

Closed
testinginprod opened this issue Dec 9, 2022 · 10 comments
Labels
FrozenDueToAge generics Issue is related to generics NeedsDecision Feedback is required from experts, contributors, and/or the community before a change can be made. TypeInference Issue is related to generic type inference
Milestone

Comments

@testinginprod
Copy link

testinginprod commented Dec 9, 2022

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

$ go version
go version go1.19.3 darwin/arm64

Does this issue reproduce with the latest release?

Yes, tested on playground.

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

go env Output
$ go env
GO111MODULE=""
GOARCH="arm64"
GOBIN=""
GOCACHE="/Users/*/Library/Caches/go-build"
GOENV="/Users/*/Library/Application Support/go/env"
GOEXE=""
GOEXPERIMENT=""
GOFLAGS=""
GOHOSTARCH="arm64"
GOHOSTOS="darwin"
GOINSECURE=""
GOMODCACHE="/Users/*/go/pkg/mod"
GONOPROXY=""
GONOSUMDB=""
GOOS="darwin"
GOPATH="/Users/*/go"
GOPRIVATE=""
GOPROXY="https://proxy.golang.org,direct"
GOROOT="/opt/homebrew/Cellar/go/1.19.3/libexec"
GOSUMDB="sum.golang.org"
GOTMPDIR=""
GOTOOLDIR="/opt/homebrew/Cellar/go/1.19.3/libexec/pkg/tool/darwin_arm64"
GOVCS=""
GOVERSION="go1.19.3"
GCCGO="gccgo"
AR="ar"
CC="clang"
CXX="clang++"
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 -arch arm64 -pthread -fno-caret-diagnostics -Qunused-arguments -fmessage-length=0 -fdebug-prefix-map=/var/folders/cl/x9ccznh92fbc0_57g_7nk2gc0000gn/T/go-build3394357358=/tmp/go-build -gno-record-gcc-switches -fno-common"

What did you do?

Given GenericInterface1[T any], GenericInterface2[T any] { GenericInterface1[T] }, and a function func F[T any](GI1 GenericInterface1[T](), T is not inferred if we pass to F a GenericInterface2.

https://go.dev/play/p/dsZOHq2Y1Au

What did you expect to see?

Type inference to work since GenericInterface2 is also GenericInterface1, and since passing to F a declared variable of type GenericInterface1[T] works (if it's not explicitly declared, then type inference cannot infer T even for GenericInterface1).

What did you see instead?

Compilation failure.

@seankhliao seankhliao changed the title compiler: composing generic interfaces breaks type inference cmd/compile: composing generic interfaces breaks type inference Dec 9, 2022
@gopherbot gopherbot added the compiler/runtime Issues related to the Go compiler and/or runtime. label Dec 9, 2022
@seankhliao seankhliao added the NeedsInvestigation Someone must examine and confirm this is a valid issue and not a duplicate of an existing one. label Dec 9, 2022
@ianlancetaylor
Copy link
Contributor

This is expected behavior for the current language spec. Changing this to be a language issue.

@ianlancetaylor ianlancetaylor changed the title cmd/compile: composing generic interfaces breaks type inference spec: composing generic interfaces breaks type inference Dec 9, 2022
@ianlancetaylor ianlancetaylor added generics Issue is related to generics TypeInference Issue is related to generic type inference and removed compiler/runtime Issues related to the Go compiler and/or runtime. labels Dec 9, 2022
@ianlancetaylor ianlancetaylor added this to the Backlog milestone Dec 9, 2022
@ianlancetaylor
Copy link
Contributor

I would not be surprised if fixing #41176 fixes this one as well.

@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 May 18, 2023
@griesemer
Copy link
Contributor

griesemer commented May 18, 2023

There are two issues here:

  1. Two interface types, even with the same type set (which includes methods), do not unify if they are defined types with different names. We probably should ignore the names of interface types.

  2. Two interface types which don't have identical type sets do not unify. But if one interface's typeset is a superset of the other (e.g., the method set of one interface is a subset of the method set of the other), they probably should unify. More generally, if the intersection of the type sets of two interfaces unifies, they probably should unify.

If we do 1) and 2), this example will work.

Or putting it differently, two interfaces should unify if one of them can be made (through unification) to implement the other.

@griesemer griesemer changed the title spec: composing generic interfaces breaks type inference spec: interfaces that have different-size method sets never unify (but they possibly could) May 19, 2023
@gopherbot
Copy link
Contributor

Change https://go.dev/cl/496256 mentions this issue: go/types, types2: more flexible type unification of interface types

@griesemer
Copy link
Contributor

In fact this is directly related to #40055 with a similar solution: if one interface implements the other under unification, this will work.

@gopherbot
Copy link
Contributor

Change https://go.dev/cl/497015 mentions this issue: go/types, types2: use implements relation for type inference across interfaces

@griesemer
Copy link
Contributor

Submitted accidentally, revert in progress. Reopening.

@griesemer griesemer reopened this May 23, 2023
@gopherbot
Copy link
Contributor

Change https://go.dev/cl/497656 mentions this issue: go/types, types2: consider shared methods when unifying against interfaces

@gopherbot
Copy link
Contributor

Change https://go.dev/cl/497657 mentions this issue: go/types, types2: enable interface inference

gopherbot pushed a commit that referenced this issue May 23, 2023
…faces

When unifying two types A and B where one or both of them are
interfaces, consider the shared method signatures in unification.

1) If a defined interface (an interface with a type name) is unified
   with another (defined) interface, currently they must originate
   in the same type declaration (same origin) for unification to
   succeed. This is more restrictive than necessary for assignments:
   when interfaces are assigned to each other, corresponding methods
   must match, but the interfaces don't have to be identical.
   In unification, we don't know which direction the assignment is
   happening (or if we have an assignment in the first place), but
   in any case one interface must implement the other. Thus, we
   check that one interface has a subset of the methods of the other
   and that corresponding method signatures unify.
   The assignment or instantiation may still not be possible but that
   will be checked when instantiation and parameter passing is checked.
   If two interfaces are compared as part of another type during
   unification, the types must be equal. If they are not, unifying
   a method subset may still succeed (and possibly produce more type
   arguments), but that is ok: again, subsequent instantiation and
   assignment will fail if the types are indeed not identical.

2) In a non-interface type is unified with an interface, currently
   unification fails. If this unification is a consequence of an
   assignment (parameter passing), this is again too restrictive:
   the non-interface type must only implement the interface (possibly
   among other type set requirements). In any case, all methods of the
   interface type must be present in the non-interface type and unify
   with the corresponding interface methods. If they don't, unification
   will fail either way. If they do, we may infer additional type
   arguments. Again, the resulting types may still not be correct but
   that will be determined by the instantiation and parameter passing
   or assignment checks. If the non-interface type and the interface
   type appear as component of another type, unification may now
   produce additional type arguments. But that is again ok because the
   respective types won't pass instantiation or assignment checks since
   they are different types.

This CL introduces a new unifier flag, enableInterfaceInference, to
enable this new behavior. It is currently disabled.

For #60353.
For #41176.
For #57192.

Change-Id: I983d0ad5f043c7fe9d377dbb95f6b9342f36f45f
Reviewed-on: https://go-review.googlesource.com/c/go/+/497656
TryBot-Result: Gopher Robot <[email protected]>
Reviewed-by: Robert Griesemer <[email protected]>
Reviewed-by: Robert Findley <[email protected]>
Run-TryBot: Robert Griesemer <[email protected]>
Auto-Submit: Robert Griesemer <[email protected]>
@gopherbot
Copy link
Contributor

Change https://go.dev/cl/499282 mentions this issue: doc/go1.21: document type inference changes

gopherbot pushed a commit that referenced this issue May 31, 2023
For #39661.
For #41176.
For #51593.
For #52397.
For #57192.
For #58645.
For #58650.
For #58671.
For #59338.
For #59750.
For #60353.

Change-Id: Ib731c9f2879beb541f44cb10e40c36a8677d3ad4
Reviewed-on: https://go-review.googlesource.com/c/go/+/499282
TryBot-Bypass: Robert Griesemer <[email protected]>
Reviewed-by: Ian Lance Taylor <[email protected]>
Reviewed-by: Robert Griesemer <[email protected]>
@golang golang locked and limited conversation to collaborators May 30, 2024
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
FrozenDueToAge generics Issue is related to generics NeedsDecision Feedback is required from experts, contributors, and/or the community before a change can be made. TypeInference Issue is related to generic type inference
Projects
None yet
Development

No branches or pull requests

5 participants