main.mx raw

   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