mirror of
https://github.com/golang/go
synced 2024-11-14 13:50:23 -07:00
c20d959163
Adds: GOEXPERIMENT=loopvar (expected way of invoking) -d=loopvar={-1,0,1,2,11,12} (for per-package control and/or logging) -d=loopvarhash=... (for hash debugging) loopvar=11,12 are for testing, benchmarking, and debugging. If enabled,for loops of the form `for x,y := range thing`, if x and/or y are addressed or captured by a closure, are transformed by renaming x/y to a temporary and prepending an assignment to the body of the loop x := tmp_x. This changes the loop semantics by making each iteration's instance of x be distinct from the others (currently they are all aliased, and when this matters, it is almost always a bug). 3-range with captured iteration variables are also transformed, though it is a more complex transformation. "Optimized" to do a simpler transformation for 3-clause for where the increment is empty. (Prior optimization of address-taking under Return disabled, because it was incorrect; returns can have loops for children. Restored in a later CL.) Includes support for -d=loopvarhash=<binary string> intended for use with hash search and GOCOMPILEDEBUG=loopvarhash=<binary string> (use `gossahash -e loopvarhash command-that-fails`). Minor feature upgrades to hash-triggered features; clients can specify that file-position hashes use only the most-inline position, and/or that they use only the basenames of source files (not the full directory path). Most-inlined is the right choice for debugging loop-iteration change once the semantics are linked to the package across inlining; basename-only makes it tractable to write tests (which, otherwise, depend on the full pathname of the source file and thus vary). Updates #57969. Change-Id: I180a51a3f8d4173f6210c861f10de23de8a1b1db Reviewed-on: https://go-review.googlesource.com/c/go/+/411904 Reviewed-by: Matthew Dempsky <mdempsky@google.com> Run-TryBot: David Chase <drchase@google.com> TryBot-Result: Gopher Robot <gobot@golang.org>
134 lines
1.9 KiB
Go
134 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
|
|
var i 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")
|
|
}
|
|
}
|
|
}
|