Skip to content

Slice has unpredictable result #68010

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
xice-blue opened this issue Jun 15, 2024 · 16 comments
Closed

Slice has unpredictable result #68010

xice-blue opened this issue Jun 15, 2024 · 16 comments

Comments

@xice-blue
Copy link

Go version

go version go1.22.1 windows/amd64

Output of go env in your module/workspace:

set GO111MODULE=
set GOARCH=amd64
set GOBIN=
set GOCACHE=C:\Users\AppData\Local\go-build
set GOENV=C:\Users\AppData\Roaming\go\env
set GOEXE=.exe
set GOEXPERIMENT=
set GOFLAGS=
set GOHOSTARCH=amd64
set GOHOSTOS=windows
set GOINSECURE=
set GOMODCACHE=C:\Users\go\pkg\mod
set GONOPROXY=git.mycompany.com,github.com/my/private
set GONOSUMDB=git.mycompany.com,github.com/my/private
set GOOS=windows
set GOPATH=C:\Users\go
set GOPRIVATE=git.mycompany.com,github.com/my/private
set GOPROXY=https://goproxy.io,direct
set GOROOT=F:\env\Go
set GOSUMDB=sum.golang.org
set GOTMPDIR=
set GOTOOLCHAIN=auto
set GOTOOLDIR=F:\env\Go\pkg\tool\windows_amd64
set GOVCS=
set GOVERSION=go1.22.1
set GCCGO=gccgo
set GOAMD64=v1
set AR=ar
set CC=gcc
set CXX=g++
set CGO_ENABLED=1
set GOMOD=F:\work\go.mod
set GOWORK=
set CGO_CFLAGS=-O2 -g
set CGO_CPPFLAGS=
set CGO_CXXFLAGS=-O2 -g
set CGO_FFLAGS=-O2 -g
set CGO_LDFLAGS=-O2 -g
set PKG_CONFIG=pkg-config
set GOGCCFLAGS=-m64 -mthreads -Wl,--no-gc-sections -fmessage-length=0 -ffile-prefix-map=C:\Users\AppData\Local\Temp\go-build1951953803=/tmp/go-build -gno-record-gcc-switches

What did you do?

I want to use slice to store some data, which will use for some calculations.
There are two slices have the same pointer, but the data and behavior are unpredictable when I append data to them.

What did you see happen?

code:
fmt.Printf("地址(%p),值:%v \n", xz.dAA, xz.dAA)
txz := yXA[32]
fmt.Printf("地址(%p),值:%v \n", txz.dAA, txz.dAA)

txz.dAA = append(txz.dAA, [][3]int{{1023, 68, 69}})

fmt.Printf("地址(%p),值:%v \n", xz.dAA, xz.dAA)
txz := yXA[32]
fmt.Printf("地址(%p),值:%v \n", txz.dAA, txz.dAA)

output:
地址(0xc00efc9e00),值:[[[1012 36 44] [1012 50 68]] [[1013 34 71]] [[1014 32 72]] [[1015 30 73]] [[1016 22 73]] [[1017 18 74]] [[1018 14 74]] [[1019 12 74]] [[1020 34 75]] [[1021 34 75]] [[1022 32 75]]]

地址(0xc00efc9e00),值:[[[1012 36 44] [1012 50 68]] [[1013 34 71]] [[1014 32 72]] [[1015 30 73]] [[1016 22 73]] [[1017 18 74]] [[1018 14 74]] [[1019 12 74]] [[1020 34 75]] [[1021 34 75]] [[1022 32 75]] [[1023 31 47]]]

地址(0xc00efc9e00),值:[[[1012 36 44] [1012 50 68]] [[1013 34 71]] [[1014 32 72]] [[1015 30 73]] [[1016 22 73]] [[1017 18 74]] [[1018 14 74]] [[1019 12 74]] [[1020 34 75]] [[1021 34 75]] [[1022 32 75]] [[1023 68 69]]]

地址(0xc00efc9e00),值:[[[1012 36 44] [1012 50 68]] [[1013 34 71]] [[1014 32 72]] [[1015 30 73]] [[1016 22 73]] [[1017 18 74]] [[1018 14 74]] [[1019 12 74]] [[1020 34 75]] [[1021 34 75]] [[1022 32 75]] [[1023 68 69]]]

What did you expect to see?

If the slices have same pointer, then they must have same data.

@seankhliao
Copy link
Member

Unlike many projects, the Go project does not use GitHub Issues for general discussion or asking questions. GitHub Issues are used for tracking bugs and proposals only.

For questions please refer to https://github.com/golang/go/wiki/Questions

@seankhliao seankhliao closed this as not planned Won't fix, can't repro, duplicate, stale Jun 15, 2024
@xice-blue
Copy link
Author

