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

cmd/compile: FuncForPC on a generic struct method is different from a non-generic struct #63945

Open
mitar opened this issue Nov 4, 2023 · 3 comments
Assignees
Labels
compiler/runtime Issues related to the Go compiler and/or runtime. NeedsInvestigation Someone must examine and confirm this is a valid issue and not a duplicate of an existing one.
Milestone

Comments

@mitar
Copy link
Contributor

mitar commented Nov 4, 2023

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

$ go version
go version go1.21.3 linux/amd64

Does this issue reproduce with the latest release?

Yes, including tip. Also on Go Playground.

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

go env Output
$ go env
GO111MODULE=''
GOARCH='amd64'
GOBIN=''
GOCACHE='/home/mitar/.cache/go-build'
GOENV='/home/mitar/.config/go/env'
GOEXE=''
GOEXPERIMENT=''
GOFLAGS=''
GOHOSTARCH='amd64'
GOHOSTOS='linux'
GOINSECURE=''
GOMODCACHE='/home/mitar/.go/pkg/mod'
GONOPROXY=''
GONOSUMDB=''
GOOS='linux'
GOPATH='/home/mitar/.go'
GOPRIVATE=''
GOPROXY='https://proxy.golang.org,direct'
GOROOT='/usr/local/go'
GOSUMDB='sum.golang.org'
GOTMPDIR=''
GOTOOLCHAIN='auto'
GOTOOLDIR='/usr/local/go/pkg/tool/linux_amd64'
GOVCS=''
GOVERSION='go1.21.3'
GCCGO='/usr/bin/gccgo'
GOAMD64='v1'
AR='ar'
CC='gcc'
CXX='g++'
CGO_ENABLED='1'
GOMOD='/dev/null'
GOWORK=''
CGO_CFLAGS='-O2 -g'
CGO_CPPFLAGS=''
CGO_CXXFLAGS='-O2 -g'
CGO_FFLAGS='-O2 -g'
CGO_LDFLAGS='-O2 -g'
PKG_CONFIG='pkg-config'
GOGCCFLAGS='-fPIC -m64 -pthread -Wl,--no-gc-sections -fmessage-length=0 -ffile-prefix-map=/tmp/go-build1231630685=/tmp/go-build -gno-record-gcc-switches'

What did you do?

I have the following code to get name of a function passed as a pointer:

package main

import (
	"fmt"
	"reflect"
	"runtime"
)

func name(f any) string {
	fn := runtime.FuncForPC(reflect.ValueOf(f).Pointer())
	if fn == nil {
		return ""
	}
	return fn.Name()
}

func FuncName() {}

type Foo struct{}

func (_ *Foo) FuncName() {}

func (f *Foo) Get() string {
	return name(f.FuncName)
}

type Bar[T any] struct{}

func (_ *Bar[T]) FuncName() {}

func (b *Bar[T]) Get() string {
	return name(b.FuncName)
}

func main() {
	fmt.Println(name(FuncName))
	fmt.Println(name((&Foo{}).FuncName))
	fmt.Println((&Foo{}).Get())
	fmt.Println(name((&Bar[int]{}).FuncName))
	fmt.Println((&Bar[int]{}).Get())
}

What did you expect to see?

main.FuncName
main.(*Foo).FuncName-fm
main.(*Foo).FuncName-fm
main.(*Bar[...]).FuncName-fm
main.(*Bar[...]).FuncName-fm

What did you see instead?

main.FuncName
main.(*Foo).FuncName-fm
main.(*Foo).FuncName-fm
main.(*Bar[...]).FuncName-fm
main.(*Bar[...]).Get.func1
@mauri870 mauri870 changed the title Function name of a method on a generic struct is different from a non-generic struct cmd/compile: FuncForPC on a generic struct method is different from a non-generic struct Nov 5, 2023
@gopherbot gopherbot added the compiler/runtime Issues related to the Go compiler and/or runtime. label Nov 5, 2023
@mauri870 mauri870 added the NeedsInvestigation Someone must examine and confirm this is a valid issue and not a duplicate of an existing one. label Nov 5, 2023
mitar added a commit to tozd/waf that referenced this issue Nov 5, 2023
@thanm
Copy link
Contributor

thanm commented Nov 6, 2023

What's happening here is that the compiler is having to manufacture a synthetic closure in order to capture both the function itself (the generic method) and the dictionary arguments required to call it. The code is here:

https://go.googlesource.com/go/+/4cd201b14b6216e72ffa175747c20d1191e5eb57/src/cmd/compile/internal/noder/reader.go#2622

This is an inherent limitation on runtime.FuncForPC -- if you call it on a wrapper function, it returns the wrapper, not the thing wrapped. To do otherwise would be essentially to lie: the PC you handed it is not the PC of the method but the PC of the wrapper, so one needs to be prepared for that possibility (especially if you do things like ask for file/line).

I might add that in your output you are already seeing wrappers even without generics (e.g. main.(*Foo).FuncName-fm is a method value wrapper, generated here: https://go.googlesource.com/go/+/4cd201b14b6216e72ffa175747c20d1191e5eb57/src/cmd/compile/internal/noder/reader.go#3752).

@mitar
Copy link
Contributor Author

mitar commented Nov 6, 2023

Thanks for explanation. But could then this wrapper be named like FuncName-gm or something like that? Similar to how method value wrappers are named with something sensible?

@prattmic
Copy link
Member

prattmic commented Nov 8, 2023

This feels similar in concept to #60324, though there is not actually any inlining here.

@mknyszek mknyszek added this to the Backlog milestone Nov 15, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
compiler/runtime Issues related to the Go compiler and/or runtime. NeedsInvestigation Someone must examine and confirm this is a valid issue and not a duplicate of an existing one.
Projects
Development

No branches or pull requests

6 participants