mirror of
https://github.com/golang/go
synced 2024-09-04 23:44:16 +00:00
![Matthew Dempsky](/assets/img/avatar_default.png)
When deciding whether a captured variable can be passed by value, the compiler is sensitive to the order that the OCLOSURE node is typechecked relative to the order that the variable is passed to "checkassign". Today, for an assignment like: q, g = 2, func() int { return q } we get this right because we always typecheck the full RHS expression list before calling checkassign on any LHS expression. But I nearly made a change that would interleave this ordering, causing us to call checkassign on q before typechecking the function literal. And alarmingly, there weren't any tests that caught this. So this commit adds one. Change-Id: I66cacd61066c7a229070861a7d973bcc434904cc Reviewed-on: https://go-review.googlesource.com/c/go/+/280998 Trust: Matthew Dempsky <mdempsky@google.com> Run-TryBot: Matthew Dempsky <mdempsky@google.com> TryBot-Result: Go Bot <gobot@golang.org> Reviewed-by: Cuong Manh Le <cuong.manhle.vn@gmail.com>
133 lines
1.9 KiB
Go
133 lines
1.9 KiB
Go
// run
|
|
|
|
// Copyright 2015 The Go Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style
|
|
// license that can be found in the LICENSE file.
|
|
|
|
// Check that these do not use "by value" capturing,
|
|
// because changes are made to the value during the closure.
|
|
|
|
package main
|
|
|
|
var never bool
|
|
|
|
func main() {
|
|
{
|
|
type X struct {
|
|
v int
|
|
}
|
|
var x X
|
|
func() {
|
|
x.v++
|
|
}()
|
|
if x.v != 1 {
|
|
panic("x.v != 1")
|
|
}
|
|
|
|
type Y struct {
|
|
X
|
|
}
|
|
var y Y
|
|
func() {
|
|
y.v = 1
|
|
}()
|
|
if y.v != 1 {
|
|
panic("y.v != 1")
|
|
}
|
|
}
|
|
|
|
{
|
|
type Z struct {
|
|
a [3]byte
|
|
}
|
|
var z Z
|
|
func() {
|
|
i := 0
|
|
for z.a[1] = 1; i < 10; i++ {
|
|
}
|
|
}()
|
|
if z.a[1] != 1 {
|
|
panic("z.a[1] != 1")
|
|
}
|
|
}
|
|
|
|
{
|
|
w := 0
|
|
tmp := 0
|
|
f := func() {
|
|
if w != 1 {
|
|
panic("w != 1")
|
|
}
|
|
}
|
|
func() {
|
|
tmp = w // force capture of w, but do not write to it yet
|
|
_ = tmp
|
|
func() {
|
|
func() {
|
|
w++ // write in a nested closure
|
|
}()
|
|
}()
|
|
}()
|
|
f()
|
|
}
|
|
|
|
{
|
|
var g func() int
|
|
for i := range [2]int{} {
|
|
if i == 0 {
|
|
g = func() int {
|
|
return i // test that we capture by ref here, i is mutated on every interaction
|
|
}
|
|
}
|
|
}
|
|
if g() != 1 {
|
|
panic("g() != 1")
|
|
}
|
|
}
|
|
|
|
{
|
|
var g func() int
|
|
q := 0
|
|
for range [2]int{} {
|
|
q++
|
|
g = func() int {
|
|
return q // test that we capture by ref here
|
|
// q++ must on a different decldepth than q declaration
|
|
}
|
|
}
|
|
if g() != 2 {
|
|
panic("g() != 2")
|
|
}
|
|
}
|
|
|
|
{
|
|
var g func() int
|
|
var a [2]int
|
|
q := 0
|
|
for a[func() int {
|
|
q++
|
|
return 0
|
|
}()] = range [2]int{} {
|
|
g = func() int {
|
|
return q // test that we capture by ref here
|
|
// q++ must on a different decldepth than q declaration
|
|
}
|
|
}
|
|
if g() != 2 {
|
|
panic("g() != 2")
|
|
}
|
|
}
|
|
|
|
{
|
|
var g func() int
|
|
q := 0
|
|
q, g = 1, func() int { return q }
|
|
if never {
|
|
g = func() int { return 2 }
|
|
}
|
|
if g() != 1 {
|
|
panic("g() != 1")
|
|
}
|
|
}
|
|
}
|