types/ptr: deprecate ptr.To, use Go 1.26 new

Updates #18682

Change-Id: I62f6aa0de2a15ef8c1435032c6aa74a181c25f8f
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
This commit is contained in:
Brad Fitzpatrick
2026-03-05 22:48:46 +00:00
committed by Brad Fitzpatrick
parent 8cfbaa717d
commit 2a64c03c95
96 changed files with 429 additions and 532 deletions
+1 -2
View File
@@ -10,7 +10,6 @@ import (
"github.com/go-json-experiment/json"
"github.com/go-json-experiment/json/jsontext"
"github.com/google/go-cmp/cmp"
"tailscale.com/types/ptr"
)
type Interface interface {
@@ -72,7 +71,7 @@ func TestInterfaceCoders(t *testing.T) {
wantJSON: `{"Foo":"hello"}`,
}, {
label: "BarPointer",
wantVal: InterfaceWrapper{ptr.To(Bar(5))},
wantVal: InterfaceWrapper{new(Bar(5))},
wantJSON: `{"Bar":5}`,
}, {
label: "BarValue",
+1 -3
View File
@@ -6,8 +6,6 @@ package lazy
import (
"sync"
"sync/atomic"
"tailscale.com/types/ptr"
)
// DeferredInit allows one or more funcs to be deferred
@@ -91,7 +89,7 @@ func (d *DeferredInit) doSlow() (err *error) {
}()
for _, f := range d.funcs {
if err := f(); err != nil {
return ptr.To(err)
return new(err)
}
}
return nilErrPtr
+3 -5
View File
@@ -7,13 +7,11 @@ package lazy
import (
"sync"
"sync/atomic"
"tailscale.com/types/ptr"
)
// nilErrPtr is a sentinel *error value for SyncValue.err to signal
// that SyncValue.v is valid.
var nilErrPtr = ptr.To[error](nil)
var nilErrPtr = new(error(nil))
// SyncValue is a lazily computed value.
//
@@ -80,7 +78,7 @@ func (z *SyncValue[T]) GetErr(fill func() (T, error)) (T, error) {
// Update z.err after z.v; see field docs.
if err != nil {
z.err.Store(ptr.To(err))
z.err.Store(new(err))
} else {
z.err.Store(nilErrPtr)
}
@@ -145,7 +143,7 @@ func (z *SyncValue[T]) SetForTest(tb testing_TB, val T, err error) {
z.v = val
if err != nil {
z.err.Store(ptr.To(err))
z.err.Store(new(err))
} else {
z.err.Store(nilErrPtr)
}
+4 -5
View File
@@ -12,7 +12,6 @@ import (
"time"
"tailscale.com/tailcfg"
"tailscale.com/types/ptr"
)
// NodeMutation is the common interface for types that describe
@@ -55,7 +54,7 @@ type NodeMutationOnline struct {
}
func (m NodeMutationOnline) Apply(n *tailcfg.Node) {
n.Online = ptr.To(m.Online)
n.Online = new(m.Online)
}
// NodeMutationLastSeen is a NodeMutation that says a node's LastSeen
@@ -66,14 +65,14 @@ type NodeMutationLastSeen struct {
}
func (m NodeMutationLastSeen) Apply(n *tailcfg.Node) {
n.LastSeen = ptr.To(m.LastSeen)
n.LastSeen = new(m.LastSeen)
}
var peerChangeFields = sync.OnceValue(func() []reflect.StructField {
var fields []reflect.StructField
rt := reflect.TypeFor[tailcfg.PeerChange]()
for i := range rt.NumField() {
fields = append(fields, rt.Field(i))
for field := range rt.Fields() {
fields = append(fields, field)
}
return fields
})
+3 -4
View File
@@ -14,7 +14,6 @@ import (
"tailscale.com/tailcfg"
"tailscale.com/types/logger"
"tailscale.com/types/opt"
"tailscale.com/types/ptr"
)
// tests mapResponseContainsNonPatchFields
@@ -117,7 +116,7 @@ func TestMutationsFromMapResponse(t *testing.T) {
name: "patch-online",
mr: fromChanges(&tailcfg.PeerChange{
NodeID: 1,
Online: ptr.To(true),
Online: new(true),
}),
want: muts(NodeMutationOnline{1, true}),
},
@@ -125,7 +124,7 @@ func TestMutationsFromMapResponse(t *testing.T) {
name: "patch-online-false",
mr: fromChanges(&tailcfg.PeerChange{
NodeID: 1,
Online: ptr.To(false),
Online: new(false),
}),
want: muts(NodeMutationOnline{1, false}),
},
@@ -133,7 +132,7 @@ func TestMutationsFromMapResponse(t *testing.T) {
name: "patch-lastseen",
mr: fromChanges(&tailcfg.PeerChange{
NodeID: 1,
LastSeen: ptr.To(time.Unix(12345, 0)),
LastSeen: new(time.Unix(12345, 0)),
}),
want: muts(NodeMutationLastSeen{1, time.Unix(12345, 0)}),
},
+1 -2
View File
@@ -9,7 +9,6 @@ import (
jsonv2 "github.com/go-json-experiment/json"
"github.com/go-json-experiment/json/jsontext"
"tailscale.com/types/opt"
"tailscale.com/types/ptr"
"tailscale.com/types/views"
"tailscale.com/util/must"
)
@@ -47,7 +46,7 @@ func (i *Item[T]) SetManagedValue(val T) {
// It is a runtime error to call [Item.Clone] if T contains pointers
// but does not implement [views.Cloner].
func (i Item[T]) Clone() *Item[T] {
res := ptr.To(i)
res := new(i)
if v, ok := i.ValueOk(); ok {
res.s.Value.Set(must.Get(deepClone(v)))
}
+1 -2
View File
@@ -12,7 +12,6 @@ import (
"github.com/go-json-experiment/json/jsontext"
"golang.org/x/exp/constraints"
"tailscale.com/types/opt"
"tailscale.com/types/ptr"
"tailscale.com/types/views"
)
@@ -62,7 +61,7 @@ func (ls *List[T]) View() ListView[T] {
// Clone returns a copy of l that aliases no memory with l.
func (ls List[T]) Clone() *List[T] {
res := ptr.To(ls)
res := new(ls)
if v, ok := ls.s.Value.GetOk(); ok {
res.s.Value.Set(append(v[:0:0], v...))
}
+1 -2
View File
@@ -11,7 +11,6 @@ import (
"github.com/go-json-experiment/json/jsontext"
"golang.org/x/exp/constraints"
"tailscale.com/types/opt"
"tailscale.com/types/ptr"
"tailscale.com/types/views"
)
@@ -44,7 +43,7 @@ func (m *Map[K, V]) View() MapView[K, V] {
// Clone returns a copy of m that aliases no memory with m.
func (m Map[K, V]) Clone() *Map[K, V] {
res := ptr.To(m)
res := new(m)
if v, ok := m.s.Value.GetOk(); ok {
res.s.Value.Set(maps.Clone(v))
}
+1 -3
View File
@@ -7,8 +7,6 @@ package prefs
import (
"net/netip"
"tailscale.com/types/ptr"
)
// Clone makes a deep copy of TestPrefs.
@@ -67,7 +65,7 @@ func (src *TestBundle) Clone() *TestBundle {
dst := new(TestBundle)
*dst = *src
if dst.Nested != nil {
dst.Nested = ptr.To(*src.Nested)
dst.Nested = new(*src.Nested)
}
return dst
}
+1 -2
View File
@@ -11,7 +11,6 @@ import (
jsonv2 "github.com/go-json-experiment/json"
"github.com/go-json-experiment/json/jsontext"
"tailscale.com/types/opt"
"tailscale.com/types/ptr"
"tailscale.com/types/views"
)
@@ -45,7 +44,7 @@ func (ls *StructList[T]) SetManagedValue(val []T) {
// Clone returns a copy of l that aliases no memory with l.
func (ls StructList[T]) Clone() *StructList[T] {
res := ptr.To(ls)
res := new(ls)
if v, ok := ls.s.Value.GetOk(); ok {
res.s.Value.Set(deepCloneSlice(v))
}
+1 -2
View File
@@ -9,7 +9,6 @@ import (
jsonv2 "github.com/go-json-experiment/json"
"github.com/go-json-experiment/json/jsontext"
"tailscale.com/types/opt"
"tailscale.com/types/ptr"
"tailscale.com/types/views"
)
@@ -43,7 +42,7 @@ func (m *StructMap[K, V]) SetManagedValue(val map[K]V) {
// Clone returns a copy of m that aliases no memory with m.
func (m StructMap[K, V]) Clone() *StructMap[K, V] {
res := ptr.To(m)
res := new(m)
if v, ok := m.s.Value.GetOk(); ok {
res.s.Value.Set(deepCloneMap(v))
}
+10 -1
View File
@@ -2,9 +2,18 @@
// SPDX-License-Identifier: BSD-3-Clause
// Package ptr contains the ptr.To function.
//
// Deprecated: Use Go 1.26's new(value) expression instead.
// See https://go.dev/doc/go1.26#language.
package ptr
// To returns a pointer to a shallow copy of v.
//
// Deprecated: Use Go 1.26's new(value) expression instead.
// For example, ptr.To(42) can be written as new(42).
// See https://go.dev/doc/go1.26#language.
//
//go:fix inline
func To[T any](v T) *T {
return &v
return new(v)
}
+1 -2
View File
@@ -19,7 +19,6 @@ import (
jsonv2 "github.com/go-json-experiment/json"
"github.com/go-json-experiment/json/jsontext"
"go4.org/mem"
"tailscale.com/types/ptr"
)
// ByteSlice is a read-only accessor for types that are backed by a []byte.
@@ -901,7 +900,7 @@ func (p ValuePointer[T]) Clone() *T {
if p.ж == nil {
return nil
}
return ptr.To(*p.ж)
return new(*p.ж)
}
// String implements [fmt.Stringer].