mirror of
https://github.com/golang/go.git
synced 2026-02-06 02:45:06 +03:00
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. Updates #71184 Updates #73947 Fixes #74260 Fixes #75461 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>
This commit is contained in:
committed by
Gopher Robot
parent
a27261c42f
commit
a5866ebe40
@@ -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?
|
||||
|
||||
78
src/cmd/compile/testdata/script/issue75461.txt
vendored
Normal file
78
src/cmd/compile/testdata/script/issue75461.txt
vendored
Normal file
@@ -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](),
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user