Skip to content

spec: document/explain which interfaces implement comparable #50646

Closed

Description

@bobg

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

$ go version
go version go1.18beta1 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="on"
GOARCH="amd64"
GOBIN=""
GOCACHE="/home/bobg/.cache/go-build"
GOENV="/home/bobg/.config/go/env"
GOEXE=""
GOEXPERIMENT=""
GOFLAGS=""
GOHOSTARCH="amd64"
GOHOSTOS="linux"
GOINSECURE=""
GOMODCACHE="/home/bobg/go/pkg/mod"
GONOPROXY=""
GONOSUMDB=""
GOOS="linux"
GOPATH="/home/bobg/go"
GOPRIVATE=""
GOPROXY="https://proxy.golang.org,direct"
GOROOT="/home/bobg/sdk/go1.18beta1"
GOSUMDB="sum.golang.org"
GOTMPDIR=""
GOTOOLDIR="/home/bobg/sdk/go1.18beta1/pkg/tool/linux_amd64"
GOVCS=""
GOVERSION="go1.18beta1"
GCCGO="gccgo"
GOAMD64="v1"
AR="ar"
CC="gcc"
CXX="g++"
CGO_ENABLED="1"
GOMOD="/home/bobg/go/src/github.com/bobg/modver/go.mod"
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-build3923542930=/tmp/go-build -gno-record-gcc-switches"

What did you do?

https://go.dev/play/p/S-ijx-XOlu8?v=gotip

var (
	anyType        = types.Universe.Lookup("any").Type()
	comparableType = types.Universe.Lookup("comparable").Type()
)
fmt.Println(types.AssignableTo(comparableType, anyType))
fmt.Println(types.AssignableTo(anyType, comparableType))

What did you expect to see?

true
false

That is, I (naively?) expected the same check that prevents me using non-comparable types as map keys to tell me that I cannot assign a value of an unconstrained type to a variable of a comparable type.

What did you see instead?

true
true

