Skip to content

cmd/compile: //go:nointerface can result in non-nil interface with nil methods #74626

Open
@zoltanhalassy

Description

@zoltanhalassy

Go version

go version go1.25-20250711-RC00 REDACTED +6ebb5f56d9 X:fieldtrack,boringcrypto linux/amd64

Output of go env in your module/workspace:

AR='ar'
CC='gcc'
CGO_CFLAGS='-O2 -g'
CGO_CPPFLAGS=''
CGO_CXXFLAGS='-O2 -g'
CGO_ENABLED='0'
CGO_FFLAGS='-O2 -g'
CGO_LDFLAGS='-O2 -g'
CXX='g++'
GCCGO='gccgo'
GO111MODULE=''
GOAMD64='v3'
GOARCH='amd64'
GOAUTH='netrc'
GOBIN=''
GOCACHE='REDACTED'
GOCACHEPROG=''
GODEBUG=''
GOENV='REDACTED'
GOEXE=''
GOEXPERIMENT='fieldtrack,boringcrypto'
GOFIPS140='off'
GOFLAGS=''
GOGCCFLAGS='-fPIC -m64 -Wl,--no-gc-sections -fmessage-length=0 -ffile-prefix-map=/tmp/go-build889680920=/tmp/go-build -gno-record-gcc-switches'
GOHOSTARCH='amd64'
GOHOSTOS='linux'
GOINSECURE=''
GOMOD='/dev/null'
GOMODCACHE='REDACTED'
GONOPROXY=''
GONOSUMDB=''
GOOS='linux'
GOPATH='REDACTED'
GOPRIVATE=''
GOPROXY=''
GOROOT='REDACTED'
GOSUMDB=''
GOTELEMETRY='local'
GOTELEMETRYDIR='REDACTED'
GOTMPDIR=''
GOTOOLCHAIN=''
GOTOOLDIR='REDACTED'
GOVCS=''
GOVERSION='go1.25-20250711-RC00 REDACTED +6ebb5f56d9 X:fieldtrack,boringcrypto'
GOWORK=''
PKG_CONFIG='pkg-config'

What did you do?

We use //go:nointerface within a project, and we encountered the following issue:

package missing_method_repro_test

import "testing"

type Fooer interface {
	Foo() string
}

type FooImpl struct{}

//go:nointerface
func (FooImpl) Foo() string { return "foo" }

func toInterface[T Fooer](fooer T) Fooer {
	return fooer
}

func TestFooer(t *testing.T) {
	var iface Fooer = toInterface(FooImpl{})
	if iface == nil {
		t.Errorf("iface = nil, want non-nil")
	}
	if iface.Foo == nil {
		t.Errorf("iface.Foo = nil, want non-nil")
	}
	iface.Foo() // panics with nil pointer dereference
}

What did you see happen?

So even though a method is marked as nointerface, it's valid to pass this to function with the interface as a type constraint (and calling the method actually works). It looks like it's also valid to pass this value to an interface (because it passes assignability rules in the generic). And while the interface method even returns non-nil, calling the mehtod results in a nil pointer dereference. Please note that the following workaround works:

type dispatch func() string

func (d dispatch) Foo() string {
  return d()
}

func toInterface[T Fooer](fooer T) Fooer {
	return dispatch(fooer.Foo)
}

What did you expect to see?

Since the manual dispatch approach works, I guess the compiler could just figure out how to do this kind of dispatching itself.

Metadata

Metadata

Assignees

No one assigned

    Labels

    BugReportIssues describing a possible bug in the Go implementation.NeedsFixThe path to resolution is known, but the work has not been done.compiler/runtimeIssues related to the Go compiler and/or runtime.

    Type

    No type

    Projects

    Status

    No status

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions