Skip to content

Commit c4817f5

Browse files
committed
cmd/compile: on Wasm and AIX, let deferred nil function panic at invocation
The Go spec requires If a deferred function value evaluates to nil, execution panics when the function is invoked, not when the "defer" statement is executed. On Wasm and AIX, currently we actually emit a nil check at the point of defer statement, which will make it panic too early. This CL fixes this. Also, on Wasm, now the nil function will be passed through deferreturn to jmpdefer, which does an explicit nil check and calls sigpanic if it is nil. This sigpanic, being called from assembly, is ABI0. So change the assembler backend to also handle sigpanic in ABI0. Fixes #34926. Updates #8047. Change-Id: I28489a571cee36d2aef041f917b8cfdc31d557d4 Reviewed-on: https://go-review.googlesource.com/c/go/+/201297 Reviewed-by: Brad Fitzpatrick <[email protected]>
1 parent dc37bd2 commit c4817f5

File tree

3 files changed

+38
-2
lines changed

3 files changed

+38
-2
lines changed

src/cmd/compile/internal/gc/ssa.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3779,7 +3779,8 @@ func (s *state) call(n *Node, k callKind) *ssa.Value {
37793779
break
37803780
}
37813781
closure = s.expr(fn)
3782-
if thearch.LinkArch.Family == sys.Wasm || objabi.GOOS == "aix" && k != callGo {
3782+
if k != callDefer && k != callDeferStack && (thearch.LinkArch.Family == sys.Wasm || objabi.GOOS == "aix" && k != callGo) {
3783+
// Deferred nil function needs to panic when the function is invoked, not the point of defer statement.
37833784
// On AIX, the closure needs to be verified as fn can be nil, except if it's a call go. This needs to be handled by the runtime to have the "go of nil func value" error.
37843785
// TODO(neelance): On other architectures this should be eliminated by the optimization steps
37853786
s.nilCheck(closure)

src/cmd/internal/obj/wasm/wasmobj.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,7 @@ var (
129129
morestackNoCtxt *obj.LSym
130130
gcWriteBarrier *obj.LSym
131131
sigpanic *obj.LSym
132+
sigpanic0 *obj.LSym
132133
deferreturn *obj.LSym
133134
jmpdefer *obj.LSym
134135
)
@@ -143,6 +144,7 @@ func instinit(ctxt *obj.Link) {
143144
morestackNoCtxt = ctxt.Lookup("runtime.morestack_noctxt")
144145
gcWriteBarrier = ctxt.Lookup("runtime.gcWriteBarrier")
145146
sigpanic = ctxt.LookupABI("runtime.sigpanic", obj.ABIInternal)
147+
sigpanic0 = ctxt.LookupABI("runtime.sigpanic", 0) // sigpanic called from assembly, which has ABI0
146148
deferreturn = ctxt.LookupABI("runtime.deferreturn", obj.ABIInternal)
147149
// jmpdefer is defined in assembly as ABI0, but what we're
148150
// looking for is the *call* to jmpdefer from the Go function
@@ -491,7 +493,7 @@ func preprocess(ctxt *obj.Link, s *obj.LSym, newprog obj.ProgAlloc) {
491493
}
492494

493495
// return value of call is on the top of the stack, indicating whether to unwind the WebAssembly stack
494-
if call.As == ACALLNORESUME && call.To.Sym != sigpanic { // sigpanic unwinds the stack, but it never resumes
496+
if call.As == ACALLNORESUME && call.To.Sym != sigpanic && call.To.Sym != sigpanic0 { // sigpanic unwinds the stack, but it never resumes
495497
// trying to unwind WebAssembly stack but call has no resume point, terminate with error
496498
p = appendp(p, AIf)
497499
p = appendp(p, obj.AUNDEF)

test/defernil.go

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
// run
2+
3+
// Copyright 2019 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+
// Check that deferring a nil function causes a proper
8+
// panic when the deferred function is invoked (not
9+
// when the function is deferred).
10+
// See Issue #8047 and #34926.
11+
12+
package main
13+
14+
var x = 0
15+
16+
func main() {
17+
defer func() {
18+
err := recover()
19+
if err == nil {
20+
panic("did not panic")
21+
}
22+
if x != 1 {
23+
panic("FAIL")
24+
}
25+
}()
26+
f()
27+
}
28+
29+
func f() {
30+
var nilf func()
31+
defer nilf()
32+
x = 1
33+
}

0 commit comments

Comments
 (0)