Skip to content

cmd/compile: len(*f()) does not panic when f() returns (*[k]T)(nil) #72844

@aykevl

Description

@aykevl

Go version

go version go1.24.0 linux/arm64

Output of go env in your module/workspace:

AR='ar'
CC='gcc'
CGO_CFLAGS='-O2 -g'
CGO_CPPFLAGS=''
CGO_CXXFLAGS='-O2 -g'
CGO_ENABLED='1'
CGO_FFLAGS='-O2 -g'
CGO_LDFLAGS='-O2 -g'
CXX='g++'
GCCGO='gccgo'
GO111MODULE=''
GOARCH='arm64'
GOARM64='v8.0'
GOAUTH='netrc'
GOBIN=''
GOCACHE='/home/ayke/.cache/go-build'
GOCACHEPROG=''
GODEBUG=''
GOENV='/home/ayke/.config/go/env'
GOEXE=''
GOEXPERIMENT=''
GOFIPS140='off'
GOFLAGS=''
GOGCCFLAGS='-fPIC -pthread -Wl,--no-gc-sections -fmessage-length=0 -ffile-prefix-map=/tmp/go-build3048126655=/tmp/go-build -gno-record-gcc-switches'
GOHOSTARCH='arm64'
GOHOSTOS='linux'
GOINSECURE=''
GOMOD='/home/ayke/src/tinygo/tinygo/go.mod'
GOMODCACHE='/home/ayke/pkg/mod'
GONOPROXY=''
GONOSUMDB=''
GOOS='linux'
GOPATH='/home/ayke'
GOPRIVATE=''
GOPROXY='https://proxy.golang.org,direct'
GOROOT='/usr/local/go1.24.0'
GOSUMDB='sum.golang.org'
GOTELEMETRY='on'
GOTELEMETRYDIR='/home/ayke/.config/go/telemetry'
GOTMPDIR=''
GOTOOLCHAIN='auto'
GOTOOLDIR='/usr/local/go1.24.0/pkg/tool/linux_arm64'
GOVCS=''
GOVERSION='go1.24.0'
GOWORK=''
PKG_CONFIG='pkg-config'

What did you do?

I have the following file, called repro.go:

package main

func nilPtrToArray() *[2]byte {
        return nil
}

func foo() {
        println(len(*nilPtrToArray()))
}

func bar() {
        ptr := nilPtrToArray()
        println(len(*ptr))
}

I ran ssadump on it:

# Name: command-line-arguments.init
# Package: command-line-arguments
# Synthetic: package initializer
func init():
0:                                                                entry P:0 S:0
        return

# Name: command-line-arguments.nilPtrToArray
# Package: command-line-arguments
# Location: /home/ayke/src/tinygo/tinygo/repro.go:3:6
func nilPtrToArray() *[2]byte:
0:                                                                entry P:0 S:0
        return nil:*[2]byte

# Name: command-line-arguments.foo
# Package: command-line-arguments
# Location: /home/ayke/src/tinygo/tinygo/repro.go:7:6
func foo():
0:                                                                entry P:0 S:0
        t0 = nilPtrToArray()                                           *[2]byte
        t1 = *t0                                                        [2]byte
        t2 = println(2:int)                                                  ()
        return

# Name: command-line-arguments.bar
# Package: command-line-arguments
# Location: /home/ayke/src/tinygo/tinygo/repro.go:11:6
func bar():
0:                                                                entry P:0 S:0
        t0 = nilPtrToArray()                                           *[2]byte
        t1 = println(2:int)                                                  ()
        return

What did you see happen?

foo and bar are equivalent. However, they produce different output. foo results in a nil pointer dereference, while bar does not.

What did you expect to see?

I think foo and bar should have produced equivalent SSA.

I think the following part of the language spec is relevant here:

The expressions len(s) and cap(s) are constants if the type of s is an array or pointer to an array and the expression s does not contain channel receives or (non-constant) function calls; in this case s is not evaluated.

If I'm reading this correctly, the bar case is the correct way this code should be read. Also, when testing with go run, the Go compiler doesn't panic because of a nil pointer in either case.

Also see: tinygo-org/tinygo#4786

Metadata

Metadata

Assignees

Labels

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

Done

Relationships

None yet

Development

No branches or pull requests

Issue actions