位移传递拒绝了一个常数64位移位的uint64,以及在32位平台上一个uint移位32位,这两种情况都出现在正确的代码中。以下是两个示例:
从最近的x/sys/unix CL中:
func offs2lohi(offs int64) (lo, hi uintptr) {
const longBits = SizeofLong * 8
return uintptr(offs), uintptr(uint64(offs) >> longBits)
}
在64位系统上,目标是返回offs,0,该表达式确实做到了这一点,第二个表达式中使用了uint64 >> 64。我将发送一个CL,用(longBits - 1) >> 1替换>> longBits,以使旧的审查员对这段代码满意。
从最近的math/big CL中:
// bits.Len uses a lookup table for the low-order bits on some
// architectures. Neutralize any input-dependent behavior by setting all
// bits after the first one bit.
top := uint(x[i])
top |= top >> 1
top |= top >> 2
top |= top >> 4
top |= top >> 8
top |= top >> 16
top |= top >> 16 >> 16 // ">> 32" doesn't compile on 32-bit architectures
return i*_W + bits.Len(top)
注意注解说“无法编译”,但实际上问题是没有进行审查。
审查不应该重新定义语言中允许的有效代码。因为这些程序是有效的,我们应该放宽passes/shift/shift.go中的检查条件,将其更改为amt > maxSize,而不是amt >= maxSize。
9条答案
按热度按时间snz8szmq1#
https://go.dev/cl/463675提到了这个问题:
unix: avoid false positive in vet shift check
fumotvh32#
另一个可能的放宽是:(1)在所有平台上将uint和uintptr视为64位;(2)不警告关于计算常量值的移位。因此,给定
u32>>32 and u64>>64会被标记,但u>>32 and up>>32不会,即使在32位构建中也不会,u64>>(computed expression == 64)也不会。
oiopk7p53#
我认为在这种情况下,vet仍然有帮助:
关于机器字大小的歧义似乎涉及到所有这些问题。当存在可能的大小歧义(如可能的架构大小差异或y的计算值)时,是否可以放宽到">"?对于"x << y",当x或y是int、uint或uintptr类型,或者y不是BasicLit时,可以放宽。
也许vet可以尝试变得更聪明一些,尝试确定y的值是否依赖于标识符(无论是类型名还是常量值似乎值得停止)?这可能对*BasicLit来说太聪明了,收益不大。对于
x << (y+1-1)
被认为是模糊的可能是不幸的(但可能不是什么大问题)。q3qa4bjr4#
这是一个古老的警告(可以追溯到https://golang.org/cl/134780043),但我从未对这种警告感到满意。在便携式代码中,我可以使用构建标签定义类型和常量,以便在不同的系统上有所不同。当工具阻止我编写直接的代码,只是恰好在特定平台上有些奇怪时,这会让人很恼火。(我对禁止常数除以零的语言规则也有类似的担忧。)
所以正如我认为你所说的,警告混合预声明类型和超出范围的字面常量可能是可以接受的。但是我们不应该在不使用预声明类型时发出警告,也不应该在不使用字面常量时发出警告。
oxalkeyp5#
关于这个问题,我们想捕获一个常见的错误示例:
(正确的代码是 uint64(hi)<<32。)
这个版本也有问题,但我不确定我们能否在不捕获数学/大整数常量的情况下捕获它:
可能可以忽略,因为大多数沿着这条线的代码都使用了有大小的整数。
这里有一个潜在的规则:
(已经有一条规则可以豁免左侧有常量的移位操作,例如 ^uint(0) >> 63。那将保留。)
想法?
1cklez4t6#
我觉得这只是在问题边缘徘徊,对于一个GOARCH看起来令人困惑的代码实际上对另一个GOARCH是合理的。
正确的解决方案是运行所有可能的GOARCH检查,只有在所有检查触发时才报告错误。听起来有点疯狂,但我们有什么理由不能这样做呢?
5cnsuln77#
对于shift检查的具体情况,在所有GOARCHes上运行相当于如果检查将在32位和64位系统上发生,那么触发该检查。由于检查只是一个 >= 或 > 比较,假设64位正好具有这种效果。
一般来说,我们需要编译后的依赖项来实际更改GOARCH,这在许多情况下可能还需要更改GOOS。例如,没有darwin/ppc64le。API检查确实适用于所有GOOS/GOARCH组合,虽然可行,但肯定要慢10倍甚至更多。
vulvrdjw8#
检查仅适用于数值常量表达式。(7+1) 是,(N+1) 否。
在所有系统上,将 int、uint 和 uintptr 视为 64 位,以进行检查的目的。
我们之前支持像
unsafe.Sizeof(i)
这样的所有常量表达式作为副作用 gogo/types
。我们可能需要构建一个小型抽象语法树常量求值器来执行“视为 64 位”的步骤。我的猜测是我们可能不希望失去对整数上的 unsafe.Sizeof 的支持,我们可能可以放弃对 Alignof、Offsetof、len 和 cap 常量表达式的支持。想法呢?
正确的解决方案是针对所有可能的 GOARCHes 运行检查,只有在所有情况下触发检查时才报告错误。听起来有点疯狂,但有什么理由我们不能这样做呢?
这实际上确实听起来像是一个有原则的答案。一个复杂的问题是 vet 假设只有一个一致类型检查的包,其他定义可能在不同的文件中。我们目前正在从
go/types
获取常量求值作为副作用。这在一组文件上一次有效。但我认为我们可以尝试为足够多的表达式模拟“所有 GOARCHes”。8iwquhpp9#
对于位移检查的特定情况,在所有GOARCHes上运行相当于仅在32位和64位系统上触发检查,如果假设64位恰好具有相同的效果。
当然,只要移位量是整数字面量。我们放弃了移位量是构建标签控制的常量(某种程度上似乎出了问题)的情况。