]> Cypherpunks repositories - gostls13.git/commitdiff
[dev.typeparams] cmd/compile: add method value wrappers to unified IR
authorMatthew Dempsky <mdempsky@google.com>
Sat, 26 Jun 2021 19:44:53 +0000 (12:44 -0700)
committerMatthew Dempsky <mdempsky@google.com>
Sun, 27 Jun 2021 05:05:38 +0000 (05:05 +0000)
Method value wrappers will need dictionary support too, so bring them
under the unified IR umbrella as well.

Change-Id: Iec36bb04efdf59843d1b00f55d2c44bc841fa2ef
Reviewed-on: https://go-review.googlesource.com/c/go/+/331190
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Cuong Manh Le <cuong.manhle.vn@gmail.com>
Trust: Matthew Dempsky <mdempsky@google.com>

src/cmd/compile/internal/noder/reader.go

index e5ad3f4b8ef9c5ae6a9c835e0660a7b9fbb15560..66c0e99d11480058f87f4198fc1adcef2068af5e 100644 (file)
@@ -2065,6 +2065,8 @@ func (r *reader) wrapType(typ *types.Type, target *ir.Package) {
                        base.FatalfAt(meth.Pos, "invalid method: %v", meth)
                }
 
+               r.methodValueWrapper(typ, meth, target)
+
                r.methodWrapper(0, typ, meth, target)
 
                // For non-interface types, we also want *T wrappers.
@@ -2100,21 +2102,81 @@ func (r *reader) methodWrapper(derefs int, tbase *types.Type, method *types.Fiel
        // TODO(mdempsky): Use method.Pos instead?
        pos := base.AutogeneratedPos
 
+       fn := r.newWrapperFunc(pos, sym, wrapper, method, target)
+
+       var recv ir.Node = fn.Nname.Type().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))
+       }
+
+       // typecheck will add one implicit deref, if necessary,
+       // but not-in-heap types require more for their **T wrappers.
+       for i := 1; i < derefs; i++ {
+               recv = Implicit(ir.NewStarExpr(pos, recv))
+       }
+
+       addTailCall(pos, fn, recv, method)
+}
+
+func (r *reader) methodValueWrapper(tbase *types.Type, method *types.Field, target *ir.Package) {
+       recvType := tbase
+       if !tbase.IsInterface() {
+               recvType = method.Type.Recv().Type
+               if !types.Identical(tbase, types.ReceiverBaseType(recvType)) {
+                       return
+               }
+       }
+
+       sym := ir.MethodSymSuffix(recvType, method.Sym, "-fm")
+       assert(!sym.Uniq())
+       sym.SetUniq(true)
+
+       // TODO(mdempsky): Fix typecheck to not depend on creation of
+       // imported method value wrappers.
+       if false && !reflectdata.NeedEmit(tbase) {
+               return
+       }
+
+       // TODO(mdempsky): Use method.Pos instead?
+       pos := base.AutogeneratedPos
+
+       fn := r.newWrapperFunc(pos, sym, nil, method, target)
+       fn.SetNeedctxt(true)
+       sym.Def = fn
+
+       // Declare and initialize variable holding receiver.
+       recv := ir.NewNameAt(pos, typecheck.Lookup(".this"))
+       recv.Class = ir.PAUTOHEAP
+       recv.SetType(recvType)
+       recv.Curfn = fn
+       recv.SetIsClosureVar(true)
+       recv.SetByval(true)
+       fn.ClosureVars = append(fn.ClosureVars, recv)
+
+       addTailCall(pos, fn, recv, method)
+}
+
+func (r *reader) newWrapperFunc(pos src.XPos, sym *types.Sym, wrapper *types.Type, method *types.Field, target *ir.Package) *ir.Func {
        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.SetDupok(true) // TODO(mdempsky): Leave unset for local, non-generic wrappers?
 
-       fn.Nname = ir.NewNameAt(pos, sym)
-       ir.MarkFunc(fn.Nname)
-       fn.Nname.Func = fn
-       fn.Nname.Defn = fn
+       name := ir.NewNameAt(pos, sym)
+       ir.MarkFunc(name)
+       name.Func = fn
+       name.Defn = fn
+       fn.Nname = name
 
-       sig := newWrapperType(wrapper, method.Type)
-       r.setType(fn.Nname, sig)
+       sig := newWrapperType(wrapper, method)
+       r.setType(name, sig)
 
        // TODO(mdempsky): De-duplicate with similar logic in funcargs.
-       defParams := func(class ir.Class, params ...*types.Field) {
-               for _, param := range params {
+       defParams := func(class ir.Class, params *types.Type) {
+               for _, param := range params.FieldSlice() {
                        name := ir.NewNameAt(param.Pos, param.Sym)
                        name.Class = class
                        r.setType(name, param.Type)
@@ -2126,39 +2188,20 @@ func (r *reader) methodWrapper(derefs int, tbase *types.Type, method *types.Fiel
                }
        }
 
-       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))
+       defParams(ir.PPARAM, sig.Recvs())
+       defParams(ir.PPARAM, sig.Params())
+       defParams(ir.PPARAMOUT, sig.Results())
 
        target.Decls = append(target.Decls, fn)
+
+       return 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 {
+// the receiver parameter type substituted with recvType.
+// If recvType is nil, newWrapperType returns a signature
+// without a receiver parameter.
+func newWrapperType(recvType *types.Type, method *types.Field) *types.Type {
        clone := func(params []*types.Field) []*types.Field {
                res := make([]*types.Field, len(params))
                for i, param := range params {
@@ -2172,25 +2215,39 @@ func newWrapperType(wrapper, sig *types.Type) *types.Type {
                return res
        }
 
-       recv := types.NewField(sig.Recv().Pos, typecheck.Lookup(".this"), wrapper)
+       sig := method.Type
+
+       var recv *types.Field
+       if recvType != nil {
+               recv = types.NewField(sig.Recv().Pos, typecheck.Lookup(".this"), recvType)
+       }
        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 {
+func addTailCall(pos src.XPos, fn *ir.Func, recv ir.Node, method *types.Field) {
+       sig := fn.Nname.Type()
+       args := make([]ir.Node, sig.NumParams())
+       for i, param := range sig.Params().FieldSlice() {
+               args[i] = param.Nname.(*ir.Name)
+       }
+
        // TODO(mdempsky): Support creating OTAILCALL, when possible. See reflectdata.methodWrapper.
        // Not urgent though, because tail calls are currently incompatible with regabi anyway.
 
+       fn.SetWrapper(true) // TODO(mdempsky): Leave unset for tail calls?
+
        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
+               fn.Body.Append(call)
+               return
        }
 
        ret := ir.NewReturnStmt(pos, nil)
        ret.Results = []ir.Node{call}
-       return ret
+       fn.Body.Append(ret)
 }