头图

Bug

Let's take a look at the following program and think about what the output should be?

 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())
}

Think for a few seconds. . .


Before Go 1.13, the result printed by the above program was

 1

Starting with Go 1.13, the result printed by the above program is

 0

From a habitual cognition point of view, the compiler should execute in the following order

  • step 1: evaluate f() , the value of the variable n is 1
  • step 2: evaluate g() , when printing n , because step 1 has changed n to 1, it should print 1
  • step3: Execute the final function call based on the results of step 1 and step 2

The Go compiler actually follows this design. The official documentation describes it as follows:

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.

The expression of the last sentence shows that when a function call is executed, the operands of the function are parsed from left to right grammatically.

This bug was actually proposed by Matthew Dempsky , a member of the Google Go team. Go officially claimed this bug . The reason for this bug is also obvious. It was executed first g() , and then executed 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.

The current scope of the bug is as follows:

  • This bug, introduced in Go version 1.13, has been fixed and is expected to be released in version 1.19.
  • Only the official compiler gc has this bug, and if you are using the gccgo compiler, it will not have this problem.
  • This bug occurs only for function calls with multiple parameters. For example, the result of the following example f() is a function that has only one parameter, so the bug mentioned in this article will not exist.
 package main

func main() {
    n := 0

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

    f()(g())
}

The execution result of the above program is 1 .

Recommended reading

open source address

Articles and sample code are open sourced on GitHub: Beginner, Intermediate, and Advanced Tutorials in Go .

Official account: coding advanced. Follow the official account to get the latest Go interview questions and technology stacks.

Personal website: Jincheng's Blog .

Zhihu: Wuji

References


coding进阶
116 声望18 粉丝