Skip to content

Commit d7e2e2e

Browse files
committed
cmd/compile: delay fillinMethods to deal with mutually-recursive types
We need to delay fillinMethods until we get to a top-level type, so we know all the TFORW types have been filled in, and we can do the substitutions required by fillinMethods. Fixes #47710 Change-Id: I298de7e7753ed31a2c2b1ff04f35177a8afc7a66 Reviewed-on: https://go-review.googlesource.com/c/go/+/345149 Trust: Dan Scales <[email protected]> Reviewed-by: Keith Randall <[email protected]>
1 parent c927599 commit d7e2e2e

File tree

4 files changed

+112
-67
lines changed

4 files changed

+112
-67
lines changed

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

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,9 @@ type irgen struct {
149149
// statements yet.
150150
exprStmtOK bool
151151

152+
// types which we need to finish, by doing g.fillinMethods.
153+
typesToFinalize []*typeDelayInfo
154+
152155
// Fully-instantiated generic types whose methods should be instantiated
153156
instTypeList []*types.Type
154157

@@ -184,6 +187,11 @@ type delayInfo struct {
184187
off int
185188
}
186189

190+
type typeDelayInfo struct {
191+
typ *types2.Named
192+
ntyp *types.Type
193+
}
194+
187195
func (g *irgen) generate(noders []*noder) {
188196
types.LocalPkg.Name = g.self.Name()
189197
types.LocalPkg.Height = g.self.Height()

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

Lines changed: 84 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,16 @@ func (g *irgen) typ(typ types2.Type) *types.Type {
3535
types.DeferCheckSize()
3636
res := g.typ1(typ)
3737
types.ResumeCheckSize()
38+
39+
// Finish up any types on typesToFinalize, now that we are at the top of a
40+
// fully-defined (possibly recursive) type. fillinMethods could create more
41+
// types to finalize.
42+
for len(g.typesToFinalize) > 0 {
43+
l := len(g.typesToFinalize)
44+
info := g.typesToFinalize[l-1]
45+
g.typesToFinalize = g.typesToFinalize[:l-1]
46+
g.fillinMethods(info.typ, info.ntyp)
47+
}
3848
return res
3949
}
4050

@@ -151,10 +161,19 @@ func (g *irgen) typ0(typ types2.Type) *types.Type {
151161
ntyp.SetRParams(rparams)
152162
//fmt.Printf("Saw new type %v %v\n", instName, ntyp.HasTParam())
153163

154-
ntyp.SetUnderlying(g.typ1(typ.Underlying()))
155-
g.fillinMethods(typ, ntyp)
156164
// Save the symbol for the base generic type.
157165
ntyp.OrigSym = g.pkg(typ.Obj().Pkg()).Lookup(typ.Obj().Name())
166+
ntyp.SetUnderlying(g.typ1(typ.Underlying()))
167+
if typ.NumMethods() != 0 {
168+
// Save a delayed call to g.fillinMethods() (once
169+
// potentially recursive types have been fully
170+
// resolved).
171+
g.typesToFinalize = append(g.typesToFinalize,
172+
&typeDelayInfo{
173+
typ: typ,
174+
ntyp: ntyp,
175+
})
176+
}
158177
return ntyp
159178
}
160179
obj := g.obj(typ.Obj())
@@ -266,76 +285,75 @@ func (g *irgen) typ0(typ types2.Type) *types.Type {
266285
}
267286
}
268287

269-
// fillinMethods fills in the method name nodes and types for a defined type. This
270-
// is needed for later typechecking when looking up methods of instantiated types,
271-
// and for actually generating the methods for instantiated types.
288+
// fillinMethods fills in the method name nodes and types for a defined type with at
289+
// least one method. This is needed for later typechecking when looking up methods of
290+
// instantiated types, and for actually generating the methods for instantiated
291+
// types.
272292
func (g *irgen) fillinMethods(typ *types2.Named, ntyp *types.Type) {
273-
if typ.NumMethods() != 0 {
274-
targs2 := typ.TArgs()
275-
targs := make([]*types.Type, targs2.Len())
276-
for i := range targs {
277-
targs[i] = g.typ1(targs2.At(i))
278-
}
293+
targs2 := typ.TArgs()
294+
targs := make([]*types.Type, targs2.Len())
295+
for i := range targs {
296+
targs[i] = g.typ1(targs2.At(i))
297+
}
279298

280-
methods := make([]*types.Field, typ.NumMethods())
281-
for i := range methods {
282-
m := typ.Method(i)
283-
recvType := deref2(types2.AsSignature(m.Type()).Recv().Type())
284-
var meth *ir.Name
285-
if m.Pkg() != g.self {
286-
// Imported methods cannot be loaded by name (what
287-
// g.obj() does) - they must be loaded via their
288-
// type.
289-
meth = g.obj(recvType.(*types2.Named).Obj()).Type().Methods().Index(i).Nname.(*ir.Name)
299+
methods := make([]*types.Field, typ.NumMethods())
300+
for i := range methods {
301+
m := typ.Method(i)
302+
recvType := deref2(types2.AsSignature(m.Type()).Recv().Type())
303+
var meth *ir.Name
304+
if m.Pkg() != g.self {
305+
// Imported methods cannot be loaded by name (what
306+
// g.obj() does) - they must be loaded via their
307+
// type.
308+
meth = g.obj(recvType.(*types2.Named).Obj()).Type().Methods().Index(i).Nname.(*ir.Name)
309+
} else {
310+
meth = g.obj(m)
311+
}
312+
if recvType != types2.Type(typ) {
313+
// Unfortunately, meth is the type of the method of the
314+
// generic type, so we have to do a substitution to get
315+
// the name/type of the method of the instantiated type,
316+
// using m.Type().RParams() and typ.TArgs()
317+
inst2 := instTypeName2("", typ.TArgs())
318+
name := meth.Sym().Name
319+
i1 := strings.Index(name, "[")
320+
i2 := strings.Index(name[i1:], "]")
321+
assert(i1 >= 0 && i2 >= 0)
322+
// Generate the name of the instantiated method.
323+
name = name[0:i1] + inst2 + name[i1+i2+1:]
324+
newsym := meth.Sym().Pkg.Lookup(name)
325+
var meth2 *ir.Name
326+
if newsym.Def != nil {
327+
meth2 = newsym.Def.(*ir.Name)
290328
} else {
291-
meth = g.obj(m)
292-
}
293-
if recvType != types2.Type(typ) {
294-
// Unfortunately, meth is the type of the method of the
295-
// generic type, so we have to do a substitution to get
296-
// the name/type of the method of the instantiated type,
297-
// using m.Type().RParams() and typ.TArgs()
298-
inst2 := instTypeName2("", typ.TArgs())
299-
name := meth.Sym().Name
300-
i1 := strings.Index(name, "[")
301-
i2 := strings.Index(name[i1:], "]")
302-
assert(i1 >= 0 && i2 >= 0)
303-
// Generate the name of the instantiated method.
304-
name = name[0:i1] + inst2 + name[i1+i2+1:]
305-
newsym := meth.Sym().Pkg.Lookup(name)
306-
var meth2 *ir.Name
307-
if newsym.Def != nil {
308-
meth2 = newsym.Def.(*ir.Name)
309-
} else {
310-
meth2 = ir.NewNameAt(meth.Pos(), newsym)
311-
rparams := types2.AsSignature(m.Type()).RParams()
312-
tparams := make([]*types.Type, rparams.Len())
313-
for i := range tparams {
314-
tparams[i] = g.typ1(rparams.At(i))
315-
}
316-
assert(len(tparams) == len(targs))
317-
ts := typecheck.Tsubster{
318-
Tparams: tparams,
319-
Targs: targs,
320-
}
321-
// Do the substitution of the type
322-
meth2.SetType(ts.Typ(meth.Type()))
323-
// Add any new fully instantiated types
324-
// seen during the substitution to
325-
// g.instTypeList.
326-
g.instTypeList = append(g.instTypeList, ts.InstTypeList...)
327-
newsym.Def = meth2
329+
meth2 = ir.NewNameAt(meth.Pos(), newsym)
330+
rparams := types2.AsSignature(m.Type()).RParams()
331+
tparams := make([]*types.Type, rparams.Len())
332+
for i := range tparams {
333+
tparams[i] = g.typ1(rparams.At(i))
328334
}
329-
meth = meth2
335+
assert(len(tparams) == len(targs))
336+
ts := typecheck.Tsubster{
337+
Tparams: tparams,
338+
Targs: targs,
339+
}
340+
// Do the substitution of the type
341+
meth2.SetType(ts.Typ(meth.Type()))
342+
// Add any new fully instantiated types
343+
// seen during the substitution to
344+
// g.instTypeList.
345+
g.instTypeList = append(g.instTypeList, ts.InstTypeList...)
346+
newsym.Def = meth2
330347
}
331-
methods[i] = types.NewField(meth.Pos(), g.selector(m), meth.Type())
332-
methods[i].Nname = meth
333-
}
334-
ntyp.Methods().Set(methods)
335-
if !ntyp.HasTParam() && !ntyp.HasShape() {
336-
// Generate all the methods for a new fully-instantiated type.
337-
g.instTypeList = append(g.instTypeList, ntyp)
348+
meth = meth2
338349
}
350+
methods[i] = types.NewField(meth.Pos(), g.selector(m), meth.Type())
351+
methods[i].Nname = meth
352+
}
353+
ntyp.Methods().Set(methods)
354+
if !ntyp.HasTParam() && !ntyp.HasShape() {
355+
// Generate all the methods for a new fully-instantiated type.
356+
g.instTypeList = append(g.instTypeList, ntyp)
339357
}
340358
}
341359

src/cmd/compile/internal/reflectdata/reflect.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -927,7 +927,7 @@ func formalType(t *types.Type) *types.Type {
927927

928928
func writeType(t *types.Type) *obj.LSym {
929929
t = formalType(t)
930-
if t.IsUntyped() {
930+
if t.IsUntyped() || t.HasTParam() {
931931
base.Fatalf("writeType %v", t)
932932
}
933933

test/typeparam/issue47710.go

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
// compile -G=3
2+
3+
// Copyright 2021 The Go Authors. All rights reserved.
4+
// Use of this source code is governed by a BSD-style
5+
// license that can be found in the LICENSE file.
6+
7+
package p
8+
9+
type FooType[t any] interface {
10+
Foo(BarType[t])
11+
}
12+
type BarType[t any] interface {
13+
Int(IntType[t]) FooType[int]
14+
}
15+
16+
type IntType[t any] int
17+
18+
func (n IntType[t]) Foo(BarType[t]) {}
19+
func (n IntType[_]) String() {}

0 commit comments

Comments
 (0)