Skip to content

Commit a73c6cf

Browse files
committed
cmd/compile/internal/types2: ensure named types are expanded after type-checking
This is a clean port of CL 356490 from go/types to types2. Fixes #48703. Fixes #48974. Change-Id: I08c0db0b92250cbb043325541b21a577726b40ca Reviewed-on: https://go-review.googlesource.com/c/go/+/356515 Trust: Robert Griesemer <[email protected]> Reviewed-by: Robert Findley <[email protected]>
1 parent 3a07ab7 commit a73c6cf

File tree

4 files changed

+85
-15
lines changed

4 files changed

+85
-15
lines changed

src/cmd/compile/internal/types2/check.go

+28
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,7 @@ type Checker struct {
132132
untyped map[syntax.Expr]exprInfo // map of expressions without final type
133133
delayed []action // stack of delayed action segments; segments are processed in FIFO order
134134
objPath []Object // path of object dependencies during type inference (for cycle reporting)
135+
defTypes []*Named // defined types created during type checking, for final validation.
135136

136137
// context within which the current object is type-checked
137138
// (valid only for the duration of type-checking a specific object)
@@ -302,6 +303,9 @@ func (check *Checker) checkFiles(files []*syntax.File) (err error) {
302303
print("== processDelayed ==")
303304
check.processDelayed(0) // incl. all functions
304305

306+
print("== expandDefTypes ==")
307+
check.expandDefTypes()
308+
305309
print("== initOrder ==")
306310
check.initOrder()
307311

@@ -321,6 +325,7 @@ func (check *Checker) checkFiles(files []*syntax.File) (err error) {
321325
check.pkgPathMap = nil
322326
check.seenPkgMap = nil
323327
check.recvTParamMap = nil
328+
check.defTypes = nil
324329

325330
// TODO(gri) There's more memory we should release at this point.
326331

@@ -347,6 +352,29 @@ func (check *Checker) processDelayed(top int) {
347352
check.delayed = check.delayed[:top]
348353
}
349354

355+
func (check *Checker) expandDefTypes() {
356+
// Ensure that every defined type created in the course of type-checking has
357+
// either non-*Named underlying, or is unresolved.
358+
//
359+
// This guarantees that we don't leak any types whose underlying is *Named,
360+
// because any unresolved instances will lazily compute their underlying by
361+
// substituting in the underlying of their origin. The origin must have
362+
// either been imported or type-checked and expanded here, and in either case
363+
// its underlying will be fully expanded.
364+
for i := 0; i < len(check.defTypes); i++ {
365+
n := check.defTypes[i]
366+
switch n.underlying.(type) {
367+
case nil:
368+
if n.resolver == nil {
369+
panic("nil underlying")
370+
}
371+
case *Named:
372+
n.under() // n.under may add entries to check.defTypes
373+
}
374+
n.check = nil
375+
}
376+
}
377+
350378
func (check *Checker) record(x *operand) {
351379
// convert x into a user-friendly set of values
352380
// TODO(gri) this code can be simplified

src/cmd/compile/internal/types2/named.go

+8-15
Original file line numberDiff line numberDiff line change
@@ -65,22 +65,9 @@ func (check *Checker) newNamed(obj *TypeName, orig *Named, underlying Type, tpar
6565
if obj.typ == nil {
6666
obj.typ = typ
6767
}
68-
// Ensure that typ is always expanded, at which point the check field can be
69-
// nilled out.
70-
//
71-
// Note that currently we cannot nil out check inside typ.under(), because
72-
// it's possible that typ is expanded multiple times.
73-
//
74-
// TODO(gri): clean this up so that under is the only function mutating
75-
// named types.
68+
// Ensure that typ is always expanded and sanity-checked.
7669
if check != nil {
77-
check.later(func() {
78-
switch typ.under().(type) {
79-
case *Named:
80-
panic("unexpanded underlying type")
81-
}
82-
typ.check = nil
83-
})
70+
check.defTypes = append(check.defTypes, typ)
8471
}
8572
return typ
8673
}
@@ -239,6 +226,12 @@ func expandNamed(ctxt *Context, n *Named, instPos syntax.Pos) (tparams *TypePara
239226

240227
check := n.check
241228

229+
if _, unexpanded := n.orig.underlying.(*Named); unexpanded {
230+
// We should only get an unexpanded underlying here during type checking
231+
// (for example, in recursive type declarations).
232+
assert(check != nil)
233+
}
234+
242235
// Mismatching arg and tparam length may be checked elsewhere.
243236
if n.orig.tparams.Len() == n.targs.Len() {
244237
// We must always have a context, to avoid infinite recursion.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
// Copyright 2021 The Go Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style
3+
// license that can be found in the LICENSE file.
4+
5+
package p
6+
7+
import "unsafe"
8+
9+
// The actual example from the issue.
10+
type List[P any] struct{}
11+
12+
func (_ List[P]) m() (_ List[List[P]]) { return }
13+
14+
// Other types of recursion through methods.
15+
type R[P any] int
16+
17+
func (*R[R /* ERROR must be an identifier */ [int]]) m0() {}
18+
func (R[P]) m1(R[R[P]]) {}
19+
func (R[P]) m2(R[*P]) {}
20+
func (R[P]) m3([unsafe.Sizeof(new(R[P]))]int) {}
21+
func (R[P]) m4([unsafe.Sizeof(new(R[R[P]]))]int) {}
22+
23+
// Mutual recursion
24+
type M[P any] int
25+
26+
func (R[P]) m5(M[M[P]]) {}
27+
func (M[P]) m(R[R[P]]) {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
// Copyright 2021 The Go Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style
3+
// license that can be found in the LICENSE file.
4+
5+
package p
6+
7+
type Fooer interface {
8+
Foo()
9+
}
10+
11+
type Fooable[F Fooer] struct {
12+
ptr F
13+
}
14+
15+
func (f *Fooable[F]) Adapter() *Fooable[*FooerImpl[F]] {
16+
return &Fooable[*FooerImpl[F]]{&FooerImpl[F]{}}
17+
}
18+
19+
type FooerImpl[F Fooer] struct {
20+
}
21+
22+
func (fi *FooerImpl[F]) Foo() {}

0 commit comments

Comments
 (0)