Skip to content

Commit e94f7df

Browse files
committed
go/types, types2: generalize cleanup phase after type checking
Use a cleanup method and simple registration mechanism for types that need some final processing before the end of type checking. Use cleanup mechanism instead of expandDefTypes mechanism for *Named types. There is no change in functionality here. Use cleanup mechanism also for TypeParam and Interface types to ensure that their check fields are nilled out at the end. Introduce a simple constructor method for Interface types to ensure that the cleanup method is always registered. In go/types, add tracing code to Checker.checkFiles to match types2. Minor comment adjustments. Fixes #51316. Fixes #51326. Change-Id: I7b2bd79cc419717982f3c918645af623c0e80d5e Reviewed-on: https://go-review.googlesource.com/c/go/+/387417 Trust: Robert Griesemer <[email protected]> Run-TryBot: Robert Griesemer <[email protected]> Reviewed-by: Robert Findley <[email protected]> TryBot-Result: Gopher Robot <[email protected]>
1 parent 163da6f commit e94f7df

File tree

12 files changed

+168
-93
lines changed

12 files changed

+168
-93
lines changed

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

Lines changed: 21 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,7 @@ type Checker struct {
126126
untyped map[syntax.Expr]exprInfo // map of expressions without final type
127127
delayed []action // stack of delayed action segments; segments are processed in FIFO order
128128
objPath []Object // path of object dependencies during type inference (for cycle reporting)
129-
defTypes []*Named // defined types created during type checking, for final validation.
129+
cleaners []cleaner // list of types that may need a final cleanup at the end of type-checking
130130

131131
// environment within which the current object is type-checked (valid only
132132
// for the duration of type-checking a specific object)
@@ -205,6 +205,16 @@ func (check *Checker) pop() Object {
205205
return obj
206206
}
207207

208+
type cleaner interface {
209+
cleanup()
210+
}
211+
212+
// needsCleanup records objects/types that implement the cleanup method
213+
// which will be called at the end of type-checking.
214+
func (check *Checker) needsCleanup(c cleaner) {
215+
check.cleaners = append(check.cleaners, c)
216+
}
217+
208218
// NewChecker returns a new Checker instance for a given package.
209219
// Package files may be added incrementally via checker.Files.
210220
func NewChecker(conf *Config, pkg *Package, info *Info) *Checker {
@@ -247,6 +257,8 @@ func (check *Checker) initFiles(files []*syntax.File) {
247257
check.methods = nil
248258
check.untyped = nil
249259
check.delayed = nil
260+
check.objPath = nil
261+
check.cleaners = nil
250262

251263
// determine package name and collect valid files
252264
pkg := check.pkg
@@ -315,8 +327,8 @@ func (check *Checker) checkFiles(files []*syntax.File) (err error) {
315327
print("== processDelayed ==")
316328
check.processDelayed(0) // incl. all functions
317329

318-
print("== expandDefTypes ==")
319-
check.expandDefTypes()
330+
print("== cleanup ==")
331+
check.cleanup()
320332

321333
print("== initOrder ==")
322334
check.initOrder()
@@ -344,7 +356,6 @@ func (check *Checker) checkFiles(files []*syntax.File) (err error) {
344356
check.recvTParamMap = nil
345357
check.brokenAliases = nil
346358
check.unionTypeSets = nil
347-
check.defTypes = nil
348359
check.ctxt = nil
349360

350361
// TODO(gri) There's more memory we should release at this point.
@@ -372,27 +383,13 @@ func (check *Checker) processDelayed(top int) {
372383
check.delayed = check.delayed[:top]
373384
}
374385

375-
func (check *Checker) expandDefTypes() {
376-
// Ensure that every defined type created in the course of type-checking has
377-
// either non-*Named underlying, or is unresolved.
378-
//
379-
// This guarantees that we don't leak any types whose underlying is *Named,
380-
// because any unresolved instances will lazily compute their underlying by
381-
// substituting in the underlying of their origin. The origin must have
382-
// either been imported or type-checked and expanded here, and in either case
383-
// its underlying will be fully expanded.
384-
for i := 0; i < len(check.defTypes); i++ {
385-
n := check.defTypes[i]
386-
switch n.underlying.(type) {
387-
case nil:
388-
if n.resolver == nil {
389-
panic("nil underlying")
390-
}
391-
case *Named:
392-
n.under() // n.under may add entries to check.defTypes
393-
}
394-
n.check = nil
386+
// cleanup runs cleanup for all collected cleaners.
387+
func (check *Checker) cleanup() {
388+
// Don't use a range clause since Named.cleanup may add more cleaners.
389+
for i := 0; i < len(check.cleaners); i++ {
390+
check.cleaners[i].cleanup()
395391
}
392+
check.cleaners = nil
396393
}
397394

398395
func (check *Checker) record(x *operand) {

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

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ func NewInterfaceType(methods []*Func, embeddeds []Type) *Interface {
3737
}
3838

3939
// set method receivers if necessary
40-
typ := new(Interface)
40+
typ := (*Checker)(nil).newInterface()
4141
for _, m := range methods {
4242
if sig := m.typ.(*Signature); sig.recv == nil {
4343
sig.recv = NewVar(m.pos, m.pkg, "", typ)
@@ -54,6 +54,15 @@ func NewInterfaceType(methods []*Func, embeddeds []Type) *Interface {
5454
return typ
5555
}
5656

57+
// check may be nil
58+
func (check *Checker) newInterface() *Interface {
59+
typ := &Interface{check: check}
60+
if check != nil {
61+
check.needsCleanup(typ)
62+
}
63+
return typ
64+
}
65+
5766
// MarkImplicit marks the interface t as implicit, meaning this interface
5867
// corresponds to a constraint literal such as ~T or A|B without explicit
5968
// interface embedding. MarkImplicit should be called before any concurrent use
@@ -100,6 +109,11 @@ func (t *Interface) String() string { return TypeString(t, nil) }
100109
// ----------------------------------------------------------------------------
101110
// Implementation
102111

112+
func (t *Interface) cleanup() {
113+
t.check = nil
114+
t.embedPos = nil
115+
}
116+
103117
func (check *Checker) interfaceType(ityp *Interface, iface *syntax.InterfaceType, def *Named) {
104118
addEmbedded := func(pos syntax.Pos, typ Type) {
105119
ityp.embeddeds = append(ityp.embeddeds, typ)
@@ -162,16 +176,10 @@ func (check *Checker) interfaceType(ityp *Interface, iface *syntax.InterfaceType
162176
// (don't sort embeddeds: they must correspond to *embedPos entries)
163177
sortMethods(ityp.methods)
164178

165-
// Compute type set with a non-nil *Checker as soon as possible
166-
// to report any errors. Subsequent uses of type sets will use
167-
// this computed type set and won't need to pass in a *Checker.
168-
//
169-
// Pin the checker to the interface type in the interim, in case the type set
170-
// must be used before delayed funcs are processed (see issue #48234).
171-
// TODO(rfindley): clean up use of *Checker with computeInterfaceTypeSet
172-
ityp.check = check
179+
// Compute type set as soon as possible to report any errors.
180+
// Subsequent uses of type sets will use this computed type
181+
// set and won't need to pass in a *Checker.
173182
check.later(func() {
174183
computeInterfaceTypeSet(check, iface.Pos(), ityp)
175-
ityp.check = nil
176184
}).describef(iface, "compute type set for %s", ityp)
177185
}

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

Lines changed: 26 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -72,11 +72,31 @@ func (check *Checker) newNamed(obj *TypeName, orig *Named, underlying Type, tpar
7272
}
7373
// Ensure that typ is always expanded and sanity-checked.
7474
if check != nil {
75-
check.defTypes = append(check.defTypes, typ)
75+
check.needsCleanup(typ)
7676
}
7777
return typ
7878
}
7979

80+
func (t *Named) cleanup() {
81+
// Ensure that every defined type created in the course of type-checking has
82+
// either non-*Named underlying, or is unresolved.
83+
//
84+
// This guarantees that we don't leak any types whose underlying is *Named,
85+
// because any unresolved instances will lazily compute their underlying by
86+
// substituting in the underlying of their origin. The origin must have
87+
// either been imported or type-checked and expanded here, and in either case
88+
// its underlying will be fully expanded.
89+
switch t.underlying.(type) {
90+
case nil:
91+
if t.resolver == nil {
92+
panic("nil underlying")
93+
}
94+
case *Named:
95+
t.under() // t.under may add entries to check.cleaners
96+
}
97+
t.check = nil
98+
}
99+
80100
// Obj returns the type name for the declaration defining the named type t. For
81101
// instantiated types, this is the type name of the base type.
82102
func (t *Named) Obj() *TypeName { return t.orig.obj } // for non-instances this is the same as t.obj
@@ -360,11 +380,11 @@ func expandNamed(ctxt *Context, n *Named, instPos syntax.Pos) (tparams *TypePara
360380
// that it wasn't substituted. In this case we need to create a new
361381
// *Interface before modifying receivers.
362382
if iface == n.orig.underlying {
363-
iface = &Interface{
364-
embeddeds: iface.embeddeds,
365-
complete: iface.complete,
366-
implicit: iface.implicit, // should be false but be conservative
367-
}
383+
old := iface
384+
iface = check.newInterface()
385+
iface.embeddeds = old.embeddeds
386+
iface.complete = old.complete
387+
iface.implicit = old.implicit // should be false but be conservative
368388
underlying = iface
369389
}
370390
iface.methods = methods

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

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -160,7 +160,10 @@ func (subst *subster) typ(typ Type) Type {
160160
methods, mcopied := subst.funcList(t.methods)
161161
embeddeds, ecopied := subst.typeList(t.embeddeds)
162162
if mcopied || ecopied {
163-
iface := &Interface{embeddeds: embeddeds, implicit: t.implicit, complete: t.complete}
163+
iface := subst.check.newInterface()
164+
iface.embeddeds = embeddeds
165+
iface.implicit = t.implicit
166+
iface.complete = t.complete
164167
// If we've changed the interface type, we may need to replace its
165168
// receiver if the receiver type is the original interface. Receivers of
166169
// *Named type are replaced during named type expansion.

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

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ func NewTypeParam(obj *TypeName, constraint Type) *TypeParam {
3636
return (*Checker)(nil).newTypeParam(obj, constraint)
3737
}
3838

39+
// check may be nil
3940
func (check *Checker) newTypeParam(obj *TypeName, constraint Type) *TypeParam {
4041
// Always increment lastID, even if it is not used.
4142
id := nextID()
@@ -50,9 +51,7 @@ func (check *Checker) newTypeParam(obj *TypeName, constraint Type) *TypeParam {
5051
// iface may mutate typ.bound, so we must ensure that iface() is called
5152
// at least once before the resulting TypeParam escapes.
5253
if check != nil {
53-
check.later(func() {
54-
typ.iface()
55-
})
54+
check.needsCleanup(typ)
5655
} else if constraint != nil {
5756
typ.iface()
5857
}
@@ -93,9 +92,12 @@ func (t *TypeParam) String() string { return TypeString(t, nil) }
9392
// ----------------------------------------------------------------------------
9493
// Implementation
9594

95+
func (t *TypeParam) cleanup() {
96+
t.iface()
97+
t.check = nil
98+
}
99+
96100
// iface returns the constraint interface of t.
97-
// TODO(gri) If we make tparamIsIface the default, this should be renamed to under
98-
// (similar to Named.under).
99101
func (t *TypeParam) iface() *Interface {
100102
bound := t.bound
101103

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -342,7 +342,7 @@ func (check *Checker) typInternal(e0 syntax.Expr, def *Named) (T Type) {
342342
return typ
343343

344344
case *syntax.InterfaceType:
345-
typ := new(Interface)
345+
typ := check.newInterface()
346346
def.setUnderlying(typ)
347347
if def != nil {
348348
typ.obj = def.obj

src/go/types/check.go

Lines changed: 35 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,7 @@ type Checker struct {
133133
untyped map[ast.Expr]exprInfo // map of expressions without final type
134134
delayed []action // stack of delayed action segments; segments are processed in FIFO order
135135
objPath []Object // path of object dependencies during type inference (for cycle reporting)
136-
defTypes []*Named // defined types created during type checking, for final validation.
136+
cleaners []cleaner // list of types that may need a final cleanup at the end of type-checking
137137

138138
// environment within which the current object is type-checked (valid only
139139
// for the duration of type-checking a specific object)
@@ -212,6 +212,16 @@ func (check *Checker) pop() Object {
212212
return obj
213213
}
214214

215+
type cleaner interface {
216+
cleanup()
217+
}
218+
219+
// needsCleanup records objects/types that implement the cleanup method
220+
// which will be called at the end of type-checking.
221+
func (check *Checker) needsCleanup(c cleaner) {
222+
check.cleaners = append(check.cleaners, c)
223+
}
224+
215225
// NewChecker returns a new Checker instance for a given package.
216226
// Package files may be added incrementally via checker.Files.
217227
func NewChecker(conf *Config, fset *token.FileSet, pkg *Package, info *Info) *Checker {
@@ -255,6 +265,8 @@ func (check *Checker) initFiles(files []*ast.File) {
255265
check.methods = nil
256266
check.untyped = nil
257267
check.delayed = nil
268+
check.objPath = nil
269+
check.cleaners = nil
258270

259271
// determine package name and collect valid files
260272
pkg := check.pkg
@@ -304,22 +316,37 @@ func (check *Checker) checkFiles(files []*ast.File) (err error) {
304316

305317
defer check.handleBailout(&err)
306318

319+
print := func(msg string) {
320+
if trace {
321+
fmt.Println()
322+
fmt.Println(msg)
323+
}
324+
}
325+
326+
print("== initFiles ==")
307327
check.initFiles(files)
308328

329+
print("== collectObjects ==")
309330
check.collectObjects()
310331

332+
print("== packageObjects ==")
311333
check.packageObjects()
312334

335+
print("== processDelayed ==")
313336
check.processDelayed(0) // incl. all functions
314337

315-
check.expandDefTypes()
338+
print("== cleanup ==")
339+
check.cleanup()
316340

341+
print("== initOrder ==")
317342
check.initOrder()
318343

319344
if !check.conf.DisableUnusedImportCheck {
345+
print("== unusedImports ==")
320346
check.unusedImports()
321347
}
322348

349+
print("== recordUntyped ==")
323350
check.recordUntyped()
324351

325352
if check.firstErr == nil {
@@ -337,7 +364,6 @@ func (check *Checker) checkFiles(files []*ast.File) (err error) {
337364
check.recvTParamMap = nil
338365
check.brokenAliases = nil
339366
check.unionTypeSets = nil
340-
check.defTypes = nil
341367
check.ctxt = nil
342368

343369
// TODO(rFindley) There's more memory we should release at this point.
@@ -365,27 +391,13 @@ func (check *Checker) processDelayed(top int) {
365391
check.delayed = check.delayed[:top]
366392
}
367393

368-
func (check *Checker) expandDefTypes() {
369-
// Ensure that every defined type created in the course of type-checking has
370-
// either non-*Named underlying, or is unresolved.
371-
//
372-
// This guarantees that we don't leak any types whose underlying is *Named,
373-
// because any unresolved instances will lazily compute their underlying by
374-
// substituting in the underlying of their origin. The origin must have
375-
// either been imported or type-checked and expanded here, and in either case
376-
// its underlying will be fully expanded.
377-
for i := 0; i < len(check.defTypes); i++ {
378-
n := check.defTypes[i]
379-
switch n.underlying.(type) {
380-
case nil:
381-
if n.resolver == nil {
382-
panic("nil underlying")
383-
}
384-
case *Named:
385-
n.under() // n.under may add entries to check.defTypes
386-
}
387-
n.check = nil
394+
// cleanup runs cleanup for all collected cleaners.
395+
func (check *Checker) cleanup() {
396+
// Don't use a range clause since Named.cleanup may add more cleaners.
397+
for i := 0; i < len(check.cleaners); i++ {
398+
check.cleaners[i].cleanup()
388399
}
400+
check.cleaners = nil
389401
}
390402

391403
func (check *Checker) record(x *operand) {

0 commit comments

Comments
 (0)