Skip to content

cmd/compile: generic code seems to produce duplicate type descriptor #53087

Closed
@Porges

Description

@Porges

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)
	}
}

Metadata

Metadata

Assignees

Labels

FrozenDueToAgeNeedsFixThe path to resolution is known, but the work has not been done.compiler/runtimeIssues related to the Go compiler and/or runtime.

Type

No type

Projects

Status

Done

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions