go cmd/compile: 通用代码与非通用代码的行为不同

chhqkbe1  于 4个月前  发布在  Go
关注(0)|答案(6)|浏览(54)

你正在使用哪个版本的Go(go version)?

$ go version
go version go1.20.3 linux/amd64

这个问题在最新版本中是否会重现?

是的

你做了什么?

package main

type I interface {
	M(int)
	P()
}

type V int

func (v V) P()       { println(v) }
func (v *V) M(n int) { *v = V(n) }

type S struct{ *V }

func F[T I](x T) {
	defer x.P()
	x.M(9)
}

func G(x *S) {
	defer x.P()
	x.M(9)
}

func main() {
	{
		var s = S{V: new(V)}
		F(&s) // 9
	}
	{
		var s = S{V: new(V)}
		G(&s) // 0
	}
}

你的预期结果是什么?

相同的行为。

你实际看到的结果是什么?

不同的行为。

ldfqzlk8

ldfqzlk81#

这不是泛型与非泛型的比较,而是具体类型与接口类型的比较。将 F 改为接口类型后,它还会打印 9:

func F(x I) {
	defer x.P()
	x.M(9)
}

据我所知,这种行为是正确的。根据规范:
对于类型为 I 的值 x,其中 I 是接口类型,x.f 表示 x 的动态值的实际方法 f。

qcbq4gxm

qcbq4gxm2#

但是&s不是一个接口,这就是问题所在。

vom3gejh

vom3gejh3#

可能与 #52025 有关。
CC @randall77@mdempsky

km0tfn4u

km0tfn4u4#

我认为这应该是一个新的问题,而不是一个积压问题。

kmpatx3s

kmpatx3s5#

正如@cuonglm所指出的,使用类型参数的通用代码的行为与接口调用的工作方式相同。我同意这一点,这与将相同的代码专门用于执行单个具体类型参数时的工作方式不同。

我认为这是一个规范歧义问题。在我看来,提升方法、接口方法调用、方法值和延迟语句的语义仍然模糊不清。

cczfrluj

cczfrluj6#

看起来这个特定问题更多地是关于接口方法值和非接口方法值评估之间的不一致性:

package main

type I interface {M(int); P()}

type V int
func (v *V) M(n int) {*v = V(n)}
func (v V) P() {print(v)}

func F(x *V) {defer x.P(); x.M(7)}

func G[T I](x T) {defer x.P(); x.M(7)}

func H(x I) {defer x.P(); x.M(7)}

func main() {
	F(new(V)) // 0
	G(new(V)) // 7
	H(new(V)) // 7
}

这确实打破了许多人将类型参数始终视为接口的预期。

相关问题