Skip to content

Commit 1700898

Browse files
Tweak the Cast safety doc comment
1 parent 7c9ab2a commit 1700898

2 files changed

Lines changed: 15 additions & 12 deletions

File tree

command.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -325,8 +325,8 @@ func (cmd *Command) flagSet() *flag.Set {
325325

326326
// root returns the root of the command tree.
327327
func (cmd *Command) root() *Command {
328-
if cmd.parent != nil {
329-
return cmd.parent.root()
328+
for cmd.parent != nil {
329+
cmd = cmd.parent
330330
}
331331

332332
return cmd

internal/parse/parse.go

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -194,19 +194,22 @@ func Float64(str string) (float64, error) {
194194
//
195195
// # Safety
196196
//
197-
// This function uses [unsafe.Pointer] underneath to reassign the types but we know this is safe to do
198-
// based on the compile time checks provided by generics. Further, it fits the following valid pattern
199-
// specified in the docs for [unsafe.Pointer].
197+
// Cast uses [unsafe.Pointer] to reinterpret *T1 as *T2. The type parameters are
198+
// both [any]. The compiler does not enforce that T1 and T2 are layout-compatible,
199+
// so callers are fully responsible for that invariant.
200200
//
201-
// Conversion of a *T1 to Pointer to *T2
201+
// All current call sites follow the same pattern: within a type switch on
202+
// any(*f.value) (where f.value is *T for some [flag.Flaggable] T), the matched
203+
// case has already proven the dynamic type, so *T and *Matched point to
204+
// bit-identical memory. The unsafe.Pointer round-trip is a no-op at runtime, it
205+
// exists solely because Go does not let us assign across generic type parameters
206+
// without going through [any], which would allocate.
202207
//
203-
// Provided that T2 is no larger than T1 and that the two share an equivalent
204-
// memory layout, this conversion allows reinterpreting data of one type as
205-
// data of another type.
208+
// This matches the [unsafe.Pointer] rule "(1) Conversion of a *T1 to Pointer to
209+
// *T2": T2 is no larger than T1 and shares an equivalent memory layout.
206210
//
207-
// This describes our use case as we're converting a *T to e.g a *string but *only* when we know
208-
// that a Flag[T] is actually Flag[string], so the memory layout and size is guaranteed by the
209-
// compiler to be equivalent.
211+
// If a new call site is added, verify the type-switch invariant holds. Cast
212+
// outside that pattern is undefined behaviour.
210213
func Cast[T2, T1 any](v *T1) *T2 {
211214
return (*T2)(unsafe.Pointer(v))
212215
}

0 commit comments

Comments
 (0)