asoffer / Icarus

An experimental general-purpose programming language
Apache License 2.0
9 stars 2 forks source link

Scope state objects are never destroyed #61

Closed perimosocordiae closed 2 years ago

perimosocordiae commented 3 years ago

At least, as far as I can tell. I first noticed this in gh-60.

I'll update with a reproducer soon.

perimosocordiae commented 3 years ago

Reproducer:

io ::= import "io.ic"

State ::= struct {
  (copy) ::= (self: *State) -> State {
    io.Print("Copying State now\n")
    return State.{}
  }
  (destroy) ::= (self: *State) -> () {
    io.Print("Destroying State now\n")
  }
}

Demo ::= scope(State) {
  enter ::= jump [state: *State] () {
    io.Print("in enter\n")
    goto ok()
  }
  ok ::= block {
    before ::= () -> () {
      io.Print("in ok.before\n")
    }
    after ::= jump [state: *State] () {
      io.Print("in ok.after\n")
      goto done()
    }
  }
  exit ::= () -> () {
    io.Print("in exit\n")
  }
}

io.Print("before scope\n")
Demo () ok {
  io.Print("during scope\n")
}
io.Print("after scope\n")

Output:

before scope
in enter
in ok.before
during scope
in ok.after
after scope

So it turns out we're not destroying the State struct, but also we're never calling the exit function. I did a little code spelunking and found this suspicious pair of TODOs: https://github.com/asoffer/Icarus/blob/2cd5779aa733e5153801422685505085cd8de7bc/compiler/emit/yield_stmt.cc#L39-L40

asoffer commented 3 years ago

Yeah there's still a fair bit of design work needed here, both for the spec (interaction with labeled yields) and the code emission (do we prefer branching or code duplication)?

At least some of this can be fixed readily. The non-labeled case shouldn't be hard for local yields. Returning out nested blocks isnt bad either.

asoffer commented 2 years ago

This is obsoleted by #81