nullable: type, compile and test 'isset _attr'
[nit.git] / src / compiling / compiling_methods.nit
index 3e20f95..3f79777 100644 (file)
@@ -24,7 +24,8 @@ redef class CompilerVisitor
        # Compile a statment node
        meth compile_stmt(n: PExpr)
        do
-               add_instr("/* Compile stmt {n.locate} */")
+               if n == null then return
+               #add_instr("/* Compile stmt {n.locate} */")
                n.prepare_compile_stmt(self)
                var i = cfc._variable_index
                n.compile_stmt(self)
@@ -34,14 +35,14 @@ redef class CompilerVisitor
        # Compile is expression node
        meth compile_expr(n: PExpr): String
        do
-               add_instr("/* Compile expr {n.locate} */")
+               #add_instr("/* Compile expr {n.locate} */")
                var i = cfc._variable_index
                var s = n.compile_expr(self)
                cfc._variable_index = i
-               if s[0] == ' ' then
+               if s[0] == ' ' or cfc.is_valid_variable(s) then
                        return s
                end
-               var v = cfc.get_var("Result for expr {n.locate}")
+               var v = cfc.get_var("Result")
                add_assignment(v, s)
                return v
        end
@@ -49,7 +50,8 @@ redef class CompilerVisitor
        # Ensure that a c expression is a var
        meth ensure_var(s: String, comment: String): String
        do
-               if s.substring(0,3) == "variable" then
+               if cfc.is_valid_variable(s) then
+                       add_instr("/* Ensure var {s}: {comment}*/")
                        return s
                end
                var v = cfc.get_var(null)
@@ -83,11 +85,6 @@ redef class CompilerVisitor
                return s.to_s
        end
 
-       redef init(module: MMSrcModule)
-       do
-               super
-       end
-
        meth invoke_super_init_calls_after(start_prop: MMMethod)
        do
                var n = nmc.method.node
@@ -128,7 +125,7 @@ redef class CompilerVisitor
                                end
                        end
                        #s.append(" {p}")
-                       p.compile_call(self, cargs)
+                       p.compile_stmt_call(self, cargs)
                        i += 1
                end
                #s.append(" ]")
@@ -235,6 +232,15 @@ class CFunctionContext
                end
        end
 
