Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Type assertion for embedded structs fails when interface name equals function name #42333

Closed
andig opened this issue Nov 2, 2020 · 4 comments

Comments

@andig
Copy link
Contributor

andig commented Nov 2, 2020

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

$ go version
go version go1.15.3 darwin/amd64

Does this issue reproduce with the latest release?

yes

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

go env Output
$ go env
GO111MODULE=""
GOARCH="amd64"
GOBIN=""
GOCACHE="/Users/andig/Library/Caches/go-build"
GOENV="/Users/andig/Library/Application Support/go/env"
GOEXE=""
GOFLAGS=""
GOHOSTARCH="amd64"
GOHOSTOS="darwin"
GOINSECURE=""
GOMODCACHE="/Users/andig/go/pkg/mod"
GONOPROXY=""
GONOSUMDB=""
GOOS="darwin"
GOPATH="/Users/andig/go"
GOPRIVATE=""
GOPROXY="https://proxy.golang.org,direct"
GOROOT="/usr/local/Cellar/go/1.15.3/libexec"
GOSUMDB="sum.golang.org"
GOTMPDIR=""
GOTOOLDIR="/usr/local/Cellar/go/1.15.3/libexec/pkg/tool/darwin_amd64"
GCCGO="gccgo"
AR="ar"
CC="clang"
CXX="clang++"
CGO_ENABLED="1"
GOMOD="/Users/andig/htdocs/go.mod"
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 -fno-caret-diagnostics -Qunused-arguments -fmessage-length=0 -fdebug-prefix-map=/var/folders/73/89ycv7qn51j4kbm04jsz9b840000gn/T/go-build072318584=/tmp/go-build -gno-record-gcc-switches -fno-common"

What did you do?

Create an interface with embedded function of identical name and type-assert for the interface name.

Working:

type Decorator interface {
	Func2()
}
if _, ok := o.(Decorator); ok { ... }

Not working:

type Func3 interface {
	Func3()
}
if _, ok := o.(Func3); ok { ... }

See https://play.golang.org/p/2MFegoWq5MU. The patterns of the Decorator and Func3 interface are identical. While Decorator type assertion is positive, Func3 ist not. There is no compile error either.

What did you expect to see?

Positive type assertion if interface is implemented.

What did you see instead?

Type assertion false.

@andig andig changed the title Type assertion always evaluate to false when interface name equals function name Type assertion for embedded structs fails when interface name equals function name Nov 2, 2020
@seankhliao
Copy link
Member

Methods from embedded things can only be promoted when they are unambiguous.
o.Func3 would refer to the embedded struct/interface and the method would be referred to as o.Func3.Func3(),
so it is correct in that o does not satisfy the interface

@andig
Copy link
Contributor Author

andig commented Nov 2, 2020

I get your point but that's still a little confusing. I would argue that o.Func3 is the embedded interface and hence o implements Func3. On the otherhand side, o.Func3() is ambiguous and hence o.Func3 does not implement Func3 anymore although it's type promises exactly that?

@toothrot
Copy link
Contributor

toothrot commented Nov 2, 2020

It may help to read through the recent discussions about embedding, such as #6977

/cc @griesemer

@griesemer
Copy link
Contributor

This is working as expected. A simpler example will make this clearer:

package main

type M interface {
	M()
}

type S struct {
	M
}

func main() {
	var _ M = S{}
}

The type S is a struct that embeds M: This means it has a field, which - by spec rules - is called M. Since that field is embedded, the struct "inherits" M's methods, which happen to be just M. But that method is not visible; it's a level deeper than the field M itself. In short, S doesn't have a method M, it has a field M. When trying to assign an S value S{} to M we get a pretty good compiler error message:

./prog.go:12:6: S.M is a field, not a method
./prog.go:12:6: cannot use S literal (type S) as type M in assignment:
	S does not implement M (missing M method)

Consequently, if we try to see if an empty interface holding an S{} is an M using a type assertion, it will fail, which is what happens in the original example. There's not much the compiler can do here in terms of error messages. It's a perfectly valid type assertion that simply fails.

Changing the method name from M to M1 makes this work: Now the method M1, even though at a level deeper than M, is not shadowed anymore by M.

I agree that this can be confusing. Embedding is one of the most complicated parts of Go. Advice: Don't name methods the same as fields (or in this case, types).

Closing as working as expected.

PS: Just a friendly reminder that it helps to whittle down examples to the bare minimum when filing issues. Often it makes it clear what's wrong and the issue doesn't need to be filed in the first place. And if it is filed, it helps us to get to the core more quickly because we don't have to simplify the example first to see what's going on. Thanks.

@golang golang locked and limited conversation to collaborators Nov 2, 2021
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

5 participants