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("", ""), 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