Skip to content

Commit ff6d7c2

Browse files
committed
go/types: delay type-checking of function literals
R=go1.11 Functions (at the package level) were collected and their bodies type-checked after all other package-level objects were checked. But function literals where type-checked right away when they were encountered so that they could see the correct, partially populated surrounding scope, and also to mark variables of the surrounding function as used. This approach, while simple, breaks down in esoteric cases where a function literal appears inside the declaration of an object that its body depends on: If the body is type-checked before the object is completely set up, the literal may use incomplete data structures, possibly leading to spurious errors. This change postpones type-checking of function literals to later; after the current expression or statement, but before any changes to the enclosing scope (so that the delayed type-checking sees the correct scope contents). The new mechanism is general and now is also used for other (non-function) delayed checks. Fixes #22992. Change-Id: Ic95f709560858b4bdf8c645be70abe4449f6184d Reviewed-on: https://go-review.googlesource.com/83397 Reviewed-by: Alan Donovan <[email protected]>
1 parent 574fc66 commit ff6d7c2

File tree

11 files changed

+88
-54
lines changed

11 files changed

+88
-54
lines changed

src/go/types/check.go

Lines changed: 7 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -39,14 +39,6 @@ type exprInfo struct {
3939
val constant.Value // constant value; or nil (if not a constant)
4040
}
4141

