compiler: do not call C `nit_alloc` directly but a Nit method.
[nit.git] / src / compiler / abstract_compiler.nit
index a1827d0..e9eacef 100644 (file)
@@ -28,15 +28,15 @@ import counter
 # Add compiling options
 redef class ToolContext
        # --output
-       var opt_output = new OptionString("Output file", "-o", "--output")
+       var opt_output = new OptionString("Filename of the generated executable", "-o", "--output")
        # --dir
        var opt_dir = new OptionString("Output directory", "--dir")
        # --no-cc
-       var opt_no_cc = new OptionBool("Do not invoke C compiler", "--no-cc")
+       var opt_no_cc = new OptionBool("Do not invoke the C compiler", "--no-cc")
        # --no-main
        var opt_no_main = new OptionBool("Do not generate main entry point", "--no-main")
        # --make-flags
-       var opt_make_flags = new OptionString("Additional options to make", "--make-flags")
+       var opt_make_flags = new OptionString("Additional options to the `make` command", "--make-flags")
        # --max-c-lines
        var opt_max_c_lines = new OptionInt("Maximum number of lines in generated C files. Use 0 for unlimited", 10000, "--max-c-lines")
        # --group-c-files
@@ -50,7 +50,7 @@ redef class ToolContext
        # --no-check-attr-isset
        var opt_no_check_attr_isset = new OptionBool("Disable isset tests before each attribute access (dangerous)", "--no-check-attr-isset")
        # --no-check-assert
-       var opt_no_check_assert = new OptionBool("Disable the evaluation of explicit 'assert' and 'as' (dangerous)", "--no-check-assert")
+       var opt_no_check_assert = new OptionBool("Disable the evaluation of explicit `assert` and `as` (dangerous)", "--no-check-assert")
        # --no-check-autocast
        var opt_no_check_autocast = new OptionBool("Disable implicit casts on unsafe expression usage (dangerous)", "--no-check-autocast")
        # --no-check-null
@@ -66,11 +66,11 @@ redef class ToolContext
        # --no-stacktrace
        var opt_no_stacktrace = new OptionBool("Disable the generation of stack traces", "--no-stacktrace")
        # --no-gcc-directives
-       var opt_no_gcc_directive = new OptionArray("Disable a advanced gcc directives for optimization", "--no-gcc-directive")
+       var opt_no_gcc_directive = new OptionArray("Disable advanced gcc directives for optimization", "--no-gcc-directive")
        # --release
        var opt_release = new OptionBool("Compile in release mode and finalize application", "--release")
        # -g
-       var opt_debug = new OptionBool("Compile in debug mode (no C-side optimization)", "--debug", "-g")
+       var opt_debug = new OptionBool("Compile in debug mode (no C-side optimization)", "-g", "--debug")
 
        redef init
        do
@@ -349,7 +349,7 @@ class MakefileToolchain
                end
                var debug = toolcontext.opt_debug.value
 
-               makefile.write("CC = ccache cc\nCXX = ccache c++\nCFLAGS = -g{ if not debug then " -O2 " else " "}-Wno-unused-value -Wno-switch -Wno-attributes\nCINCL =\nLDFLAGS ?= \nLDLIBS  ?= -lm {linker_options.join(" ")}\n\n")
+               makefile.write("CC = ccache cc\nCXX = ccache c++\nCFLAGS = -g{ if not debug then " -O2 " else " "}-Wno-unused-value -Wno-switch -Wno-attributes -Wno-trigraphs\nCINCL =\nLDFLAGS ?= \nLDLIBS  ?= -lm {linker_options.join(" ")}\n\n")
 
                makefile.write "\n# SPECIAL CONFIGURATION FLAGS\n"
                if platform.supports_libunwind then
@@ -577,7 +577,7 @@ abstract class AbstractCompiler
 
        # The list of all associated files
        # Used to generate .c files
-       var files = new List[CodeFile]
+       var files = new Array[CodeFile]
 
        # Initialize a visitor specific for a compiler engine
        fun new_visitor: VISITOR is abstract
@@ -651,12 +651,28 @@ 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")
+               self.header.add_decl("#ifdef __linux__")
+               self.header.add_decl("  #include <endian.h>")
+               self.header.add_decl("#endif")
                self.header.add_decl("#include <inttypes.h>\n")
                self.header.add_decl("#include \"gc_chooser.h\"")
