Merge: Introducing the do ... catch ... end structure
[nit.git] / src / compiler / abstract_compiler.nit
index 0e40831..31dfcc4 100644 (file)
@@ -651,6 +651,8 @@ abstract class AbstractCompiler
                self.header.add_decl("#include <stdlib.h>")
                self.header.add_decl("#include <stdio.h>")
                self.header.add_decl("#include <string.h>")
+               # longjmp !
+               self.header.add_decl("#include <setjmp.h>\n")
                self.header.add_decl("#include <sys/types.h>\n")
                self.header.add_decl("#include <unistd.h>\n")
                self.header.add_decl("#include <stdint.h>\n")
@@ -679,6 +681,7 @@ abstract class AbstractCompiler
 
                compile_header_structs
                compile_nitni_structs
+               compile_catch_stack
 
                var gccd_disable = modelbuilder.toolcontext.opt_no_gcc_directive.value
                if gccd_disable.has("noreturn") or gccd_disable.has("all") then
@@ -707,6 +710,17 @@ abstract class AbstractCompiler
                self.header.add_decl("extern val *glob_sys;")
        end
 
+       # Stack stocking environment for longjumps
+       protected fun compile_catch_stack do
+               self.header.add_decl """
+struct catch_stack_t {
+       int cursor;
+       jmp_buf envs[100];
+};
+extern struct catch_stack_t catchStack;
+"""
+       end
+
        # Declaration of structures for live Nit types
        protected fun compile_header_structs is abstract
 
@@ -790,6 +804,7 @@ extern void nitni_global_ref_decr( struct nitni_ref *ref );
                v.add_decl("int glob_argc;")
                v.add_decl("char **glob_argv;")
                v.add_decl("val *glob_sys;")
+               v.add_decl("struct catch_stack_t catchStack;")
 
                if self.modelbuilder.toolcontext.opt_typing_test_metrics.value then
                        for tag in count_type_test_tags do
@@ -878,6 +893,7 @@ extern void nitni_global_ref_decr( struct nitni_ref *ref );
                v.add("signal(SIGPIPE, SIG_IGN);")
 
                v.add("glob_argc = argc; glob_argv = argv;")
+               v.add("catchStack.cursor = -1;")
                v.add("initialize_gc_option();")
 
                v.add "initialize_nitni_global_refs();"
@@ -1273,17 +1289,21 @@ abstract class AbstractCompilerVisitor
        do
                mtype = self.anchor(mtype)
                var valmtype = value.mcasttype
+
+               # Do nothing if useless autocast
                if valmtype.is_subtype(self.compiler.mainmodule, null, mtype) then
                        return value
                end
 
+               # Just as_not_null if the target is not nullable.
+               #
+               # eg `nullable PreciseType` adapted to `Object` gives precisetype.
                if valmtype isa MNullableType and valmtype.mtype.is_subtype(self.compiler.mainmodule, null, mtype) then
-                       var res = new RuntimeVariable(value.name, valmtype, valmtype.mtype)
-                       return res
-               else
-                       var res = new RuntimeVariable(value.name, valmtype, mtype)
-                       return res
+                       mtype = valmtype.mtype
                end
+
+               var res = new RuntimeVariable(value.name, value.mtype, mtype)
+               return res
        end
 
        # Generate a super call from a method definition
@@ -1351,13 +1371,18 @@ abstract class AbstractCompilerVisitor
 
        # Checks
 
+       # Can value be null? (according to current knowledge)
+       fun maybenull(value: RuntimeVariable): Bool
+       do
+               return value.mcasttype isa MNullableType or value.mcasttype isa MNullType
+       end
+
        # Add a check and an abort for a null receiver if needed
        fun check_recv_notnull(recv: RuntimeVariable)
        do
                if self.compiler.modelbuilder.toolcontext.opt_no_check_null.value then return
 
-               var maybenull = recv.mcasttype isa MNullableType or recv.mcasttype isa MNullType
-               if maybenull then
+               if maybenull(recv) then
                        self.add("if (unlikely({recv} == NULL)) \{")
                        self.add_abort("Receiver is null")
                        self.add("\}")
@@ -1722,6 +1747,9 @@ abstract class AbstractCompilerVisitor
        # used by aborts, asserts, casts, etc.
        fun add_abort(message: String)
        do
+               self.add("if(catchStack.cursor >= 0)\{")
+               self.add("longjmp(catchStack.envs[catchStack.cursor], 1);")
+               self.add("\}")
                self.add("PRINT_ERROR(\"Runtime error: %s\", \"{message.escape_to_c}\");")
                add_raw_abort
        end
@@ -3110,7 +3138,7 @@ redef class AAttrPropdef
                        assert arguments.length == 2
                        var recv = arguments.first
                        var arg = arguments[1]
-                       if is_optional then
+                       if is_optional and v.maybenull(arg) then
                                var value = v.new_var(self.mpropdef.static_mtype.as(not null))
                                v.add("if ({arg} == NULL) \{")
                                v.assign(value, evaluate_expr(v, recv))
@@ -3362,8 +3390,19 @@ end
 redef class ADoExpr
        redef fun stmt(v)
        do
-               v.stmt(self.n_block)
-               v.add_escape_label(break_mark)
+               if self.n_catch != null then
+                       v.add("catchStack.cursor += 1;")
+                       v.add("if(!setjmp(catchStack.envs[catchStack.cursor]))\{")
+                       v.stmt(self.n_block)
+                       v.add("catchStack.cursor -= 1;")
+                       v.add("\}else \{")
+                       v.add("catchStack.cursor -= 1;")
+                       v.stmt(self.n_catch)
+                       v.add("\}")
+               else
+                       v.stmt(self.n_block)
+               end
+                       v.add_escape_label(break_mark)
        end
 end
 
@@ -3532,6 +3571,9 @@ redef class AOrElseExpr
        do
                var res = v.new_var(self.mtype.as(not null))
                var i1 = v.expr(self.n_expr, null)
+
+               if not v.maybenull(i1) then return i1
+
                v.add("if ({i1}!=NULL) \{")
                v.assign(res, i1)
                v.add("\} else \{")
@@ -3783,7 +3825,7 @@ redef class AAsNotnullExpr
                var i = v.expr(self.n_expr, null)
                if v.compiler.modelbuilder.toolcontext.opt_no_check_assert.value then return i
 
-               if i.mtype.is_c_primitive then return i
+               if not v.maybenull(i) then return i
 
                v.add("if (unlikely({i} == NULL)) \{")
                v.add_abort("Cast failed")