diff options
Diffstat (limited to 'src/cmd/compile/internal/noder/reader.go')
-rw-r--r-- | src/cmd/compile/internal/noder/reader.go | 190 |
1 files changed, 188 insertions, 2 deletions
diff --git a/src/cmd/compile/internal/noder/reader.go b/src/cmd/compile/internal/noder/reader.go index d2fe575ffd..3a496816cc 100644 --- a/src/cmd/compile/internal/noder/reader.go +++ b/src/cmd/compile/internal/noder/reader.go @@ -16,6 +16,7 @@ import ( "cmd/compile/internal/deadcode" "cmd/compile/internal/dwarfgen" "cmd/compile/internal/ir" + "cmd/compile/internal/reflectdata" "cmd/compile/internal/typecheck" "cmd/compile/internal/types" "cmd/internal/obj" @@ -419,7 +420,7 @@ func (r *reader) interfaceType() *types.Type { if len(fields) == 0 { return types.Types[types.TINTER] // empty interface } - return types.NewInterface(tpkg, fields) + return r.needWrapper(types.NewInterface(tpkg, fields)) } func (r *reader) structType() *types.Type { @@ -440,7 +441,7 @@ func (r *reader) structType() *types.Type { } fields[i] = f } - return types.NewStruct(tpkg, fields) + return r.needWrapper(types.NewStruct(tpkg, fields)) } func (r *reader) signature(tpkg *types.Pkg, recv *types.Field) *types.Type { @@ -597,6 +598,10 @@ func (pr *pkgReader) objIdx(idx int, implicits, explicits []*types.Type) ir.Node typ.Methods().Set(methods) } + if !typ.IsPtr() { + r.needWrapper(typ) + } + return name case objVar: @@ -2015,3 +2020,184 @@ func usedLocals(body []ir.Node) ir.NameSet { }) return used } + +// @@@ Method wrappers + +// needWrapperTypes lists types for which we may need to generate +// method wrappers. +var needWrapperTypes []*types.Type + +func (r *reader) needWrapper(typ *types.Type) *types.Type { + // TODO(mdempsky): Be more judicious about generating wrappers. + // For now, generating all possible wrappers is simple and correct, + // but potentially wastes a lot of time/space. + + if typ.IsPtr() { + base.Fatalf("bad pointer type: %v", typ) + } + + needWrapperTypes = append(needWrapperTypes, typ) + return typ +} + +func (r *reader) wrapTypes(target *ir.Package) { + // always generate a wrapper for error.Error (#29304) + r.needWrapper(types.ErrorType) + + seen := make(map[string]*types.Type) + for _, typ := range needWrapperTypes { + if typ.Sym() == nil { + key := typ.ShortString() + if prev := seen[key]; prev != nil { + if !types.Identical(typ, prev) { + base.Fatalf("collision: types %v and %v have short string %q", typ, prev, key) + } + continue + } + seen[key] = typ + } + + r.wrapType(typ, target) + } + + needWrapperTypes = nil +} + +func (r *reader) wrapType(typ *types.Type, target *ir.Package) { + if !typ.IsInterface() { + typecheck.CalcMethods(typ) + } + for _, meth := range typ.AllMethods().Slice() { + if meth.Sym.IsBlank() || !meth.IsMethod() { + base.FatalfAt(meth.Pos, "invalid method: %v", meth) + } + + r.methodWrapper(0, typ, meth, target) + + // For non-interface types, we also want *T wrappers. + if !typ.IsInterface() { + r.methodWrapper(1, typ, meth, target) + + // For not-in-heap types, *T is a scalar, not pointer shaped, + // so the interface wrappers use **T. + if typ.NotInHeap() { + r.methodWrapper(2, typ, meth, target) + } + } + } +} + +func (r *reader) methodWrapper(derefs int, tbase *types.Type, method *types.Field, target *ir.Package) { + wrapper := tbase + for i := 0; i < derefs; i++ { + wrapper = types.NewPtr(wrapper) + } + + sym := ir.MethodSym(wrapper, method.Sym) + assert(!sym.Siggen()) + sym.SetSiggen(true) + + wrappee := method.Type.Recv().Type + if types.Identical(wrapper, wrappee) || + !types.IsMethodApplicable(wrapper, method) || + !reflectdata.NeedEmit(tbase) { + return + } + + // TODO(mdempsky): Use method.Pos instead? + pos := base.AutogeneratedPos + + fn := ir.NewFunc(pos) + fn.SetDupok(true) // TODO(mdempsky): Leave unset for local, non-generic wrappers? + fn.SetWrapper(true) // TODO(mdempsky): Leave unset for tail calls? + + fn.Nname = ir.NewNameAt(pos, sym) + ir.MarkFunc(fn.Nname) + fn.Nname.Func = fn + fn.Nname.Defn = fn + + sig := newWrapperType(wrapper, method.Type) + r.setType(fn.Nname, sig) + + // TODO(mdempsky): De-duplicate with similar logic in funcargs. + defParams := func(class ir.Class, params ...*types.Field) { + for _, param := range params { + name := ir.NewNameAt(param.Pos, param.Sym) + name.Class = class + r.setType(name, param.Type) + + name.Curfn = fn + fn.Dcl = append(fn.Dcl, name) + + param.Nname = name + } + } + + defParams(ir.PPARAM, sig.Recv()) + defParams(ir.PPARAM, sig.Params().FieldSlice()...) + defParams(ir.PPARAMOUT, sig.Results().FieldSlice()...) + + var recv ir.Node = sig.Recv().Nname.(*ir.Name) + + // For simple *T wrappers around T methods, panicwrap produces a + // nicer panic message. + if wrapper.IsPtr() && types.Identical(wrapper.Elem(), wrappee) { + cond := ir.NewBinaryExpr(pos, ir.OEQ, recv, types.BuiltinPkg.Lookup("nil").Def.(ir.Node)) + then := []ir.Node{ir.NewCallExpr(pos, ir.OCALL, typecheck.LookupRuntime("panicwrap"), nil)} + fn.Body.Append(ir.NewIfStmt(pos, cond, then, nil)) + } + + // Add implicit derefs, as necessary. typecheck will add one deref, + // but not-in-heap types will need another for their **T wrappers. + for i := 0; i < derefs; i++ { + recv = Implicit(ir.NewStarExpr(pos, recv)) + } + + args := make([]ir.Node, sig.NumParams()) + for i, param := range sig.Params().FieldSlice() { + args[i] = param.Nname.(*ir.Name) + } + + fn.Body.Append(newTailCall(pos, method, recv, args)) + + target.Decls = append(target.Decls, fn) +} + +// newWrapperType returns a copy of the given signature type, but with +// the receiver parameter type substituted with wrapper. +func newWrapperType(wrapper, sig *types.Type) *types.Type { + clone := func(params []*types.Field) []*types.Field { + res := make([]*types.Field, len(params)) + for i, param := range params { + sym := param.Sym + if sym == nil || sym.Name == "_" { + sym = typecheck.LookupNum(".anon", i) + } + res[i] = types.NewField(param.Pos, sym, param.Type) + res[i].SetIsDDD(param.IsDDD()) + } + return res + } + + recv := types.NewField(sig.Recv().Pos, typecheck.Lookup(".this"), wrapper) + params := clone(sig.Params().FieldSlice()) + results := clone(sig.Results().FieldSlice()) + + return types.NewSignature(types.NoPkg, recv, nil, params, results) +} + +func newTailCall(pos src.XPos, method *types.Field, recv ir.Node, args []ir.Node) ir.Node { + // TODO(mdempsky): Support creating OTAILCALL, when possible. See reflectdata.methodWrapper. + // Not urgent though, because tail calls are currently incompatible with regabi anyway. + + call := ir.NewCallExpr(pos, ir.OCALL, ir.NewSelectorExpr(pos, ir.OXDOT, recv, method.Sym), args) + call.IsDDD = method.Type.IsVariadic() + + if method.Type.NumResults() == 0 { + return call + } + + ret := ir.NewReturnStmt(pos, nil) + ret.Results = []ir.Node{call} + return ret +} |