go: cmd/compile: incorrect type set intersection computation

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

$ go version
go version go1.19.4 linux/amd64

Does this issue reproduce with the latest release?

Yes, also for tip.

What did you do?

package main

func main() {}

type C1 interface {
	comparable
}

type C2 interface {
	comparable
	[2]any | int
}

func G1[T C1](t T) {}
func G2[T C2](t T) {}

func F1[V [2]any](v V) {
	_ = G1[V] // error: V does not implement comparable
}

func F2[V [2]any](v V) {
	_ = G2[V] // okay (but should not?)
}

What did you expect to see?

Both F1 and F2 fail to compile.

What did you see instead?

F2 compiles okay.

My understanding is that the type set of C2 only contains int, because [2]any is not strictly comparable, though my understanding might be wrong.

About this issue

  • Original URL
  • State: closed
  • Created 2 years ago
  • Comments: 27 (11 by maintainers)

Most upvoted comments

The type set of C2 should just be {int} but at the moment it is {[2]any, int} which is incorrect (it’s the intersection of all strictly comparable types with the type set {[2]any, int}; [2]any is not strictly comparable and thus we should be left with int).

The intersection computation is using the wrong “comparable” predicate (not strictly comparable but “spec-comparable”). Fix forthcoming, later today.

In the first example, it is correct that

func F1[V [2]any](v V) {
	_ = G1[V] // error: V does not implement comparable
}

V doesn’t implement comparable: the array [2]any is not strictly comparable, V is a type parameter, and therefore V does not implement comparable (also _ = v == v is not permitted in F1).

I think the handling of F2 is incorrect, this is a bug in the type checker.

@go101 you’re right. I was wrong that the type set is empty, it’s actually contain [2]any and int. Running gotype with tracing enabled:

p.go:10:6:	-- checking func G2 (white, objPath = )
p.go:10:9:	.  type params = [T₁]
p.go:10:11:	.  -- type C2
p.go:10:11:	.  => C2 (under = interface{comparable; [2]any | int}) // *Named
p.go:10:17:	.  -- type T
p.go:5:6:	.  -- type set for interface{comparable; [2]any | int}
p.go:5:6:	.  => {[2]any | int}
p.go:10:17:	.  => T₁ (under = interface{comparable; [2]any | int}) // *TypeParam
p.go:10:6:	=> func G2[T C2](t T) (black)

@go101 (from my understanding) The real constraint is interface{[2]any} It has to implement comparable but does not implement the constraint strictly comparable. (easily understood since implementation is type set inclusion)

Strictly comparable is different from comparable in that it asserts that the members of its type set are themselves implementing comparable instead of just satisfying it. (i.e. the members of stricly comparable are themselves strictly comparable)

According to my current reading of the spec at tip, strictly comparable does not matter here because [2]any is not an interface. [2]any satisfies comparable because it has the comparison operators. We have type set inclusion. So the type parameter constraint you are using actually implements C2. All is fine.

The compiler is correct.