Unlike many projects, the Go project does not use GitHub Issues for general discussion or asking questions. GitHub Issues are used for tracking bugs and proposals only.

For questions please refer to https://github.com/golang/go/wiki/Questions

This is a bug. This is not a concurrency issue. It was happened on a debug model, and I did not use any concurrency logic.

@xice-blue
Copy link
Author

There is a big loop (tens millions of times) out of the code, and there are tens thousands slices were created and disdroyed. Before about 3 millions of times of the loop, the slice has correct behavior. But the bug must happen before the end of the loop.

@seankhliao
Copy link
Member

please provide a self contained reproducer for the issue.

@xice-blue
Copy link
Author

In the test code, it is worse when the 'n' becomes smaller. Please see the line of fmt.printf(...).

package test

import (
	"fmt"
	"math/rand"
	"testing"
)

type txs struct {
	r   int
	dAA [][][3]int
}

func (zg *txs) j(i, j int) {
	ldA := zg.dAA[len(zg.dAA)-1]
	if i > ldA[0][0] {
		zg.dAA = append(zg.dAA, [][3]int{{i, j, j}})
	} else {
		ldA[len(ldA)-1][2] = j
	}
}

func (zg *txs) l(ng *txs) {
	ic := zg.dAA[0][0][0] - ng.dAA[0][0][0]
	if ic < 0 {
		for ngi := range ng.dAA {
			zgi := ngi - ic
			zgx := zg.dAA[zgi][0][1]
			ngx := ng.dAA[ngi][0][1]
			if zgx < ngx {
				zg.dAA[zgi] = append(zg.dAA[zgi], ng.dAA[ngi]...)
			} else if ngx < zgx {
				zg.dAA[zgi] = append(ng.dAA[ngi], zg.dAA[zgi]...)
			}
		}
	} else {
		for ngi := ic; ngi < len(ng.dAA); ngi++ {
			zgi := ngi - ic
			zgdA := zg.dAA[zgi]
			zgzx := zgdA[0][1]
			zgyx := zgdA[len(zgdA)-1][2]
			ngdA := ng.dAA[ngi]
			ngzx := ngdA[0][1]
			ngyx := ngdA[len(ngdA)-1][2]
			if zgyx < ngzx {
				zg.dAA[zgi] = append(zgdA, ngdA...)
			} else if zgzx > ngyx {
				zg.dAA[zgi] = append(ngdA, zgdA...)
			}
		}
		if ic > 0 {
			zg.dAA = append(ng.dAA[:ic], zg.dAA...)
		}
	}
	ng.dAA = zg.dAA
}

