return lsym
}
- // Only generate (*T).M wrappers for T.M in T's own package, except for
- // instantiated methods.
- if rcvr.IsPtr() && rcvr.Elem() == method.Type.Recv().Type &&
- rcvr.Elem().Sym() != nil && rcvr.Elem().Sym().Pkg != types.LocalPkg &&
- !rcvr.Elem().IsFullyInstantiated() {
- return lsym
+ // imported reports whether typ is a defined type that was declared
+ // in an imported package, and therefore must have been compiled in
+ // that package.
+ importedType := func(typ *types.Type) bool {
+ return typ.Sym() != nil && typ.Sym().Pkg != types.LocalPkg &&
+
+ // 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
- // 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() {
+ if importedType(rcvr) || rcvr.IsPtr() && importedType(rcvr.Elem()) {
return lsym
}
ir.CurFunc = fn
typecheck.Stmts(fn.Body)
- // Inline calls within (*T).M wrappers. This is safe because we only
- // generate those wrappers within the same compilation unit as (T).M.
- // TODO(mdempsky): Investigate why we can't enable this more generally.
+ // TODO(mdempsky): Make this unconditional. The exporter now
+ // includes all of the inline bodies we need, and the "importedType"
+ // 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 {
inline.InlineCalls(fn)
}
// 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.
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.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
}
}
+// 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) {