mirror of
https://github.com/golang/go
synced 2024-09-04 23:44:16 +00:00
[dev.typeparams] cmd/compile: fix crawling of embeddable types
In reflectdata, we have a hack to only apply inlining for (*T).M wrappers generated around T.M. This was a hack because I didn't understand at the time why other cases were failing. But I understand now: during export, we generally skip exporting the inline bodies for unexported methods (unless they're reachable through some other exported method). But it doesn't take into account that embedding a type requires generating wrappers for promoted methods, including imported, unexported methods. For example: package a type T struct{} func (T) m() {} // previously omitted by exported package b import "./a" type U struct { a.T } // needs U.m -> T.m wrapper This CL adds extra logic to the crawler to recognize that T is an exported type directly reachable by the user, so *all* of its methods need to be re-exported. This finally allows simplifying reflectdata.methodWrapper to always call inline.InlineCalls. Change-Id: I25031d41fd6b6cd69d31c6a864b5329cdb5780e2 Reviewed-on: https://go-review.googlesource.com/c/go/+/327872 Trust: Matthew Dempsky <mdempsky@google.com> Reviewed-by: Cuong Manh Le <cuong.manhle.vn@gmail.com>
This commit is contained in:
parent
8f95eaddd3
commit
132ea56d29
|
@ -1795,20 +1795,24 @@ func methodWrapper(rcvr *types.Type, method *types.Field, forItab bool) *obj.LSy
|
||||||
return lsym
|
return lsym
|
||||||
}
|
}
|
||||||
|
|
||||||
// Only generate (*T).M wrappers for T.M in T's own package, except for
|
// imported reports whether typ is a defined type that was declared
|
||||||
// instantiated methods.
|
// in an imported package, and therefore must have been compiled in
|
||||||
if rcvr.IsPtr() && rcvr.Elem() == method.Type.Recv().Type &&
|
// that package.
|
||||||
rcvr.Elem().Sym() != nil && rcvr.Elem().Sym().Pkg != types.LocalPkg &&
|
importedType := func(typ *types.Type) bool {
|
||||||
!rcvr.Elem().IsFullyInstantiated() {
|
return typ.Sym() != nil && typ.Sym().Pkg != types.LocalPkg &&
|
||||||
return lsym
|
|
||||||
|
// Exception: need wrapper for error.Error (#29304).
|
||||||
|
// TODO(mdempsky): Put this in package runtime, like we do for
|
||||||
|
// the type descriptors for predeclared types.
|
||||||
|
typ != types.ErrorType &&
|
||||||
|
|
||||||
|
// Exception: parameterized types may have been instantiated
|
||||||
|
// with new type arguments, so we don't assume they've been
|
||||||
|
// compiled before.
|
||||||
|
!typ.IsFullyInstantiated()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Only generate I.M wrappers for I in I's own package
|
if importedType(rcvr) || rcvr.IsPtr() && importedType(rcvr.Elem()) {
|
||||||
// but keep doing it for error.Error (was issue #29304)
|
|
||||||
// and methods of instantiated interfaces.
|
|
||||||
if rcvr.IsInterface() && rcvr != types.ErrorType &&
|
|
||||||
rcvr.Sym() != nil && rcvr.Sym().Pkg != types.LocalPkg &&
|
|
||||||
!rcvr.IsFullyInstantiated() {
|
|
||||||
return lsym
|
return lsym
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1922,9 +1926,16 @@ func methodWrapper(rcvr *types.Type, method *types.Field, forItab bool) *obj.LSy
|
||||||
ir.CurFunc = fn
|
ir.CurFunc = fn
|
||||||
typecheck.Stmts(fn.Body)
|
typecheck.Stmts(fn.Body)
|
||||||
|
|
||||||
// Inline calls within (*T).M wrappers. This is safe because we only
|
// TODO(mdempsky): Make this unconditional. The exporter now
|
||||||
// generate those wrappers within the same compilation unit as (T).M.
|
// includes all of the inline bodies we need, and the "importedType"
|
||||||
// TODO(mdempsky): Investigate why we can't enable this more generally.
|
// logic above now correctly suppresses compiling out-of-package
|
||||||
|
// types that we might not have inline bodies for. The only problem
|
||||||
|
// now is that the extra inlining can now introduce further new
|
||||||
|
// itabs, and gc.dumpdata's ad hoc compile loop doesn't handle this.
|
||||||
|
//
|
||||||
|
// CL 327871 will address this by writing itabs and generating
|
||||||
|
// wrappers as part of the loop, so we won't have to worry about
|
||||||
|
// "itabs changed after compile functions loop" errors anymore.
|
||||||
if rcvr.IsPtr() && rcvr.Elem() == method.Type.Recv().Type && rcvr.Elem().Sym() != nil {
|
if rcvr.IsPtr() && rcvr.Elem() == method.Type.Recv().Type && rcvr.Elem().Sym() != nil {
|
||||||
inline.InlineCalls(fn)
|
inline.InlineCalls(fn)
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,14 +15,18 @@ import (
|
||||||
// callable by importers are marked with ExportInline so that
|
// callable by importers are marked with ExportInline so that
|
||||||
// iexport.go knows to re-export their inline body.
|
// iexport.go knows to re-export their inline body.
|
||||||
func crawlExports(exports []*ir.Name) {
|
func crawlExports(exports []*ir.Name) {
|
||||||
p := crawler{marked: make(map[*types.Type]bool)}
|
p := crawler{
|
||||||
|
marked: make(map[*types.Type]bool),
|
||||||
|
embedded: make(map[*types.Type]bool),
|
||||||
|
}
|
||||||
for _, n := range exports {
|
for _, n := range exports {
|
||||||
p.markObject(n)
|
p.markObject(n)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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
|
||||||
}
|
}
|
||||||
|
|
||||||
// markObject visits a reachable object.
|
// markObject visits a reachable object.
|
||||||
|
@ -31,6 +35,12 @@ func (p *crawler) markObject(n *ir.Name) {
|
||||||
p.markInlBody(n)
|
p.markInlBody(n)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If a declared type name is reachable, users can embed it in their
|
||||||
|
// own types, which makes even its unexported methods reachable.
|
||||||
|
if n.Op() == ir.OTYPE {
|
||||||
|
p.markEmbed(n.Type())
|
||||||
|
}
|
||||||
|
|
||||||
p.markType(n.Type())
|
p.markType(n.Type())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -46,7 +56,7 @@ func (p *crawler) markType(t *types.Type) {
|
||||||
}
|
}
|
||||||
p.marked[t] = true
|
p.marked[t] = true
|
||||||
|
|
||||||
// If this is a named type, mark all of its associated
|
// If this is a defined type, mark all of its associated
|
||||||
// methods. Skip interface types because t.Methods contains
|
// methods. Skip interface types because t.Methods contains
|
||||||
// only their unexpanded method set (i.e., exclusive of
|
// only their unexpanded method set (i.e., exclusive of
|
||||||
// interface embeddings), and the switch statement below
|
// interface embeddings), and the switch statement below
|
||||||
|
@ -107,6 +117,48 @@ func (p *crawler) markType(t *types.Type) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// markEmbed is similar to markType, but handles finding methods that
|
||||||
|
// need to be re-exported because t can be embedded in user code
|
||||||
|
// (possibly transitively).
|
||||||
|
func (p *crawler) markEmbed(t *types.Type) {
|
||||||
|
if t.IsPtr() {
|
||||||
|
// Defined pointer type; not allowed to embed anyway.
|
||||||
|
if t.Sym() != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
t = t.Elem()
|
||||||
|
}
|
||||||
|
|
||||||
|
if t.IsInstantiatedGeneric() {
|
||||||
|
// Re-instantiated types don't add anything new, so don't follow them.
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if p.embedded[t] {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
p.embedded[t] = true
|
||||||
|
|
||||||
|
// If t is a defined type, then re-export all of its methods. Unlike
|
||||||
|
// in markType, we include even unexported methods here, because we
|
||||||
|
// still need to generate wrappers for them, even if the user can't
|
||||||
|
// refer to them directly.
|
||||||
|
if t.Sym() != nil && t.Kind() != types.TINTER {
|
||||||
|
for _, m := range t.Methods().Slice() {
|
||||||
|
p.markObject(m.Nname.(*ir.Name))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If t is a struct, recursively visit its embedded fields.
|
||||||
|
if t.IsStruct() {
|
||||||
|
for _, f := range t.FieldSlice() {
|
||||||
|
if f.Embedded != 0 {
|
||||||
|
p.markEmbed(f.Type)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 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) {
|
||||||
|
|
Loading…
Reference in a new issue