myst-lang / myst

A structured, dynamic, general-purpose language.
http://myst-lang.org
MIT License
119 stars 17 forks source link

No variable or method `x` for y, thrown after a potentially raising method is called #155

Closed Jens0512 closed 6 years ago

Jens0512 commented 6 years ago

This is as minimal as I could get it:

def foo(a, b)
  bar(a) + (b * 2)
end

def bar(a)
  when a == 1
    return (a.to_s * 5)
  else
    raise "<(a)> must be 1!"
  end
end

STDOUT.puts(foo(1, 3))

This is expected to print "111116\n", but instead it raises the following error:

Uncaught Exception: No variable or method `b` for Kernel
  from `b` at .../example.mt:2:13
  from `+` at .../example.mt:2:3
  from `foo` at .../example.mt:13:13
  from `puts` at .../example.mt:13:8

This is where the error occurs:

def foo(a, b)
  bar(a) + (b * 2) # "No variable or method `b` for Kernel"
end
Jens0512 commented 6 years ago

If you remove the raise ..., the error is not thrown

faultyserver commented 6 years ago

I misread this issue the first few times and assumed there was a rescue on bar, so I was attributing the bug with how ExceptionHandler deals with popping scopes.

The fact that there isn't a rescue or ensure involved here is more confusing. A raise that doesn't get executed shouldn't have any effect on the surrounding code, and I'm really not sure how it could be leaking like this. Will definitely need to look more into it.

faultyserver commented 6 years ago

Messed around with it a little bit and it looks like this is actually an issue with return. Here's a minimized example that raises the same error:

def bar(a)
  return a
end

def foo(a, b)
  bar(a)
  b
end

foo(1, 3)

Removing the explicit return from line 2 fixes the error. With that, this issue is probably happening in the Invocation struct, where invoke handles rescuing BreakException, NextException, and ReturnException.

faultyserver commented 6 years ago

I think explicit returns/breaks might also have issues with restoring the correct value of self. This failed build occurred because an explicit return in IO#gets skipped popping a value from the self_stack: https://travis-ci.org/myst-lang/myst/builds/341175297

faultyserver commented 6 years ago

Some more testing makes this look like an issue with scope_stack rather than self_stack. Still looking into it.