icode: ITypeCheck requires a receiver
[nit.git] / src / syntax / icode_generation.nit
index fb4a801..260d1e5 100644 (file)
@@ -24,7 +24,7 @@ private import primitive_info
 
 # An AST2ICode context stores the currently built icode informations
 class A2IContext
-special ICodeBuilder
+       super ICodeBuilder
        redef fun stmt(s: ICode)
        do
                if _current_node != null then
@@ -39,7 +39,7 @@ special ICodeBuilder
        fun add_new_array(stype: MMType, length: Int): IRegister
        do
                var prop = visitor.get_method(stype, once "with_capacity".to_symbol)
-               var ni = expr(new INative("TAG_Int({length})", null), visitor.type_int)
+               var ni = expr(new IIntValue(length.to_s), visitor.type_int)
                return expr(new INew(stype, prop, [ni]), stype)
        end
 
@@ -58,9 +58,7 @@ special ICodeBuilder
                if _variables.has_key(v) then
                        return _variables[v]
                else
-                       var t = v.stype
-                       if t == null then t = visitor.type_object.as_nullable
-                       var reg = new_register(t)
+                       var reg = new_register(v.stype.as(not null))
                        _variables[v] = reg
                        return reg
                end
@@ -84,9 +82,12 @@ special ICodeBuilder
        # The method associated to the iroutine (if any)
        readable var _method: nullable MMMethod
 
+       # The register of self (if any)
+       var selfreg: nullable IRegister writable
+
        init(visitor: AbsSyntaxVisitor, r: IRoutine, m: nullable MMMethod)
        do
-               super(visitor.module, r)
+               super(visitor.mmmodule, r)
                _visitor = visitor
                _return_seq = r.body
                _return_value = r.result
@@ -281,7 +282,7 @@ redef class MMImplicitInit
 end
 
 class A2IVisitor
-special AbsSyntaxVisitor
+       super AbsSyntaxVisitor
        writable var _icode_ctx: nullable A2IContext
        fun icode_ctx: A2IContext do return _icode_ctx.as(not null)
        redef fun visit(n) do n.accept_icode_generation(self)
@@ -347,11 +348,18 @@ redef class AClosureDecl
                v.seq = iclos.body
                escapable.continue_seq = iclos.body
                escapable.continue_value = iclos.result
+               escapable.break_seq = v.return_seq
+               escapable.break_value = v.return_value
                n_signature.fill_iroutine_parameters(v, variable.closure.signature, iclos.params, null)
 
                if n_expr != null then
                        v.generate_stmt(n_expr)
                        v.iroutine.closure_decls[position].default = iclos
+
+                       # Add a final break in case of break block witout value
+                       if variable.closure.is_break and v.return_value == null then
+                               v.add_escape(v.return_seq.as(not null))
+                       end
                end
                v.seq = old_seq
        end
@@ -363,6 +371,7 @@ redef class AConcreteMethPropdef
                var params = v.iroutine.params.to_a
                var selfreg = v.variable(self_var)
                v.stmt(new IMove(selfreg, params[0]))
+               v.selfreg = selfreg
                params.shift
 
                var orig_meth: MMLocalProperty = method.global.intro
@@ -378,6 +387,7 @@ redef class AConcreteMethPropdef
                if n_block != null then
                        v.generate_stmt(n_block)
                end
+               v.selfreg = null
        end
 end
 
@@ -392,25 +402,11 @@ redef class AExternMethPropdef
        redef fun fill_iroutine(v, method)
        do
                var params = v.iroutine.params
