aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/cmd/compile/internal/gc/main.go2
-rw-r--r--src/cmd/compile/internal/noder/reader.go190
-rw-r--r--src/cmd/compile/internal/noder/unified.go7
-rw-r--r--src/cmd/compile/internal/reflectdata/reflect.go10
-rw-r--r--test/fixedbugs/issue46903.go32
-rw-r--r--test/typeparam/issue44688.go2
6 files changed, 234 insertions, 9 deletions
diff --git a/src/cmd/compile/internal/gc/main.go b/src/cmd/compile/internal/gc/main.go
index c0346c0206..c94f19fd47 100644
--- a/src/cmd/compile/internal/gc/main.go
+++ b/src/cmd/compile/internal/gc/main.go
@@ -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.
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
+}
diff --git a/src/cmd/compile/internal/noder/unified.go b/src/cmd/compile/internal/noder/unified.go
index 7a1bb88537..292fd13c67 100644
--- a/src/cmd/compile/internal/noder/unified.go
+++ b/src/cmd/compile/internal/noder/unified.go
@@ -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])
diff --git a/src/cmd/compile/internal/reflectdata/reflect.go b/src/cmd/compile/internal/reflectdata/reflect.go
index ba4bbc7631..8421e36b3d 100644
--- a/src/cmd/compile/internal/reflectdata/reflect.go
+++ b/src/cmd/compile/internal/reflectdata/reflect.go
@@ -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
index 0000000000..3237a583d5
--- /dev/null
+++ b/test/fixedbugs/issue46903.go
@@ -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")
+ }
+}
diff --git a/test/typeparam/issue44688.go b/test/typeparam/issue44688.go
index d70f94f706..de1140b67c 100644
--- a/test/typeparam/issue44688.go
+++ b/test/typeparam/issue44688.go
@@ -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