Activity

  1. bobg commented on Jan 16, 2022

    @bobg
    ContributorAuthor

    A little more context: I am updating github.com/bobg/modver to be generics-aware. This package can compare two versions of a Go module and tell whether a major, minor, or patchlevel version-number bump is needed, according to semver rules.

    If two versions of a Go object differ only in the constraint on a type parameter, what should the result be? The answer depends on the type set implied by the constraint.

    If the two type sets are identical, the result should be None. If the older type set is a strict subset of the newer type set, that is a backward-compatible change and the result should be Minor. Otherwise it's an incompatible change and the result should be Major.

    I had hoped to use types.Identical and types.AssignableTo to distinguish these cases, but if I can't, what's the best way to do it instead? Must I write my own type-set-comparing logic, or is there something in the stdlib I can use?

    Thanks...

  2. ianlancetaylor commented on Jan 16, 2022

    @ianlancetaylor
    Contributor

    CC @griesemer @findleyr

    In the current language no variable can have the type comparable, so at least at present it doesn't necessarily make sense to call AssignableTo. On the other hand, Implements does make sense, but it also returns true for both directions.

    The question you want to ask seems like a reasonable one but I don't know whether the go/types package supports answering that question yet.

  3. added
    NeedsInvestigationSomeone must examine and confirm this is a valid issue and not a duplicate of an existing one.
    on Jan 16, 2022
  4. added this to the Go1.18 milestone on Jan 16, 2022
  5. griesemer commented on Jan 17, 2022

    @griesemer
    Contributor

    This is simply a bug and needs to be fixed. Thanks for the report.

  6. self-assigned this
    on Jan 17, 2022
  7. changed the title [-]go/types: AssignableTo does not distinguish between any and comparable[/-] [+]go/types, types2: AssignableTo does not distinguish between any and comparable[/+] on Jan 17, 2022
  8. added
    NeedsFixThe path to resolution is known, but the work has not been done.
    and removed
    NeedsInvestigationSomeone must examine and confirm this is a valid issue and not a duplicate of an existing one.
    on Jan 17, 2022
  9. griesemer commented on Jan 24, 2022

    @griesemer
    Contributor

    When we check whether a (a value of) type V can be assigned to a (variable of) interface type T (types.AssignableTo(V, T)), because T is an interface, we need to check if V implements T. In this specific case we need to check if the empty interface (any) implements comparable. The comparable interface is implemented by any type that supports == and !=. (Even) in non-generic Go, == and != can be used with a variable of interface type (even the empty interface!), it's just that we might get a run-time panic if the dynamic type doesn't support comparison with == and !=. Thus, any interface (including any) supports == and !=, which leads to the (admittedly counter-intuitive at first) conclusion that any actually implements comparable and thus any is assignable to comparable.

    If comparable is used as (or embedded in a constraint), it is therefore ok to instantiate the respective type parameter with an arbitrary interface (that satisfies all the other aspects of the constraint), because comparison is always supported by all interfaces. However, when running that instantiated code, we may get a run-time panic if the dynamic type of the interface does not support comparison (as we do in regular Go).

    In short, this is not a bug but simply a consequence of existing rules.

    Leaving this issue open for now so we can close it with a CL that adds some more tests.

  10. added
    DocumentationIssues describing a change to documentation.
    and removed
    NeedsFixThe path to resolution is known, but the work has not been done.
    on Jan 24, 2022
  11. changed the title [-]go/types, types2: AssignableTo does not distinguish between any and comparable[/-] [+]go/types, types2: document that all interfaces implement `comparable`[/+] on Jan 24, 2022
  12. gopherbot commented on Jan 25, 2022

    @gopherbot
    Contributor

    Change https://golang.org/cl/380654 mentions this issue: cmd/compile: all interfaces implement comparable (add tests)

  13. changed the title [-]go/types, types2: document that all interfaces implement `comparable`[/-] [+]spec: document that all interfaces implement `comparable`[/+] on Jan 25, 2022
  14. 35 remaining items

  15. griesemer commented on Jan 31, 2022

    @griesemer
    Contributor

    This is now fixed in the type checker. What is outstanding is the documentation in the spec. Not a release blocker anymore.

  16. tinytina2021 commented on Feb 1, 2022

    @tinytina2021

    @griesemer "... This is something we cannot change."
    Why not? We can now still introduce a new notation like =! for this case that was not legal Go pre 1.18 while all previously working Go programs would still compile and 1.18+ programs will still remain fully backward compatible.

  17. griesemer commented on Feb 1, 2022

    @griesemer
    Contributor

    @tinytina2021 I'm not sure I follow: we cannot change the behavior of == and != for interfaces as that would invalidate existing programs. But we can have different rules for type parameters, which is what we're doing. I'm not sure how introducing =! (and what about the negation of =!?) would help or why it would be useful.

  18. griesemer commented on Feb 1, 2022

    @griesemer
    Contributor

    The implementation of AssignableTo has been fixed and https://golang.org/cl/381896 documents the behavior or comparable. Closing.

  19. billinghamj commented on Feb 18, 2022

    @billinghamj

    @griesemer Just to flag, the change so that any no longer counts as comparable makes it difficult to write generic code in some quite common situations

    It kinda seems like this change has been made a little last-minute and without particularly widespread discussion, but may have a fairly significant impact on people's ability to use generics in Go

    For example, when using golang-set, it's pretty reasonable that you might want to make a set containing any comparable type - e.g.:

    	NewSetFromSlice[any](
    		[]any{
    			1,
    			2,
    			3,
    			"test",
    		},
    	)

    The code cannot be written as NewSetFromSlice[comparable]([]comparable{ - this results in the error interface is (or embeds) comparable compiler(ErrorCode(142))

    So as far as I can see, it is no longer possible to write this code at all - though maybe I'm missing something obvious?

    If you're firm that any will never implement comparable, maybe comparable should be allowed in non-type constraint interfaces?

    Honestly, this seems significant enough to be a release blocker, although it probably could be fixed later without a breaking change

    Edit: opened as a new issue - #51257 - as I appreciate this one has been closed for a couple of weeks now

  20. griesemer commented on Feb 23, 2022

    @griesemer
    Contributor

    I think the correct answer here is to allow comparable as a regular type. See #51257 for more.

  21. gopherbot commented on Apr 23, 2022

    @gopherbot
    Contributor

    Change https://go.dev/cl/401874 mentions this issue: spec: clarify type set and interface are different

  22. locked and limited conversation to collaborators on Jun 22, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions