cmd/cloner: deep-clone pointer elements in map-of-slice values
The cloner's codegen for map[K][]*V fields was doing a shallow append (copying pointer values) instead of cloning each element. This meant that cloned structs aliased the original's pointed-to values through the map's slice entries. Mirror the existing standalone-slice logic that checks ContainsPointers(sliceType.Elem()) and generates per-element cloning for pointer, interface, and struct types. Regenerate net/dns and tailcfg which both had affected map[...][]*dnstype.Resolver fields. Fixes #19284 Signed-off-by: Andrew Dunham <andrew@tailscale.com>
This commit is contained in:
committed by
Andrew Dunham
parent
47ecbe5845
commit
d52ae45e9b
@@ -1,7 +1,7 @@
|
||||
// Copyright (c) Tailscale Inc & contributors
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
//go:generate go run tailscale.com/cmd/cloner -clonefunc=true -type SliceContainer,InterfaceContainer,MapWithPointers,DeeplyNestedMap,NamedMapContainer
|
||||
//go:generate go run tailscale.com/cmd/cloner -clonefunc=true -type SliceContainer,InterfaceContainer,MapWithPointers,DeeplyNestedMap,NamedMapContainer,MapSlicePointerContainer
|
||||
|
||||
// Package clonerex is an example package for the cloner tool.
|
||||
package clonerex
|
||||
@@ -60,6 +60,13 @@ type NamedMapContainer struct {
|
||||
Attrs NamedMap
|
||||
}
|
||||
|
||||
// MapSlicePointerContainer has a map whose values are slices of pointers.
|
||||
// This tests that the cloner deep-clones the pointer elements in the slice,
|
||||
// not just the slice itself (which would leave aliased pointers).
|
||||
type MapSlicePointerContainer struct {
|
||||
Routes map[string][]*SliceContainer
|
||||
}
|
||||
|
||||
// DeeplyNestedMap tests arbitrary depth of map nesting (3+ levels)
|
||||
type DeeplyNestedMap struct {
|
||||
ThreeLevels map[string]map[string]map[string]int
|
||||
|
||||
@@ -176,9 +176,41 @@ var _NamedMapContainerCloneNeedsRegeneration = NamedMapContainer(struct {
|
||||
Attrs NamedMap
|
||||
}{})
|
||||
|
||||
// Clone makes a deep copy of MapSlicePointerContainer.
|
||||
// The result aliases no memory with the original.
|
||||
func (src *MapSlicePointerContainer) Clone() *MapSlicePointerContainer {
|
||||
if src == nil {
|
||||
return nil
|
||||
}
|
||||
dst := new(MapSlicePointerContainer)
|
||||
*dst = *src
|
||||
if dst.Routes != nil {
|
||||
dst.Routes = map[string][]*SliceContainer{}
|
||||
for k, sv := range src.Routes {
|
||||
if sv == nil {
|
||||
continue
|
||||
}
|
||||
dst.Routes[k] = make([]*SliceContainer, len(sv))
|
||||
for i := range sv {
|
||||
if sv[i] == nil {
|
||||
dst.Routes[k][i] = nil
|
||||
} else {
|
||||
dst.Routes[k][i] = sv[i].Clone()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return dst
|
||||
}
|
||||
|
||||
// A compilation failure here means this code must be regenerated, with the command at the top of this file.
|
||||
var _MapSlicePointerContainerCloneNeedsRegeneration = MapSlicePointerContainer(struct {
|
||||
Routes map[string][]*SliceContainer
|
||||
}{})
|
||||
|
||||
// Clone duplicates src into dst and reports whether it succeeded.
|
||||
// To succeed, <src, dst> must be of types <*T, *T> or <*T, **T>,
|
||||
// where T is one of SliceContainer,InterfaceContainer,MapWithPointers,DeeplyNestedMap,NamedMapContainer.
|
||||
// where T is one of SliceContainer,InterfaceContainer,MapWithPointers,DeeplyNestedMap,NamedMapContainer,MapSlicePointerContainer.
|
||||
func Clone(dst, src any) bool {
|
||||
switch src := src.(type) {
|
||||
case *SliceContainer:
|
||||
@@ -226,6 +258,15 @@ func Clone(dst, src any) bool {
|
||||
*dst = src.Clone()
|
||||
return true
|
||||
}
|
||||
case *MapSlicePointerContainer:
|
||||
switch dst := dst.(type) {
|
||||
case *MapSlicePointerContainer:
|
||||
*dst = *src.Clone()
|
||||
return true
|
||||
case **MapSlicePointerContainer:
|
||||
*dst = src.Clone()
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user