x/tools/gopls: go编译器和gopls之间的类型约束差异

nr9pn0ug  于 6个月前  发布在  Go
关注(0)|答案(8)|浏览(46)

在VSCode中使用Go项目时,我注意到gopls报告了一个问题,这个问题对于编译器来说是正常的。
我提取了一个最小示例如下:
假设我们有一个项目,包含包foo(文件foo.go)和bar(文件bar.go)。
foo导出类型约束A:

// foo.go
package foo

type A comparable

bar消耗约束A作为

// bar.go
package bar

// import "foo"

func F[T foo.A](a T, b T) bool {
	return a == b
}

由于约束A "继承"自comparable,表达式a == b是明确定义的:T被约束于A,后者是可以比较的,因此==可用于类型为T的值。编译器对代码满意。
但是VSCode报告a == b作为问题:
无效操作:a == b(在类型集中不可比较的类型)
在编辑器中,

两个观察:

  • 如果约束type A comparable在包bar中定义,那么对于gopls来说是没问题的——这是一个跨包问题
  • 如果约束被定义为type A = comparable(即在包foo中定义为typedef,而不是"newtype"),那么也是没问题的
ztigrdn8

ztigrdn81#

根据描述,我怀疑是导出/导入的问题。
确认了。如果我关闭导出数据,问题就消失了。

6vl6ewon

6vl6ewon2#

问题在于命名类型转发破坏了内置的 comparableA 之间的关系。也就是说:生成的类型 A 的底层是一个空接口,隐藏的 comparable 位被设置。
请注意,type A interface{comparable} 实际上是相同的类型,但没有这个问题,因为可比位被编码为显式嵌入的可比类型。
修复这个问题有点棘手,因为在 go/types API中无法直接访问或设置可比位。我们唯一能做的就是通过检查 A.Underlying()types.Universe.Lookup("comparable").Underlying() 之间的指针相等来识别这个特定情况。虽然感觉有点脏,但应该有效。
这需要仔细审查,所以不会发布 v0.13.0 版本。转到 v0.13.1。

s8vozzvw

s8vozzvw3#

另外想说:感谢你仔细的发布报告!这使得追踪这个问题变得更容易。

s5a0g9ez

s5a0g9ez4#

感谢您提供的解决方法 type A interface{comparable} -它对我有效:)

o7jaxewo

o7jaxewo5#

感谢您提供的解决方法 type A interface{comparable} -它对我有效:)

qvsjd97n

qvsjd97n6#

由于这种情况相对较不可能发生,并且有一个简单的解决方法,因此将问题抛给gopls@v0.14.0。

8yparm6h

8yparm6h7#

我猜这是go/types的问题。
这个问题在使用go1.21rc3构建的gopls和v0.13.0-pre.2版本的gopls时是可重现的。
cc @findleyr@griesemer@adonovan

omtl5h9j

omtl5h9j8#

根据描述,我怀疑是关于导出/导入的问题。我会紧急查看并解决这个问题。对于v0.13.0版本来说,修复这个问题会很好。

相关问题