Skip to content

Commit d311cc9

Browse files
committed
cmd/compile: change status of "bad iterator" panic
Execution of the loop body previously either terminated the iteration (returned false because of a break, goto, or return) or actually panicked. The check against abi.RF_READY ensures that the body can no longer run and also panics. This CL in addition transitions the loop state to abi.RF_PANIC so that if this already badly-behaved iterator defer-recovers this panic, then the exit check at the loop context will catch the problem and panic there. Previously, panics triggered by attempted execution of a no-longer active loop would not trigger a panic at the loop context if they were defer-recovered. Change-Id: Ieeed2fafd0d65edb66098dc27dc9ae8c1e6bcc8c Reviewed-on: https://go-review.googlesource.com/c/go/+/625455 LUCI-TryBot-Result: Go LUCI <[email protected]> Reviewed-by: Tim King <[email protected]>
1 parent 7c40544 commit d311cc9

File tree

2 files changed

+142
-63
lines changed

2 files changed

+142
-63
lines changed

src/cmd/compile/internal/rangefunc/rangefunc_test.go

+107-46
Original file line numberDiff line numberDiff line change
@@ -185,10 +185,11 @@ func Check2[U, V any](forall Seq2[U, V]) Seq2[U, V] {
185185
return func(body func(U, V) bool) {
186186
state := READY
187187
forall(func(u U, v V) bool {
188-
if state != READY {
189-
panic(fail[state])
190-
}
188+
tmp := state
191189
state = PANIC
190+
if tmp != READY {
191+
panic(fail[tmp])
192+
}
192193
ret := body(u, v)
193194
if ret {
194195
state = READY
@@ -208,10 +209,11 @@ func Check[U any](forall Seq[U]) Seq[U] {
208209
return func(body func(U) bool) {
209210
state := READY
210211
forall(func(u U) bool {
211-
if state != READY {
212-
panic(fail[state])
213-
}
212+
tmp := state
214213
state = PANIC
214+
if tmp != READY {
215+
panic(fail[tmp])
216+
}
215217
ret := body(u)
216218
if ret {
217219
state = READY
@@ -1122,12 +1124,12 @@ func TestPanickyIterator1(t *testing.T) {
11221124
} else {
11231125
t.Errorf("Saw wrong panic '%v'", r)
11241126
}
1127+
if !slices.Equal(expect, result) {
1128+
t.Errorf("Expected %v, got %v", expect, result)
1129+
}
11251130
} else {
11261131
t.Errorf("Wanted to see a failure, result was %v", result)
11271132
}
1128-
if !slices.Equal(expect, result) {
1129-
t.Errorf("Expected %v, got %v", expect, result)
1130-
}
11311133
}()
11321134
for _, z := range PanickyOfSliceIndex([]int{1, 2, 3, 4}) {
11331135
result = append(result, z)
@@ -1172,12 +1174,12 @@ func TestPanickyIterator2(t *testing.T) {
11721174
} else {
11731175
t.Errorf("Saw wrong panic '%v'", r)
11741176
}
1177+
if !slices.Equal(expect, result) {
1178+
t.Errorf("Expected %v, got %v", expect, result)
1179+
}
11751180
} else {
11761181
t.Errorf("Wanted to see a failure, result was %v", result)
11771182
}
1178-
if !slices.Equal(expect, result) {
1179-
t.Errorf("Expected %v, got %v", expect, result)
1180-
}
11811183
}()
11821184
for _, x := range OfSliceIndex([]int{100, 200}) {
11831185
result = append(result, x)
@@ -1207,11 +1209,11 @@ func TestPanickyIterator2Check(t *testing.T) {
12071209
} else {
12081210
t.Errorf("Saw wrong panic '%v'", r)
12091211
}
1212+
if !slices.Equal(expect, result) {
1213+
t.Errorf("Expected %v, got %v", expect, result)
1214+
}
12101215
} else {
1211-
t.Errorf("Wanted to see a failure, result was %v", result)
1212-
}
1213-
if !slices.Equal(expect, result) {
1214-
t.Errorf("Expected %v, got %v", expect, result)
1216+
t.Errorf("Wanted to see a panic, result was %v", result)
12151217
}
12161218
}()
12171219
for _, x := range Check2(OfSliceIndex([]int{100, 200})) {
@@ -1234,13 +1236,19 @@ func TestPanickyIterator2Check(t *testing.T) {
12341236

12351237
func TestPanickyIterator3(t *testing.T) {
12361238
var result []int
1237-
var expect = []int{100, 10, 1, 2, 200, 10, 1, 2}
1239+
var expect = []int{100, 10, 1, 2}
12381240
defer func() {
12391241
if r := recover(); r != nil {
1240-
t.Errorf("Unexpected panic '%v'", r)
1241-
}
1242-
if !slices.Equal(expect, result) {
1243-
t.Errorf("Expected %v, got %v", expect, result)
1242+
if matchError(r, RERR_MISSING) {
1243+
t.Logf("Saw expected panic '%v'", r)
1244+
} else {
1245+
t.Errorf("Saw wrong panic '%v'", r)
1246+
}
1247+
if !slices.Equal(expect, result) {
1248+
t.Errorf("Expected %v, got %v", expect, result)
1249+
}
1250+
} else {
1251+
t.Errorf("Wanted to see a panic, result was %v", result)
12441252
}
12451253
}()
12461254
for _, x := range OfSliceIndex([]int{100, 200}) {
@@ -1262,13 +1270,19 @@ func TestPanickyIterator3(t *testing.T) {
12621270
}
12631271
func TestPanickyIterator3Check(t *testing.T) {
12641272
var result []int
1265-
var expect = []int{100, 10, 1, 2, 200, 10, 1, 2}
1273+
var expect = []int{100, 10, 1, 2}
12661274
defer func() {
12671275
if r := recover(); r != nil {
1268-
t.Errorf("Unexpected panic '%v'", r)
1269-
}
1270-
if !slices.Equal(expect, result) {
1271-
t.Errorf("Expected %v, got %v", expect, result)
1276+
if matchError(r, CERR_MISSING) {
1277+
t.Logf("Saw expected panic '%v'", r)
1278+
} else {
1279+
t.Errorf("Saw wrong panic '%v'", r)
1280+
}
1281+
if !slices.Equal(expect, result) {
1282+
t.Errorf("Expected %v, got %v", expect, result)
1283+
}
1284+
} else {
1285+
t.Errorf("Wanted to see a panic, result was %v", result)
12721286
}
12731287
}()
12741288
for _, x := range Check2(OfSliceIndex([]int{100, 200})) {
@@ -1298,9 +1312,11 @@ func TestPanickyIterator4(t *testing.T) {
12981312
} else {
12991313
t.Errorf("Saw wrong panic '%v'", r)
13001314
}
1301-
}
1302-
if !slices.Equal(expect, result) {
1303-
t.Errorf("Expected %v, got %v", expect, result)
1315+
if !slices.Equal(expect, result) {
1316+
t.Errorf("Expected %v, got %v", expect, result)
1317+
}
1318+
} else {
1319+
t.Errorf("Wanted to see a panic, result was %v", result)
13041320
}
13051321
}()
13061322
for _, x := range SwallowPanicOfSliceIndex([]int{1, 2, 3, 4}) {
@@ -1321,9 +1337,11 @@ func TestPanickyIterator4Check(t *testing.T) {
13211337
} else {
13221338
t.Errorf("Saw wrong panic '%v'", r)
13231339
}
1324-
}
1325-
if !slices.Equal(expect, result) {
1326-
t.Errorf("Expected %v, got %v", expect, result)
1340+
if !slices.Equal(expect, result) {
1341+
t.Errorf("Expected %v, got %v", expect, result)
1342+
}
1343+
} else {
1344+
t.Errorf("Wanted to see a panic, result was %v", result)
13271345
}
13281346
}()
13291347
for _, x := range Check2(SwallowPanicOfSliceIndex([]int{1, 2, 3, 4})) {
@@ -1409,33 +1427,76 @@ X:
14091427

14101428
// TestVeryBad1 checks the behavior of an extremely poorly behaved iterator.
14111429
func TestVeryBad1(t *testing.T) {
1412-
result := veryBad([]int{10, 20, 30, 40, 50}) // odd length
1413-
expect := []int{1, 10}
1430+
expect := []int{} // assignment does not happen
1431+
var result []int
14141432

1415-
if !slices.Equal(expect, result) {
1416-
t.Errorf("Expected %v, got %v", expect, result)
1433+
defer func() {
1434+
if r := recover(); r != nil {
1435+
expectPanic(t, r, RERR_MISSING)
1436+
if !slices.Equal(expect, result) {
1437+
t.Errorf("(Inner) Expected %v, got %v", expect, result)
1438+
}
1439+
} else {
1440+
t.Error("Wanted to see a failure")
1441+
}
1442+
}()
1443+
1444+
result = veryBad([]int{10, 20, 30, 40, 50}) // odd length
1445+
1446+
}
1447+
1448+
func expectPanic(t *testing.T, r any, s string) {
1449+
if matchError(r, s) {
1450+
t.Logf("Saw expected panic '%v'", r)
1451+
} else {
1452+
t.Errorf("Saw wrong panic '%v'", r)
1453+
}
1454+
}
1455+
1456+
func expectError(t *testing.T, err any, s string) {
1457+
if matchError(err, s) {
1458+
t.Logf("Saw expected error '%v'", err)
1459+
} else {
1460+
t.Errorf("Saw wrong error '%v'", err)
14171461
}
14181462
}
14191463

14201464
// TestVeryBad2 checks the behavior of an extremely poorly behaved iterator.
14211465
func TestVeryBad2(t *testing.T) {
1422-
result := veryBad([]int{10, 20, 30, 40}) // even length
1423-
expect := []int{1, 10}
1466+
result := []int{}
1467+
expect := []int{}
1468+
1469+
defer func() {
1470+
if r := recover(); r != nil {
1471+
expectPanic(t, r, RERR_MISSING)
1472+
if !slices.Equal(expect, result) {
1473+
t.Errorf("(Inner) Expected %v, got %v", expect, result)
1474+
}
1475+
} else {
1476+
t.Error("Wanted to see a failure")
1477+
}
1478+
}()
1479+
1480+
result = veryBad([]int{10, 20, 30, 40}) // even length
14241481

1425-
if !slices.Equal(expect, result) {
1426-
t.Errorf("Expected %v, got %v", expect, result)
1427-
}
14281482
}
14291483

14301484
// TestVeryBadCheck checks the behavior of an extremely poorly behaved iterator,
14311485
// which also suppresses the exceptions from "Check"
14321486
func TestVeryBadCheck(t *testing.T) {
1433-
result := veryBadCheck([]int{10, 20, 30, 40}) // even length
1434-
expect := []int{1, 10}
1487+
expect := []int{}
1488+
var result []int
1489+
defer func() {
1490+
if r := recover(); r != nil {
1491+
expectPanic(t, r, CERR_MISSING)
1492+
}
1493+
if !slices.Equal(expect, result) {
1494+
t.Errorf("Expected %v, got %v", expect, result)
1495+
}
1496+
}()
1497+
1498+
result = veryBadCheck([]int{10, 20, 30, 40}) // even length
14351499

1436-
if !slices.Equal(expect, result) {
1437-
t.Errorf("Expected %v, got %v", expect, result)
1438-
}
14391500
}
14401501

14411502
// TestOk is the nice version of the very bad iterator.

src/cmd/compile/internal/rangefunc/rewrite.go

+35-17
Original file line numberDiff line numberDiff line change
@@ -157,8 +157,9 @@ The value of #stateK transitions
157157
158158
(3) at the beginning of the iteration of the loop body,
159159
160-
if #stateN != abi.RF_READY { runtime.panicrangestate(#stateN) }
160+
if #stateN != abi.RF_READY { #stateN = abi.RF_PANIC ; runtime.panicrangestate(#stateN) }
161161
#stateN = abi.RF_PANIC
162+
// This is slightly rearranged below for better code generation.
162163
163164
(4) when loop iteration continues,
164165
@@ -183,7 +184,7 @@ becomes
183184
{
184185
var #state1 = abi.RF_READY
185186
f(func(x T1) bool {
186-
if #state1 != abi.RF_READY { runtime.panicrangestate(#state1) }
187+
if #state1 != abi.RF_READY { #state1 = abi.RF_PANIC; runtime.panicrangestate(#state1) }
187188
#state1 = abi.RF_PANIC
188189
...
189190
if ... { #state1 = abi.RF_DONE ; return false }
@@ -232,11 +233,11 @@ becomes
232233
)
233234
var #state1 = abi.RF_READY
234235
f(func() bool {
235-
if #state1 != abi.RF_READY { runtime.panicrangestate(#state1) }
236+
if #state1 != abi.RF_READY { #state1 = abi.RF_PANIC; runtime.panicrangestate(#state1) }
236237
#state1 = abi.RF_PANIC
237238
var #state2 = abi.RF_READY
238239
g(func() bool {
239-
if #state2 != abi.RF_READY { runtime.panicrangestate(#state2) }
240+
if #state2 != abi.RF_READY { #state2 = abi.RF_PANIC; runtime.panicrangestate(#state2) }
240241
...
241242
{
242243
// return a, b
@@ -324,15 +325,15 @@ becomes
324325
var #next int
325326
var #state1 = abi.RF_READY
326327
f(func() { // 1,2
327-
if #state1 != abi.RF_READY { runtime.panicrangestate(#state1) }
328+
if #state1 != abi.RF_READY { #state1 = abi.RF_PANIC; runtime.panicrangestate(#state1) }
328329
#state1 = abi.RF_PANIC
329330
var #state2 = abi.RF_READY
330331
g(func() { // 3,4
331-
if #state2 != abi.RF_READY { runtime.panicrangestate(#state2) }
332+
if #state2 != abi.RF_READY { #state2 = abi.RF_PANIC; runtime.panicrangestate(#state2) }
332333
#state2 = abi.RF_PANIC
333334
var #state3 = abi.RF_READY
334335
h(func() { // 5,6
335-
if #state3 != abi.RF_READY { runtime.panicrangestate(#state3) }
336+
if #state3 != abi.RF_READY { #state3 = abi.RF_PANIC; runtime.panicrangestate(#state3) }
336337
#state3 = abi.RF_PANIC
337338
...
338339
{
@@ -425,16 +426,16 @@ becomes
425426
var #next int
426427
var #state1 = abi.RF_READY
427428
f(func() {
428-
if #state1 != abi.RF_READY{ runtime.panicrangestate(#state1) }
429+
if #state1 != abi.RF_READY{ #state1 = abi.RF_PANIC; runtime.panicrangestate(#state1) }
429430
#state1 = abi.RF_PANIC
430431
var #state2 = abi.RF_READY
431432
g(func() {
432-
if #state2 != abi.RF_READY { runtime.panicrangestate(#state2) }
433+
if #state2 != abi.RF_READY { #state2 = abi.RF_PANIC; runtime.panicrangestate(#state2) }
433434
#state2 = abi.RF_PANIC
434435
...
435436
var #state3 bool = abi.RF_READY
436437
h(func() {
437-
if #state3 != abi.RF_READY { runtime.panicrangestate(#state3) }
438+
if #state3 != abi.RF_READY { #state3 = abi.RF_PANIC; runtime.panicrangestate(#state3) }
438439
#state3 = abi.RF_PANIC
439440
...
440441
{
@@ -1182,8 +1183,25 @@ func (r *rewriter) bodyFunc(body []syntax.Stmt, lhs []syntax.Expr, def bool, fty
11821183
loop := r.forStack[len(r.forStack)-1]
11831184

11841185
if r.checkFuncMisuse() {
1185-
bodyFunc.Body.List = append(bodyFunc.Body.List, r.assertReady(start, loop))
1186+
// #tmpState := #stateVarN
1187+
// #stateVarN = abi.RF_PANIC
1188+
// if #tmpState != abi.RF_READY {
1189+
// runtime.panicrangestate(#tmpState)
1190+
// }
1191+
//
1192+
// That is a slightly code-size-optimized version of
1193+
//
1194+
// if #stateVarN != abi.RF_READY {
1195+
// #stateVarN = abi.RF_PANIC // If we ever need to specially detect "iterator swallowed checking panic" we put a different value here.
1196+
// runtime.panicrangestate(#tmpState)
1197+
// }
1198+
// #stateVarN = abi.RF_PANIC
1199+
//
1200+
1201+
tmpDecl, tmpState := r.declSingleVar("#tmpState", r.int.Type(), r.useObj(loop.stateVar))
1202+
bodyFunc.Body.List = append(bodyFunc.Body.List, tmpDecl)
11861203
bodyFunc.Body.List = append(bodyFunc.Body.List, r.setState(abi.RF_PANIC, start))
1204+
bodyFunc.Body.List = append(bodyFunc.Body.List, r.assertReady(start, tmpState))
11871205
}
11881206

11891207
// Original loop body (already rewritten by editStmt during inspect).
@@ -1327,14 +1345,14 @@ func setValueType(x syntax.Expr, typ syntax.Type) {
13271345

13281346
// assertReady returns the statement:
13291347
//
1330-
// if #stateK != abi.RF_READY { runtime.panicrangestate(#stateK) }
1331-
//
1332-
// where #stateK is the state variable for loop.
1333-
func (r *rewriter) assertReady(start syntax.Pos, loop *forLoop) syntax.Stmt {
1348+
// if #tmpState != abi.RF_READY { runtime.panicrangestate(#tmpState) }
1349+
func (r *rewriter) assertReady(start syntax.Pos, tmpState *types2.Var) syntax.Stmt {
1350+
13341351
nif := &syntax.IfStmt{
1335-
Cond: r.cond(syntax.Neq, r.useObj(loop.stateVar), r.stateConst(abi.RF_READY)),
1352+
Cond: r.cond(syntax.Neq, r.useObj(tmpState), r.stateConst(abi.RF_READY)),
13361353
Then: &syntax.BlockStmt{
1337-
List: []syntax.Stmt{r.callPanic(start, r.useObj(loop.stateVar))},
1354+
List: []syntax.Stmt{
1355+
r.callPanic(start, r.useObj(tmpState))},
13381356
},
13391357
}
13401358
setPos(nif, start)

0 commit comments

Comments
 (0)