Repository: hylang/hy

Issue:
we don't return in try / catch blocks anymore



Discussion:
(breaks the hy site) 

``` diff
diff --git a/tests/native_tests/language.hy b/tests/native_tests/language.hy
index 20f4d1c..6abc96d 100644
--- a/tests/native_tests/language.hy
+++ b/tests/native_tests/language.hy
@@ -648,3 +648,8 @@
     xxx
     (assert False))
    (except [NameError])))
+
+
+(defn test-try-except-return []
+  "NATIVE: test we can return from in a try except"
+ (assert ((fn [] (try xxx (except [NameError] (+ 1 1)))))) 2)
```

will patch when I can be bothered (shortly!)

Sorry about that! There's XXXs where it counts, it shouldn't be too hard to do.

s'alright, it'll be a good chance to get my hands in the new compiler stuff more -- we knew there were little bugs like this, @olasd, no big deal. We're still net bug negative on the changeset

grumble grumble

try / except is fucking huge. we also need to treat it like we treat an if / else, so that we can share the same `tmpvar` to store the result from, which the current code will blow up on, since try / except are compiled in their own bits.

I'm going to refactor this to work. I'm assigning myself so I can learn the complexities in some of the new stuff better.


Resolving patch:
diff --git a/hy/compiler.py b/hy/compiler.py
--- a/hy/compiler.py
+++ b/hy/compiler.py
@@ -573,8 +573,24 @@ def compile_try_expression(self, expr):
         # (try something…)
         body = self.compile(body)
 
-        # XXX we will likely want to make this a tempvar
-        body += body.expr_as_stmt()
+        var = self.get_anon_var()
+        name = ast.Name(id=ast_str(var), arg=ast_str(var),
+                        ctx=ast.Store(),
+                        lineno=expr.start_line,
+                        col_offset=expr.start_column)
+
+        expr_name = ast.Name(id=ast_str(var), arg=ast_str(var),
+                             ctx=ast.Load(),
+                             lineno=expr.start_line,
+                             col_offset=expr.start_column)
+
+        returnable = Result(expr=expr_name, temp_variables=[expr_name, name])
+
+        body += ast.Assign(targets=[name],
+                           value=body.force_expr,
+                           lineno=expr.start_line,
+                           col_offset=expr.start_column)
+
         body = body.stmts
         if not body:
             body = [ast.Pass(lineno=expr.start_line,
@@ -590,7 +606,7 @@ def compile_try_expression(self, expr):
                 raise HyTypeError(e, "Empty list not allowed in `try'")
 
             if e[0] in (HySymbol("except"), HySymbol("catch")):
-                handler_results += self.compile(e)
+                handler_results += self._compile_catch_expression(e, name)
                 handlers.append(handler_results.stmts.pop())
             elif e[0] == HySymbol("else"):
                 if orelse:
@@ -642,7 +658,7 @@ def compile_try_expression(self, expr):
                 body=body,
                 handlers=handlers,
                 orelse=orelse,
-                finalbody=finalbody)
+                finalbody=finalbody) + returnable
 
         if finalbody:
             if handlers:
@@ -655,30 +671,34 @@ def compile_try_expression(self, expr):
                         handlers=handlers,
                         body=body,
                         orelse=orelse)],
-                    finalbody=finalbody)
+                    finalbody=finalbody) + returnable
 
             return ret + ast.TryFinally(
                 lineno=expr.start_line,
                 col_offset=expr.start_column,
                 body=body,
-                finalbody=finalbody)
+                finalbody=finalbody) + returnable
 
         return ret + ast.TryExcept(
             lineno=expr.start_line,
             col_offset=expr.start_column,
             handlers=handlers,
             body=body,
-            orelse=orelse)
+            orelse=orelse) + returnable
 
-    @builds("catch")
     @builds("except")
-    def compile_catch_expression(self, expr):
+    @builds("catch")
+    def magic_internal_form(self, expr):
+        raise TypeError("Error: `%s' can't be used like that." % (expr[0]))
+
+    def _compile_catch_expression(self, expr, var):
         catch = expr.pop(0)  # catch
 
         try:
             exceptions = expr.pop(0)
         except IndexError:
             exceptions = HyList()
+
         # exceptions catch should be either:
         # [[list of exceptions]]
         # or
@@ -689,6 +709,7 @@ def compile_catch_expression(self, expr):
         # [exception]
         # or
         # []
+
         if not isinstance(exceptions, HyList):
             raise HyTypeError(exceptions,
                               "`%s' exceptions list is not a list" % catch)
