1 // Regression: a deferred interface method call was dropped in silence.
2 //
3 // buildDeferStmt lowered `defer c.Close()` by building a bound method value,
4 // which the method-value path cannot produce for an interface receiver - that
5 // call is an invoke, an instruction rather than a value - so the defer was
6 // dropped and Close never ran. A deferred method call now goes through a thunk
7 // whose first parameter is the receiver, the way legacy lowers it, which also
8 // keeps `defer c.Close()` free of the capture the bound wrapper used to make.
9 //
10 // The deferred calls must run when their enclosing function returns, so each
11 // step checks the effect before moving on.
12 package main
13 14 type Counter struct {
15 n int32
16 }
17 18 func (c *Counter) Close() {
19 c.n = c.n + 1
20 }
21 22 type Closer interface {
23 Close()
24 }
25 26 func deferConcrete(c *Counter) {
27 defer c.Close()
28 }
29 30 func deferInterface(c Closer) {
31 defer c.Close()
32 }
33 34 func main() {
35 c := &Counter{}
36 deferConcrete(c)
37 if c.n != 1 {
38 panic("deferred concrete method call did not run")
39 }
40 deferInterface(c)
41 if c.n != 2 {
42 panic("deferred interface method call did not run")
43 }
44 println("ok")
45 }
46