From bbb76271230a801e085bb813e82fd40000b65367 Mon Sep 17 00:00:00 2001 From: Cuong Manh Le Date: Mon, 15 Sep 2025 17:31:46 +0700 Subject: [PATCH] [release-branch.go1.25] cmd/compile: prevent shapifying of pointer shape type CL 641955 changes the Unified IR reader to not doing shapify when reading reshaping expression, prevent losing of the original type. This is an oversight, as the main problem isn't about shaping during the reshaping process itself, but about the specific case of shaping a pointer shape type. This bug occurs when instantiating a generic function within another generic function with a pointer shape type as type parameter, which will convert `*[]go.shape.T` to `*go.shape.uint8`, resulting in the loss of the original expression's type. This commit changes Unified IR reader to avoid pointer shaping for `*[]go.shape.T`, ensures that the original type is preserved when processing reshaping expressions. Fixes #75480 Change-Id: Icede6b73247d0d367bb485619f2dafb60ad66806 Reviewed-on: https://go-review.googlesource.com/c/go/+/704095 Auto-Submit: Cuong Manh Le LUCI-TryBot-Result: Go LUCI Reviewed-by: David Chase Reviewed-by: Junyang Shao Reviewed-on: https://go-review.googlesource.com/c/go/+/706216 Reviewed-by: Ed Schouten Reviewed-by: Cherry Mui --- src/cmd/compile/internal/noder/reader.go | 46 ++++------- .../compile/testdata/script/issue75461.txt | 78 +++++++++++++++++++ 2 files changed, 91 insertions(+), 33 deletions(-) create mode 100644 src/cmd/compile/testdata/script/issue75461.txt diff --git a/src/cmd/compile/internal/noder/reader.go b/src/cmd/compile/internal/noder/reader.go index 38b0bc1d8a..7256801965 100644 --- a/src/cmd/compile/internal/noder/reader.go +++ b/src/cmd/compile/internal/noder/reader.go @@ -49,9 +49,6 @@ type pkgReader struct { // but bitwise inverted so we can detect if we're missing the entry // or not. newindex []index - - // indicates whether the data is reading during reshaping. - reshaping bool } func newPkgReader(pr pkgbits.PkgDecoder) *pkgReader { @@ -119,10 +116,6 @@ type reader struct { // find parameters/results. funarghack bool - // reshaping is used during reading exprReshape code, preventing - // the reader from shapifying the re-shaped type. - reshaping bool - // methodSym is the name of method's name, if reading a method. // It's nil if reading a normal function or closure body. methodSym *types.Sym @@ -937,8 +930,19 @@ func shapify(targ *types.Type, basic bool) *types.Type { // types, and discarding struct field names and tags. However, we'll // need to start tracking how type parameters are actually used to // implement some of these optimizations. + pointerShaping := basic && targ.IsPtr() && !targ.Elem().NotInHeap() + // The exception is when the type parameter is a pointer to a type + // which `Type.HasShape()` returns true, but `Type.IsShape()` returns + // false, like `*[]go.shape.T`. This is because the type parameter is + // used to instantiate a generic function inside another generic function. + // In this case, we want to keep the targ as-is, otherwise, we may lose the + // original type after `*[]go.shape.T` is shapified to `*go.shape.uint8`. + // See issue #54535, #71184. + if pointerShaping && !targ.Elem().IsShape() && targ.Elem().HasShape() { + return targ + } under := targ.Underlying() - if basic && targ.IsPtr() && !targ.Elem().NotInHeap() { + if pointerShaping { under = types.NewPtr(types.Types[types.TUINT8]) } @@ -1014,25 +1018,7 @@ func (pr *pkgReader) objDictIdx(sym *types.Sym, idx index, implicits, explicits // arguments. for i, targ := range dict.targs { basic := r.Bool() - isPointerShape := basic && targ.IsPtr() && !targ.Elem().NotInHeap() - // We should not do shapify during the reshaping process, see #71184. - // However, this only matters for shapify a pointer type, which will - // lose the original underlying type. - // - // Example with a pointer type: - // - // - First, shapifying *[]T -> *uint8 - // - During the reshaping process, *uint8 is shapified to *go.shape.uint8 - // - This ends up with a different type with the original *[]T - // - // For a non-pointer type: - // - // - int -> go.shape.int - // - go.shape.int -> go.shape.int - // - // We always end up with the identical type. - canShapify := !pr.reshaping || !isPointerShape - if dict.shaped && canShapify { + if dict.shaped { dict.targs[i] = shapify(targ, basic) } } @@ -2470,10 +2456,7 @@ func (r *reader) expr() (res ir.Node) { case exprReshape: typ := r.typ() - old := r.reshaping - r.reshaping = true x := r.expr() - r.reshaping = old if types.IdenticalStrict(x.Type(), typ) { return x @@ -2596,10 +2579,7 @@ func (r *reader) funcInst(pos src.XPos) (wrapperFn, baseFn, dictPtr ir.Node) { info := r.dict.subdicts[idx] explicits := r.p.typListIdx(info.explicits, r.dict) - old := r.p.reshaping - r.p.reshaping = r.reshaping baseFn = r.p.objIdx(info.idx, implicits, explicits, true).(*ir.Name) - r.p.reshaping = old // TODO(mdempsky): Is there a more robust way to get the // dictionary pointer type here? diff --git a/src/cmd/compile/testdata/script/issue75461.txt b/src/cmd/compile/testdata/script/issue75461.txt new file mode 100644 index 0000000000..05f0fd4cfa --- /dev/null +++ b/src/cmd/compile/testdata/script/issue75461.txt @@ -0,0 +1,78 @@ +go build main.go +! stdout . +! stderr . + +-- main.go -- +package main + +import ( + "demo/registry" +) + +func main() { + _ = registry.NewUserRegistry() +} + +-- go.mod -- +module demo + +go 1.24 + +-- model/user.go -- +package model + +type User struct { + ID int +} + +func (c *User) String() string { + return "" +} + +-- ordered/map.go -- +package ordered + +type OrderedMap[K comparable, V any] struct { + m map[K]V +} + +func New[K comparable, V any](options ...any) *OrderedMap[K, V] { + orderedMap := &OrderedMap[K, V]{} + return orderedMap +} + +-- registry/user.go -- +package registry + +import ( + "demo/model" + "demo/ordered" +) + +type baseRegistry = Registry[model.User, *model.User] + +type UserRegistry struct { + *baseRegistry +} + +type Registry[T any, P PStringer[T]] struct { + m *ordered.OrderedMap[string, P] +} + +type PStringer[T any] interface { + *T + String() string +} + +func NewRegistry[T any, P PStringer[T]]() *Registry[T, P] { + r := &Registry[T, P]{ + m: ordered.New[string, P](), + } + return r +} + +func NewUserRegistry() *UserRegistry { + return &UserRegistry{ + baseRegistry: NewRegistry[model.User](), + } +} -- 2.52.0