+               self.header.add_decl("#ifdef __APPLE__")
+               self.header.add_decl("  #include <libkern/OSByteOrder.h>")
+               self.header.add_decl("  #define be32toh(x) OSSwapBigToHostInt32(x)")
+               self.header.add_decl("#endif")
+               self.header.add_decl("#ifdef __pnacl__")
+               self.header.add_decl("  #define be16toh(val) (((val) >> 8) | ((val) << 8))")
+               self.header.add_decl("  #define be32toh(val) ((be16toh((val) << 16) | (be16toh((val) >> 16))))")
+               self.header.add_decl("#endif")
                self.header.add_decl("#ifdef ANDROID")
+               self.header.add_decl("  #ifndef be32toh")
+               self.header.add_decl("          #define be32toh(val) betoh32(val)")
+               self.header.add_decl("  #endif")
                self.header.add_decl("  #include <android/log.h>")
                self.header.add_decl("  #define PRINT_ERROR(...) (void)__android_log_print(ANDROID_LOG_WARN, \"Nit\", __VA_ARGS__)")
                self.header.add_decl("#else")
@@ -665,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
@@ -693,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
 
@@ -776,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
@@ -853,15 +882,18 @@ extern void nitni_global_ref_decr( struct nitni_ref *ref );
                        v.add_decl("int main(int argc, char** argv) \{")
                end
 
+               v.add "#if !defined(__ANDROID__) && !defined(TARGET_OS_IPHONE)"
                v.add("signal(SIGABRT, sig_handler);")
                v.add("signal(SIGFPE, sig_handler);")
                v.add("signal(SIGILL, sig_handler);")
                v.add("signal(SIGINT, sig_handler);")
                v.add("signal(SIGTERM, sig_handler);")
                v.add("signal(SIGSEGV, sig_handler);")
+               v.add "#endif"
                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();"
@@ -1095,8 +1127,8 @@ end
 # Where to store generated lines
 class CodeWriter
        var file: CodeFile
-       var lines: List[String] = new List[String]
-       var decl_lines: List[String] = new List[String]
+       var lines = new Array[String]
+       var decl_lines = new Array[String]
 
        # Add a line in the main part of the generated C
        fun add(s: String) do self.lines.add(s)
@@ -1147,6 +1179,7 @@ abstract class AbstractCompilerVisitor
 
        fun compile_callsite(callsite: CallSite, arguments: Array[RuntimeVariable]): nullable RuntimeVariable
        do
+               if callsite.is_broken then return null
                var initializers = callsite.mpropdef.initializers
                if not initializers.is_empty then
                        var recv = arguments.first
@@ -1175,9 +1208,7 @@ abstract class AbstractCompilerVisitor
 
        fun native_array_instance(elttype: MType, length: RuntimeVariable): RuntimeVariable is abstract
 
-       fun calloc_array(ret_type: MType, arguments: Array[RuntimeVariable]) is abstract
-
-       fun native_array_def(pname: String, ret_type: nullable MType, arguments: Array[RuntimeVariable]) is abstract
+       fun native_array_def(pname: String, ret_type: nullable MType, arguments: Array[RuntimeVariable]): Bool do return false
 
        # Return an element of a native array.
        # The method is unsafe and is just a direct wrapper for the specific implementation of native arrays
@@ -1187,6 +1218,16 @@ abstract class AbstractCompilerVisitor
        # The method is unsafe and is just a direct wrapper for the specific implementation of native arrays
        fun native_array_set(native_array: RuntimeVariable, index: Int, value: RuntimeVariable) is abstract
 
+       # Allocate `size` bytes with the low_level `nit_alloc` C function
+       #
+       # This method can be redefined to inject statistic or tracing code.
+       #
+       # `tag` if any, is used to mark the class of the allocated object.
+       fun nit_alloc(size: String, tag: nullable String): String
+       do
+               return "nit_alloc({size})"
+       end
+
        # Evaluate `args` as expressions in the call of `mpropdef` on `recv`.
        # This method is used to manage varargs in signatures and returns the real array
        # of runtime variables to use in the call.
@@ -1221,8 +1262,8 @@ abstract class AbstractCompilerVisitor
                                res.add(null_instance)
                                continue
                        end