func TestSliceBug(t *testing.T) {
	n := 3
	cn := 0

	syXA := make([]*txs, 2000)
	yXA := make([]*txs, 2000)

	zx := rand.Int() % n
	xz := &txs{zx, [][][3]int{{{0, 0, 0}}}}
	syXA[0] = xz

	for j := range 2000 {
		x := rand.Int() % n
		if zx == x {
			xz.j(0, j)
		} else {
			xz = &txs{zx, [][][3]int{{{0, j, j}}}}
			zx = x
		}
		syXA[j] = xz
	}
	for i := 1; i < 3000; i++ {
		zx = rand.Int() % n
		sxz := syXA[0]
		if zx == sxz.r {
			xz = sxz
			xz.j(i, 0)
		} else {
			xz = &txs{zx, [][][3]int{{{i, 0, 0}}}}
		}
		yXA[0] = xz
		for j := 1; j < 2000; j++ {
			x := rand.Int() % n
			sxz := syXA[j]
			if x == sxz.r {
				if xz == sxz {
					xzdl := len(xz.dAA)
					txz := syXA[xz.dAA[xzdl-1][0][1]]
					txzdl := len(txz.dAA)
					xzdap := fmt.Sprintf("%p", xz.dAA)
					txzdap := fmt.Sprintf("%p", txz.dAA)
					se := false
					if xzdap == txzdap {
						if xzdl == txzdl {
							cn++
						} else {
							se = true
							fmt.Printf("Correct times: %v \n", cn)
							fmt.Printf("Error! Pointer(%p), values:%v \n", xz.dAA, xz.dAA)
							fmt.Printf("Error! Pointer(%p), values:%v \n", txz.dAA, txz.dAA)
						}
					}

					xz.j(i, j)

					if se {
						fmt.Printf("Error! Pointer(%p), values:%v \n", xz.dAA, xz.dAA)
						fmt.Printf("Error! Pointer(%p), values:%v \n", txz.dAA, txz.dAA)
						t.FailNow()
					}
				} else if x == xz.r {
					xzdl := len(xz.dAA)
					txz := syXA[xz.dAA[xzdl-1][0][1]]
					txzdl := len(txz.dAA)
					xzdap := fmt.Sprintf("%p", xz.dAA)
					txzdap := fmt.Sprintf("%p", txz.dAA)
					se := false
					if xzdap == txzdap {
						if xzdl == txzdl {
							cn++
						} else {
							se = true
							fmt.Printf("Correct times: %v \n", cn)
							fmt.Printf("Error! Pointer(%p), values:%v \n", xz.dAA, xz.dAA)
							fmt.Printf("Error! Pointer(%p), values:%v \n", txz.dAA, txz.dAA)
						}
					}

					xz.j(i, j)

					if se {
						fmt.Printf("Error! Pointer(%p), values:%v \n", xz.dAA, xz.dAA)
						fmt.Printf("Error! Pointer(%p), values:%v \n", txz.dAA, txz.dAA)
						t.FailNow()
					}

					xz.l(sxz)
				} else {
					xz = sxz
					zx = x

					xzdl := len(xz.dAA)
					txz := syXA[xz.dAA[xzdl-1][0][1]]
					txzdl := len(txz.dAA)
					xzdap := fmt.Sprintf("%p", xz.dAA)
					txzdap := fmt.Sprintf("%p", txz.dAA)
					se := false
					if xzdap == txzdap {
						if xzdl == txzdl {
							cn++
						} else {
							se = true
							fmt.Printf("Correct times: %v \n", cn)
							fmt.Printf("Error! Pointer(%p), values:%v \n", xz.dAA, xz.dAA)
							fmt.Printf("Error! Pointer(%p), values:%v \n", txz.dAA, txz.dAA)
						}
					}

					xz.j(i, j)

					if se {
						fmt.Printf("Error! Pointer(%p), values:%v \n", xz.dAA, xz.dAA)
						fmt.Printf("Error! Pointer(%p), values:%v \n", txz.dAA, txz.dAA)
						t.FailNow()
					}
				}
			} else if x == zx {
				xzdl := len(xz.dAA)
				txz := syXA[xz.dAA[xzdl-1][0][1]]
				txzdl := len(txz.dAA)
				xzdap := fmt.Sprintf("%p", xz.dAA)
				txzdap := fmt.Sprintf("%p", txz.dAA)
				se := false
				if xzdap == txzdap {
					if xzdl == txzdl {
						cn++
					} else {
						se = true
						fmt.Printf("Correct times: %v \n", cn)
						fmt.Printf("Error! Pointer(%p), values:%v \n", xz.dAA, xz.dAA)
						fmt.Printf("Error! Pointer(%p), values:%v \n", txz.dAA, txz.dAA)
					}
				}

				xz.j(i, j)

				if se {
					fmt.Printf("Error! Pointer(%p), values:%v \n", xz.dAA, xz.dAA)
					fmt.Printf("Error! Pointer(%p), values:%v \n", txz.dAA, txz.dAA)
					t.FailNow()
				}
			} else {
				zx = x
				xz = &txs{zx, [][][3]int{{{i, j, j}}}}
			}
			yXA[j] = xz
		}
		tt := syXA
		syXA = yXA
		yXA = tt
	}
}

@randall77
Copy link
Contributor

I'm afraid from your test program it isn't obvious that there is any bug. It would help if the program could be minimized.

If the slices have same pointer, then they must have same data.

That's not true. Slices can have the same pointer but different lengths, which is what appears to be happening in your error prints.

package main

import "fmt"

func main() {
	a := []int{3, 4}
	b := a[:1]
	fmt.Printf("%p %p %v %v\n", a, b, a, b)
}
0x14000110020 0x14000110020 [3 4] [3]

@xice-blue
Copy link
Author

I'm afraid from your test program it isn't obvious that there is any bug. It would help if the program could be minimized.

If the slices have same pointer, then they must have same data.

That's not true. Slices can have the same pointer but different lengths, which is what appears to be happening in your error prints.

package main

import "fmt"

func main() {
	a := []int{3, 4}
	b := a[:1]
	fmt.Printf("%p %p %v %v\n", a, b, a, b)
}
0x14000110020 0x14000110020 [3 4] [3]

There is only one place of using a[:] model, zg.dAA = append(ng.dAA[:ic], zg.dAA...), and then the slice was set to two txses, and the lenth of the backend array should be same with the slice. The propose of "func l" is to merge two slices. So that I confused why that happened when the lenth of slice should be same as the lenth of the backend array (but I cannot see the backend array).

@randall77
Copy link
Contributor

There is only one place of using a[:] model

The same behavior can happen using append instead of slicing.

package main

import "fmt"

func main() {
	a := []int{3, 4}
	b := append(a, 5)
	c := append(b, 6)
	fmt.Printf("%p %p %v %v\n", b, c, b, c)
}
0x14000126000 0x14000126000 [3 4 5] [3 4 5 6]

@xice-blue
Copy link
Author

