mirror of https://github.com/golang/go.git
cmd/compile: fix generic type handling when crawling inline body
For base generic type that is written to export file, we need to mark all of its methods, include exported+unexported methods, as reachable, so they can be available for instantiation if necessary. But markType only looks for exported methods, thus causing the crash in #49143. To fix this, we introduce new method p.markGeneric, to mark all methods of the base generic type. This issue has happend for a while (maybe since we add generic import/export during go1.18 cycle), and was un-intentionally "fixed" in CL 356254, when we agresssively call p.markEmbed(t). CL 357232 fixed that wrong agressive behavior, thus reproduce the bug on tip. Fixes #49143 Change-Id: Ie64574a05fffb282e9dcc8739df4378c5b6b0468 Reviewed-on: https://go-review.googlesource.com/c/go/+/358814 Trust: Cuong Manh Le <cuong.manhle.vn@gmail.com> Trust: Dan Scales <danscales@google.com> Run-TryBot: Cuong Manh Le <cuong.manhle.vn@gmail.com> TryBot-Result: Go Bot <gobot@golang.org> Reviewed-by: Keith Randall <khr@golang.org> Reviewed-by: Dan Scales <danscales@google.com>
This commit is contained in:
parent
13eccaa990
commit
ca5f65d771
|
|
@ -20,6 +20,7 @@ func crawlExports(exports []*ir.Name) {
|
||||||
p := crawler{
|
p := crawler{
|
||||||
marked: make(map[*types.Type]bool),
|
marked: make(map[*types.Type]bool),
|
||||||
embedded: make(map[*types.Type]bool),
|
embedded: make(map[*types.Type]bool),
|
||||||
|
generic: make(map[*types.Type]bool),
|
||||||
}
|
}
|
||||||
for _, n := range exports {
|
for _, n := range exports {
|
||||||
p.markObject(n)
|
p.markObject(n)
|
||||||
|
|
@ -29,6 +30,7 @@ func crawlExports(exports []*ir.Name) {
|
||||||
type crawler struct {
|
type crawler struct {
|
||||||
marked map[*types.Type]bool // types already seen by markType
|
marked map[*types.Type]bool // types already seen by markType
|
||||||
embedded map[*types.Type]bool // types already seen by markEmbed
|
embedded map[*types.Type]bool // types already seen by markEmbed
|
||||||
|
generic map[*types.Type]bool // types already seen by markGeneric
|
||||||
}
|
}
|
||||||
|
|
||||||
// markObject visits a reachable object (function, method, global type, or global variable)
|
// markObject visits a reachable object (function, method, global type, or global variable)
|
||||||
|
|
@ -168,6 +170,30 @@ func (p *crawler) markEmbed(t *types.Type) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// markGeneric takes an instantiated type or a base generic type t, and
|
||||||
|
// marks all the methods of the base generic type of t. If a base generic
|
||||||
|
// type is written to export file, even if not explicitly marked for export,
|
||||||
|
// all of its methods need to be available for instantiation if needed.
|
||||||
|
func (p *crawler) markGeneric(t *types.Type) {
|
||||||
|
if t.IsPtr() {
|
||||||
|
t = t.Elem()
|
||||||
|
}
|
||||||
|
if t.OrigSym() != nil {
|
||||||
|
// Convert to the base generic type.
|
||||||
|
t = t.OrigSym().Def.Type()
|
||||||
|
}
|
||||||
|
if p.generic[t] {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
p.generic[t] = true
|
||||||
|
|
||||||
|
if t.Sym() != nil && t.Kind() != types.TINTER {
|
||||||
|
for _, m := range t.Methods().Slice() {
|
||||||
|
p.markObject(m.Nname.(*ir.Name))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// markInlBody marks n's inline body for export and recursively
|
// markInlBody marks n's inline body for export and recursively
|
||||||
// ensures all called functions are marked too.
|
// ensures all called functions are marked too.
|
||||||
func (p *crawler) markInlBody(n *ir.Name) {
|
func (p *crawler) markInlBody(n *ir.Name) {
|
||||||
|
|
@ -197,12 +223,7 @@ func (p *crawler) markInlBody(n *ir.Name) {
|
||||||
t := n.Type()
|
t := n.Type()
|
||||||
if t != nil {
|
if t != nil {
|
||||||
if t.HasTParam() || t.IsFullyInstantiated() {
|
if t.HasTParam() || t.IsFullyInstantiated() {
|
||||||
// Ensure that we call markType() on any base generic type
|
p.markGeneric(t)
|
||||||
// that is written to the export file (even if not explicitly
|
|
||||||
// marked for export), so we will call markInlBody on its
|
|
||||||
// methods, and the methods will be available for
|
|
||||||
// instantiation if needed.
|
|
||||||
p.markType(t)
|
|
||||||
}
|
}
|
||||||
if base.Debug.Unified == 0 {
|
if base.Debug.Unified == 0 {
|
||||||
// If a method of un-exported type is promoted and accessible by
|
// If a method of un-exported type is promoted and accessible by
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,24 @@
|
||||||
|
// Copyright 2021 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package a
|
||||||
|
|
||||||
|
import "sync"
|
||||||
|
|
||||||
|
type Loader[K comparable, R any] struct {
|
||||||
|
batch *LoaderBatch[K, R]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *Loader[K, R]) Load() error {
|
||||||
|
l.batch.f()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type LoaderBatch[K comparable, R any] struct {
|
||||||
|
once *sync.Once
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *LoaderBatch[K, R]) f() {
|
||||||
|
b.once.Do(func() {})
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,16 @@
|
||||||
|
// Copyright 2021 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package b
|
||||||
|
|
||||||
|
import "./a"
|
||||||
|
|
||||||
|
|
||||||
|
type Loaders struct {
|
||||||
|
Loader *a.Loader[int, int]
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewLoaders() *Loaders {
|
||||||
|
return new(Loaders)
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,15 @@
|
||||||
|
// Copyright 2021 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package c
|
||||||
|
|
||||||
|
import "./b"
|
||||||
|
|
||||||
|
type Resolver struct{}
|
||||||
|
|
||||||
|
type todoResolver struct{ *Resolver }
|
||||||
|
|
||||||
|
func (r *todoResolver) F() {
|
||||||
|
b.NewLoaders().Loader.Load()
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
// Copyright 2021 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package p
|
||||||
|
|
||||||
|
import (
|
||||||
|
"./c"
|
||||||
|
)
|
||||||
|
|
||||||
|
var _ = &c.Resolver{}
|
||||||
|
|
@ -0,0 +1,7 @@
|
||||||
|
// compiledir -G=3
|
||||||
|
|
||||||
|
// Copyright 2021 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package ignored
|
||||||
Loading…
Reference in New Issue