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.Cloned 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)
}
}
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 envOutputWhat 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:
The expectation is that this prints
truefour 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:
The implementation of
Withlooks like this:I narrowed down the problem to
maps.Clone. If I copy it as a local function then this continues to fail:However, if the
Mparameter is replaced withmap[K]Vdirectly, then it works as expected: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.Cloned is the one that fails to cast correctly.Repro
Switch between
CloneBad(a copy ofmaps.Clone) andCloneGoodto see the behaviour change.