There is only one place of using a[:] model

The same behavior can happen using append instead of slicing.

package main

import "fmt"

func main() {
	a := []int{3, 4}
	b := append(a, 5)
	c := append(b, 6)
	fmt.Printf("%p %p %v %v\n", b, c, b, c)
}
0x14000126000 0x14000126000 [3 4 5] [3 4 5 6]

For your statement, a and b are [:] model, and a is smaller. When append to b, a will not change.

func TestSlices(t *testing.T) {
	a := []int{1, 2, 3, 4, 5}
	b := a[:3]
	c := append(a[:4], 6)
	fmt.Printf("%v,%v", b, c)
}

[1 2 3],[1 2 3 4 6]PASS

This is also a different behavior with the bug code.

@randall77
Copy link
Contributor

I'm still not understanding what you think the bug is.

@xice-blue
Copy link
Author

I'm still not understanding what you think the bug is.

Whatever the behavior of slice shoud be, it must be keep the same in anywhere (except for concurrency case).
If slices are [:] model, and the lenth of them are different, then append to one should not change another, right?
If slices are full model of the backend array, they should have same value,right?
But, in the bug code, sometimes, the slices have same value, sometimes, the slices have different lenth, but after append to one, another is changed.
I cannot see the code of append, so that I don't know why the behaviors of slice in the bug code will happen.

@randall77
Copy link
Contributor

Whatever the behavior of slice shoud be, it must be keep the same in anywhere (except for concurrency case).

I think you are misunderstanding slices. Several slices can refer to the same underlying array. Modifications through one slice, including through the use of append, can be seen through the other slices.

Think of slices as pointers.

If slices are [:] model, and the lenth of them are different, then append to one should not change another, right?

Wrong.

package main

import "fmt"

func main() {
	a := []int{3, 4}
	b := append(a, 5)
	c := append(b, 6)
	fmt.Printf("%v\n", c)
	d := append(b, 7)
	fmt.Printf("%v %v\n", c, d)
}
[3 4 5 6]
[3 4 5 7] [3 4 5 7]

You probably want to read https://go.dev/blog/slices-intro.

@xice-blue
Copy link
Author

Whatever the behavior of slice shoud be, it must be keep the same in anywhere (except for concurrency case).

I think you are misunderstanding slices. Several slices can refer to the same underlying array. Modifications through one slice, including through the use of append, can be seen through the other slices.

Think of slices as pointers.

If slices are [:] model, and the lenth of them are different, then append to one should not change another, right?

Wrong.

package main

import "fmt"

func main() {
	a := []int{3, 4}
	b := append(a, 5)
	c := append(b, 6)
	fmt.Printf("%v\n", c)
	d := append(b, 7)
	fmt.Printf("%v %v\n", c, d)
}
[3 4 5 6]
[3 4 5 7] [3 4 5 7]

You probably want to read https://go.dev/blog/slices-intro.

I understood this sub slice case. But my case is not the sub slice case. I only need the 'c', so that I set the 'c' to everywhere. But the behavior of append c is different in the bug code.

@randall77
Copy link
Contributor

But my case is not the sub slice case. I only need the 'c', so that I set the 'c' to everywhere.

Normally the way to ensure that you never use the old slice is to always overwrite the slice you're appending to.
Your code does that in some cases, like

zg.dAA[zgi] = append(zg.dAA[zgi], ng.dAA[ngi]...)

But in other cases it isn't.

zg.dAA[zgi] = append(ng.dAA[ngi], zg.dAA[zgi]...)

How do you know that later on you're not appending something else to ng.dAA[ngi]?

In any case, we're going to need a more demonstrable bug reproducer to reopen this issue. Just saying "it isn't doing what I expect" could be a bug in Go or a bug in your expectations. It's hard to tell because the expected behavior of the program you posted is inscrutable.

@xice-blue
Copy link
Author

But my case is not the sub slice case. I only need the 'c', so that I set the 'c' to everywhere.

Normally the way to ensure that you never use the old slice is to always overwrite the slice you're appending to. Your code does that in some cases, like

zg.dAA[zgi] = append(zg.dAA[zgi], ng.dAA[ngi]...)

But in other cases it isn't.

zg.dAA[zgi] = append(ng.dAA[ngi], zg.dAA[zgi]...)

How do you know that later on you're not appending something else to ng.dAA[ngi]?

In any case, we're going to need a more demonstrable bug reproducer to reopen this issue. Just saying "it isn't doing what I expect" could be a bug in Go or a bug in your expectations. It's hard to tell because the expected behavior of the program you posted is inscrutable.

This is second level of slice, but the bug is for first level of slice. I have no question for one level slice, and the second level of slice has no issue.

The logic is complex, and I hope you can track the append code which I cannot track. Append code is black box for me......

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants