typing&engines: accept `is_same_instance` on `null`
[nit.git] / src / interpreter / naive_interpreter.nit
index 21e7cf5..5cc3892 100644 (file)
@@ -21,6 +21,7 @@ import literal
 import semantize
 private import parser::tables
 import mixin
+import primitive_types
 
 redef class ToolContext
        # --discover-call-trace
@@ -67,16 +68,17 @@ class NaiveInterpreter
        var arguments: Array[String]
 
        # The main Sys instance
-       var mainobj: nullable Instance
+       var mainobj: nullable Instance is noinit
 
-       init(modelbuilder: ModelBuilder, mainmodule: MModule, arguments: Array[String])
+       init
        do
-               self.modelbuilder = modelbuilder
-               self.mainmodule = mainmodule
-               self.arguments = arguments
-               self.true_instance = new PrimitiveInstance[Bool](mainmodule.bool_type, true)
-               self.false_instance = new PrimitiveInstance[Bool](mainmodule.bool_type, false)
-               self.null_instance = new MutableInstance(mainmodule.model.null_type)
+               if mainmodule.model.get_mclasses_by_name("Bool") != null then
+                       self.true_instance = new PrimitiveInstance[Bool](mainmodule.bool_type, true)
+                       init_instance_primitive(self.true_instance)
+                       self.false_instance = new PrimitiveInstance[Bool](mainmodule.bool_type, false)
+                       init_instance_primitive(self.false_instance)
+               end
+               self.null_instance = new PrimitiveInstance[nullable Object](mainmodule.model.null_type, null)
        end
 
        # Starts the interpreter on the main module of a program
@@ -101,56 +103,40 @@ class NaiveInterpreter
        # Subtype test in the context of the mainmodule
        fun is_subtype(sub, sup: MType): Bool
        do
-               return sub.is_subtype(self.mainmodule, self.frame.arguments.first.mtype.as(MClassType), sup)
+               return sub.is_subtype(self.mainmodule, current_receiver_class, sup)
        end
 
+       # Get a primitive method in the context of the main module
        fun force_get_primitive_method(name: String, recv: MType): MMethod
        do
                assert recv isa MClassType
-               return self.modelbuilder.force_get_primitive_method(self.frame.current_node, name, recv.mclass, self.mainmodule)
+               return self.modelbuilder.force_get_primitive_method(current_node, name, recv.mclass, self.mainmodule)
        end
 
        # Is a return executed?
        # Set this mark to skip the evaluation until the end of the specified method frame
        var returnmark: nullable Frame = null
 
-       # Is a break executed?
-       # Set this mark to skip the evaluation until a labeled statement catch it with `is_break`
-       var breakmark: nullable EscapeMark = null
-
-       # Is a continue executed?
-       # Set this mark to skip the evaluation until a labeled statement catch it with `is_continue`
-       var continuemark: nullable EscapeMark = null
+       # Is a break or a continue executed?
+       # Set this mark to skip the evaluation until a labeled statement catch it with `is_escape`
+       var escapemark: nullable EscapeMark = null
 
        # Is a return or a break or a continue executed?
        # Use this function to know if you must skip the evaluation of statements
-       fun is_escaping: Bool do return returnmark != null or breakmark != null or continuemark != null
+       fun is_escaping: Bool do return returnmark != null or escapemark != null
 
        # The value associated with the current return/break/continue, if any.
        # Set the value when you set a escapemark.
        # Read the value when you catch a mark or reach the end of a method
        var escapevalue: nullable Instance = null
 
-       # If there is a break and is associated with `escapemark`, then return true an clear the mark.
-       # If there is no break or if `escapemark` is null then return false.
-       # Use this function to catch a potential break.
-       fun is_break(escapemark: nullable EscapeMark): Bool
-       do
-               if escapemark != null and self.breakmark == escapemark then
-                       self.breakmark = null
-                       return true
-               else
-                       return false
-               end
-       end
-
-       # If there is a continue and is associated with `escapemark`, then return true an clear the mark.
-       # If there is no continue or if `escapemark` is null then return false.
-       # Use this function to catch a potential continue.
-       fun is_continue(escapemark: nullable EscapeMark): Bool
+       # If there is a break/continue and is associated with `escapemark`, then return true and clear the mark.
+       # If there is no break/continue or if `escapemark` is null then return false.
+       # Use this function to catch a potential break/continue.
+       fun is_escape(escapemark: nullable EscapeMark): Bool
        do
-               if escapemark != null and self.continuemark == escapemark then
-                       self.continuemark = null
+               if escapemark != null and self.escapemark == escapemark then
+                       self.escapemark = null
                        return true
                else
                        return false
@@ -187,14 +173,20 @@ class NaiveInterpreter
        # If `n` cannot be evaluated, then aborts.
        fun stmt(n: nullable AExpr)
        do
-               if n != null then
-                       var frame = self.frame
-                       var old = frame.current_node
-                       frame.current_node = n
-                       #n.debug("Execute stmt")
-                       n.stmt(self)
-                       frame.current_node = old
+               if n == null then return
+
+               if n.comprehension != null then
+                       var comprehension = frame.comprehension.as(not null)
+                       var i = expr(n)
+                       if i != null then comprehension.add(i)
+                       return
                end
+
+               var frame = self.frame
+               var old = frame.current_node
+               frame.current_node = n
+               n.stmt(self)
+               frame.current_node = old
        end
 
        # Map used to store values of nodes that must be evaluated once in the system (`AOnceExpr`)
@@ -209,46 +201,55 @@ class NaiveInterpreter
        # Return the integer instance associated with `val`.
        fun int_instance(val: Int): Instance
        do
-               var ic = self.mainmodule.get_primitive_class("Int")
-               return new PrimitiveInstance[Int](ic.mclass_type, val)
+               var ic = get_primitive_class("Int")
+               var instance = new PrimitiveInstance[Int](ic.mclass_type, val)
+               init_instance_primitive(instance)
+               return instance
        end
 
        # Return the char instance associated with `val`.
        fun char_instance(val: Char): Instance
        do
-               var ic = self.mainmodule.get_primitive_class("Char")
-               return new PrimitiveInstance[Char](ic.mclass_type, val)
+               var ic = get_primitive_class("Char")
+               var instance = new PrimitiveInstance[Char](ic.mclass_type, val)
+               init_instance_primitive(instance)
+               return instance
        end
 
        # Return the float instance associated with `val`.
        fun float_instance(val: Float): Instance
        do
-               var ic = self.mainmodule.get_primitive_class("Float")
-               return new PrimitiveInstance[Float](ic.mclass_type, val)
+               var ic = get_primitive_class("Float")
+               var instance = new PrimitiveInstance[Float](ic.mclass_type, val)
+               init_instance_primitive(instance)
+               return instance
        end
 
        # The unique instance of the `true` value.
-       var true_instance: Instance
+       var true_instance: Instance is noinit
 
        # The unique instance of the `false` value.
-       var false_instance: Instance
+       var false_instance: Instance is noinit
 
        # The unique instance of the `null` value.
-       var null_instance: Instance
+       var null_instance: Instance is noinit
 
        # Return a new array made of `values`.
        # The dynamic type of the result is Array[elttype].
        fun array_instance(values: Array[Instance], elttype: MType): Instance
        do
                assert not elttype.need_anchor
-               var nat = new PrimitiveInstance[Array[Instance]](self.mainmodule.get_primitive_class("NativeArray").get_mtype([elttype]), values)
-               var mtype = self.mainmodule.get_primitive_class("Array").get_mtype([elttype])
+               var nat = new PrimitiveInstance[Array[Instance]](get_primitive_class("NativeArray").get_mtype([elttype]), values)
+               init_instance_primitive(nat)
+               var mtype = get_primitive_class("Array").get_mtype([elttype])
                var res = new MutableInstance(mtype)
                self.init_instance(res)
                self.send(self.force_get_primitive_method("with_native", mtype), [res, nat, self.int_instance(values.length)])
                return res
        end
 
+       # Return a instance associated to a primitive class
+       # Current primitive classes are `Int`, `Bool`, and `String`
        fun value_instance(object: Object): Instance
        do
                if object isa Int then
@@ -267,8 +268,10 @@ class NaiveInterpreter
        do
                var val = new FlatBuffer.from(txt)
                val.add('\0')
-               var ic = self.mainmodule.get_primitive_class("NativeString")
-               return new PrimitiveInstance[Buffer](ic.mclass_type, val)
+               var ic = get_primitive_class("NativeString")
+               var instance = new PrimitiveInstance[Buffer](ic.mclass_type, val)
+               init_instance_primitive(instance)
+               return instance
        end
 
        # Return a new String instance for `txt`
@@ -298,13 +301,27 @@ class NaiveInterpreter
                return b.to_s
        end
 
+       # The current node, used to print errors, debug and stack-traces
+       fun current_node: nullable ANode
+       do
+               if frames.is_empty then return null
+               return frames.first.current_node
+       end
+
+       # The dynamic type of the current `self`
+       fun current_receiver_class: MClassType
+       do
+               return frames.first.arguments.first.mtype.as(MClassType)
+       end
+
        # Exit the program with a message
        fun fatal(message: String)
        do
-               if frames.is_empty then
+               var node = current_node
+               if node == null then
                        print message
                else
-                       self.frame.current_node.fatal(self, message)
+                       node.fatal(self, message)
                end
                exit(1)
        end
@@ -312,10 +329,11 @@ class NaiveInterpreter
        # Debug on the current node
        fun debug(message: String)
        do
-               if frames.is_empty then
+               var node = current_node
+               if node == null then
                        print message
                else
-                       self.frame.current_node.debug(message)
+                       node.debug(message)
                end
        end
 
@@ -354,6 +372,13 @@ class NaiveInterpreter
 
                for i in [0..msignature.arity[ do
                        if i == vararg_rank then
+                               var ne = args[i]
+                               if ne isa AVarargExpr then
+                                       var e = self.expr(ne.n_expr)
+                                       if e == null then return null
+                                       res.add(e)
+                                       continue
+                               end
                                var vararg = new Array[Instance]
                                for j in [vararg_rank..vararg_rank+vararg_len] do
                                        var e = self.expr(args[j])
@@ -385,16 +410,18 @@ class NaiveInterpreter
                assert args.length == mpropdef.msignature.arity + 1 else debug("Invalid arity for {mpropdef}. {args.length} arguments given.")
 
                # Look for the AST node that implements the property
-               var mproperty = mpropdef.mproperty
                var val = mpropdef.constant_value
-               if self.modelbuilder.mpropdef2npropdef.has_key(mpropdef) then
-                       var npropdef = self.modelbuilder.mpropdef2npropdef[mpropdef]
-                       self.parameter_check(npropdef, mpropdef, args)
-                       return npropdef.call(self, mpropdef, args)
-               else if mproperty.name == "init" then
-                       var nclassdef = self.modelbuilder.mclassdef2nclassdef[mpropdef.mclassdef]
-                       self.parameter_check(nclassdef, mpropdef, args)
-                       return nclassdef.call(self, mpropdef, args)
+
+               var node = modelbuilder.mpropdef2node(mpropdef)
+               if node isa APropdef then
+                       self.parameter_check(node, mpropdef, args)
+                       return node.call(self, mpropdef, args)
+               else if node isa AClassdef then
+                       self.parameter_check(node, mpropdef, args)
+                       return node.call(self, mpropdef, args)
+               else if node != null then
+                       fatal("Fatal Error: method {mpropdef} associated to unexpected AST node {node.location}")
+                       abort
                else if val != null then
                        return value_instance(val)
                else
@@ -403,7 +430,7 @@ class NaiveInterpreter
                end
        end
 
-       # Generate type checks in the C code to check covariant parameters
+       # Execute type checks of covariant parameters
        fun parameter_check(node: ANode, mpropdef: MMethodDef, args: Array[Instance])
        do
                var msignature = mpropdef.msignature
@@ -415,6 +442,8 @@ class NaiveInterpreter
                        var origmtype =  mpropdef.mproperty.intro.msignature.mparameters[i].mtype
                        if not origmtype.need_anchor then continue
 
+                       #print "{mpropdef}: {mpropdef.mproperty.intro.msignature.mparameters[i]}"
+
                        # get the parameter type
                        var mtype = msignature.mparameters[i].mtype
                        var anchor = args.first.mtype.as(MClassType)
@@ -429,7 +458,7 @@ class NaiveInterpreter
        fun send_commons(mproperty: MMethod, args: Array[Instance], mtype: MType): nullable Instance
        do
                if mtype isa MNullType then
-                       if mproperty.name == "==" then
+                       if mproperty.name == "==" or mproperty.name == "is_same_instance" then
                                return self.bool_instance(args[0] == args[1])
                        else if mproperty.name == "!=" then
                                return self.bool_instance(args[0] != args[1])
@@ -458,7 +487,7 @@ class NaiveInterpreter
                                        self.send(p, args)
                                else if p isa MAttribute then
                                        assert recv isa MutableInstance
-                                       recv.attributes[p] = arguments[i]
+                                       write_attribute(p, recv, arguments[i])
                                        i += 1
                                else abort
                        end
@@ -518,13 +547,7 @@ class NaiveInterpreter
                var cds = mtype.collect_mclassdefs(self.mainmodule).to_a
                self.mainmodule.linearize_mclassdefs(cds)
                for cd in cds do
-                       if not self.modelbuilder.mclassdef2nclassdef.has_key(cd) then continue
-                       var n = self.modelbuilder.mclassdef2nclassdef[cd]
-                       for npropdef in n.n_propdefs do
-                               if npropdef isa AAttrPropdef then
-                                       res.add(npropdef)
-                               end
-                       end
+                       res.add_all(modelbuilder.collect_attr_propdef(cd))
                end
 
                cache[mtype] = res
@@ -542,10 +565,20 @@ class NaiveInterpreter
                end
        end
 
-       # This function determine the correct type according the reciever of the current definition (self).
+       # A hook to initialize a `PrimitiveInstance`
+       fun init_instance_primitive(recv: Instance) do end
+
+       # Return the primitive `MClass` corresponding to the `name` given in parameter
+       # `name` : name of the primitive class
+       fun get_primitive_class(name: String): MClass
+       do
+               return mainmodule.get_primitive_class(name)
+       end
+
+       # This function determines the correct type according to the receiver of the current propdef (self).
        fun unanchor_type(mtype: MType): MType
        do
-               return mtype.anchor_to(self.mainmodule, self.frame.arguments.first.mtype.as(MClassType))
+               return mtype.anchor_to(self.mainmodule, current_receiver_class)
        end
 
        # Placebo instance used to mark internal error result when `null` already have a meaning.
@@ -580,7 +613,7 @@ abstract class Instance
 
        # The real value encapsulated if the instance is primitive.
        # Else aborts.
-       fun val: Object do abort
+       fun val: nullable Object do abort
 end
 
 # A instance with attribute (standards objects)
@@ -593,18 +626,12 @@ end
 
 # Special instance to handle primitives values (int, bool, etc.)
 # The trick it just to encapsulate the <<real>> value
-class PrimitiveInstance[E: Object]
+class PrimitiveInstance[E]
        super Instance
 
        # The real value encapsulated
        redef var val: E
 
-       init(mtype: MType, val: E)
-       do
-               super(mtype)
-               self.val = val
-       end
-
        redef fun is_true
        do
                if val == true then return true
@@ -614,17 +641,17 @@ class PrimitiveInstance[E: Object]
 
        redef fun ==(o)
        do
-               if not o isa PrimitiveInstance[Object] then return false
+               if not o isa PrimitiveInstance[nullable Object] then return false
                return self.val == o.val
        end
 
        redef fun eq_is(o)
        do
-               if not o isa PrimitiveInstance[Object] then return false
+               if not o isa PrimitiveInstance[nullable Object] then return false
                return self.val.is_same_instance(o.val)
        end
 
-       redef fun to_s do return "{mtype}#{val.object_id}({val})"
+       redef fun to_s do return "{mtype}#{val.object_id}({val or else "null"})"
 
        redef fun to_i do return val.as(Int)
 
@@ -643,6 +670,7 @@ class Frame
        var arguments: Array[Instance]
        # Mapping between a variable and the current value
        private var map: Map[Variable, Instance] = new HashMap[Variable, Instance]
+       var comprehension: nullable Array[Instance] = null
 end
 
 redef class ANode
@@ -778,6 +806,12 @@ redef class AMethPropdef
                else if pname == "exit" then
                        exit(args[1].to_i)
                        abort
+               else if pname == "buffer_mode_full" then
+                       return v.int_instance(sys.buffer_mode_full)
+               else if pname == "buffer_mode_line" then
+                       return v.int_instance(sys.buffer_mode_line)
+               else if pname == "buffer_mode_none" then
+                       return v.int_instance(sys.buffer_mode_none)
                else if pname == "sys" then
                        return v.mainobj
                else if cname == "Int" then
@@ -899,9 +933,11 @@ redef class AMethPropdef
                                return v.bool_instance(args[0].to_f.is_nan)
                        else if pname == "is_inf_extern" then
                                return v.bool_instance(args[0].to_f.is_inf != 0)
+                       else if pname == "round" then
+                               return v.float_instance(args[0].to_f.round)
                        end
                else if cname == "NativeString" then
-                       if pname == "init" then
+                       if pname == "new" then
                                return v.native_string_instance("!" * args[1].to_i)
                        end
                        var recvval = args.first.val.as(Buffer)
@@ -959,12 +995,22 @@ redef class AMethPropdef
                        else if pname == "atof" then
                                return v.float_instance(recvval.to_f)
                        end
+               else if cname == "String" then
+                       var cs = v.send(v.force_get_primitive_method("to_cstring", args.first.mtype), [args.first])
+                       var str = cs.val.to_s
+                       if pname == "files" then
+                               var res = new Array[Instance]
+                               for f in str.files do res.add v.string_instance(f)
+                               return v.array_instance(res, v.get_primitive_class("String").mclass_type)
+                       end
                else if pname == "calloc_string" then
                        return v.native_string_instance("!" * args[1].to_i)
                else if cname == "NativeArray" then
-                       if pname == "init" then
+                       if pname == "new" then
                                var val = new Array[Instance].filled_with(v.null_instance, args[1].to_i)
-                               return new PrimitiveInstance[Array[Instance]](args[0].mtype, val)
+                               var instance = new PrimitiveInstance[Array[Instance]](args[0].mtype, val)
+                               v.init_instance_primitive(instance)
+                               return instance
                        end
                        var recvval = args.first.val.as(Array[Instance])
                        if pname == "[]" then
@@ -978,46 +1024,66 @@ redef class AMethPropdef
                        else if pname == "length" then
                                return v.int_instance(recvval.length)
                        else if pname == "copy_to" then
-                               recvval.copy(0, args[2].to_i, args[1].val.as(Array[Instance]), 0)
+                               recvval.copy_to(0, args[2].to_i, args[1].val.as(Array[Instance]), 0)
                                return null
                        end
                else if cname == "NativeFile" then
                        if pname == "native_stdout" then
-                               return new PrimitiveInstance[OStream](mpropdef.mclassdef.mclass.mclass_type, sys.stdout)
+                               var inst = new PrimitiveNativeFile.native_stdout
+                               var instance = new PrimitiveInstance[PrimitiveNativeFile](mpropdef.mclassdef.mclass.mclass_type, inst)
+                               v.init_instance_primitive(instance)
+                               return instance
                        else if pname == "native_stdin" then
-                               return new PrimitiveInstance[IStream](mpropdef.mclassdef.mclass.mclass_type, sys.stdin)
+                               var inst = new PrimitiveNativeFile.native_stdin
+                               var instance = new PrimitiveInstance[PrimitiveNativeFile](mpropdef.mclassdef.mclass.mclass_type, inst)
+                               v.init_instance_primitive(instance)
+                               return instance
                        else if pname == "native_stderr" then
-                               return new PrimitiveInstance[OStream](mpropdef.mclassdef.mclass.mclass_type, sys.stderr)
+                               var inst = new PrimitiveNativeFile.native_stderr
+                               var instance = new PrimitiveInstance[PrimitiveNativeFile](mpropdef.mclassdef.mclass.mclass_type, inst)
+                               v.init_instance_primitive(instance)
+                               return instance
                        else if pname == "io_open_read" then
                                var a1 = args[1].val.as(Buffer)
-                               return new PrimitiveInstance[IStream](mpropdef.mclassdef.mclass.mclass_type, new IFStream.open(a1.to_s))
+                               var inst = new PrimitiveNativeFile.io_open_read(a1.to_s)
+                               var instance = new PrimitiveInstance[PrimitiveNativeFile](mpropdef.mclassdef.mclass.mclass_type, inst)
+                               v.init_instance_primitive(instance)
+                               return instance
                        else if pname == "io_open_write" then
                                var a1 = args[1].val.as(Buffer)
-                               return new PrimitiveInstance[OStream](mpropdef.mclassdef.mclass.mclass_type, new OFStream.open(a1.to_s))
+                               var inst = new PrimitiveNativeFile.io_open_write(a1.to_s)
+                               var instance = new PrimitiveInstance[PrimitiveNativeFile](mpropdef.mclassdef.mclass.mclass_type, inst)
+                               v.init_instance_primitive(instance)
+                               return instance
                        end
                        var recvval = args.first.val
                        if pname == "io_write" then
                                var a1 = args[1].val.as(Buffer)
-                               recvval.as(OStream).write(a1.substring(0, args[2].to_i).to_s)
-                               return args[2]
+                               return v.int_instance(recvval.as(PrimitiveNativeFile).io_write(a1.to_cstring, args[2].to_i))
                        else if pname == "io_read" then
-                               var str = recvval.as(IStream).read(args[2].to_i)
                                var a1 = args[1].val.as(Buffer)
-                               new FlatBuffer.from(str).copy(0, str.length, a1.as(FlatBuffer), 0)
-                               return v.int_instance(str.length)
+                               var ns = new NativeString(a1.length)
+                               var len = recvval.as(PrimitiveNativeFile).io_read(ns, args[2].to_i)
+                               a1.clear
+                               a1.append(ns.to_s_with_length(len))
+                               return v.int_instance(len)
+                       else if pname == "flush" then
+                               recvval.as(PrimitiveNativeFile).flush
+                               return null
                        else if pname == "io_close" then
-                               recvval.as(IOS).close
-                               return v.int_instance(0)
-                       else if pname == "address_is_null" then
-                               return v.false_instance
+                               return v.int_instance(recvval.as(PrimitiveNativeFile).io_close)
+                       else if pname == "set_buffering_type" then
+                               return v.int_instance(recvval.as(PrimitiveNativeFile).set_buffering_type(args[1].to_i, args[2].to_i))
                        end
                else if pname == "calloc_array" then
                        var recvtype = args.first.mtype.as(MClassType)
                        var mtype: MType
-                       mtype = recvtype.supertype_to(v.mainmodule, recvtype, v.mainmodule.get_primitive_class("ArrayCapable"))
+                       mtype = recvtype.supertype_to(v.mainmodule, recvtype, v.get_primitive_class("ArrayCapable"))
                        mtype = mtype.arguments.first
                        var val = new Array[Instance].filled_with(v.null_instance, args[1].to_i)
-                       return new PrimitiveInstance[Array[Instance]](v.mainmodule.get_primitive_class("NativeArray").get_mtype([mtype]), val)
+                       var instance = new PrimitiveInstance[Array[Instance]](v.get_primitive_class("NativeArray").get_mtype([mtype]), val)
+                       v.init_instance_primitive(instance)
+                       return instance
                else if pname == "native_argc" then
                        return v.int_instance(v.arguments.length)
                else if pname == "native_argv" then
@@ -1030,6 +1096,9 @@ redef class AMethPropdef
                        return v.native_string_instance(txt)
                else if pname == "get_time" then
                        return v.int_instance(get_time)
+               else if pname == "srand" then
+                       srand
+                       return null
                else if pname == "srand_from" then
                        srand_from(args[1].to_i)
                        return null
@@ -1050,19 +1119,16 @@ redef class AMethPropdef
                else if pname == "errno" then
                        return v.int_instance(sys.errno)
                else if pname == "address_is_null" then
+                       var recv = args[0]
+                       if recv isa PrimitiveInstance[PrimitiveNativeFile] then
+                               return v.bool_instance(recv.val.address_is_null)
+                       end
                        return v.false_instance
                end
                return v.error_instance
        end
 end
 
-redef class AbstractArray[E]
-       fun copy(start: Int, len: Int, dest: AbstractArray[E], new_start: Int)
-       do
-               self.copy_to(start, len, dest, new_start)
-       end
-end
-
 redef class AAttrPropdef
        redef fun call(v, mpropdef, args)
        do
@@ -1086,8 +1152,7 @@ redef class AAttrPropdef
        private fun init_expr(v: NaiveInterpreter, recv: Instance)
        do
                if is_lazy then return
-               var nexpr = self.n_expr
-               if nexpr != null then
+               if has_value then
                        evaluate_expr(v, recv)
                        return
                end
@@ -1101,12 +1166,26 @@ redef class AAttrPropdef
        private fun evaluate_expr(v: NaiveInterpreter, recv: Instance): Instance
        do
                assert recv isa MutableInstance
-               var nexpr = self.n_expr
-               assert nexpr != null
                var f = new Frame(self, self.mpropdef.as(not null), [recv])
                v.frames.unshift(f)
-               var val = v.expr(nexpr)
+
+               var val
+
+               var nexpr = self.n_expr
+               var nblock = self.n_block
+               if nexpr != null then
+                       val = v.expr(nexpr)
+               else if nblock != null then
+                       v.stmt(nblock)
+                       assert v.returnmark == f
+                       val = v.escapevalue
+                       v.returnmark = null
+                       v.escapevalue = null
+               else
+                       abort
+               end
                assert val != null
+
                v.frames.shift
                assert not v.is_escaping
                v.write_attribute(self.mpropdef.mproperty, recv, val)
@@ -1223,20 +1302,7 @@ redef class ASelfExpr
        end
 end
 
-redef class AContinueExpr
-       redef fun stmt(v)
-       do
-               var ne = self.n_expr
-               if ne != null then
-                       var i = v.expr(ne)
-                       if i == null then return
-                       v.escapevalue = i
-               end
-               v.continuemark = self.escapemark
-       end
-end
-
-redef class ABreakExpr
+redef class AEscapeExpr
        redef fun stmt(v)
        do
                var ne = self.n_expr
@@ -1245,7 +1311,7 @@ redef class ABreakExpr
                        if i == null then return
                        v.escapevalue = i
                end
-               v.breakmark = self.escapemark
+               v.escapemark = self.escapemark
        end
 end
 
@@ -1311,7 +1377,7 @@ redef class ADoExpr
        redef fun stmt(v)
        do
                v.stmt(self.n_block)
-               v.is_break(self.escapemark) # Clear the break (if any)
+               v.is_escape(self.break_mark) # Clear the break (if any)
        end
 end
 
@@ -1323,8 +1389,8 @@ redef class AWhileExpr
                        if cond == null then return
                        if not cond.is_true then return
                        v.stmt(self.n_block)
-                       if v.is_break(self.escapemark) then return
-                       v.is_continue(self.escapemark) # Clear the break
+                       if v.is_escape(self.break_mark) then return
+                       v.is_escape(self.continue_mark) # Clear the break
                        if v.is_escaping then return
                end
        end
@@ -1335,8 +1401,8 @@ redef class ALoopExpr
        do
                loop
                        v.stmt(self.n_block)
-                       if v.is_break(self.escapemark) then return
-                       v.is_continue(self.escapemark) # Clear the break
+                       if v.is_escape(self.break_mark) then return
+                       v.is_escape(self.continue_mark) # Clear the break
                        if v.is_escaping then return
                end
        end
@@ -1368,8 +1434,8 @@ redef class AForExpr
                                abort
                        end
                        v.stmt(self.n_block)
-                       if v.is_break(self.escapemark) then break
-                       v.is_continue(self.escapemark) # Clear the break
+                       if v.is_escape(self.break_mark) then break
+                       v.is_escape(self.continue_mark) # Clear the break
                        if v.is_escaping then break
                        v.callsite(method_next, [iter])
                end
@@ -1473,11 +1539,18 @@ redef class AArrayExpr
        redef fun expr(v)
        do
                var val = new Array[Instance]
-               for nexpr in self.n_exprs.n_exprs do
-                       var i = v.expr(nexpr)
-                       if i == null then return null
-                       val.add(i)
+               var old_comprehension = v.frame.comprehension
+               v.frame.comprehension = val
+               for nexpr in self.n_exprs do
+                       if nexpr isa AForExpr then
+                               v.stmt(nexpr)
+                       else
+                               var i = v.expr(nexpr)
+                               if i == null then return null
+                               val.add(i)
+                       end
                end
+               v.frame.comprehension = old_comprehension
                var mtype = v.unanchor_type(self.mtype.as(not null)).as(MClassType)
                var elttype = mtype.arguments.first
                return v.array_instance(val, elttype)
@@ -1501,7 +1574,7 @@ redef class ASuperstringExpr
                        if i == null then return null
                        array.add(i)
                end
-               var i = v.array_instance(array, v.mainmodule.get_primitive_class("Object").mclass_type)
+               var i = v.array_instance(array, v.get_primitive_class("Object").mclass_type)
                var res = v.send(v.force_get_primitive_method("to_s", i.mtype), [i])
                assert res != null
                return res
@@ -1689,7 +1762,7 @@ end
 redef class ANewExpr
        redef fun expr(v)
        do
-               var mtype = v.unanchor_type(self.mtype.as(not null))
+               var mtype = v.unanchor_type(self.recvtype.as(not null))
                var recv: Instance = new MutableInstance(mtype)
                v.init_instance(recv)
                var args = v.varargize(callsite.mpropdef, recv, self.n_args.n_exprs)