Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

disallow return outside function? #30499

Closed
krrutkow opened this issue Dec 23, 2018 · 9 comments
Closed

disallow return outside function? #30499

krrutkow opened this issue Dec 23, 2018 · 9 comments
Assignees
Labels
doc This change adds or pertains to documentation

Comments

@krrutkow
Copy link

On v1.0.2 I used a return statement from within a let block that got assigned to a variable. This didn't work as I expected, but it also didn't cause any errors. The surprising side effect was that the variable assigned to was never actually created.

julia> x = let
           # some ugly calc
           42
       end
42

julia> x
42

julia> y = let
           # some ugly calc
           return 42
       end
42

julia> y
ERROR: UndefVarError: y not defined
@JeffBezanson
Copy link
Sponsor Member

return doesn't return from the enclosing block, it returns from the enclosing function. Here there is no clear function, so this perhaps should be disallowed, but it happens to return from the entire top-level expression. So the whole input finishes before the assignment can happen, and y is never assigned.

@mbauman mbauman added the lowering Syntax lowering (compiler front end, 2nd stage) label Dec 31, 2018
@mbauman
Copy link
Sponsor Member

mbauman commented Dec 31, 2018

👍 to an error. It looks like this is a lowering thing?

julia> Meta.@lower y = let
                 # some ugly calc
                 return 42
             end
42

julia> Meta.@lower y = let
                 # some ugly calc
                 return 42
                 32
             end
:($(Expr(:thunk, CodeInfo(
1return 42
2 ─     y = 32
└──     return 32
))))

@JeffBezanson JeffBezanson changed the title Let-block with return statement disallow return outside function Dec 31, 2018
@JeffBezanson JeffBezanson added minor change Marginal behavior change acceptable for a minor release error handling Handling of exceptions by Julia or the user decision A decision on this change is needed labels Dec 31, 2018
@JeffBezanson JeffBezanson self-assigned this Dec 31, 2018
@JeffBezanson JeffBezanson changed the title disallow return outside function disallow return outside function? Dec 31, 2018
@JeffBezanson
Copy link
Sponsor Member

Thinking this over, I'm not sure disallowing return at the top level is the right thing. It might be useful for copy-pasting code from a function to the REPL. It's also a bit strange to have a way to exit an entire function body early, but no way to exit an entire top-level expression early.

@JeffBezanson JeffBezanson added the triage This should be discussed on a triage call label Jan 2, 2019
@krrutkow
Copy link
Author

krrutkow commented Jan 3, 2019

It's also a bit strange to have a way to exit an entire function body early, but no way to exit an entire top-level expression early.

That was how I had hoped to use the syntax... Another side effect from this issue is that the variable name seems to be created in a module when using const, but not with no const.

julia> module Mod
           x = let
               return 42
           end
           
           const y = let
               return 42
           end
       end
Main.Mod

julia> names(Mod, all=true)
6-element Array{Symbol,1}:
 Symbol("#eval")   
 Symbol("#include")
 :Mod              
 :eval             
 :include          
 :y                

julia> Mod.y
ERROR: UndefVarError: y not defined

@StefanKarpinski
Copy link
Sponsor Member

StefanKarpinski commented Jan 3, 2019

Allowing return, continue, break, etc. in the REPL is extremely handy for cut-and-paste debugging. IMO, return outside of a function and continue and break outside of a loop should all just stop evaluating the top-level expression immediately. We could be stricter in other top-level contexts such as inside modules or in non-interactive scripts, but I've found having to edit pasted code to remove continue and break to be a huge hassle and would hate to see more of the same.

@JeffBezanson JeffBezanson added needs docs Documentation for this change is required and removed decision A decision on this change is needed error handling Handling of exceptions by Julia or the user lowering Syntax lowering (compiler front end, 2nd stage) minor change Marginal behavior change acceptable for a minor release triage This should be discussed on a triage call labels Jan 3, 2019
@JeffBezanson
Copy link
Sponsor Member

Let's just document that return also exits top-level expressions. We can separately decide what to do with break and continue at the top level.

@StefanKarpinski
Copy link
Sponsor Member

From discussion on triage, in older Julias the actual behavior was that if you didn't evaluate the continue or break then there was no error, whereas later it became a static error which means that anything earlier in the expression isn't evaluated, which is where the annoyance comes in. What might be good is to go back to the older behavior where it's only an error if the continue or break are actually evaluated. Not sure where return would fall into that; I kind of like the way return works.

@JeffBezanson JeffBezanson added doc This change adds or pertains to documentation and removed needs docs Documentation for this change is required labels Jan 14, 2019
JeffBezanson added a commit that referenced this issue Jan 14, 2019
also add some missing doc strings and improve others
JeffBezanson added a commit that referenced this issue Jan 16, 2019
also add some missing doc strings and improve others
KristofferC pushed a commit that referenced this issue Feb 11, 2019
also add some missing doc strings and improve others

(cherry picked from commit 0bb0332)
@KristofferC KristofferC mentioned this issue Feb 11, 2019
39 tasks
@janrous-rai
Copy link

I have found out that this can lead to some hard-to-diagnose bugs when return statements are incorrectly embedded in macros that are called within functions.

Consider following function:

function f()
   x = return 5
   return x + 1
end

Clearly, f() == 5. However, the statement x = return 5 feels like something that is almost certainly unintended and should not be permitted.

In this case, it is obvious, but the situation becomes much more opaque if such return statement comes out of a macro:

x = @some_macro_that_gives_back_return

In such situation, code calling the macro is short-circuited which may not be apparent.

@janrous-rai
Copy link

Let me clarify - I understand the mechanics of why this is happening but would like to have some safety in place to make it easier to diagnose when this is done accidentally and in situations that are almost certainly incorrect.

Based on other duplicates of this bug it seems that other people also find this behavior counter-intuitive and easy to trip over.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
doc This change adds or pertains to documentation
Projects
None yet
Development

No branches or pull requests

5 participants