]> Cypherpunks repositories - gostls13.git/commitdiff
cmd/compile: fix panic with dead hidden closures
authorCuong Manh Le <cuong.manhle.vn@gmail.com>
Sun, 15 Aug 2021 19:15:28 +0000 (02:15 +0700)
committerCuong Manh Le <cuong.manhle.vn@gmail.com>
Mon, 16 Aug 2021 18:20:12 +0000 (18:20 +0000)
Currently, for hidden closures, we always push them to compile queue
during typechecking. If the hidden closure is discarded from the outer
function body during deadcode, any desugaring phase after deadcode won't
be applied to the closure. Thus, some un-expected OPs are passed to
downstream passes, which they can't handle, the compiler goes boom!

To fix this, we keep track of discarded hidden closures during deadcode
pass, and won't compile them then.

Fixes #47712

Change-Id: I078717d5d1f4f2fa39cbaf610cfffbb042e70ceb
Reviewed-on: https://go-review.googlesource.com/c/go/+/342350
Trust: Cuong Manh Le <cuong.manhle.vn@gmail.com>
Run-TryBot: Cuong Manh Le <cuong.manhle.vn@gmail.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
src/cmd/compile/internal/deadcode/deadcode.go
src/cmd/compile/internal/gc/main.go
src/cmd/compile/internal/ir/func.go
test/fixedbugs/issue47712.go [new file with mode: 0644]

index 520203787f02dc97968e4cd71c577f7fc646d24d..3658c89912b85f52f30d3fcf0d94f2d9f6aa0617 100644 (file)
@@ -38,6 +38,7 @@ func Func(fn *ir.Func) {
                }
        }
 
+       ir.VisitList(fn.Body, markHiddenClosureDead)
        fn.Body = []ir.Node{ir.NewBlockStmt(base.Pos, nil)}
 }
 
@@ -62,9 +63,11 @@ func stmts(nn *ir.Nodes) {
                        if ir.IsConst(n.Cond, constant.Bool) {
                                var body ir.Nodes
                                if ir.BoolVal(n.Cond) {
+                                       ir.VisitList(n.Else, markHiddenClosureDead)
                                        n.Else = ir.Nodes{}
                                        body = n.Body
                                } else {
+                                       ir.VisitList(n.Body, markHiddenClosureDead)
                                        n.Body = ir.Nodes{}
                                        body = n.Else
                                }
@@ -150,3 +153,13 @@ func expr(n ir.Node) ir.Node {
        }
        return n
 }
+
+func markHiddenClosureDead(n ir.Node) {
+       if n.Op() != ir.OCLOSURE {
+               return
+       }
+       clo := n.(*ir.ClosureExpr)
+       if clo.Func.IsHiddenClosure() {
+               clo.Func.SetIsDeadcodeClosure(true)
+       }
+}
index 6a373ce33d7851cc121ec9e49833d4156e1435a5..9660ef9dd57c22d18226c299b0a1934ac904a17c 100644 (file)
@@ -289,6 +289,10 @@ func Main(archInit func(*ssagen.ArchInfo)) {
        fcount := int64(0)
        for i := 0; i < len(typecheck.Target.Decls); i++ {
                if fn, ok := typecheck.Target.Decls[i].(*ir.Func); ok {
+                       // Don't try compiling dead hidden closure.
+                       if fn.IsDeadcodeClosure() {
+                               continue
+                       }
                        enqueueFunc(fn)
                        fcount++
                }
index 269b6f14ec1873188c0ca353ce912cd810cfcad5..18d0b023ad6e916c94cc98bec2787e6cba4bff15 100644 (file)
@@ -196,6 +196,7 @@ const (
        // true if closure inside a function; false if a simple function or a
        // closure in a global variable initialization
        funcIsHiddenClosure
+       funcIsDeadcodeClosure        // true if closure is deadcode
        funcHasDefer                 // contains a defer statement
        funcNilCheckDisabled         // disable nil checks when compiling this function
        funcInlinabilityChecked      // inliner has already determined whether the function is inlinable
@@ -216,6 +217,7 @@ func (f *Func) ABIWrapper() bool               { return f.flags&funcABIWrapper !
 func (f *Func) Needctxt() bool                 { return f.flags&funcNeedctxt != 0 }
 func (f *Func) ReflectMethod() bool            { return f.flags&funcReflectMethod != 0 }
 func (f *Func) IsHiddenClosure() bool          { return f.flags&funcIsHiddenClosure != 0 }
+func (f *Func) IsDeadcodeClosure() bool        { return f.flags&funcIsDeadcodeClosure != 0 }
 func (f *Func) HasDefer() bool                 { return f.flags&funcHasDefer != 0 }
 func (f *Func) NilCheckDisabled() bool         { return f.flags&funcNilCheckDisabled != 0 }
 func (f *Func) InlinabilityChecked() bool      { return f.flags&funcInlinabilityChecked != 0 }
@@ -230,6 +232,7 @@ func (f *Func) SetABIWrapper(b bool)               { f.flags.set(funcABIWrapper,
 func (f *Func) SetNeedctxt(b bool)                 { f.flags.set(funcNeedctxt, b) }
 func (f *Func) SetReflectMethod(b bool)            { f.flags.set(funcReflectMethod, b) }
 func (f *Func) SetIsHiddenClosure(b bool)          { f.flags.set(funcIsHiddenClosure, b) }
+func (f *Func) SetIsDeadcodeClosure(b bool)        { f.flags.set(funcIsDeadcodeClosure, b) }
 func (f *Func) SetHasDefer(b bool)                 { f.flags.set(funcHasDefer, b) }
 func (f *Func) SetNilCheckDisabled(b bool)         { f.flags.set(funcNilCheckDisabled, b) }
 func (f *Func) SetInlinabilityChecked(b bool)      { f.flags.set(funcInlinabilityChecked, b) }
diff --git a/test/fixedbugs/issue47712.go b/test/fixedbugs/issue47712.go
new file mode 100644 (file)
index 0000000..81a2681
--- /dev/null
@@ -0,0 +1,23 @@
+// compile
+
+// 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 p
+
+func f() {
+       if false {
+               defer func() {
+                       _ = recover()
+               }()
+       }
+}
+
+func g() {
+       for false {
+               defer func() {
+                       _ = recover()
+               }()
+       }
+}