diff options
author | Matthew Dempsky <mdempsky@google.com> | 2021-06-14 19:21:14 -0700 |
---|---|---|
committer | Matthew Dempsky <mdempsky@google.com> | 2021-06-16 17:32:49 +0000 |
commit | 132ea56d292eac0226eef4bc32d784b0300c3bce (patch) | |
tree | b3158a88a1ccfe5e1bed716e952e318b81294e9c /src/cmd/compile/internal/typecheck/crawler.go | |
parent | 8f95eaddd334e61b1832628741b97462ddc84975 (diff) | |
download | go-132ea56d292eac0226eef4bc32d784b0300c3bce.tar.gz go-132ea56d292eac0226eef4bc32d784b0300c3bce.zip |
[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>
Diffstat (limited to 'src/cmd/compile/internal/typecheck/crawler.go')
-rw-r--r-- | src/cmd/compile/internal/typecheck/crawler.go | 58 |
1 files changed, 55 insertions, 3 deletions
diff --git a/src/cmd/compile/internal/typecheck/crawler.go b/src/cmd/compile/internal/typecheck/crawler.go index c78a604a8d..655ac6e465 100644 --- a/src/cmd/compile/internal/typecheck/crawler.go +++ b/src/cmd/compile/internal/typecheck/crawler.go @@ -15,14 +15,18 @@ import ( // callable by importers are marked with ExportInline so that // iexport.go knows to re-export their inline body. 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 { p.markObject(n) } } 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. @@ -31,6 +35,12 @@ func (p *crawler) markObject(n *ir.Name) { 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()) } @@ -46,7 +56,7 @@ func (p *crawler) markType(t *types.Type) { } 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 // only their unexpanded method set (i.e., exclusive of // 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 // ensures all called functions are marked too. func (p *crawler) markInlBody(n *ir.Name) { |