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

Slice unpacking to variadic function not honored #29767

Closed
ghost opened this issue Jan 16, 2019 · 4 comments
Closed

Slice unpacking to variadic function not honored #29767

ghost opened this issue Jan 16, 2019 · 4 comments

Comments

@ghost
Copy link

ghost commented Jan 16, 2019

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

go1.10.4 linux/amd64

Does this issue reproduce with the latest release?

Haven't tried.

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

go env Output
GOARCH="amd64"
GOBIN=""
GOCACHE="/home/<>/.cache/go-build"
GOEXE=""
GOHOSTARCH="amd64"
GOHOSTOS="linux"
GOOS="linux"
GOPATH="/home/<>/go"
GORACE=""
GOROOT="/usr/lib/go-1.10"
GOTMPDIR=""
GOTOOLDIR="/usr/lib/go-1.10/pkg/tool/linux_amd64"
GCCGO="/usr/bin/gccgo"
CC="gcc"
CXX="g++"
CGO_ENABLED="1"
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-build779194954=/tmp/go-build -gno-record-gcc-switches"

What did you do?

Defined function with signature: func variadic(args ...interface{}) and attempted to call it by unpacking a slice: variadic([]int{1,2,3}...).

What did you expect to see?

Compilation succeeds.

What did you see instead?

Conversion error from []int to []interface{}.

The reason why is understood, as per FAQ:

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

However, the first sentence suggests that this optimization should not have occurred because []int is not assignable to []interface{}:

A value x is assignable to a variable of type T ("x is assignable to T") if one of the following conditions applies:
x's type is identical to T

False, []int is not of type []interface{}.

x's type V and T have identical underlying types and at least one of V or T is not a defined type.

False, both types are defined.

T is an interface type and x implements T.

False, []int is not an interface.

x is a bidirectional channel value, T is a channel type, x's type V and T have identical element types, and at least one of V or T is not a defined type.

False, []int is not a channel.

x is the predeclared identifier nil and T is a pointer, function, slice, map, channel, or interface type.

False, []int is not nil.

x is an untyped constant representable by a value of type T.

False, []int is a type.

Since []int is not assignable to []interface{} the slice should have been unpacked and each element passed individually to the variadic function.

There are several related issues (eg. #1616, #18547, #27795) that expose the same problem, probably more but none appear to go further than claiming that this is "working as intended".

If I wanted to pass the []int as-is, I could have simply omitted unpacking by writing variadic([]int{1,2,3}) instead of variadic([]int{1,2,3}...). By typing the latter, I explicitly say "I want to unpack this slice". The compiler however decided to take my statement as suggestion since it believed []int is assignable to []interface{} and decided not to not honor my request, instead passing the slice as-is.

I this believe is in error.

@ianlancetaylor
Copy link
Contributor

I'm not completely sure what you are saying, but I think you may be misreading the paragraph in the spec. The spec says that if you write variadic([]int{1,2,3}...), with the trailing ..., then the slice will be passed unchanged to the variadic function. That is only permitted if the slice is assignable to []T, where T is the variadic type, in your case interface{}. Since []int is not assignable to []interface{}, as you know, the use of the trailing ... in the call is forbidden. So compilation fails, as it should.

It sounds like you may be saying that by using the trailing ..., you are suggesting that the compiler should unpack the slice rather than passing it directly. But that is not what the trailing ... means. The trailing ... means to pass the slice directly. The compiler doesn't implement that differently based on whether the slice is assignable to []T or not.

Closing because this is indeed working as intended.

@ianlancetaylor
Copy link
Contributor

You may want to read the discussion in #18605.

@ghost
Copy link
Author

ghost commented Jan 16, 2019

If ... means to pass as-is then this should be clearly mentioned in the spec (I could not find the actual definition of ... other than it being an operator). General consensus however seems to be that ... is the "unpack operator", comparable to other languages. This would imply that it unpacks, which it does not.

In addition, the way that the FAQ section is worded suggests that if slice []A cannot be assigned to slice []B:

Otherwise, 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.

I don't see the creation of that new slice happening, which is why I reported it. If not a defect in ... operation, it is a defect in the FAQ for not being clear what operator ... actually does.

From what I understand #18605 appears to propose adding support for variadic(int(1), []int{2,3,4}). While nice to have, it is something else and not the issue I was reporting.

@ianlancetaylor
Copy link
Contributor

I mentioned #18605 because the discussion on that issue discusses the point about slices being passed directly.

The language spec says, in the section "Passing arguments to ... parameters", "If the final argument is assignable to a slice type []T, it may be passed unchanged as the value for a ...T parameter if the argument is followed by .... In this case no new slice is created" (https://golang.org/ref/spec#Passing_arguments_to_..._parameters). That seems precise to me: if the final argument is followed by ..., no new slice is created. I'm not sure what general consensus says that ... is the unpack operator; I don't think it can reasonably be described that way.

The second of the spec that you quote above, which discusses a new slice, is the section in which the arguments are passed individually, without using ....

@golang golang locked and limited conversation to collaborators Jan 16, 2020
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

2 participants