go version)?$ go version go version go1.14.4 linux/amd64
I believe go1.14.4 is the latest stable release, so yes.
go env)?go env Output
$ go env
GO111MODULE=""
GOARCH="amd64"
GOBIN=""
GOCACHE="/home/finnb/.cache/go-build"
GOENV="/home/finnb/.config/go/env"
GOEXE=""
GOFLAGS=""
GOHOSTARCH="amd64"
GOHOSTOS="linux"
GOINSECURE=""
GONOPROXY=""
GONOSUMDB=""
GOOS="linux"
GOPATH="/home/finnb/go:/home/finnb/git/finnbear/mazean"
GOPRIVATE=""
GOPROXY="https://proxy.golang.org,direct"
GOROOT="/snap/go/5830"
GOSUMDB="sum.golang.org"
GOTMPDIR=""
GOTOOLDIR="/snap/go/5830/pkg/tool/linux_amd64"
GCCGO="gccgo"
AR="ar"
CC="gcc"
CXX="g++"
CGO_ENABLED="1"
GOMOD=""
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-build362984802=/tmp/go-build -gno-record-gcc-switches"
Playground link: https://play.golang.org/p/Xf3mskprGZV
package main
import (
"fmt"
)
type Nilable interface {
IsNil() bool
}
type Child struct {
}
func (child *Child) IsNil() bool {
return child == nil
}
type Parent struct {
Child
}
func main() {
var something Nilable
var child *Child
something = child
fmt.Println(something.IsNil())
var parent *Parent
something = parent
fmt.Println(something.IsNil()) // Panic here
}
true
panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x1 addr=0x0 pc=0x4919f5]
goroutine 1 [running]:
main.(*Parent).IsNil(0x0, 0xc0000b6008)
<autogenerated>:1 +0x5
main.main()
/tmp/sandbox080043792/prog.go:36 +0xad
I suspect what is happening is that calling IsNil on parent acts on parent.Child, thus dereferencing Child from a nil pointer parent, but this happens silently. I think one of the following should be done:
1) Assuming my theory is correct, the panic message should be improved to not act as if the issue occurred in the (*Child) IsNil receiver but in the intermediate step of accessing parent.Child
2) The documentation should be updated to warn about this behavior
3) In the unlikely event that this is a bug in Go, it should be fixed
@griesemer @ianlancetaylor
This is intended behavior. Dereferencing a nil pointer in order to access an embedded field panics.
But in a quick glance at the language spec I don't see that documented anywhere, so I guess we ought to have an additional sentence in the spec somewhere.
I think the panic message is as good as it is going to get. The IsNil method is promoted to become a method of *Parent, and that is where the crash happens. There isn't an intermediate step that could appear in the stack trace or the panic message.
I now realize my statement
the panic message should be improved to not act as if the issue occurred in the (*Child) IsNil receiver
was wrong, since the panic actually states the location was in main.(*Parent).IsNil and it does say <autogenerated> which is a decent hint.
An additional line in the spec/doc would be nice though.
Also, how is it even possible that Parent*Parent can implement Nilable if it contains Child not *Child and only *Child has IsNil? I'm guessing that the receiver acts like a pointer receiver but there is no actual *Child?
Parent doesn't implement Nilable, *Parent does.
related: https://github.com/golang/go/issues/32021 https://github.com/golang/go/issues/32035
Parentdoesn't implementNilable,*Parentdoes.
I misspoke, my question was directed at why *Parent can implement Nilable. I have to do more research into whether *Parent has an internal/pseudo Child (like Parent does), *Child (which would be weird, I think), or something else...
I believe this is exactly the case described in #18617.
(Note that that issue is a proposal for a backward-incompatible change to the language.)
@finnbear
By the current rules,
struct{T} gets all the methods of type T, *struct{T}, type struct{*T}, and type *struct{*T} get all the methods of type *T (as the method set of *T is always a super-set of T, so the three types also get all the methods of T)