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

text/template: pointer receivers methods do not work with non pointer values in templates #35079

Closed
lucacasonato opened this issue Oct 22, 2019 · 3 comments

Comments

@lucacasonato
Copy link

@lucacasonato lucacasonato commented Oct 22, 2019

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

$ go version
go version go1.13.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="/Users/***/go/bin"
GOCACHE="/Users/***/Library/Caches/go-build"
GOENV="/Users/***/Library/Application Support/go/env"
GOEXE=""
GOFLAGS=""
GOHOSTARCH="amd64"
GOHOSTOS="darwin"
GONOPROXY=""
GONOSUMDB=""
GOOS="darwin"
GOPATH="/Users/***/go"
GOPRIVATE=""
GOPROXY="https://proxy.golang.org,direct"
GOROOT="/usr/local/go"
GOSUMDB="sum.golang.org"
GOTMPDIR=""
GOTOOLDIR="/usr/local/go/pkg/tool/darwin_amd64"
GCCGO="gccgo"
AR="ar"
CC="clang"
CXX="clang++"
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 -fno-caret-diagnostics -Qunused-arguments -fmessage-length=0 -fdebug-prefix-map=/var/folders/x_/ktsmwdms0sgcdvpp051973b00000gn/T/go-build142077690=/tmp/go-build -gno-record-gcc-switches -fno-common"GOGCCFLAGS="-fPIC -m64 -pthread -fno-caret-diagnostics -Qunused-arguments -fmessage-length=0 -fdebug-prefix-map=/var/folders/x_/ktsmwdms0sgcdvpp051973b00000gn/T/go-build592860461=/tmp/go-build -gno-record-gcc-switches -fno-common"

What did you do?

I tried to call a method with a pointer reciever on a non pointer value in a template.

Here is an example: https://play.golang.org/p/GRqFTgTCwZh

  • Template 1 is just the control
  • Template 2 shows what doesnt work
  • Template 3 is just the control for arrays
  • Template 4 shows that this does work in arrays (presumably because arrays are more pointer than value)

What did you expect to see?

That the call to the method with a pointer reciever would behave the same in the template as in regular Go syntax. I should be able to call the method on a non pointer value.

What did you see instead?

The template execution errored out with the error template: :1:2: executing "" at <.PrintPointer>: can't evaluate field PrintPointer in type main.Data

Calling the method with a pointer reciever does work when the item is in an array.

@dmitshur

This comment has been minimized.

Copy link
Member

@dmitshur dmitshur commented Oct 22, 2019

Thanks for the issue report.

Issue #18816 may be somewhat relevant.

Template 3 is just the control for arrays
Template 4 shows that this does work in arrays (presumably because arrays are more pointer than value)

I see that you're using slices in template 3 and 4. Did you mean to say slice instead of array?

That the call to the method with a pointer reciever would behave the same in the template as in regular Go syntax.

I think there's an inconsistency in that you're using a local variable d in your test:

d := Data{Field: "test"}

That means when you write d.PrintPointer(), Go interprets that as (&d).PrintPointer(). It takes the address of the local variable d, resulting in *Data, and then calling its PrintPointer method. This is described in https://golang.org/ref/spec#Calls, specifically:

If x is addressable and &x's method set contains m, x.m() is shorthand for (&x).m():

var p Point
p.Scale(3.5)

Take a look at https://play.golang.org/p/qsyqsjW3i7Z.

If you change it to use a Data struct literal directly (see https://play.golang.org/p/-7Q7YRalYp_K), you get the same error in Go syntax as you do from the template execution:

./prog.go:7:21: cannot call pointer method on composite literal
./prog.go:7:21: cannot take the address of composite literal

If you apply the changes above to the original playground snippet, then everything looks consistent:

https://play.golang.org/p/h31FBL_qCIV

/cc @mvdan per owners.

@lucacasonato

This comment has been minimized.

Copy link
Author

@lucacasonato lucacasonato commented Oct 24, 2019

I see that you're using slices in template 3 and 4. Did you mean to say slice instead of array?

I did mean to say slice.

If you change it to use a Data struct literal directly (see https://play.golang.org/p/-7Q7YRalYp_K), you get the same error in Go syntax as you do from the template execution:

That clears up the confusion. Thank you.

I looked into it a little more and it seems it does not work in the template because the reflect package can not address the value in the template (ptr.CanAddr() returns false) - which makes sense as it is not a pointer. I don't know if this behavior should be changed, and if so, how.

@dmitshur

This comment has been minimized.

Copy link
Member

@dmitshur dmitshur commented Nov 2, 2019

That clears up the confusion. Thank you.

Glad to hear that.

I looked into it a little more and it seems it does not work in the template because the reflect package can not address the value in the template (ptr.CanAddr() returns false) - which makes sense as it is not a pointer. I don't know if this behavior should be changed, and if so, how.

That's right, the value needs to be addressable.

I don't think anything can or should be changed in text/template. It's common to pass a pointer to a struct to Execute for this reason, and also because it can be more efficient if the template data is large enough that copying it by value is expensive.

I'll close this since there's nothing to do here. Let me know if you disagree.

@dmitshur dmitshur closed this Nov 2, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
2 participants
You can’t perform that action at this time.