Skip to content

go: compiler: possible bug in variadic function arguments supplied from "ellipsed" slice #60362

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

Closed
markusheukelom opened this issue May 23, 2023 · 4 comments

Comments

@markusheukelom
Copy link

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

$ go version
go version go1.20.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="on"
GOARCH="amd64"
GOBIN=""
GOCACHE="/Users/Markus/Library/Caches/go-build"
GOENV="/Users/Markus/Library/Application Support/go/env"
GOEXE=""
GOEXPERIMENT=""
GOFLAGS=""
GOHOSTARCH="amd64"
GOHOSTOS="darwin"
GOINSECURE=""
GOMODCACHE="/Users/Markus/go/pkg/mod"
GONOPROXY="github.com/mheugo"
GONOSUMDB="github.com/mheugo"
GOOS="darwin"
GOPATH="/Users/Markus/go"
GOPRIVATE="github.com/mheugo"
GOPROXY="direct"
GOROOT="/usr/local/go"
GOSUMDB="sum.golang.org"
GOTMPDIR=""
GOTOOLDIR="/usr/local/go/pkg/tool/darwin_amd64"
GOVCS=""
GOVERSION="go1.20.3"
GCCGO="gccgo"
GOAMD64="v1"
AR="ar"
CC="clang"
CXX="clang++"
CGO_ENABLED="1"
GOMOD="/Users/Markus/Projects/go/go.mod"
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 -arch x86_64 -m64 -pthread -fno-caret-diagnostics -Qunused-arguments -fmessage-length=0 -fdebug-prefix-map=/var/folders/bf/kbhmwc8j7nx3kjb285knl23c0000gn/T/go-build1623327632=/tmp/go-build -gno-record-gcc-switches -fno-common"

What did you do?

package main

type I interface{}
type A struct{}

func F(vals ...I) {}

func main() {
	a := A{}
	F(a, a) // ok

	aSlice := []A{A{}, A{}}

	F(aSlice...) // BUG: error: cannot use aSlice (variable of type []A) as []I value in argument to F
}

(Play: https://go.dev/play/p/amaSBp_AJjW)

What did you expect to see?

I expected this to compile because the language specification states: "[...] the value passed is a new slice of type []T with a new underlying array whose successive elements are the actual arguments, which all must be assignable to T"

In my example A is assignable to I, so I would expect this to work (F(a, a) also compiles fine).

A bit later this is stated in the spec:

"If the final argument is assignable to a slice type []T and is followed by ..., it is passed unchanged as the value for a ...T parameter. In this case no new slice is created."

This seems to be an optimisation, I think. However, in the given example aSlice is not assignable to a variable of type I[] so this shortcut (optimisation) is not applicable. Or is the specification is just unclear (to me)?

What did you see instead?

"error: cannot use aSlice (variable of type []A) as []I value in argument to F"

@seankhliao
Copy link
Member

I believe that:

then within f the type of p is equivalent to type []T.

and

If the final argument is assignable to a slice type []T and is followed by ..., it is passed unchanged as the value for a ...T parameter. In this case no new slice is created.

together is sufficient to define the current behavior as working as intended.
Note that spread only applies with an exact type match, not just with assignable elements.

@seankhliao seankhliao closed this as not planned Won't fix, can't repro, duplicate, stale May 23, 2023
@markusheukelom
Copy link
Author

@seankhliao I am not sure I understand you I think. I don't want to debate the decision, just understand it better.

Consider this:

	F(aSlice[0], aSlice[1])   // this is fine (A)
	F(aSlice...) // BUG: error: cannot use aSlice (variable of type []A) as []I value in argument to F (B)

Isn't that strange? To me it is, but maybe I am expecting to much of the spread operator.

Please note that there are two questions:

  1. Is this indeed intended behaviour as per the specification; if so, should this be made clearer?
  2. If this is indeed intended behaviour, why is that so? Would it make sense to propose (B) to also work?

I am just curious to know if I miss something obvious for why (B) is not needed or is not a good idea.

Thank you!

@zephyrtronium
Copy link
Contributor

F(aSlice[0], aSlice[1]) creates a new slice and assigns to its first two elements the corresponding arguments from aSlice, so the individual elements of aSlice need to be assignable to the element type of the argument. F(aSlice...) passes aSlice itself as the argument, so its (slice) type needs to be assignable to the argument itself. This is described in Passing arguments to ... parameters. https://go.dev/doc/faq#convert_slice_of_interface explains further why (B) cannot work.

@markusheukelom
Copy link
Author

markusheukelom commented May 24, 2023

@zephyrtronium Thanks, it is clear now. I misinterpreted "..." as an actual (spread) operator, while it is not.

Since a new slice is created for individual arguments given to a variadic function call, could this also not be done if those arguments come from a "...T" construct instruct instead?

I suspect the reason might be that with actual individual arguments the size of the array backing the slice is known at compile time and so can be statically allocated. Allowing this with "...T" would potentially do huge dynamic array allocation.

But from a language perspective it feels a bit weird not being able to do it, might be just me of course.

EDIT: (added an extra note)

Converting []T to []interface{} is disallowed as described here: https://go.dev/doc/faq#convert_slice_of_interface
The reason is that a copy must be made. But for variadic arguments a copy is made if individual arguments are given anyway. Plus the ... in "T..." gives a explicit indication that said copy is made, if this were allowed.

@golang golang locked and limited conversation to collaborators May 23, 2024
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

4 participants