Description
What version of Go are you using (go version
)?
Tested on 1.18 and 1.18.2.
Does this issue reproduce with the latest release?
Yes.
What operating system and processor architecture are you using (go env
)?
go env
Output
$ go env GO111MODULE="auto" GOARCH="amd64" GOBIN="" GOCACHE="/home/vscode/.cache/go-build" GOENV="/home/vscode/.config/go/env" GOEXE="" GOEXPERIMENT="" GOFLAGS="" GOHOSTARCH="amd64" GOHOSTOS="linux" GOINSECURE="" GOMODCACHE="/go/pkg/mod" GONOPROXY="" GONOSUMDB="" GOOS="linux" GOPATH="/go" GOPRIVATE="" GOPROXY="https://proxy.golang.org,direct" GOROOT="/usr/local/go" GOSUMDB="sum.golang.org" GOTMPDIR="" GOTOOLDIR="/usr/local/go/pkg/tool/linux_amd64" GOVCS="" GOVERSION="go1.18.2" GCCGO="gccgo" GOAMD64="v1" AR="ar" CC="gcc" CXX="g++" CGO_ENABLED="1" GOMOD="/workspaces/azure-service-operator/v2/tools/generator/go.mod" GOWORK="" CGO_CFLAGS="-g -O2" CGO_CPPFLAGS="" CGO_CXXFLAGS="-g -O2" CGO_FFLAGS="-g -O2" CGO_LDFLAGS="-g -O2" PKG_CONFIG="pkg-config" GOGCCFLAGS="-fPIC -m64 -pthread -fmessage-length=0 -fdebug-prefix-map=/tmp/go-build1336644465=/tmp/go-build -gno-record-gcc-switches"
What did you do?
After using maps.Clone
, values retrieved from the (copied) map fail to typecast correctly.
I’m still working on a minimal repro, but here is what I’m seeing.
See small repro below.
The original code in question looks like this:
var fromF astmodel.Function = convertFrom
_, ok := fromF.(*functions.PropertyAssignmentFunction)
fmt.Printf("from ok: %v\n", ok)
var toF astmodel.Function = convertTo
_, ok = toF.(*functions.PropertyAssignmentFunction)
fmt.Printf("to ok: %v\n", ok)
m := readonly.EmptyMap[string, astmodel.Function]()
m = m.With("a", convertFrom)
m = m.With("b", convertTo)
it, found := m.Get("a")
if found {
_, ok = it.(*functions.PropertyAssignmentFunction)
fmt.Printf("from ok: %v\n", ok)
}
it, found = m.Get("b")
if found {
_, ok = it.(*functions.PropertyAssignmentFunction)
fmt.Printf("to ok: %v\n", ok)
}
The expectation is that this prints true
four times as the values retrieved from the map are the same as those put into it. However, it does not; the first value retrieved from the map fails to cast correctly.
Output:
from ok: true
to ok: true
from ok: false
to ok: true
The implementation of With
looks like this:
func (m Map[K, V]) With(key K, value V) Map[K, V] {
result := maps.Clone(m.inner)
result[key] = value
return CreateMapUnsafe(result)
}
I narrowed down the problem to maps.Clone
. If I copy it as a local function then this continues to fail:
func CloneBad[M ~map[K]V, K comparable, V any](m M) M {
r := make(M, len(m))
for k, v := range m {
r[k] = v
}
return r
}
However, if the M
parameter is replaced with map[K]V
directly, then it works as expected:
func CloneGood[K comparable, V any](m map[K]V) map[K]V {
r := make(map[K]V, len(m))
for k, v := range m {
r[k] = v
}
return r
}
Note that this also happens if the same value is input as both "a" and "b"; whichever value was in the map before it was maps.Clone
d is the one that fails to cast correctly.
Repro
Switch between CloneBad
(a copy of maps.Clone
) and CloneGood
to see the behaviour change.
package main
import (
"fmt"
)
type I interface {
Run() string
}
type S struct {
str string
}
func (s *S) Run() string {
return s.str
}
var _ I = &S{}
type CloningMap[K comparable, V any] struct {
inner map[K]V
}
func (cm CloningMap[K, V]) With(key K, value V) CloningMap[K, V] {
result := CloneBad(cm.inner)
result[key] = value
return CloningMap[K, V]{result}
}
func CloneGood[K comparable, V any](m map[K]V) map[K]V {
r := make(map[K]V, len(m))
for k, v := range m {
r[k] = v
}
return r
}
func CloneBad[M ~map[K]V, K comparable, V any](m M) M {
r := make(M, len(m))
for k, v := range m {
r[k] = v
}
return r
}
func main() {
s1 := &S{"one"}
s2 := &S{"two"}
m := CloningMap[string, I]{inner: make(map[string]I)}
m = m.With("a", s1)
m = m.With("b", s2)
it, found := m.inner["a"]
if found {
_, ok := it.(*S)
fmt.Printf("from ok: %v\n", ok)
}
it, found = m.inner["b"]
if found {
_, ok := it.(*S)
fmt.Printf("to ok: %v\n", ok)
}
}
Metadata
Metadata
Assignees
Labels
Type
Projects
Status