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: calling string(...) on generic slices fails regardless of type constraints. #50421

Open
tmr232 opened this issue Jan 4, 2022 · 7 comments
Assignees
Labels
generics Issue is related to generics NeedsDecision Feedback is required from experts, contributors, and/or the community before a change can be made.
Milestone

Comments

@tmr232
Copy link

tmr232 commented Jan 4, 2022

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

$ go version
go version go1.18beta1 windows/amd64

Does this issue reproduce with the latest release?

Yes (1.18 beta)

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

go env Output
$ go env
set GO111MODULE=
set GOARCH=amd64
set GOBIN=
set GOCACHE=C:\Users\tamir.bahar\AppData\Local\go-build
set GOENV=C:\Users\tamir.bahar\AppData\Roaming\go\env
set GOEXE=.exe
set GOEXPERIMENT=
set GOFLAGS=
set GOHOSTARCH=amd64
set GOHOSTOS=windows
set GOINSECURE=
set GOMODCACHE=C:\Users\tamir.bahar\go\pkg\mod
set GONOPROXY=
set GONOSUMDB=
set GOOS=windows
set GOPATH=C:\Users\tamir.bahar\go
set GOPRIVATE=
set GOPROXY=https://proxy.golang.org,direct
set GOROOT=C:\Program Files\Go
set GOSUMDB=sum.golang.org
set GOTMPDIR=
set GOTOOLDIR=C:\Program Files\Go\pkg\tool\windows_amd64
set GOVCS=
set GOVERSION=go1.18beta1
set GCCGO=gccgo
set GOAMD64=v1
set AR=ar
set CC=gcc
set CXX=g++
set CGO_ENABLED=1
set GOMOD=NUL
set GOWORK=
set CGO_CFLAGS=-g -O2
set CGO_CPPFLAGS=
set CGO_CXXFLAGS=-g -O2
set CGO_FFLAGS=-g -O2
set CGO_LDFLAGS=-g -O2
set PKG_CONFIG=pkg-config
set GOGCCFLAGS=-m64 -mthreads -fno-caret-diagnostics -Qunused-arguments -fmessage-length=0 -fdebug-prefix-map=C:\Users\TAMIR~1.BAH\AppData\Local\Temp\go-build3924345009=/tmp/go-build -gno-record-gcc-switches

What did you do?

Compile the following code:

func toString[T byte|rune](slice []T) string {
   return string(slice)
}

What did you expect to see?

Since string(...) can take either a []byte or a []rune, I expected the code to compile successfully.

What did you see instead?

Compilation fails with:

cannot convert slice (variable of type []T) to type string

See https://gotipplay.golang.org/p/ZgvdU6Xv9jB

@ALTree
Copy link
Member

ALTree commented Jan 4, 2022

cc @griesemer

@ALTree ALTree added this to the Go1.18 milestone Jan 4, 2022
@ALTree ALTree added the NeedsInvestigation Someone must examine and confirm this is a valid issue and not a duplicate of an existing one. label Jan 4, 2022
@rogpeppe rogpeppe added the generics Issue is related to generics label Jan 4, 2022
@griesemer griesemer added NeedsDecision Feedback is required from experts, contributors, and/or the community before a change can be made. and removed NeedsInvestigation Someone must examine and confirm this is a valid issue and not a duplicate of an existing one. labels Jan 4, 2022
@griesemer griesemer self-assigned this Jan 4, 2022
@griesemer
Copy link
Contributor

griesemer commented Jan 4, 2022

Pet the current spec rules on generic conversions, this conversion is not permitted.

Note that the following code does work:

func toString[T []byte|[]rune](slice T) string {
        return string(slice)
}

Maybe the rules need to be relaxed.
@ianlancetaylor for additional input.

@tmr232
Copy link
Author

tmr232 commented Jan 4, 2022

Thanks!

As motivation for the rule relaxation, I'll present my use-case.

I have a generic type Iterator[T] that represents a Python-like iterator (it can be any other collection that's generic over the element type), and I want to convert it to a string.

Due to the current rules, I cannot just write the following:

func ToString[T rune | uint8](iter Iterator[T]) string {
	return string(ToSlice(iter))
}

Trying to work around it doesn't work either:

func sliceToString[T []rune | []uint8](slice T) string {
	return string(slice)
}

func ToString[T rune | uint8](iter Iterator[T]) string {
	return sliceToString(ToSlice(iter))
}

And fails with

[]T does not implement []rune|[]uint8

Which is hard for me to understand as T is either rune or uint8, making []T effectively []rune|[]uint8.

This means that I'll have to write 2 toString functions. One for rune and one for uint8.
It's not that bad in that one case, but I think it would also mean that code that is generic over string-elements would either need to be written twice (once for uint8 and once for rune), or that it will need non-generic free-functions for converting back to string.

@ianlancetaylor
Copy link
Contributor

ianlancetaylor commented Jan 4, 2022

Thanks. We're not going to change this for 1.18. In the long run I think we should probably permit this, but let's leave real consideration for later. Thanks.

@ianlancetaylor ianlancetaylor modified the milestones: Go1.18, Go1.19 Jan 4, 2022
@go101
Copy link

go101 commented Jan 5, 2022

Per the current spec rules on generic conversions, this conversion is not permitted.

@griesemer Could you more specific on which line in the current spec disallows this conversion? I only found this line

A non-constant value x can be converted to type T in any of these cases:

  • x is an integer or a slice of bytes or runes and T is a string type.

The line doesn't mention type parameters.

@ianlancetaylor
Copy link
Contributor

ianlancetaylor commented Jan 5, 2022

In your example slice is not a slice of bytes or runes, it is a slice of T, which is a type parameter. The rule you mention ("x is an integer or a slice of bytes or runes and T is a string type") doesn't apply. I'm being pedantic, but that is what the spec is: pedantic.

@griesemer griesemer modified the milestones: Go1.19, Go1.20 Apr 26, 2022
@go101
Copy link

go101 commented Sep 23, 2022

Is this the same problem?

type Bytes []byte

func hos[T Bytes](x []byte) T {
	return x // okay
}

func lay[T []byte](x Bytes) T {
	return x // error
}

func rez[T []byte](x Bytes) T {
	return T(x) // okay
}

[edit]: It is not the same problem. It is an assignment rule problem,

@griesemer griesemer modified the milestones: Go1.20, Go1.21 Nov 15, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
generics Issue is related to generics NeedsDecision Feedback is required from experts, contributors, and/or the community before a change can be made.
Projects
None yet
Development

No branches or pull requests

6 participants