]> Cypherpunks repositories - gostls13.git/commitdiff
[release-branch.go1.25] cmd/compile: prevent shapifying of pointer shape type
authorCuong Manh Le <cuong.manhle.vn@gmail.com>
Mon, 15 Sep 2025 10:31:46 +0000 (17:31 +0700)
committerMichael Knyszek <mknyszek@google.com>
Mon, 27 Oct 2025 16:44:24 +0000 (09:44 -0700)
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 <cuong.manhle.vn@gmail.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: David Chase <drchase@google.com>
Reviewed-by: Junyang Shao <shaojunyang@google.com>
Reviewed-on: https://go-review.googlesource.com/c/go/+/706216
Reviewed-by: Ed Schouten <ed@nuxi.nl>
Reviewed-by: Cherry Mui <cherryyz@google.com>
src/cmd/compile/internal/noder/reader.go
src/cmd/compile/testdata/script/issue75461.txt [new file with mode: 0644]

index 38b0bc1d8a41530fb710fdc7ad04cb683b27ba62..7256801965cc4447fd6f74602ece713276913c86 100644 (file)
@@ -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 (file)
index 0000000..05f0fd4
--- /dev/null
@@ -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](),
+       }
+}