Skip to content

spec: Empty struct address comparison returns false even if addresses are equal #23440

@quasilyte

Description

@quasilyte

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

Reproducible in Go 1.8.1, Go devel, and on playground.

Does this issue reproduce with the latest release?

Yes.

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

(some paths are manually edited to avoid personal data leakage.)

GOARCH="amd64"
GOBIN=""
GOEXE=""
GOHOSTARCH="amd64"
GOHOSTOS="linux"
GOOS="linux"
GORACE=""
GOTMPDIR=""
GOTOOLDIR="?/go/go/pkg/tool/linux_amd64"
GCCGO="?/gccgo"
CC="gcc"
CXX="g++"
CGO_ENABLED="1"
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-build150273130=/tmp/go-build -gno-record-gcc-switches"

What did you do?

Run this code: https://play.golang.org/p/9C0puRUstrP.

What did you expect to see?

Both functions print same address and
address comparison yields true.

What did you see instead?

Both functions print same address, but
only f2 prints true for address comparison.

Works as expected in GCCGO:

go run -compiler gccgo empty.go
0x7ff2fc9834c0
0x7ff2fc9834c0
true
0x7ff2fc9834c0
0x7ff2fc9834c0
true

Attaching 6g output here for comparison:

go run empty.go
0xc420045f50
0xc420045f50
false
0x54ca38
0x54ca38
true

The actual addresses does not matter. Only true/false results do.

The spec only mentions that their addresses may be identical,
which seems to be the case, but for some reason comparison disagrees.

Two distinct zero-size variables may have the same address in memory.

The false case can be reduced:

func f1() bool {
	var a, b struct{}
	return &a == &b
}

The assembly output on x86 for it is:

"".f1 STEXT nosplit size=6 args=0x8 locals=0x0
	0x0000 00000 (empty.go:7)	TEXT	"".f1(SB), NOSPLIT, $0-8
	0x0000 00000 (empty.go:7)	FUNCDATA	$0, gclocals·2a5305abe05176240e61b8620e19a815(SB)
	0x0000 00000 (empty.go:7)	FUNCDATA	$1, gclocals·33cdeccccebe80329f1fdbee7f5874cb(SB)
	0x0000 00000 (empty.go:9)	MOVB	$0, "".~r0+8(SP)
	0x0005 00005 (empty.go:9)	RET

...which basically means return false.
If I understand this right, unless we seen that addresses are equal by printing it or assigning it numerical representation, comparison can always return false.

Using numerical value returns true:

return uintptr(unsafe.Pointer(&a)) == uintptr(unsafe.Pointer(&b))

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions