关于golang:Go-113版本引入的bug你遇到过这个坑么

49次阅读

共计 1841 个字符,预计需要花费 5 分钟才能阅读完成。

Bug

大家看看上面这段程序,思考下输入后果应该是什么?

func main() {
  n := 0

  f := func() func(int, int) {
    n = 1
    return func(int, int) {}}
  g := func() (int, int) {println(n)
    return 0, 0
  }

  f()(g())
}

先思考几秒钟。。。


在 Go 1.13 版本之前,上述程序打印的后果是

1

在 Go 1.13 版本开始,上述程序打印的后果是

0

从习惯认知来看,编译器应该依照如下程序执行

  • step 1: evaluate f(),此时变量 n 的值为 1
  • step 2: evaluate g(),此时打印 n 时,因为 step 1 曾经把 n 批改为 1 了,所以应该打印 1
  • step3: 依据 step 1 和 step 2 的后果,执行最终的函数调用

Go 编译器其实也是遵循这个设计的,官网文档的形容如下:

At package level, initialization dependencies determine the evaluation order of individual initialization expressions in variable declarations.

Otherwise, when evaluating the operands of an expression, assignment, or return statement, all function calls, method calls, and communication operations are evaluated in lexical left-to-right order.

最初一句的表述能够晓得,在执行函数调用时,对于函数的操作数,语法上是依照从左到右的程序进行解析的。

这个 bug 其实是 Google Go 团队成员 Matthew Dempsky 提出来的,Go 官网认领了这个 bug,这个 bug 产生的起因也很显著,就是先执行了g(),而后执行的f()

This is because we rewrite f()(g()) into t1, t2 := g(); f()(t1, t2) during type checking.

gccgo compiles it correctly.

@cuonglm Yeah, rewriting as t0 := f(); t1, t2 := g(); t0(t1, t2) instead seems like a reasonable quick fix to me. I think direct function calls and method calls are probably the most common case though, so we should make sure those are still handled efficiently.

Longer term, I think we should probably incorporate order.go directly into unified IR.

目前这个 bug 的影响范畴如下:

  • Go 1.13 版本开始引入的这个 bug,目前曾经被修复,预计在 1.19 版本公布。
  • 只有官网的编译器 gc 有这个 bug,如果你应用的是 gccgo 编译器,也不会有这个问题。
  • 只对多个参数的函数调用才会有这个 bug,比方上面这个例子 f() 的后果是一个函数,该函数只有 1 个参数,就不会有本文提到的这个 bug。
package main

func main() {
    n := 0

    f := func() func(int) {
        n = 1
        return func(int) {}}
    g := func() int {println(n)
        return 0
    }

    f()(g())
}

下面程序执行后果是1

举荐浏览

  • 泛型

    • 泛型:Go 泛型入门官网教程
    • 泛型:一文读懂 Go 泛型设计和应用场景
    • 泛型:Go 1.18 正式版本将从规范库中移除 constraints 包
    • 泛型:什么场景应该应用泛型
  • Fuzzing

    • Fuzzing: Go Fuzzing 入门官网教程
    • Fuzzing: 一文读懂 Go Fuzzing 应用和原理
  • 工作区模式

    • Go 1.18:工作区模式 workspace mode 简介
    • Go 1.18:工作区模式最佳实际

开源地址

文章和示例代码开源在 GitHub: Go 语言高级、中级和高级教程。

公众号:coding 进阶。关注公众号能够获取最新 Go 面试题和技术栈。

集体网站:Jincheng’s Blog。

知乎:无忌

References

  • https://twitter.com/go100and1…
  • https://github.com/golang/go/…
  • https://go.dev/ref/spec#Order…

正文完
 0