golang / go

The Go programming language
https://go.dev
BSD 3-Clause "New" or "Revised" License
123.97k stars 17.66k forks source link

cmd/compile: type parameter involving constraint with channels seems like it should be inferrable #69153

Open mark-rushakoff opened 2 months ago

mark-rushakoff commented 2 months ago

Go version

go1.23 and go tip

Output of go env in your module/workspace:

n/a, using go.dev/play

What did you do?

https://go.dev/play/p/-psvliJDE_j

package main

import "fmt"

// Constraint to allow function to operate on a channel,
// regardless of the channel's directionality.
type channelConstraint[T any] interface {
    chan T | <-chan T | chan<- T
}

func main() {
    var sendOnlyCh chan<- string = make(chan string)
    printCap(sendOnlyCh)
    // Needs to be:
    printCap[string](sendOnlyCh)

    var receiveOnlyCh <-chan int = make(chan int, 1)
    printCap(receiveOnlyCh)
    // Needs to be:
    printCap[int](receiveOnlyCh)

    bidiCh := make(chan any)
    printCap(bidiCh)
    // Needs to be:
    printCap[any](bidiCh)
}

// printCap prints the capacity of ch,
// regardless of ch's directionality.
func printCap[T any, C channelConstraint[T]](ch C) {
    fmt.Println(cap(ch))
}

I want to write a small utility that takes a specific action that is determined by a channel's length and/or capacity. In this codebase, there are a general mix of directional and bidirectional channels. I thought I would be able to write a constraint such that my helper can accept a channel of any directionality. And I can indeed write that constraint, but when I call a function with that constraint, I have to provide the channel's element type as a type parameter in order to get the program to compile.

I am surprised that I have to provide the channel's element type, but maybe I am missing something about constraints or generics.

What did you see happen?

./prog.go:13:10: in call to printCap, cannot infer T (prog.go:28:15)

What did you expect to see?

I expected that printCap(ch) would compile; that I would not have to write printCap[int](ch).

I tried searching the issues for variations of "constraints", "channels", and "direction" or "directionality" but I was not able to find any issues that looked similar.

gabyhelp commented 2 months ago

Related Issues and Documentation

(Emoji vote if this was helpful or unhelpful; more detailed feedback welcome in this discussion.)

ianlancetaylor commented 2 months ago

CC @griesemer

I think we would need a special type inference rule for channel types.

Note that I think that your program isn't going to work anyhow. The cap(ch) call isn't permitted, though that would be fixed by #63940.

mark-rushakoff commented 2 months ago

Adjusting the code to remove the lines that failed compilation, it does appear that cap(ch) is reported correctly, on go1.23. https://go.dev/play/p/jS1_QIeyrUF

It looks like len(ch) works too.

griesemer commented 2 months ago

What @ianlancetaylor said. The issue here is that there's no core type for the constraint. At the moment inference does work if the constraint was just chan T | <-chan T or chan T | chan<- T (there's a core type) but instantiation doesn't work of course for the one direction that's not in the constraint.

This might also be addressed by #63940.

griesemer commented 2 months ago

Marking for 1.24 - not promising that this will be addressed, but so I keep an eye on it while investigating #63940.

griesemer commented 2 weeks ago

Moving to 1.25.