+       # Is s a valid variable
+       protected meth is_valid_variable(s: String): Bool
+       do
+               for i in [0.._variable_index[ do
+                       if s == variable(i) then return true
+               end
+               return false
+       end
+
        # Mark the variable available
        meth free_var(v: String)
        do
@@ -302,11 +308,30 @@ redef class ClosureVariable
 end
 
 redef class MMMethod
+       # Compile as an expression.
+       # require that signature.return_type != null
+       meth compile_expr_call(v: CompilerVisitor, cargs: Array[String]): String
+       do
+               assert signature.return_type != null
+               var s = intern_compile_call(v, cargs)
+               assert s != null
+               return s
+       end
+
+       # Compile as a statement.
+       # require that signature.return_type == null
+       meth compile_stmt_call(v: CompilerVisitor, cargs: Array[String])
+       do
+               assert signature.return_type == null
+               var s = intern_compile_call(v, cargs)
+               assert s == null
+       end
+
        # Compile a call on self for given arguments
        # Most calls are compiled with a table access,
        # primitive calles are inlined
        # == and != are guarded and possibly inlined
-       meth compile_call(v: CompilerVisitor, cargs: Array[String]): String
+       private meth intern_compile_call(v: CompilerVisitor, cargs: Array[String]): String
        do
                var i = self
                if i isa MMSrcMethod then
@@ -321,7 +346,7 @@ redef class MMMethod
                var ne = once "!=".to_symbol
                if name == ne then
                        var eqp = signature.recv.local_class.select_method(ee)
-                       var eqcall = eqp.compile_call(v, cargs)
+                       var eqcall = eqp.compile_expr_call(v, cargs)
                        return "TAG_Bool(!UNTAG_Bool({eqcall}))"
                end
                if global.is_init then
@@ -376,7 +401,7 @@ redef class MMMethod
                v.nmc.break_value = old_bv
 
                # Call
-               var e = compile_call(v, realcargs)
+               var e = intern_compile_call(v, realcargs)
                if e != null then
                        v.add_assignment(ve, e)
                        e = ve
@@ -429,11 +454,27 @@ redef class MMMethod
 end
 
 redef class MMAttribute
-       # Compile an acces on selffor a given reciever.
-       # Result is a valid C left-value for assigment
-       meth compile_access(v: CompilerVisitor, recv: String): String
+       # Compile a read acces on selffor a given reciever.
+       meth compile_isset(v: CompilerVisitor, n: PNode, recv: String): String
+       do
+               return "TAG_Bool({global.attr_access}({recv})!=NIT_NULL) /* isset {local_class}::{name}*/"
+       end
+
+       # Compile a read acces on selffor a given reciever.
+       meth compile_read_access(v: CompilerVisitor, n: PNode, recv: String): String
+       do
+               var res = "{global.attr_access}({recv}) /*{local_class}::{name}*/"
+               if not signature.return_type.is_nullable and v.tc.opt_warn.value > 0 then
+                       res = v.ensure_var(res, "{local_class}::{name}")
+                       v.add_instr("if ({res} == NIT_NULL) \{ fprintf(stderr, \"Uninitialized attribute %s\", \"{name}\"); {v.printf_locate_error(n)} } /* implicit isset */;")
+               end
+               return res
+       end
+
+       # Compile a write acces on selffor a given reciever.
+       meth compile_write_access(v: CompilerVisitor, n: PNode, recv: String, value: String)
        do
-               return "{global.attr_access}({recv}) /*{local_class}::{name}*/"
+               v.add_instr("{global.attr_access}({recv}) /*{local_class}::{name}*/ = {value};")
        end
 end
 
@@ -541,14 +582,14 @@ end
 redef class MMReadImplementationMethod
        redef meth do_compile_inside(v, params)
        do
-               return node.prop.compile_access(v, params[0])
+               return node.prop.compile_read_access(v, node, params[0])
        end
 end
 
 redef class MMWriteImplementationMethod
        redef meth do_compile_inside(v, params)
        do
-               v.add_assignment(node.prop.compile_access(v, params[0]), params[1])
+               node.prop.compile_write_access(v, node, params[0], params[1])
                return null
        end
 end
@@ -574,14 +615,14 @@ redef class MMImplicitInit
                                for i in [1..f[ do
                                        args.add(params[i])
                                end
-                               sp.compile_call(v, args)
+                               sp.compile_stmt_call(v, args)
                        else
-                               sp.compile_call(v, args_recv)
+                               sp.compile_stmt_call(v, args_recv)
                        end
                end
                for i in [f..params.length[ do
                        var attribute = unassigned_attributes[i-f]
-                       v.add_assignment(attribute.compile_access(v, recv), params[i])
+                       attribute.compile_write_access(v, null, recv, params[i])
                end
                return null
        end
@@ -590,19 +631,49 @@ end
 redef class MMType
        # Compile a subtype check to self
        # Return a NIT Bool
-       meth compile_cast(v: CompilerVisitor, recv: String): String
+       meth compile_cast(v: CompilerVisitor, recv: String, fromtype: MMType): String
        do
                # Fixme: handle formaltypes
                var g = local_class.global
-               return "TAG_Bool(({recv}==NIT_NULL) || VAL_ISA({recv}, {g.color_id}, {g.id_id})) /*cast {self}*/"
+               var s = ""
+               if fromtype.is_nullable then
+                       if self.is_nullable then
+                               s = "({recv}==NIT_NULL) || "
+                       else
+                               s = "({recv}!=NIT_NULL) && "
+                       end
+               else
+                       # FIXME This is used to not break code without the nullable KW
+                       s = "({recv}==NIT_NULL) || "
+               end
+               return "TAG_Bool({s}VAL_ISA({recv}, {g.color_id}, {g.id_id})) /*cast {self}*/"
        end
 
        # Compile a cast assertion
-       meth compile_type_check(v: CompilerVisitor, recv: String, n: PNode)
+       meth compile_type_check(v: CompilerVisitor, recv: String, n: PNode, fromtype: MMType)
        do
                # Fixme: handle formaltypes
                var g = local_class.global
-               v.add_instr("if (({recv}!=NIT_NULL) && !VAL_ISA({recv}, {g.color_id}, {g.id_id})) \{ fprintf(stderr, \"Cast failled\"); {v.printf_locate_error(n)} nit_exit(1); } /*cast {self}*/;")
+               var s = ""
+               if fromtype.is_nullable then
+                       if self.is_nullable then
+                               s = "({recv}!=NIT_NULL) && "
+                       else
+                               s = "({recv}==NIT_NULL) || "
+                       end
+               else
+                       # FIXME This is used to not break code without the nullable KW
+                       s = "({recv}!=NIT_NULL) && "
+               end
+               v.add_instr("if ({s}!VAL_ISA({recv}, {g.color_id}, {g.id_id})) \{ fprintf(stderr, \"Cast failled\"); {v.printf_locate_error(n)} nit_exit(1); } /*cast {self}*/;")
+       end
+
+       # Compile a notnull cast assertion
+       meth compile_notnull_check(v: CompilerVisitor, recv: String, n: PNode)
+       do
+               if is_nullable then
+                       v.add_instr("if (({recv}==NIT_NULL)) \{ fprintf(stderr, \"Cast failled\"); {v.printf_locate_error(n)} nit_exit(1); } /*cast {self}*/;")
+               end
        end
 end
 
@@ -628,7 +699,7 @@ redef class ASignature
                                # FIXME: do not test always
                                # FIXME: handle formal types
                                v.add_instr("/* check if p<{ap.variable.stype} with p:{orig_type} */")
-                               ap.variable.stype.compile_type_check(v, params[ap.position], ap)
+                               ap.variable.stype.compile_type_check(v, params[ap.position], ap, orig_type)
                        end
                        v.add_assignment(cname, params[ap.position])
                end
@@ -670,9 +741,7 @@ redef class AConcreteMethPropdef
                if self isa AConcreteInitPropdef then
                        v.invoke_super_init_calls_after(null)
                end
-               if n_block != null then
-                       v.compile_stmt(n_block)
-               end
+               v.compile_stmt(n_block)
                v.add_instr("{v.nmc.return_label}: while(false);")
                if self isa AConcreteInitPropdef then
                        v.add_instr("init_table[{itpos}] = 1;")
@@ -991,9 +1060,7 @@ end
 redef class ADoExpr
        redef meth compile_stmt(v)
        do
-                if n_block != null then
-                        v.compile_stmt(n_block)
-                end
+               v.compile_stmt(n_block)
        end
 end
 
@@ -1038,7 +1105,8 @@ redef class AIfexprExpr
        end
 end
 
-redef class AControlableBlock
+class AControlableBlock
+special PExpr
        meth compile_inside_block(v: CompilerVisitor) is abstract
        redef meth compile_stmt(v)
        do
@@ -1057,6 +1125,7 @@ redef class AControlableBlock
 end
 
 redef class AWhileExpr
+special AControlableBlock
        redef meth compile_inside_block(v)
        do
                v.add_instr("while (true) \{ /*while*/")
@@ -1064,9 +1133,7 @@ redef class AWhileExpr
                var e = v.compile_expr(n_expr)
                v.add_instr("if (!UNTAG_Bool({e})) break; /* while*/")
                v.cfc.free_var(e)
-               if n_block != null then
-                       v.compile_stmt(n_block)
-               end
+               v.compile_stmt(n_block)
                v.add_instr("{v.nmc.continue_label}: while(0);")
                v.unindent
                v.add_instr("}")
@@ -1075,30 +1142,27 @@ redef class AWhileExpr
 end
 
 redef class AForExpr
+special AControlableBlock
        redef meth compile_inside_block(v)
        do
                var e = v.compile_expr(n_expr)
                var ittype = meth_iterator.signature.return_type
                v.cfc.free_var(e)
                var iter = v.cfc.get_var("For iterator")
-               v.add_assignment(iter, meth_iterator.compile_call(v, [e]))
+               v.add_assignment(iter, meth_iterator.compile_expr_call(v, [e]))
                v.add_instr("while (true) \{ /*for*/")
                v.indent
                var ok = v.cfc.get_var("For 'is_ok' result")
-               v.add_assignment(ok, meth_is_ok.compile_call(v, [iter]))
+               v.add_assignment(ok, meth_is_ok.compile_expr_call(v, [iter]))
                v.add_instr("if (!UNTAG_Bool({ok})) break; /*for*/")
                v.cfc.free_var(ok)
-               var e = meth_item.compile_call(v, [iter])
+               var e = meth_item.compile_expr_call(v, [iter])
                e = v.ensure_var(e, "For item")
                var cname = v.cfc.register_variable(variable)
                v.add_assignment(cname, e)
-               var n_block = n_block
-               if n_block != null then
-                       v.compile_stmt(n_block)
-               end
+               v.compile_stmt(n_block)
                v.add_instr("{v.nmc.continue_label}: while(0);")
-               e = meth_next.compile_call(v, [iter])
-               assert e == null
+               meth_next.compile_stmt_call(v, [iter])
                v.unindent
                v.add_instr("}")
                v.add_instr("{v.nmc.break_label}: while(0);")
@@ -1137,7 +1201,7 @@ redef class AVarReassignExpr
        do
                var e1 = v.cfc.varname(variable)
                var e2 = v.compile_expr(n_value)
-               var e3 = assign_method.compile_call(v, [e1, e2])
+               var e3 = assign_method.compile_expr_call(v, [e1, e2])
                v.add_assignment(v.cfc.varname(variable), "{e3} /*{variable.name}*/")
        end
 end
@@ -1199,7 +1263,7 @@ redef class AIsaExpr
        redef meth compile_expr(v)
        do
                var e = v.compile_expr(n_expr)
-               return n_type.stype.compile_cast(v, e)
+               return n_type.stype.compile_cast(v, e, n_expr.stype)
        end
 end
 
@@ -1207,7 +1271,16 @@ redef class AAsCastExpr
        redef meth compile_expr(v)
        do
                var e = v.compile_expr(n_expr)
-               n_type.stype.compile_type_check(v, e, self)
+               n_type.stype.compile_type_check(v, e, self, n_expr.stype)
+               return e
+       end
+end
+
+redef class AAsNotnullExpr
+       redef meth compile_expr(v)
+       do
+               var e = v.compile_expr(n_expr)
+               n_expr.stype.compile_notnull_check(v, e, self)
                return e
        end
 end
@@ -1324,13 +1397,13 @@ redef class ASuperstringExpr
                        var e = v.ensure_var(v.compile_expr(ne), "super-string element")
                        if ne.stype != stype then
                                v.cfc.free_var(e)
-                               e = meth_to_s.compile_call(v, [e])
+                               e = meth_to_s.compile_expr_call(v, [e])
                        end
                        v.cfc.free_var(e)
-                       meth_add.compile_call(v, [array, e])
+                       meth_add.compile_stmt_call(v, [array, e])
                end
 
-               return meth_to_s.compile_call(v, [array])
+               return meth_to_s.compile_expr_call(v, [array])
        end
 end
 
@@ -1349,7 +1422,7 @@ redef class AArrayExpr
 
                for ne in n_exprs do
                        var e = v.compile_expr(ne)
-                       meth_add.compile_call(v, [recv, e])
+                       meth_add.compile_stmt_call(v, [recv, e])
                end
                return recv
        end
@@ -1367,12 +1440,21 @@ end
 redef class ASuperExpr
        redef meth compile_stmt(v)
        do
-               var e = compile_expr(v)
-               if e != null then v.add_instr("{e};")
+               var e = intern_compile_call(v)
+               if e != null then
+                       v.add_instr(e + ";")
+               end
        end
 
        redef meth compile_expr(v)
        do
+               var e = intern_compile_call(v)
+               assert e != null
+               return e
+       end
+
+       private meth intern_compile_call(v: CompilerVisitor): String
+       do
                var arity = v.nmc.method_params.length - 1
                if init_in_superclass != null then
                        arity = init_in_superclass.signature.arity
@@ -1390,7 +1472,7 @@ redef class ASuperExpr
                end
                #return "{prop.cname}({args.join(", ")}) /*super {prop.local_class}::{prop.name}*/"
                if init_in_superclass != null then
-                       return init_in_superclass.compile_call(v, args)
+                       return init_in_superclass.intern_compile_call(v, args)
                else
                        if prop.global.is_init then args.add("init_table")
                        return prop.compile_super_call(v, args)
@@ -1402,7 +1484,7 @@ redef class AAttrExpr
        redef meth compile_expr(v)
        do
                var e = v.compile_expr(n_expr)
-               return prop.compile_access(v, e)
+               return prop.compile_read_access(v, n_id, e)
        end
 end
 
@@ -1411,33 +1493,50 @@ redef class AAttrAssignExpr
        do
                var e = v.compile_expr(n_expr)
                var e2 = v.compile_expr(n_value)
-               v.add_assignment(prop.compile_access(v, e), e2)
+               prop.compile_write_access(v, n_id, e, e2)
        end
 end
 redef class AAttrReassignExpr
        redef meth compile_stmt(v)
        do
                var e1 = v.compile_expr(n_expr)
-               var e2 = prop.compile_access(v, e1)
+               var e2 = prop.compile_read_access(v, n_id, e1)
                var e3 = v.compile_expr(n_value)
-               var e4 = assign_method.compile_call(v, [e2, e3])
-               v.add_assignment(e2, e4)
+               var e4 = assign_method.compile_expr_call(v, [e2, e3])
+               prop.compile_write_access(v, n_id, e1, e4)
        end
 end
 
-redef class ASendExpr
+redef class AIssetAttrExpr
        redef meth compile_expr(v)
        do
-               var recv = v.compile_expr(n_expr)
-               var cargs = new Array[String]
-               cargs.add(recv)
+               var e = v.compile_expr(n_expr)
+               return prop.compile_isset(v, n_id, e)
+       end
+end
+
+redef class AAbsAbsSendExpr
+       # Compile each argument and add them to the array
+       meth compile_arguments_in(v: CompilerVisitor, cargs: Array[String])
+       do
                for a in arguments do
                        cargs.add(v.compile_expr(a))
                end
+       end
+
+end
+
+redef class ASendExpr
+       private meth intern_compile_call(v: CompilerVisitor): String
+       do
+               var recv = v.compile_expr(n_expr)
+               var cargs = new Array[String]
+               cargs.add(recv)
+               compile_arguments_in(v, cargs)
 
                var e: String
                if prop_signature.closures.is_empty then
-                       e = prop.compile_call(v, cargs)
+                       e = prop.intern_compile_call(v, cargs)
                else
                        e = prop.compile_call_and_closures(v, cargs, closure_defs)
                end
@@ -1448,9 +1547,16 @@ redef class ASendExpr
                return e
        end
 
+       redef meth compile_expr(v)
+       do
+               var e = intern_compile_call(v)
+               assert e != null
+               return e
+       end
+
        redef meth compile_stmt(v)
        do
-               var e = compile_expr(v)
+               var e = intern_compile_call(v)
                if e != null then
                        v.add_instr(e + ";")
                end
@@ -1458,20 +1564,20 @@ redef class ASendExpr
 end
 
 redef class ASendReassignExpr
-       redef meth compile_expr(v)
+       redef meth compile_expr(v) do abort
+
+       redef meth compile_stmt(v)
        do
                var recv = v.compile_expr(n_expr)
                var cargs = new Array[String]
                cargs.add(recv)
-               for a in arguments do
-                       cargs.add(v.compile_expr(a))
-               end
+               compile_arguments_in(v, cargs)
 
-               var e2 = read_prop.compile_call(v, cargs)
+               var e2 = read_prop.compile_expr_call(v, cargs)
                var e3 = v.compile_expr(n_value)
-               var e4 = assign_method.compile_call(v, [e2, e3])
+               var e4 = assign_method.compile_expr_call(v, [e2, e3])
                cargs.add(e4)
-               return prop.compile_call(v, cargs)
+               prop.compile_stmt_call(v, cargs)
        end
 end
 
@@ -1479,11 +1585,11 @@ redef class ANewExpr
        redef meth compile_expr(v)
        do
                var cargs = new Array[String]
-               for a in arguments do
-                       cargs.add(v.compile_expr(a))
-               end
+               compile_arguments_in(v, cargs)
                return prop.compile_constructor_call(v, stype, cargs) 
        end
+
+       redef meth compile_stmt(v) do abort
 end
 
 redef class PClosureDef
@@ -1605,7 +1711,7 @@ redef class AClosureDef
                v.nmc.continue_label = "continue_label{v.new_number}"
                v.nmc.break_label = v.nmc.return_label
 
-               if n_expr != null then v.compile_stmt(n_expr)
+               v.compile_stmt(n_expr)
 
                v.add_instr("{v.nmc.continue_label}: while(false);")
 
@@ -1636,7 +1742,7 @@ redef class AClosureDecl
                v.nmc.continue_label = "continue_label{v.new_number}"
                v.nmc.break_label = v.nmc.return_label
 
-               if n_expr != null then v.compile_stmt(n_expr)
+               v.compile_stmt(n_expr)
 
                v.add_instr("{v.nmc.continue_label}: while(false);")
 
@@ -1652,10 +1758,10 @@ redef class AClosureDecl
 end
 
 redef class AClosureCallExpr
-       redef meth compile_expr(v)
+       meth intern_compile_call(v: CompilerVisitor): String
        do
                var cargs = new Array[String]
-               for a in arguments do cargs.add(v.compile_expr(a))
+               compile_arguments_in(v, cargs)
                var va: String = null
                if variable.closure.signature.return_type != null then va = v.cfc.get_var("Closure call result value")
 
@@ -1696,6 +1802,21 @@ redef class AClosureCallExpr
                end
                return va
        end
+
+       redef meth compile_expr(v)
+       do
+               var e = intern_compile_call(v)
+               assert e != null
+               return e
+       end
+
+       redef meth compile_stmt(v)
+       do
+               var e = intern_compile_call(v)
+               if e != null then
+                       v.add_instr(e + ";")
+               end
+       end
 end
 
 redef class AProxyExpr