-                       if param.is_vararg and map.vararg_decl > 0 then
-                               var vararg = exprs.sub(j, map.vararg_decl)
+                       if param.is_vararg and args[i].vararg_decl > 0 then
+                               var vararg = exprs.sub(j, args[i].vararg_decl)
                                var elttype = param.mtype
                                var arg = self.vararg_instance(mpropdef, recv, vararg, elttype)
                                res.add(arg)
@@ -1256,17 +1297,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
@@ -1334,13 +1379,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("\}")
@@ -1585,6 +1635,18 @@ abstract class AbstractCompilerVisitor
                return res
        end
 
+       # Generates a NativeString instance fully escaped in C-style \xHH fashion
+       fun native_string_instance(ns: NativeString, len: Int): RuntimeVariable do
+               var mtype = mmodule.native_string_type
+               var nat = new_var(mtype)
+               var byte_esc = new Buffer.with_cap(len * 4)
+               for i in [0 .. len[ do
+                       byte_esc.append("\\x{ns[i].to_s.substring_from(2)}")
+               end
+               self.add("{nat} = \"{byte_esc}\";")
+               return nat
+       end
+
        # Generate a string value
        fun string_instance(string: String): RuntimeVariable
        do
@@ -1598,9 +1660,9 @@ abstract class AbstractCompilerVisitor
                var native_mtype = mmodule.native_string_type
                var nat = self.new_var(native_mtype)
                self.add("{nat} = \"{string.escape_to_c}\";")
-               var bytelen = self.int_instance(string.bytelen)
+               var byte_length = self.int_instance(string.byte_length)
                var unilen = self.int_instance(string.length)
-               self.add("{res} = {self.send(self.get_property("to_s_full", native_mtype), [nat, bytelen, unilen]).as(not null)};")
+               self.add("{res} = {self.send(self.get_property("to_s_full", native_mtype), [nat, byte_length, unilen]).as(not null)};")
                self.add("{name} = {res};")
                self.add("\}")
                return res
@@ -1656,7 +1718,7 @@ abstract class AbstractCompilerVisitor
        # This is used for the legacy FFI
        fun add_extern(mmodule: MModule)
        do
-               var file = mmodule.location.file.filename
+               var file = mmodule.filepath
                file = file.strip_extension(".nit")
                var tryfile = file + ".nit.h"
                if tryfile.file_exists then
@@ -1693,6 +1755,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
@@ -2021,6 +2086,7 @@ redef class MMethodDef
        fun can_inline(v: VISITOR): Bool
        do
                if is_abstract then return true
+               if constant_value != null then return true
                var modelbuilder = v.compiler.modelbuilder
                var node = modelbuilder.mpropdef2node(self)
                if node isa APropdef then
@@ -2028,6 +2094,8 @@ redef class MMethodDef
                else if node isa AClassdef then
                        # Automatic free init is always inlined since it is empty or contains only attribtes assigments
                        return true
+               else if node == null then
+                       return true
                else
                        abort
                end
@@ -2073,20 +2141,23 @@ redef class MMethodDef
        do
                if v.compiler.modelbuilder.toolcontext.opt_no_check_covariance.value then return
 
+               var msignature = self.msignature.as(not null)
+
                for i in [0..msignature.arity[ do
+                       var mp = msignature.mparameters[i]
                        # skip test for vararg since the array is instantiated with the correct polymorphic type
-                       if msignature.vararg_rank == i then continue
+                       if mp.is_vararg then continue
 
                        # skip if the cast is not required
                        var origmtype =  self.mproperty.intro.msignature.mparameters[i].mtype
                        if not origmtype.need_anchor then continue
 
                        # get the parameter type
-                       var mtype = self.msignature.mparameters[i].mtype
+                       var mtype = mp.mtype
 
                        # generate the cast
                        # note that v decides if and how to implements the cast
-                       v.add("/* Covariant cast for argument {i} ({self.msignature.mparameters[i].name}) {arguments[i+1].inspect} isa {mtype} */")
+                       v.add("/* Covariant cast for argument {i} ({mp.name}) {arguments[i+1].inspect} isa {mtype} */")
                        v.add_cast(arguments[i+1], mtype, "covariance")
                end
        end
@@ -2128,7 +2199,8 @@ redef class AMethPropdef
                # Try special compilation
                if mpropdef.is_intern then
                        if compile_intern_to_c(v, mpropdef, arguments) then return
-               else if mpropdef.is_extern then
+               end
+               if mpropdef.is_extern then
                        if mpropdef.mproperty.is_init then
                                if compile_externinit_to_c(v, mpropdef, arguments) then return
                        else
@@ -2243,6 +2315,21 @@ redef class AMethPropdef
                        else if pname == "to_b" then
                                v.ret(v.new_expr("(unsigned char){arguments[0]}", ret.as(not null)))
                                return true
+                       else if pname == "code_point" then
+                               v.ret(v.new_expr("(uint32_t){arguments[0]}", ret.as(not null)))
+                               return true
+                       else if pname == "&" then
+                               v.ret(v.new_expr("{arguments[0]} & {arguments[1]}", ret.as(not null)))
+                               return true
+                       else if pname == "|" then
+                               v.ret(v.new_expr("{arguments[0]} | {arguments[1]}", ret.as(not null)))
+                               return true
+                       else if pname == ">>" then
+                               v.ret(v.new_expr("{arguments[0]} >> {arguments[1]}", ret.as(not null)))
+                               return true
+                       else if pname == "<<" then
+                               v.ret(v.new_expr("{arguments[0]} << {arguments[1]}", ret.as(not null)))
+                               return true
                        end
                else if cname == "Char" then
                        if pname == "object_id" then
@@ -2276,6 +2363,9 @@ redef class AMethPropdef
                        else if pname == "to_i" then
                                v.ret(v.new_expr("{arguments[0]}-'0'", ret.as(not null)))
                                return true
+                       else if pname == "code_point" then
+                               v.ret(v.new_expr("(long){arguments[0]}", ret.as(not null)))
+                               return true
                        end
                else if cname == "Byte" then
                        if pname == "output" then
@@ -2324,6 +2414,15 @@ redef class AMethPropdef
                        else if pname == ">=" then
                                v.ret(v.new_expr("{arguments[0]} >= {arguments[1]}", ret.as(not null)))
                                return true
+                       else if pname == ">>" then
+                               v.ret(v.new_expr("{arguments[0]} >> {arguments[1]}", ret.as(not null)))
+                               return true
+                       else if pname == "<<" then
+                               v.ret(v.new_expr("{arguments[0]} << {arguments[1]}", ret.as(not null)))
+                               return true
+                       else if pname == "&" then
+                               v.ret(v.new_expr("{arguments[0]} & {arguments[1]}", ret.as(not null)))
+                               return true
                        else if pname == "to_i" then
                                v.ret(v.new_expr("(long){arguments[0]}", ret.as(not null)))
                                return true
@@ -2345,6 +2444,9 @@ redef class AMethPropdef
                        else if pname == "to_u32" then
                                v.ret(v.new_expr("(uint32_t){arguments[0]}", ret.as(not null)))
                                return true
+                       else if pname == "ascii" then
+                               v.ret(v.new_expr("(uint32_t){arguments[0]}", ret.as(not null)))
+                               return true
                        end
                else if cname == "Bool" then
                        if pname == "output" then
@@ -2449,13 +2551,26 @@ redef class AMethPropdef
                        else if pname == "fast_cstring" then
                                v.ret(v.new_expr("{arguments[0]} + {arguments[1]}", ret.as(not null)))
                                return true
+                       else if pname == "==" then
+                               v.ret(v.equal_test(arguments[0], arguments[1]))
+                               return true
+                       else if pname == "!=" then
+                               var res = v.equal_test(arguments[0], arguments[1])
+                               v.ret(v.new_expr("!{res}", ret.as(not null)))
+                               return true
                        else if pname == "new" then
-                               v.ret(v.new_expr("(char*)nit_alloc({arguments[1]})", ret.as(not null)))
+                               var alloc = v.nit_alloc(arguments[1].to_s, "NativeString")
+                               v.ret(v.new_expr("(char*){alloc}", ret.as(not null)))
+                               return true
+                       else if pname == "fetch_4_chars" then
+                               v.ret(v.new_expr("(long)*((uint32_t*)({arguments[0]} + {arguments[1]}))", ret.as(not null)))
+                               return true
+                       else if pname == "fetch_4_hchars" then
+                               v.ret(v.new_expr("(long)be32toh(*((uint32_t*)({arguments[0]} + {arguments[1]})))", ret.as(not null)))
                                return true
                        end
                else if cname == "NativeArray" then
-                       v.native_array_def(pname, ret, arguments)
-                       return true
+                       return v.native_array_def(pname, ret, arguments)
                else if cname == "Int8" then
                        if pname == "output" then
                                v.add("printf(\"%\"PRIi8 \"\\n\", {arguments.first});")
@@ -2898,12 +3013,6 @@ redef class AMethPropdef
                else if pname == "sys" then
                        v.ret(v.new_expr("glob_sys", ret.as(not null)))
                        return true
-               else if pname == "calloc_string" then
-                       v.ret(v.new_expr("(char*)nit_alloc({arguments[1]})", ret.as(not null)))
-                       return true
-               else if pname == "calloc_array" then
-                       v.calloc_array(ret.as(not null), arguments)
-                       return true
                else if pname == "object_id" then
                        v.ret(v.new_expr("(long){arguments.first}", ret.as(not null)))
                        return true
@@ -3030,7 +3139,18 @@ redef class AAttrPropdef
                        v.assign(v.frame.returnvar.as(not null), res)
                else if mpropdef == mwritepropdef then
                        assert arguments.length == 2
-                       v.write_attribute(self.mpropdef.mproperty, arguments.first, arguments[1])
+                       var recv = arguments.first
+                       var arg = arguments[1]
+                       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))
+                               v.add("\} else \{")
+                               v.assign(value, arg)
+                               v.add("\}")
+                               arg = value
+                       end
+                       v.write_attribute(self.mpropdef.mproperty, arguments.first, arg)
                        if is_lazy then
                                var ret = self.mtype
                                var useiset = not ret.is_c_primitive and not ret isa MNullableType
@@ -3045,7 +3165,7 @@ redef class AAttrPropdef
 
        fun init_expr(v: AbstractCompilerVisitor, recv: RuntimeVariable)
        do
-               if has_value and not is_lazy and not n_expr isa ANullExpr then evaluate_expr(v, recv)
+               if has_value and not is_lazy and not is_optional and not n_expr isa ANullExpr then evaluate_expr(v, recv)
        end
 
        # Evaluate, store and return the default value of the attribute
@@ -3105,8 +3225,7 @@ end
 redef class AClassdef
        private fun compile_to_c(v: AbstractCompilerVisitor, mpropdef: MMethodDef, arguments: Array[RuntimeVariable])
        do
-               if mpropdef == self.mfree_init then
-                       assert mpropdef.mproperty.is_root_init
+               if mpropdef.mproperty.is_root_init then
                        assert arguments.length == 1
                        if not mpropdef.is_intro then
                                v.supercall(mpropdef, arguments.first.mtype.as(MClassType), arguments)
@@ -3274,8 +3393,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
 
@@ -3306,53 +3436,68 @@ end
 redef class AForExpr
        redef fun stmt(v)
        do
-               var cl = v.expr(self.n_expr, null)
-               var it_meth = self.method_iterator
-               assert it_meth != null
-               var it = v.compile_callsite(it_meth, [cl])
-               assert it != null
+               for g in n_groups do
+                       var cl = v.expr(g.n_expr, null)
+                       var it_meth = g.method_iterator
+                       assert it_meth != null
+                       var it = v.compile_callsite(it_meth, [cl])
+                       assert it != null
+                       g.it = it
+               end
                v.add("for(;;) \{")
-               var isok_meth = self.method_is_ok
-               assert isok_meth != null
-               var ok = v.compile_callsite(isok_meth, [it])
-               assert ok != null
-               v.add("if(!{ok}) break;")
-               if self.variables.length == 1 then
-                       var item_meth = self.method_item
-                       assert item_meth != null
-                       var i = v.compile_callsite(item_meth, [it])
-                       assert i != null
-                       v.assign(v.variable(variables.first), i)
-               else if self.variables.length == 2 then
-                       var key_meth = self.method_key
-                       assert key_meth != null
-                       var i = v.compile_callsite(key_meth, [it])
-                       assert i != null
-                       v.assign(v.variable(variables[0]), i)
-                       var item_meth = self.method_item
-                       assert item_meth != null
-                       i = v.compile_callsite(item_meth, [it])
-                       assert i != null
-                       v.assign(v.variable(variables[1]), i)
-               else
-                       abort
+               for g in n_groups do
+                       var it = g.it
+                       var isok_meth = g.method_is_ok
+                       assert isok_meth != null
+                       var ok = v.compile_callsite(isok_meth, [it])
+                       assert ok != null
+                       v.add("if(!{ok}) break;")
+                       if g.variables.length == 1 then
+                               var item_meth = g.method_item
+                               assert item_meth != null
+                               var i = v.compile_callsite(item_meth, [it])
+                               assert i != null
+                               v.assign(v.variable(g.variables.first), i)
+                       else if g.variables.length == 2 then
+                               var key_meth = g.method_key
+                               assert key_meth != null
+                               var i = v.compile_callsite(key_meth, [it])
+                               assert i != null
+                               v.assign(v.variable(g.variables[0]), i)
+                               var item_meth = g.method_item
+                               assert item_meth != null
+                               i = v.compile_callsite(item_meth, [it])
+                               assert i != null
+                               v.assign(v.variable(g.variables[1]), i)
+                       else
+                               abort
+                       end
                end
                v.stmt(self.n_block)
                v.add_escape_label(continue_mark)
-               var next_meth = self.method_next
-               assert next_meth != null
-               v.compile_callsite(next_meth, [it])
+               for g in n_groups do
+                       var next_meth = g.method_next
+                       assert next_meth != null
+                       v.compile_callsite(next_meth, [g.it])
+               end
                v.add("\}")
                v.add_escape_label(break_mark)
 
-               var method_finish = self.method_finish
-               if method_finish != null then
-                       # TODO: Find a way to call this also in long escape (e.g. return)
-                       v.compile_callsite(method_finish, [it])
+               for g in n_groups do
+                       var method_finish = g.method_finish
+                       if method_finish != null then
+                               # TODO: Find a way to call this also in long escape (e.g. return)
+                               v.compile_callsite(method_finish, [g.it])
+                       end
                end
        end
 end
 
+redef class AForGroup
+       # C variable representing the iterator
+       private var it: RuntimeVariable is noinit
+end
+
 redef class AAssertExpr
        redef fun stmt(v)
        do
@@ -3429,6 +3574,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 \{")
@@ -3458,7 +3606,11 @@ redef class AFloatExpr
 end
 
 redef class ACharExpr
-       redef fun expr(v) do return v.char_instance(self.value.as(not null))
+       redef fun expr(v) do
+               if is_ascii then return v.byte_instance(value.as(not null).ascii)
+               if is_code_point then return v.int_instance(value.as(not null).code_point)
+               return v.char_instance(self.value.as(not null))
+       end
 end
 
 redef class AArrayExpr
@@ -3479,14 +3631,75 @@ redef class AArrayExpr
        end
 end
 
+redef class AugmentedStringFormExpr
+       # Factorize the making of a `Regex` object from a literal prefixed string
+       protected fun make_re(v: AbstractCompilerVisitor, rs: RuntimeVariable): nullable RuntimeVariable do
+               var re = to_re
+               assert re != null
+               var res = v.compile_callsite(re, [rs])
+               if res == null then
+                       print "Cannot call property `to_re` on {self}"
+                       abort
+               end
+               for i in suffix.chars do
+                       if i == 'i' then
+                               var ign = ignore_case
+                               assert ign != null
+                               v.compile_callsite(ign, [res, v.bool_instance(true)])
+                               continue
+                       end
+                       if i == 'm' then
+                               var nl = newline
+                               assert nl != null
+                               v.compile_callsite(nl, [res, v.bool_instance(true)])
+                               continue
+                       end
+                       if i == 'b' then
+                               var ext = extended
+                               assert ext != null
+                               v.compile_callsite(ext, [res, v.bool_instance(false)])
+                               continue
+                       end
+                       # Should not happen, this needs to be updated
+                       # along with the addition of new suffixes
+                       abort
+               end
+               return res
+       end
+end
+
 redef class AStringFormExpr
-       redef fun expr(v) do return v.string_instance(self.value.as(not null))
+       redef fun expr(v) do return v.string_instance(value)
+end
+
+redef class AStringExpr
+       redef fun expr(v) do
+               var s = v.string_instance(value)
+               if is_string then return s
+               if is_bytestring then
+                       var ns = v.native_string_instance(bytes.items, bytes.length)
+                       var ln = v.int_instance(bytes.length)
+                       var cs = to_bytes_with_copy
+                       assert cs != null
+                       var res = v.compile_callsite(cs, [ns, ln])
+                       assert res != null
+                       s = res
+               else if is_re then
+                       var res = make_re(v, s)
+                       assert res != null
+                       s = res
+               else
+                       print "Unimplemented prefix or suffix for {self}"
+                       abort
+               end
+               return s
+       end
 end
 
 redef class ASuperstringExpr
        redef fun expr(v)
        do
-               var type_string = mtype.as(not null)
+               var type_string = v.mmodule.string_type
 
                # Collect elements of the superstring
                var array = new Array[AExpr]
@@ -3540,10 +3753,14 @@ redef class ASuperstringExpr
 
                # Fast join the native string to get the result
                var res = v.send(v.get_property("native_to_s", a.mtype), [a])
+               assert res != null
+
+               if is_re then res = make_re(v, res)
 
                # We finish to work with the native array,
                # so store it so that it can be reused
                v.add("{varonce} = {a};")
+
                return res
        end
 end
@@ -3611,7 +3828,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")
@@ -3650,6 +3867,7 @@ redef class ASendExpr
        do
                var recv = v.expr(self.n_expr, null)
                var callsite = self.callsite.as(not null)
+               if callsite.is_broken then return null
                var args = v.varargize(callsite.mpropdef, callsite.signaturemap, recv, self.raw_arguments)
                return v.compile_callsite(callsite, args)
        end
@@ -3660,6 +3878,7 @@ redef class ASendReassignFormExpr
        do
                var recv = v.expr(self.n_expr, null)
                var callsite = self.callsite.as(not null)
+               if callsite.is_broken then return
                var args = v.varargize(callsite.mpropdef, callsite.signaturemap, recv, self.raw_arguments)
 
                var value = v.expr(self.n_value, null)
@@ -3678,17 +3897,19 @@ end
 redef class ASuperExpr
        redef fun expr(v)
        do
-               var recv = v.frame.arguments.first
+               var frame = v.frame.as(not null)
+               var recv = frame.arguments.first
 
                var callsite = self.callsite
                if callsite != null then
+                       if callsite.is_broken then return null
                        var args
 
                        if self.n_args.n_exprs.is_empty then
                                # Add automatic arguments for the super init call
                                args = [recv]
                                for i in [0..callsite.msignature.arity[ do
-                                       args.add(v.frame.arguments[i+1])
+                                       args.add(frame.arguments[i+1])
                                end
                        else
                                args = v.varargize(callsite.mpropdef, callsite.signaturemap, recv, self.n_args.n_exprs)
@@ -3703,7 +3924,7 @@ redef class ASuperExpr
 
                var args
                if self.n_args.n_exprs.is_empty then
-                       args = v.frame.arguments
+                       args = frame.arguments
                else
                        args = v.varargize(mpropdef, signaturemap, recv, self.n_args.n_exprs)
                end
@@ -3727,10 +3948,35 @@ redef class ANewExpr
                        return v.native_array_instance(elttype, l)
                end
 
-               var recv = v.init_instance_or_extern(mtype)
-
                var callsite = self.callsite
-               if callsite == null then return recv
+               if callsite == null then return v.init_instance_or_extern(mtype)
+               if callsite.is_broken then return null
+
+               var recv
+               # new factories are badly implemented.
+               # They assume a stub temporary receiver exists.
+               # This temporary receiver is required because it
+               # currently holds the method and the formal types.
+               #
+               # However, this object could be reused if the formal types are the same.
+               # Therefore, the following code will `once` it in these case
+               if callsite.mproperty.is_new and not mtype.need_anchor then
+                       var name = v.get_name("varoncenew")
+                       var guard = v.get_name(name + "_guard")
+                       v.add_decl("static {mtype.ctype} {name};")
+                       v.add_decl("static int {guard};")
+                       recv = v.new_var(mtype)
+                       v.add("if (likely({guard})) \{")
+                       v.add("{recv} = {name};")
+                       v.add("\} else \{")
+                       var i = v.init_instance_or_extern(mtype)
+                       v.add("{recv} = {i};")
+                       v.add("{name} = {recv};")
+                       v.add("{guard} = 1;")
+                       v.add("\}")
+               else
+                       recv = v.init_instance_or_extern(mtype)
+               end
 
                var args = v.varargize(callsite.mpropdef, callsite.signaturemap, recv, self.n_args.n_exprs)
                var res2 = v.compile_callsite(callsite, args)
@@ -3870,7 +4116,8 @@ end
 # Here we load an process all modules passed on the command line
 var mmodules = modelbuilder.parse(arguments)
 
-if mmodules.is_empty then return
+if mmodules.is_empty then toolcontext.quit
+
 modelbuilder.run_phases
 
 for mmodule in mmodules do