Skip to content

go/types: improve documentation on *types.Named identity #53914

@aykevl

Description

@aykevl

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

$ go version
go version go1.19beta1 linux/amd64

Does this issue reproduce with the latest release?

It also reproduces in Go 1.18.3.

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

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

What did you do?

I am using golang.org/x/tools/go/ssa to build the SSA form for TinyGo.
We recently added generics support, which was surprisingly easy because the ssa package does all the hard work of instantiating generic functions.
Unfortunately, it resulted in an issue. Previously, we could rely on *types.Named to have pointer identity, meaning, that we could create a map[*types.Named]someOtherType and rely on the fact that every unique named type would have only one entry in the map.

What did you expect to see?

I expected this to remain the same while adding support for generics.

What did you see instead?

It broke. It works most of the time, but in some cases (in particular, methods on generic structs) there can be two *types.Named objects for the same named type.

Here is a reproducer:
https://go.dev/play/p/AIZZDnqqm0l?v=gotip

I've tried to look at the documentation and comments inside the package what the intended behavior is, but I see somewhat conflicting information. In any case, it would be very helpful if *types.Named would be unique again.

  1. This comment seems to suggest *types.Named is intended to have pointer identity but doesn't at the moment.
    // TODO(gri) Why is x == y not sufficient? And if it is,
    // we can just return false here because x == y
    // is caught in the very beginning of this function.
    return x.obj == y.obj
  2. types.Instantiate says that not all types may be uniqued:

    If ctxt is non-nil, it may be used to de-duplicate the instance against previous instances with the same identity. As a special case, generic *Signature origin types are only considered identical if they are pointer equivalent, so that instantiating distinct (but possibly identical) signatures will yield different instances. The use of a shared context does not guarantee that identical instances are deduplicated in all cases.

  3. The comment at the top of src/go/types/named.go says that *types.Named may not be unique:

    go/src/go/types/named.go

    Lines 74 to 81 in 2aa473c

    // Some invariants to keep in mind: each declared Named type has a single
    // corresponding object, and that object's type is the (possibly generic) Named
    // type. Declared Named types are identical if and only if their pointers are
    // identical. On the other hand, multiple instantiated Named types may be
    // identical even though their pointers are not identical. One has to use
    // Identical to compare them. For instantiated named types, their obj is a
    // synthetic placeholder that records their position of the corresponding
    // instantiation in the source (if they were constructed during type checking).

From this, it appears to be a design decision to change the uniqueness of *types.Named pointers for generic types.
This is a problem for me, as I need a way to create unique IDs per *types.Named instance (types.Identical is not a solution, because you can't use it in a map). I tried typ.Obj().Pkg().Path() + "." + typ.Obj().Name() but that doesn't work for named types inside functions (which create a new scope but may have the same name as a named type in the outer scope).

I'm reporting this as a bug as it feels like a regression to me, even though it's not technically a regression.

Metadata

Metadata

Assignees

No one assigned

    Labels

    DocumentationIssues describing a change to documentation.NeedsFixThe path to resolution is known, but the work has not been done.help wanted

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions