]> Cypherpunks repositories - gostls13.git/commitdiff
[dev.typeparams] cmd/compile: generate wrappers within unified IR
authorMatthew Dempsky <mdempsky@google.com>
Thu, 24 Jun 2021 04:33:24 +0000 (21:33 -0700)
committerMatthew Dempsky <mdempsky@google.com>
Fri, 25 Jun 2021 05:00:20 +0000 (05:00 +0000)
This CL extends unified IR to handle creating wrapper methods. There's
relatively little about this code that's actually specific to unified
IR, but rewriting this logic allows a few benefits:

1. It decouples unified IR from reflectdata.methodWrapper, so the
latter code can evolve freely for -G=3's needs. This will also allow
the new code to evolve to unified IR's wrapper needs, which I
anticipate will operate slightly differently.

2. It provided an opportunity to revisit a lot of the code and
simplify/update it to current style. E.g., in the process, I
discovered #46903, which unified IR now gets correctly. (I have not
yet attempted to fix reflectdata.methodWrapper.)

3. It gives a convenient way for unified IR to ensure all of the
wrapper methods it needs are generated correctly.

For now, the wrapper generation is specific to non-quirks mode.

Change-Id: I5798de6b141f29e8eb6a5c563e7049627ff2868a
Reviewed-on: https://go-review.googlesource.com/c/go/+/330569
Trust: Matthew Dempsky <mdempsky@google.com>
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Cuong Manh Le <cuong.manhle.vn@gmail.com>
src/cmd/compile/internal/gc/main.go
src/cmd/compile/internal/noder/reader.go
src/cmd/compile/internal/noder/unified.go
src/cmd/compile/internal/reflectdata/reflect.go
test/fixedbugs/issue46903.go [new file with mode: 0644]
test/typeparam/issue44688.go

index c0346c020651f62924d096b15471d373477f256c..c94f19fd47c96df8163bd63998fd0cd834c2a861 100644 (file)
@@ -186,6 +186,7 @@ func Main(archInit func(*ssagen.ArchInfo)) {
        base.AutogeneratedPos = makePos(src.NewFileBase("<autogenerated>", "<autogenerated>"), 1, 0)
 
        typecheck.InitUniverse()
+       typecheck.InitRuntime()
 
        // Parse and typecheck input.
        noder.LoadPackage(flag.Args())
@@ -194,7 +195,6 @@ func Main(archInit func(*ssagen.ArchInfo)) {
 
        // Prepare for backend processing. This must happen before pkginit,
        // because it generates itabs for initializing global variables.
-       typecheck.InitRuntime()
        ssagen.InitConfig()
 
        // Build init task.
index d2fe575ffd4d49493abc71abc454c3be6f50d12e..3a496816cc2c8d54b488c5f59cfffd4971ce8f4d 100644 (file)
@@ -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
+}
index 7a1bb88537a1dc3964effc93f72a34a404ea0642..292fd13c679660aacf84878819d471f750d0ce69 100644 (file)
@@ -74,6 +74,8 @@ func unified(noders []*noder) {
 
        if !quirksMode() {
                writeNewExportFunc = writeNewExport
+       } else if base.Flag.G != 0 {
+               base.Errorf("cannot use -G and -d=quirksmode together")
        }
 
        newReadImportFunc = func(data string, pkg1 *types.Pkg, check *types2.Checker, packages map[string]*types2.Package) (pkg2 *types2.Package, err error) {
@@ -126,6 +128,11 @@ func unified(noders []*noder) {
        }
        todoBodies = nil
 
+       if !quirksMode() {
+               // TODO(mdempsky): Investigate generating wrappers in quirks mode too.
+               r.wrapTypes(target)
+       }
+
        // Don't use range--typecheck can add closures to Target.Decls.
        for i := 0; i < len(target.Decls); i++ {
                target.Decls[i] = typecheck.Stmt(target.Decls[i])
index ba4bbc76313953cbedcff9a358ba47bda403c7d0..8421e36b3dde38af76064ba113d959a4166971ef 100644 (file)
@@ -1760,10 +1760,6 @@ func methodWrapper(rcvr *types.Type, method *types.Field, forItab bool) *obj.LSy
                // TODO: check that we do the right thing when method is an interface method.
                generic = true
        }
-       if base.Debug.Unified != 0 {
-               // TODO(mdempsky): Support dictionaries for unified IR.
-               generic = false
-       }
        newnam := ir.MethodSym(rcvr, method.Sym)
        lsym := newnam.Linksym()
        if newnam.Siggen() {
@@ -1771,6 +1767,12 @@ func methodWrapper(rcvr *types.Type, method *types.Field, forItab bool) *obj.LSy
        }
        newnam.SetSiggen(true)
 
+       // Except in quirks mode, unified IR creates its own wrappers.
+       // Complain loudly if it missed any.
+       if base.Debug.Unified != 0 && base.Debug.UnifiedQuirks == 0 {
+               base.FatalfAt(method.Pos, "missing wrapper for %+v (%+v, %v) / %+v / %+v", rcvr, orig, types.IsDirectIface(orig), method.Sym, newnam)
+       }
+
        if !generic && types.Identical(rcvr, method.Type.Recv().Type) {
                return lsym
        }
diff --git a/test/fixedbugs/issue46903.go b/test/fixedbugs/issue46903.go
new file mode 100644 (file)
index 0000000..3237a58
--- /dev/null
@@ -0,0 +1,32 @@
+// run
+//go:build goexperiment.unified
+// +build goexperiment.unified
+
+// TODO(mdempsky): Enable test unconditionally. This test should pass
+// for non-unified mode too.
+
+// Copyright 2021 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+//go:notinheap
+type A struct{ B }
+type B struct{ x byte }
+type I interface{ M() *B }
+
+func (p *B) M() *B { return p }
+
+var (
+       a A
+       i I = &a
+)
+
+func main() {
+       got, want := i.M(), &a.B
+       if got != want {
+               println(got, "!=", want)
+               panic("FAIL")
+       }
+}
index d70f94f706df41381e2c60ca86ca46ad3effde31..de1140b67cfe3852b37ce1bcd76336f905897135 100644 (file)
@@ -1,6 +1,4 @@
 // run -gcflags=-G=3
-//go:build goexperiment.unified
-// +build !goexperiment.unified
 
 // Copyright 2021 The Go Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style