-               var ename = "{method.module.name}_{method.local_class.name}_{method.local_class.name}_{method.name}_{method.signature.arity}"
-               if n_extern != null then
-                       ename = n_extern.text
-                       ename = ename.substring(1, ename.length-2)
-               end
-               var sig = method.signature
-               assert params.length == sig.arity + 1
-               var args = new Array[String]
-               args.add(sig.recv.unboxtype("@@@"))
-               for i in [0..sig.arity[ do
-                       args.add(sig[i].unboxtype("@@@"))
-               end
-               var s = "{ename}({args.join(", ")})"
-               var rtype = sig.return_type
+               var rtype = method.signature.return_type
                if rtype != null then
-                       s = rtype.boxtype(s)
-                       v.add_return_value(v.expr(new INative(s, params), rtype))
+                       v.add_return_value(v.expr(new INative(method, params), rtype))
                else
-                       v.stmt(new INative(s, params))
+                       v.stmt(new INative(method, params))
                end
        end
 end
@@ -418,187 +414,12 @@ end
 redef class AInternMethPropdef
        redef fun fill_iroutine(v, method)
        do
-               var p = v.iroutine.params.to_a
-               var c = method.local_class.name
-               var n = method.name
-               var s: nullable String = null
-               if c == once "Int".to_symbol then
-                       if n == once "object_id".to_symbol then
-                               s = "@@@"
-                       else if n == once "unary -".to_symbol then
-                               s = "TAG_Int(-UNTAG_Int(@@@))"
-                       else if n == once "output".to_symbol then
-                               s = "printf(\"%ld\\n\", UNTAG_Int(@@@));"
-                       else if n == once "ascii".to_symbol then
-                               s = "TAG_Char(UNTAG_Int(@@@))"
-                       else if n == once "succ".to_symbol then
-                               s = "TAG_Int(UNTAG_Int(@@@)+1)"
-                       else if n == once "prec".to_symbol then
-                               s = "TAG_Int(UNTAG_Int(@@@)-1)"
-                       else if n == once "to_f".to_symbol then
-                               s = "BOX_Float((float)UNTAG_Int(@@@))"
-                       else if n == once "+".to_symbol then
-                               s = "TAG_Int(UNTAG_Int(@@@)+UNTAG_Int(@@@))"
-                       else if n == once "-".to_symbol then
-                               s = "TAG_Int(UNTAG_Int(@@@)-UNTAG_Int(@@@))"
-                       else if n == once "*".to_symbol then
-                               s = "TAG_Int(UNTAG_Int(@@@)*UNTAG_Int(@@@))"
-                       else if n == once "/".to_symbol then
-                               s = "TAG_Int(UNTAG_Int(@@@)/UNTAG_Int(@@@))"
-                       else if n == once "%".to_symbol then
-                               s = "TAG_Int(UNTAG_Int(@@@)%UNTAG_Int(@@@))"
-                       else if n == once "<".to_symbol then
-                               s = "TAG_Bool(UNTAG_Int(@@@)<UNTAG_Int(@@@))"
-                       else if n == once ">".to_symbol then
-                               s = "TAG_Bool(UNTAG_Int(@@@)>UNTAG_Int(@@@))"
-                       else if n == once "<=".to_symbol then
-                               s = "TAG_Bool(UNTAG_Int(@@@)<=UNTAG_Int(@@@))"
-                       else if n == once ">=".to_symbol then
-                               s = "TAG_Bool(UNTAG_Int(@@@)>=UNTAG_Int(@@@))"
-                       else if n == once "lshift".to_symbol then
-                               s = "TAG_Int(UNTAG_Int(@@@)<<UNTAG_Int(@@@))"
-                       else if n == once "rshift".to_symbol then
-                               s = "TAG_Int(UNTAG_Int(@@@)>>UNTAG_Int(@@@))"
-                       else if n == once "==".to_symbol then
-                               s = "TAG_Bool((@@@)==(@@@))"
-                       else if n == once "!=".to_symbol then
-                               s = "TAG_Bool((@@@)!=(@@@))"
-                       end
-               else if c == once "Float".to_symbol then
-                       if n == once "object_id".to_symbol then
-                               s = "TAG_Int((bigint)UNBOX_Float(@@@))"
-                       else if n == once "unary -".to_symbol then
-                               s = "BOX_Float(-UNBOX_Float(@@@))"
-                       else if n == once "output".to_symbol then
-                               s = "printf(\"%f\\n\", UNBOX_Float(@@@));"
-                       else if n == once "to_i".to_symbol then
-                               s = "TAG_Int((bigint)UNBOX_Float(@@@))"
-                       else if n == once "+".to_symbol then
-                               s = "BOX_Float(UNBOX_Float(@@@)+UNBOX_Float(@@@))"
-                       else if n == once "-".to_symbol then
-                               s = "BOX_Float(UNBOX_Float(@@@)-UNBOX_Float(@@@))"
-                       else if n == once "*".to_symbol then
-                               s = "BOX_Float(UNBOX_Float(@@@)*UNBOX_Float(@@@))"
-                       else if n == once "/".to_symbol then
-                               s = "BOX_Float(UNBOX_Float(@@@)/UNBOX_Float(@@@))"
-                       else if n == once "<".to_symbol then
-                               s = "TAG_Bool(UNBOX_Float(@@@)<UNBOX_Float(@@@))"
-                       else if n == once ">".to_symbol then
-                               s = "TAG_Bool(UNBOX_Float(@@@)>UNBOX_Float(@@@))"
-                       else if n == once "<=".to_symbol then
-                               s = "TAG_Bool(UNBOX_Float(@@@)<=UNBOX_Float(@@@))"
-                       else if n == once ">=".to_symbol then
-                               s = "TAG_Bool(UNBOX_Float(@@@)>=UNBOX_Float(@@@))"
-                       end
-               else if c == once "Char".to_symbol then
-                       if n == once "object_id".to_symbol then
-                               s = "TAG_Int(UNTAG_Char(@@@))"
-                       else if n == once "unary -".to_symbol then
-                               s = "TAG_Char(-UNTAG_Char(@@@))"
-                       else if n == once "output".to_symbol then
-                               s = "printf(\"%c\", (unsigned char)UNTAG_Char(@@@));"
-                       else if n == once "ascii".to_symbol then
-                               s = "TAG_Int((unsigned char)UNTAG_Char(@@@))"
-                       else if n == once "succ".to_symbol then
-                               s = "TAG_Char(UNTAG_Char(@@@)+1)"
-                       else if n == once "prec".to_symbol then
-                               s = "TAG_Char(UNTAG_Char(@@@)-1)"
-                       else if n == once "to_i".to_symbol then
-                               s = "TAG_Int(UNTAG_Char(@@@)-'0')"
-                       else if n == once "+".to_symbol then
-                               s = "TAG_Char(UNTAG_Char(@@@)+UNTAG_Char(@@@))"
-                       else if n == once "-".to_symbol then
-                               s = "TAG_Char(UNTAG_Char(@@@)-UNTAG_Char(@@@))"
-                       else if n == once "*".to_symbol then
-                               s = "TAG_Char(UNTAG_Char(@@@)*UNTAG_Char(@@@))"
-                       else if n == once "/".to_symbol then
-                               s = "TAG_Char(UNTAG_Char(@@@)/UNTAG_Char(@@@))"
-                       else if n == once "%".to_symbol then
-                               s = "TAG_Char(UNTAG_Char(@@@)%UNTAG_Char(@@@))"
-                       else if n == once "<".to_symbol then
-                               s = "TAG_Bool(UNTAG_Char(@@@)<UNTAG_Char(@@@))"
-                       else if n == once ">".to_symbol then
-                               s = "TAG_Bool(UNTAG_Char(@@@)>UNTAG_Char(@@@))"
-                       else if n == once "<=".to_symbol then
-                               s = "TAG_Bool(UNTAG_Char(@@@)<=UNTAG_Char(@@@))"
-                       else if n == once ">=".to_symbol then
-                               s = "TAG_Bool(UNTAG_Char(@@@)>=UNTAG_Char(@@@))"
-                       else if n == once "==".to_symbol then
-                               s = "TAG_Bool((@@@)==(@@@))"
-                       else if n == once "!=".to_symbol then
-                               s = "TAG_Bool((@@@)!=(@@@))"
-                       end
-               else if c == once "Bool".to_symbol then
-                       if n == once "object_id".to_symbol then
-                               s = "TAG_Int(UNTAG_Bool(@@@))"
-                       else if n == once "unary -".to_symbol then
-                               s = "TAG_Bool(-UNTAG_Bool(@@@))"
-                       else if n == once "output".to_symbol then
-                               s = "(void)printf(UNTAG_Bool(@@@)?\"true\\n\":\"false\\n\");"
-                       else if n == once "ascii".to_symbol then
-                               s = "TAG_Bool(UNTAG_Bool(@@@))"
-                       else if n == once "to_i".to_symbol then
-                               s = "TAG_Int(UNTAG_Bool(@@@))"
-                       else if n == once "==".to_symbol then
-                               s = "TAG_Bool((@@@)==(@@@))"
-                       else if n == once "!=".to_symbol then
-                               s = "TAG_Bool((@@@)!=(@@@))"
-                       end
-               else if c == once "NativeArray".to_symbol then
-                       if n == once "object_id".to_symbol then
-                               s = "TAG_Int(((Nit_NativeArray)@@@)->object_id)"
-                       else if n == once "[]".to_symbol then
-                               s = "((Nit_NativeArray)@@@)->val[UNTAG_Int(@@@)]"
-                       else if n == once "[]=".to_symbol then
-                               s = "((Nit_NativeArray)@@@)->val[UNTAG_Int(@@@)]=@@@"
-                       else if n == once "copy_to".to_symbol then
-                               var t = p[0]
-                               p[0] = p[1]
-                               p[1] = t
-                               s = "(void)memcpy(((Nit_NativeArray )@@@)->val, ((Nit_NativeArray)@@@)->val, UNTAG_Int(@@@)*sizeof(val_t))"
-                       end
-               else if c == once "NativeString".to_symbol then
-                       if n == once "object_id".to_symbol then
-                               s = "TAG_Int(UNBOX_NativeString(@@@))"
-                       else if n == once "atoi".to_symbol then
-                               s = "TAG_Int(atoi(UNBOX_NativeString(@@@)))"
-                       else if n == once "[]".to_symbol then
-                               s = "TAG_Char(UNBOX_NativeString(@@@)[UNTAG_Int(@@@)])"
-                       else if n == once "[]=".to_symbol then
-                               s = "UNBOX_NativeString(@@@)[UNTAG_Int(@@@)]=UNTAG_Char(@@@);"
-                       else if n == once "copy_to".to_symbol then
-                               var t = p[0]
-                               p[0] = p[1]
-                               p[1] = p[4]
-                               p[4] = p[2]
-                               p[2] = t
-                               s = "(void)memcpy(UNBOX_NativeString(@@@)+UNTAG_Int(@@@), UNBOX_NativeString(@@@)+UNTAG_Int(@@@), UNTAG_Int(@@@));"
-                       end
-               else if n == once "object_id".to_symbol then
-                       s = "TAG_Int((bigint)((obj_t)@@@)[1].object_id)"
-               else if n == once "sys".to_symbol then
-                       s = "(G_sys)"
-               else if n == once "is_same_type".to_symbol then
-                       s = "TAG_Bool((VAL2VFT(@@@)==VAL2VFT(@@@)))"
-               else if n == once "exit".to_symbol then
-                       p[0] = p[1]
-                       s = "exit(UNTAG_Int(@@@));"
-               else if n == once "calloc_array".to_symbol then
-                       p[0] = p[1]
-                       s = "NEW_NativeArray(UNTAG_Int(@@@), sizeof(val_t))"
-               else if n == once "calloc_string".to_symbol then
-                       p[0] = p[1]
-                       s = "BOX_NativeString((char*)raw_alloc((UNTAG_Int(@@@) * sizeof(char))))"
-               end
-               if s == null then
-                       v.visitor.error(self, "Fatal error: unknown intern method {method.full_name}.")
-                       s = "NIT_NULL"
-               end
+               var params = v.iroutine.params
                var rtype = method.signature.return_type
                if rtype != null then
-                       v.add_return_value(v.expr(new INative(s, p), rtype))
+                       v.add_return_value(v.expr(new INative(method, params), rtype))
                else
-                       v.stmt(new INative(s, p))
+                       v.stmt(new INative(method, params))
                end
        end
 end
@@ -656,7 +477,7 @@ redef class AReturnExpr
                if ne != null then
                        v.add_assignment(v.return_value.as(not null), v.generate_expr(ne))
                end
-               v.stmt(new IEscape(v.return_seq.as(not null)))
+               v.add_escape(v.return_seq.as(not null))
                return null
        end
 end
@@ -668,7 +489,7 @@ redef class ABreakExpr
                if ne != null then
                        v.add_assignment(escapable.break_value.as(not null), v.generate_expr(ne))
                end
-               v.stmt(new IEscape(escapable.break_seq.as(not null)))
+               v.add_escape(escapable.break_seq.as(not null))
                return null
        end
 end
@@ -680,7 +501,7 @@ redef class AContinueExpr
                if ne != null then
                        v.add_assignment(escapable.continue_value.as(not null), v.generate_expr(ne))
                end
-               v.stmt(new IEscape(escapable.continue_seq.as(not null)))
+               v.add_escape(escapable.continue_seq.as(not null))
                return null
        end
 end
@@ -737,7 +558,7 @@ redef class AWhileExpr
 
                # Process escape (condition is false)
                v.seq = iif.else_seq
-               v.stmt(new IEscape(iloop))
+               v.add_escape(iloop)
 
                v.seq = seq_old
                return null
@@ -770,51 +591,55 @@ end
 redef class AForExpr
        redef fun generate_icode(v)
        do
-               var expr_type = n_expr.stype
-
-               # Get iterator
-               var meth_iterator = v.visitor.get_method(expr_type, once "iterator".to_symbol)
-
-               var iter_type = meth_iterator.signature_for(expr_type).return_type.as(not null)
-               var ireg_iter = v.expr(new ICall(meth_iterator, [v.generate_expr(n_expr)]), iter_type)
-
-               # Enter loop
-               var seq_old = v.seq
-               var iloop = new ILoop
-               v.stmt(iloop)
-               escapable.break_seq = iloop
-               v.seq = iloop
-
-               # Condition evaluation
-               var meth_is_ok = v.visitor.get_method(iter_type, once ("is_ok".to_symbol))
-               var ireg_isok = v.expr(new ICall(meth_is_ok, [ireg_iter]), v.visitor.type_bool)
-               var iif = new IIf(ireg_isok)
+               var ne = n_expr
+               var expr_type = ne.stype
+               var tint = v.visitor.type_int
+               var meth # The method that call the closure
+               var args # The arguments of meth
+
+               if ne isa ARangeExpr and expr_type == v.visitor.type_range(tint) then
+                       # Shortcut. No Range[Int] object allocated.
+                       # 'for x in [y..z] do' become 'y.enumerate_to(z) !each(x) do'
+                       # 'for x in [y..z[ do' become 'y.enumerate_before(z) !each(x) do'
+                       # And both methods may be inlined
+                       args = [v.generate_expr(ne.n_expr), v.generate_expr(ne.n_expr2)]
+                       if ne isa ACrangeExpr then
+                               meth = v.visitor.get_method(tint, once "enumerate_to".to_symbol)
+                       else
+                               assert ne isa AOrangeExpr
+                               meth = v.visitor.get_method(tint, once "enumerate_before".to_symbol)
+                       end
+               else
+                       # Standard way.
+                       # 'for x in e do' become 'e.iterate !each(x) do'
+                       # Some iterate methods may be inlined (eg. the Array one)
+                       meth = v.visitor.get_method(expr_type, once "iterate".to_symbol)
+                       args = [v.generate_expr(n_expr)]
+               end
 
-               # Process insite the loop (condition is true)
-               v.stmt(iif)
-               v.seq = iif.then_seq
-               escapable.continue_seq = iif.then_seq
+               # Build closure
+               var iclos = meth.signature.closures.first.signature.generate_empty_iclosuredef(v)
+               var old_seq = v.seq
 
-               # Automatic variable assignment
-               var meth_item = v.visitor.get_method(iter_type, once ("item".to_symbol))
-               var va_stype = variable.stype.as(not null)
-               var ireg_item = v.expr(new ICall(meth_item, [ireg_iter]), va_stype)
-               var ireg_va = v.variable(variable)
-               v.add_assignment(ireg_va, ireg_item)
+               var seq = new ISeq
+               v.stmt(seq)
+               v.seq = seq
+               escapable.break_seq = seq
+               escapable.break_value = null
 
-               # Body evaluation
+               v.seq = iclos.body
+               escapable.continue_seq = iclos.body
+               escapable.continue_value = null
+               for i in [0..variables.length[ do
+                       v.stmt(new IMove(v.variable(variables[i]), iclos.params[i]))
+               end
                v.generate_stmt(n_block)
 
-               # Exit contition (condition is false)
-               v.seq = iif.else_seq
-               v.stmt(new IEscape(iloop))
-
-               # Next step
-               var meth_next = v.visitor.get_method(iter_type, once ("next".to_symbol))
-               v.seq = iloop
-               v.stmt(new ICall(meth_next, [ireg_iter]))
+               # Call closure
+               v.seq = seq
+               v.add_call(meth, args, [iclos])
 
-               v.seq = seq_old
+               v.seq = old_seq
                return null
        end
 end
@@ -958,11 +783,41 @@ redef class ANotExpr
        end
 end
 
+redef class AOrElseExpr
+       redef fun generate_icode(v)
+       do
+               # Compute left operand
+               var e = v.generate_expr(n_expr)
+
+               # Prepare result
+               var reg = v.new_register(stype)
+
+               # Compare left and null
+               var n = v.lit_null_reg
+               var c = v.expr(new IIs(e, n), v.mmmodule.type_bool)
+               var iif = new IIf(c)
+               v.stmt(iif)
+               var old_seq = v.seq
+
+               # if equal, result = right opr
+               v.seq = iif.then_seq
+               v.add_assignment(reg, v.generate_expr(n_expr2))
+
+               # else, result = left operand
+               v.seq = iif.else_seq
+               v.add_assignment(reg, e)
+
+               v.seq = old_seq
+
+               return reg
+       end
+end
+
 redef class AIsaExpr
        redef fun generate_icode(v)
        do
                var e = v.generate_expr(n_expr)
-               return v.expr(new ITypeCheck(e, n_type.stype), stype)
+               return v.expr(new ITypeCheck(v.selfreg.as(not null), e, n_type.stype), stype)
        end
 end
 
@@ -1001,21 +856,21 @@ end
 redef class AIntExpr
        redef fun generate_icode(v)
        do
-               return v.expr(new INative("TAG_Int({n_number.text})", null), stype)
+               return v.expr(new IIntValue(n_number.text), stype)
        end
 end
 
 redef class AFloatExpr
        redef fun generate_icode(v)
        do
-               return v.expr(new INative("BOX_Float({n_float.text})", null), stype)
+               return v.expr(new IFloatValue(n_float.text), stype)
        end
 end
 
 redef class ACharExpr
        redef fun generate_icode(v)
        do
-               return v.expr(new INative("TAG_Char({n_char.text})", null), stype)
+               return v.expr(new ICharValue(n_char.text), stype)
        end
 end
 
@@ -1027,8 +882,8 @@ redef class AStringFormExpr
                var ionce = new IOnce
                var reg = v.expr(ionce, stype)
                v.seq = ionce.body
-               var ns = v.expr(new INative("BOX_NativeString(\"{_cstring}\")", null), v.visitor.type_nativestring)
-               var ni = v.expr(new INative("TAG_Int({_cstring_length})", null), v.visitor.type_int)
+               var ns = v.expr(new IStringValue(_cstring.as(not null)), v.visitor.type_nativestring)
+               var ni = v.expr(new IIntValue(_cstring_length.to_s), v.visitor.type_int)
                var prop = v.visitor.get_method(stype, once "with_native".to_symbol)
                var e = v.expr(new INew(stype, prop, [ns, ni]), stype)
                v.add_assignment(reg, e)
@@ -1398,6 +1253,11 @@ redef class AClosureDef
 
                v.generate_stmt(n_expr)
 
+               # Add a final break in case of break block witout value
+               if closure.is_break and escapable.break_value == null then
+                       v.add_escape(escapable.break_seq.as(not null))
+               end
+
                v.seq = seq_old
                _iclosure_def = iclos
                return iclos