@@ -736,7 +757,10 @@ def compile_catch_expression(self, expr):
                               "`%s' needs a valid exception list" % catch)
 
         body = self._compile_branch(expr)
-        # XXX tempvar handling magic
+        body += ast.Assign(targets=[var],
+                           value=body.force_expr,
+                           lineno=expr.start_line,
+                           col_offset=expr.start_column)
         body += body.expr_as_stmt()
 
         body = body.stmts


Test patch:
diff --git a/tests/compilers/test_ast.py b/tests/compilers/test_ast.py
--- a/tests/compilers/test_ast.py
+++ b/tests/compilers/test_ast.py
@@ -140,39 +140,41 @@ def test_ast_bad_try():
 
 def test_ast_good_catch():
     "Make sure AST can compile valid catch"
-    hy_compile(tokenize("(catch)"))
-    hy_compile(tokenize("(catch [])"))
-    hy_compile(tokenize("(catch [Foobar])"))
-    hy_compile(tokenize("(catch [[]])"))
-    hy_compile(tokenize("(catch [x FooBar])"))
-    hy_compile(tokenize("(catch [x [FooBar BarFoo]])"))
-    hy_compile(tokenize("(catch [x [FooBar BarFoo]])"))
+    hy_compile(tokenize("(try 1 (catch))"))
+    hy_compile(tokenize("(try 1 (catch []))"))
+    hy_compile(tokenize("(try 1 (catch [Foobar]))"))
+    hy_compile(tokenize("(try 1 (catch [[]]))"))
+    hy_compile(tokenize("(try 1 (catch [x FooBar]))"))
+    hy_compile(tokenize("(try 1 (catch [x [FooBar BarFoo]]))"))
+    hy_compile(tokenize("(try 1 (catch [x [FooBar BarFoo]]))"))
 
 
 def test_ast_bad_catch():
     "Make sure AST can't compile invalid catch"
-    cant_compile("(catch 1)")
-    cant_compile("(catch \"A\")")
-    cant_compile("(catch [1 3])")
-    cant_compile("(catch [x [FooBar] BarBar])")
+    cant_compile("(catch 22)")   # heh
+    cant_compile("(try (catch 1))")
+    cant_compile("(try (catch \"A\"))")
+    cant_compile("(try (catch [1 3]))")
+    cant_compile("(try (catch [x [FooBar] BarBar]))")
 
 
 def test_ast_good_except():
     "Make sure AST can compile valid except"
-    hy_compile(tokenize("(except)"))
-    hy_compile(tokenize("(except [])"))
-    hy_compile(tokenize("(except [Foobar])"))
-    hy_compile(tokenize("(except [[]])"))
-    hy_compile(tokenize("(except [x FooBar])"))
-    hy_compile(tokenize("(except [x [FooBar BarFoo]])"))
-    hy_compile(tokenize("(except [x [FooBar BarFoo]])"))
+    hy_compile(tokenize("(try 1 (except))"))
+    hy_compile(tokenize("(try 1 (except []))"))
+    hy_compile(tokenize("(try 1 (except [Foobar]))"))
+    hy_compile(tokenize("(try 1 (except [[]]))"))
+    hy_compile(tokenize("(try 1 (except [x FooBar]))"))
+    hy_compile(tokenize("(try 1 (except [x [FooBar BarFoo]]))"))
+    hy_compile(tokenize("(try 1 (except [x [FooBar BarFoo]]))"))
 
 
 def test_ast_bad_except():
     "Make sure AST can't compile invalid except"
     cant_compile("(except 1)")
-    cant_compile("(except [1 3])")
-    cant_compile("(except [x [FooBar] BarBar])")
+    cant_compile("(try 1 (except 1))")
+    cant_compile("(try 1 (except [1 3]))")
+    cant_compile("(try 1 (except [x [FooBar] BarBar]))")
 
 
 def test_ast_good_assert():
diff --git a/tests/native_tests/language.hy b/tests/native_tests/language.hy
--- a/tests/native_tests/language.hy
+++ b/tests/native_tests/language.hy
@@ -681,3 +681,11 @@
              (if (if 0 True False)
                42
                43))))
+
+(defn test-try-except-return []
+  "NATIVE: test we can return from in a try except"
+  (assert (= ((fn [] (try xxx (except [NameError] (+ 1 1))))) 2))
+  (setf foo (try xxx (except [NameError] (+ 1 1))))
+  (assert (= foo 2))
+  (setf foo (try (+ 2 2) (except [NameError] (+ 1 1))))
+  (assert (= foo 4)))