42-
// funcInfo stores the information required for type-checking a function.
43-
type funcInfo struct {
44-
name string // for debugging/tracing only
45-
decl *declInfo // for cycle detection
46-
sig *Signature
47-
body *ast.BlockStmt
48-
}
49-
5042
// A context represents the context within which an object is type-checked.
5143
type context struct {
5244
decl *declInfo // package-level declaration whose init expression/function body is checked
@@ -96,8 +88,7 @@ type Checker struct {
9688
methods map[string][]*Func // maps package scope type names to associated non-blank, non-interface methods
9789
interfaces map[*TypeName]*ifaceInfo // maps interface type names to corresponding interface infos
9890
untyped map[ast.Expr]exprInfo // map of expressions without final type
99-
funcs []funcInfo // list of functions to type-check
100-
delayed []func() // delayed checks requiring fully setup types
91+
delayed []func() // stack of delayed actions
10192

10293
// context within which the current object is type-checked
10394
// (valid only for the duration of type-checking a specific object)
@@ -153,11 +144,11 @@ func (check *Checker) rememberUntyped(e ast.Expr, lhs bool, mode operandMode, ty
153144
m[e] = exprInfo{lhs, mode, typ, val}
154145
}
155146

156-
func (check *Checker) later(name string, decl *declInfo, sig *Signature, body *ast.BlockStmt) {
157-
check.funcs = append(check.funcs, funcInfo{name, decl, sig, body})
158-
}
159-
160-
func (check *Checker) delay(f func()) {
147+
// later pushes f on to the stack of actions that will be processed later;
148+
// either at the end of the current statement, or in case of a local constant
149+
// or variable declaration, before the constant or variable is in scope
150+
// (so that f still sees the scope before any new declarations).
151+
func (check *Checker) later(f func()) {
161152
check.delayed = append(check.delayed, f)
162153
}
163154

@@ -195,7 +186,6 @@ func (check *Checker) initFiles(files []*ast.File) {
195186
check.methods = nil
196187
check.interfaces = nil
197188
check.untyped = nil
198-
check.funcs = nil
199189
check.delayed = nil
200190

201191
// determine package name and collect valid files
@@ -246,17 +236,10 @@ func (check *Checker) checkFiles(files []*ast.File) (err error) {
246236

247237
check.packageObjects()
248238

249-
check.functionBodies()
239+
check.processDelayed(0) // incl. all functions
250240

251241
check.initOrder()
252242

253-
// perform delayed checks
254-
// (cannot use range - delayed checks may add more delayed checks;
255-
// e.g., when type-checking delayed embedded interfaces)
256-
for i := 0; i < len(check.delayed); i++ {
257-
check.delayed[i]()
258-
}
259-
260243
if !check.conf.DisableUnusedImportCheck {
261244
check.unusedImports()
262245
}

src/go/types/decl.go

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -355,7 +355,9 @@ func (check *Checker) funcDecl(obj *Func, decl *declInfo) {
355355
// function body must be type-checked after global declarations
356356
// (functions implemented elsewhere have no body)
357357
if !check.conf.IgnoreFuncBodies && fdecl.Body != nil {
358-
check.later(obj.name, decl, sig, fdecl.Body)
358+
check.later(func() {
359+
check.funcBody(decl, obj.name, sig, fdecl.Body)
360+
})
359361
}
360362
}
361363

@@ -373,6 +375,8 @@ func (check *Checker) declStmt(decl ast.Decl) {
373375
case *ast.ValueSpec:
374376
switch d.Tok {
375377
case token.CONST:
378+
top := len(check.delayed)
379+
376380
// determine which init exprs to use
377381
switch {
378382
case s.Type != nil || len(s.Values) > 0:
@@ -397,6 +401,9 @@ func (check *Checker) declStmt(decl ast.Decl) {
397401

398402
check.arityMatch(s, last)
399403

404+
// process function literals in init expressions before scope changes
405+
check.processDelayed(top)
406+
400407
// spec: "The scope of a constant or variable identifier declared
401408
// inside a function begins at the end of the ConstSpec or VarSpec
402409
// (ShortVarDecl for short variable declarations) and ends at the
@@ -407,6 +414,8 @@ func (check *Checker) declStmt(decl ast.Decl) {
407414
}
408415

409416
case token.VAR:
417+
top := len(check.delayed)
418+
410419
lhs0 := make([]*Var, len(s.Names))
411420
for i, name := range s.Names {
412421
lhs0[i] = NewVar(name.Pos(), pkg, name.Name, nil)
@@ -447,6 +456,9 @@ func (check *Checker) declStmt(decl ast.Decl) {
447456

448457
check.arityMatch(s, nil)
449458

459+
// process function literals in init expressions before scope changes
460+
check.processDelayed(top)
461+
450462
// declare all variables
451463
// (only at this point are the variable scopes (parents) set)
452464
scopePos := s.End() // see constant declarations

src/go/types/expr.go

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1030,16 +1030,14 @@ func (check *Checker) exprInternal(x *operand, e ast.Expr, hint Type) exprKind {
10301030
// Anonymous functions are considered part of the
10311031
// init expression/func declaration which contains
10321032
// them: use existing package-level declaration info.
1033-
//
1034-
// TODO(gri) We delay type-checking of regular (top-level)
1035-
// function bodies until later. Why don't we do
1036-
// it for closures of top-level expressions?
1037-
// (We can't easily do it for local closures
1038-
// because the surrounding scopes must reflect
1039-
// the exact position where the closure appears
1040-
// in the source; e.g., variables declared below
1041-
// must not be visible).
1042-
check.funcBody(check.decl, "", sig, e.Body)
1033+
decl := check.decl // capture for use in closure below
1034+
// Don't type-check right away because the function may
1035+
// be part of a type definition to which the function
1036+
// body refers. Instead, type-check as soon as possible,
1037+
// but before the enclosing scope contents changes (#22992).
1038+
check.later(func() {
1039+
check.funcBody(decl, "<function literal>", sig, e.Body)
1040+
})
10431041
x.mode = value
10441042
x.typ = sig
10451043
} else {

src/go/types/resolver.go

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -493,10 +493,13 @@ func (a inSourceOrder) Len() int { return len(a) }
493493
func (a inSourceOrder) Less(i, j int) bool { return a[i].order() < a[j].order() }
494494
func (a inSourceOrder) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
495495

496-
// functionBodies typechecks all function bodies.
497-
func (check *Checker) functionBodies() {
498-
for _, f := range check.funcs {
499-
check.funcBody(f.decl, f.name, f.sig, f.body)
496+
// processDelayed processes all delayed actions pushed after top.
497+
func (check *Checker) processDelayed(top int) {
498+
for len(check.delayed) > top {
499+
i := len(check.delayed) - 1
500+
f := check.delayed[i]
501+
check.delayed = check.delayed[:i]
502+
f() // may append to check.delayed
500503
}
501504
}
502505

src/go/types/stmt.go

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77
package types
88

99
import (
10-
"fmt"
1110
"go/ast"
1211
"go/constant"
1312
"go/token"
@@ -16,11 +15,10 @@ import (
1615

1716
func (check *Checker) funcBody(decl *declInfo, name string, sig *Signature, body *ast.BlockStmt) {
1817
if trace {
19-
if name == "" {
20-
name = "<function literal>"
21-
}
22-
fmt.Printf("--- %s: %s {\n", name, sig)
23-
defer fmt.Println("--- <end>")
18+
check.trace(body.Pos(), "--- %s: %s", name, sig)
19+
defer func() {
20+
check.trace(body.End(), "--- <end>")
21+
}()
2422
}
2523

2624
// set function scope extent
@@ -52,8 +50,6 @@ func (check *Checker) funcBody(decl *declInfo, name string, sig *Signature, body
5250

5351
// spec: "Implementation restriction: A compiler may make it illegal to
5452
// declare a variable inside a function body if the variable is never used."
55-
// (One could check each scope after use, but that distributes this check
56-
// over several places because CloseScope is not always called explicitly.)
5753
check.usage(sig.scope)
5854
}
5955

@@ -72,7 +68,7 @@ func (check *Checker) usage(scope *Scope) {
7268
}
7369

7470
for _, scope := range scope.children {
75-
// Don't go inside closure scopes a second time;
71+
// Don't go inside function literal scopes a second time;
7672
// they are handled explicitly by funcBody.
7773
if !scope.isFunc {
7874
check.usage(scope)
@@ -309,6 +305,9 @@ func (check *Checker) stmt(ctxt stmtContext, s ast.Stmt) {
309305
}(check.scope)
310306
}
311307

308+
// process collected function literals before scope changes
309+
defer check.processDelayed(len(check.delayed))
310+
312311
inner := ctxt &^ (fallthroughOk | finalSwitchCase)
313312
switch s := s.(type) {
314313
case *ast.BadStmt, *ast.EmptyStmt:

src/go/types/testdata/constdecl.src

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
package constdecl
66

77
import "math"
8+
import "unsafe"
89

910
var v int
1011

@@ -94,4 +95,16 @@ func _() {
9495
)
9596
}
9697

98+
// Test case for constants depending on function literals (see also #22992).
99+
const A /* ERROR initialization cycle */ = unsafe.Sizeof(func() { _ = A })
100+
101+
func _() {
102+
// The function literal below must not see a.
103+
const a = unsafe.Sizeof(func() { _ = a /* ERROR "undeclared name" */ })
104+
const b = unsafe.Sizeof(func() { _ = a })
105+
106+
// The function literal below must not see x, y, or z.
107+
const x, y, z = 0, 1, unsafe.Sizeof(func() { _ = x /* ERROR "undeclared name" */ + y /* ERROR "undeclared name" */ + z /* ERROR "undeclared name" */ })
108+
}
109+
97110
// TODO(gri) move extra tests from testdata/const0.src into here

src/go/types/testdata/cycles.src

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44

55
package cycles
66

7+
import "unsafe"
8+
79
type (
810
T0 int
911
T1 /* ERROR cycle */ T1
@@ -150,3 +152,12 @@ type (
150152
T16 map[[len(T16 /* ERROR cycle */ {1:2})]int]int
151153
T17 map[int][len(T17 /* ERROR cycle */ {1:2})]int
152154
)
155+
156+
// Test case for types depending on function literals (see also #22992).
157+
type T20 chan [unsafe.Sizeof(func(ch T20){ _ = <-ch })]byte
158+
type T22 = chan [unsafe.Sizeof(func(ch T20){ _ = <-ch })]byte
159+
160+
func _() {
161+
type T1 chan [unsafe.Sizeof(func(ch T1){ _ = <-ch })]byte
162+
type T2 = chan [unsafe.Sizeof(func(ch T2){ _ = <-ch })]byte
163+
}

src/go/types/testdata/cycles5.src

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,8 @@ type (
112112

113113
// arbitrary code may appear inside an interface
114114

115+
const n = unsafe.Sizeof(func(){})
116+
115117
type I interface {
116-
m([unsafe.Sizeof(func() { I.m(nil) })]byte) // should report an error (see #22992)
118+
m([unsafe.Sizeof(func() { I.m(nil, [n]byte{}) })]byte)
117119
}

src/go/types/testdata/init0.src

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ var x8 = x7
7575
func f2() (int, int) { return f3() + f3(), 0 }
7676
func f3() int { return x8 }
7777

78-
// cycles via closures
78+
// cycles via function literals
7979

8080
var x9 /* ERROR initialization cycle */ = func() int { return x9 }()
8181

src/go/types/testdata/vardecl.src

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -151,7 +151,7 @@ func (r T) _(a, b, c int) (u, v, w int) {
151151
return
152152
}
153153

154-
// Unused variables in closures must lead to only one error (issue #22524).
154+
// Unused variables in function literals must lead to only one error (issue #22524).
155155
func _() {
156156
_ = func() {
157157
var x /* ERROR declared but not used */ int
@@ -190,4 +190,17 @@ func _() {
190190
_ = b
191191
}
192192

193+
// Test case for variables depending on function literals (see also #22992).
194+
var A /* ERROR initialization cycle */ = func() int { return A }()
195+
196+
func _() {
197+
// The function literal below must not see a.
198+
var a = func() int { return a /* ERROR "undeclared name" */ }()
199+
var _ = func() int { return a }()
200+
201+
// The function literal below must not see x, y, or z.
202+
var x, y, z = 0, 1, func() int { return x /* ERROR "undeclared name" */ + y /* ERROR "undeclared name" */ + z /* ERROR "undeclared name" */ }()
203+
_, _, _ = x, y, z
204+
}
205+
193206
// TODO(gri) consolidate other var decl checks in this file

src/go/types/typexpr.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -317,7 +317,7 @@ func (check *Checker) typExprInternal(e ast.Expr, def *Named, path []*TypeName)
317317
//
318318
// Delay this check because it requires fully setup types;
319319
// it is safe to continue in any case (was issue 6667).
320-
check.delay(func() {
320+
check.later(func() {
321321
if !Comparable(typ.key) {
322322
check.errorf(e.Key.Pos(), "invalid map key type %s", typ.key)
323323
}
@@ -478,8 +478,8 @@ func (check *Checker) interfaceType(ityp *Interface, iface *ast.InterfaceType, d
478478
// collect embedded interfaces
479479
// Only needed for printing and API. Delay collection
480480
// to end of type-checking when all types are complete.
481-
interfaceScope := check.scope // capture for use in delayed function
482-
check.delay(func() {
481+
interfaceScope := check.scope // capture for use in closure below
482+
check.later(func() {
483483
check.scope = interfaceScope
484484
if trace {
485485
check.trace(iface.Pos(), "-- delayed checking embedded interfaces of %s", iface)

0 commit comments

Comments
 (0)