@@ -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.
210213func Cast [T2 , T1 any ](v * T1 ) * T2 {
211214 return (* T2 )(unsafe .Pointer (v ))
212215}
0 commit comments