typing: use `location` attribute instead of `node` to isolate CallSite from the AST
[nit.git] / src / semantize / typing.nit
index 2bbc78b..d0c34ac 100644 (file)
@@ -20,6 +20,7 @@ module typing
 
 import modelize
 import local_var_init
+import literal
 
 redef class ToolContext
        var typing_phase: Phase = new TypingPhase(self, [flow_phase, modelize_property_phase, local_var_init_phase])
@@ -105,22 +106,22 @@ private class TypeVisitor
        # If `sub` is a safe subtype of `sup` then return `sub`.
        # If `sub` is an unsafe subtype (ie an implicit cast is required), then return `sup`.
        #
-       # The point of the return type is to determinate the usable type on an expression:
+       # The point of the return type is to determinate the usable type on an expression when `autocast` is true:
        # If the suptype is safe, then the return type is the one on the expression typed by `sub`.
        # Is the subtype is unsafe, then the return type is the one of an implicit cast on `sup`.
-       fun check_subtype(node: ANode, sub, sup: MType): nullable MType
+       fun check_subtype(node: ANode, sub, sup: MType, autocast: Bool): nullable MType
        do
                if self.is_subtype(sub, sup) then return sub
-               if self.is_subtype(sub, self.anchor_to(sup)) then
+               if autocast and self.is_subtype(sub, self.anchor_to(sup)) then
                        # FIXME workaround to the current unsafe typing policy. To remove once fixed virtual types exists.
                        #node.debug("Unsafe typing: expected {sup}, got {sub}")
                        return sup
                end
                if sub.need_anchor then
                        var u = anchor_to(sub)
-                       self.modelbuilder.error(node, "Type error: expected {sup}, got {sub}: {u}")
+                       self.modelbuilder.error(node, "Type Error: expected `{sup}`, got `{sub}: {u}`.")
                else
-                       self.modelbuilder.error(node, "Type error: expected {sup}, got {sub}")
+                       self.modelbuilder.error(node, "Type Error: expected `{sup}`, got `{sub}`.")
                end
                return null
        end
@@ -150,7 +151,7 @@ private class TypeVisitor
                        end
                        return null # forward error
                end
-               self.error(nexpr, "Type error: expected expression.")
+               self.error(nexpr, "Error: expected an expression.")
                return null
        end
 
@@ -166,7 +167,7 @@ private class TypeVisitor
 
                if sup == null then return null # Forward error
 
-               var res = check_subtype(nexpr, sub, sup)
+               var res = check_subtype(nexpr, sub, sup, true)
                if res != sub then
                        nexpr.implicit_cast_to = res
                end
@@ -184,18 +185,18 @@ private class TypeVisitor
        end
 
 
-       fun visit_expr_cast(node: ANode, nexpr: AExpr, ntype: AType): nullable MType
+       fun check_expr_cast(node: ANode, nexpr: AExpr, ntype: AType): nullable MType
        do
-               var sub = visit_expr(nexpr)
+               var sub = nexpr.mtype
                if sub == null then return null # Forward error
 
-               var sup = self.resolve_mtype(ntype)
+               var sup = ntype.mtype
                if sup == null then return null # Forward error
 
                if sup == sub then
-                       self.modelbuilder.warning(node, "useless-type-test", "Warning: Expression is already a {sup}.")
+                       self.modelbuilder.warning(node, "useless-type-test", "Warning: expression is already a `{sup}`.")
                else if self.is_subtype(sub, sup) then
-                       self.modelbuilder.warning(node, "useless-type-test", "Warning: Expression is already a {sup} since it is a {sub}.")
+                       self.modelbuilder.warning(node, "useless-type-test", "Warning: expression is already a `{sup}` since it is a `{sub}`.")
                end
                return sup
        end
@@ -217,6 +218,10 @@ private class TypeVisitor
        # Else return true.
        fun check_can_be_null(anode: ANode, mtype: MType): Bool
        do
+               if mtype isa MNullType then
+                       modelbuilder.warning(anode, "useless-null-test", "Warning: expression is always `null`.")
+                       return true
+               end
                if can_be_null(mtype) then return true
 
                if mtype isa MFormalType then
@@ -239,12 +244,16 @@ private class TypeVisitor
 
                if not mtype2 isa MNullType then return
 
-               if mtype isa MNullType then return
-
                # Check of useless null
-               if not check_can_be_null(anode.n_expr, mtype) then return
+               if not can_be_null(mtype) then return
 
-               mtype = mtype.as_notnull
+               if mtype isa MNullType then
+                       # Because of type adaptation, we cannot just stop here
+                       # so return use `null` as a bottom type that will be merged easily (cf) `merge_types`
+                       mtype = null
+               else
+                       mtype = mtype.as_notnull
+               end
 
                # Check for type adaptation
                var variable = anode.n_expr.its_variable
@@ -252,11 +261,11 @@ private class TypeVisitor
 
                # One is null (mtype2 see above) the other is not null
                if anode isa AEqExpr then
-                       anode.after_flow_context.when_true.set_var(variable, mtype2)
-                       anode.after_flow_context.when_false.set_var(variable, mtype)
+                       anode.after_flow_context.when_true.set_var(self, variable, mtype2)
+                       anode.after_flow_context.when_false.set_var(self, variable, mtype)
                else if anode isa ANeExpr then
-                       anode.after_flow_context.when_false.set_var(variable, mtype2)
-                       anode.after_flow_context.when_true.set_var(variable, mtype)
+                       anode.after_flow_context.when_false.set_var(self, variable, mtype2)
+                       anode.after_flow_context.when_true.set_var(self, variable, mtype)
                else
                        abort
                end
@@ -297,15 +306,9 @@ private class TypeVisitor
 
                #debug("recv: {recvtype} (aka {unsafe_type})")
                if recvtype isa MNullType then
-                       # `null` only accepts some methods of object.
-                       if name == "==" or name == "!=" or name == "is_same_instance" then
-                               var objclass = get_mclass(node, "Object")
-                               if objclass == null then return null # Forward error
-                               unsafe_type = objclass.mclass_type
-                       else
-                               self.error(node, "Error: Method '{name}' call on 'null'.")
-                               return null
-                       end
+                       var objclass = get_mclass(node, "Object")
+                       if objclass == null then return null # Forward error
+                       unsafe_type = objclass.mclass_type
                end
 
                var mproperty = self.try_get_mproperty_by_name2(node, unsafe_type, name)
@@ -315,26 +318,35 @@ private class TypeVisitor
                end
 
                if mproperty == null then
-                       #self.modelbuilder.error(node, "Type error: property {name} not found in {unsafe_type} (ie {recvtype})")
                        if recv_is_self then
-                               self.modelbuilder.error(node, "Error: Method or variable '{name}' unknown in {recvtype}.")
+                               self.modelbuilder.error(node, "Error: method or variable `{name}` unknown in `{recvtype}`.")
+                       else if recvtype.need_anchor then
+                               self.modelbuilder.error(node, "Error: method `{name}` does not exists in `{recvtype}: {unsafe_type}`.")
                        else
-                               self.modelbuilder.error(node, "Error: Method '{name}' doesn't exists in {recvtype}.")
+                               self.modelbuilder.error(node, "Error: method `{name}` does not exists in `{recvtype}`.")
                        end
                        return null
                end
 
                assert mproperty isa MMethod
 
+               # `null` only accepts some methods of object.
+               if recvtype isa MNullType and not mproperty.is_null_safe then
+                       self.error(node, "Error: method `{name}` called on `null`.")
+                       return null
+               else if unsafe_type isa MNullableType and not mproperty.is_null_safe then
+                       modelbuilder.advice(node, "call-on-nullable", "Warning: method call on a nullable receiver `{recvtype}`.")
+               end
+
                if is_toplevel_context and recv_is_self and not mproperty.is_toplevel then
-                       error(node, "Error: '{name}' is not a top-level method, thus need a receiver.")
+                       error(node, "Error: `{name}` is not a top-level method, thus need a receiver.")
                end
                if not recv_is_self and mproperty.is_toplevel then
-                       error(node, "Error: cannot call '{name}', a top-level method, with a receiver.")
+                       error(node, "Error: cannot call `{name}`, a top-level method, with a receiver.")
                end
 
                if mproperty.visibility == protected_visibility and not recv_is_self and self.mmodule.visibility_for(mproperty.intro_mclassdef.mmodule) < intrude_visibility and not modelbuilder.toolcontext.opt_ignore_visibility.value then
-                       self.modelbuilder.error(node, "Error: Method '{name}' is protected and can only acceded by self.")
+                       self.modelbuilder.error(node, "Error: method `{name}` is protected and can only accessed by `self`.")
                        return null
                end
 
@@ -342,21 +354,21 @@ private class TypeVisitor
                if info != null and self.mpropdef.mproperty.deprecation == null then
                        var mdoc = info.mdoc
                        if mdoc != null then
-                               self.modelbuilder.warning(node, "deprecated-method", "Deprecation Warning: Method '{name}' is deprecated: {mdoc.content.first}")
+                               self.modelbuilder.warning(node, "deprecated-method", "Deprecation Warning: method `{name}` is deprecated: {mdoc.content.first}")
                        else
-                               self.modelbuilder.warning(node, "deprecated-method", "Deprecation Warning: Method '{name}' is deprecated.")
+                               self.modelbuilder.warning(node, "deprecated-method", "Deprecation Warning: method `{name}` is deprecated.")
                        end
                end
 
                var propdefs = mproperty.lookup_definitions(self.mmodule, unsafe_type)
                var mpropdef
                if propdefs.length == 0 then
-                       self.modelbuilder.error(node, "Type error: no definition found for property {name} in {unsafe_type}")
+                       self.modelbuilder.error(node, "Type Error: no definition found for property `{name}` in `{unsafe_type}`.")
                        return null
                else if propdefs.length == 1 then
                        mpropdef = propdefs.first
                else
-                       self.modelbuilder.warning(node, "property-conflict", "Warning: conflicting property definitions for property {name} in {unsafe_type}: {propdefs.join(" ")}")
+                       self.modelbuilder.warning(node, "property-conflict", "Warning: conflicting property definitions for property `{name}` in `{unsafe_type}`: {propdefs.join(" ")}")
                        mpropdef = mproperty.intro
                end
 
@@ -377,7 +389,7 @@ private class TypeVisitor
                        end
                end
 
-               var callsite = new CallSite(node, recvtype, mmodule, anchor, recv_is_self, mproperty, mpropdef, msignature, erasure_cast)
+               var callsite = new CallSite(node.hot_location, recvtype, mmodule, anchor, recv_is_self, mproperty, mpropdef, msignature, erasure_cast)
                return callsite
        end
 
@@ -393,48 +405,137 @@ private class TypeVisitor
        # Visit the expressions of args and check their conformity with the corresponding type in signature
        # The point of this method is to handle varargs correctly
        # Note: The signature must be correctly adapted
-       fun check_signature(node: ANode, args: Array[AExpr], name: String, msignature: MSignature): Bool
+       fun check_signature(node: ANode, args: Array[AExpr], mproperty: MProperty, msignature: MSignature): nullable SignatureMap
        do
                var vararg_rank = msignature.vararg_rank
                if vararg_rank >= 0 then
                        if args.length < msignature.arity then
-                               #self.modelbuilder.error(node, "Error: Incorrect number of parameters. Got {args.length}, expected at least {msignature.arity}. Signature is {msignature}")
-                               self.modelbuilder.error(node, "Error: arity mismatch; prototype is '{name}{msignature}'")
-                               return false
+                               modelbuilder.error(node, "Error: expected at least {msignature.arity} argument(s) for `{mproperty}{msignature}`; got {args.length}. See introduction at `{mproperty.full_name}`.")
+                               return null
                        end
                else if args.length != msignature.arity then
-                       self.modelbuilder.error(node, "Error: Incorrect number of parameters. Got {args.length}, expected {msignature.arity}. Signature is {msignature}")
-                       return false
+                       # Too much argument
+                       if args.length > msignature.arity then
+                               modelbuilder.error(node, "Error: expected {msignature.arity} argument(s) for `{mproperty}{msignature}`; got {args.length}. See introduction at `{mproperty.full_name}`.")
+                               return null
+                       end
+                       # Other cases are managed later
                end
 
+
                #debug("CALL {unsafe_type}.{msignature}")
 
+               # Associate each parameter to a position in the arguments
+               var map = new SignatureMap
+
+               # Special case for the isolated last argument
+               # TODO: reify this method characteristics (where? the param, the signature, the method?)
+               var last_is_padded = mproperty.name.chars.last == '='
+               var nbargs = args.length
+               if last_is_padded then
+                       nbargs -= 1
+                       assert not args.last isa ANamedargExpr
+                       map.map[msignature.arity - 1] = args.length - 1
+                       self.visit_expr_subtype(args.last, msignature.mparameters.last.mtype)
+               end
+
+               # First, handle named arguments
+               for i in [0..args.length[ do
+                       var e = args[i]
+                       if not e isa ANamedargExpr then continue
+                       var name = e.n_id.text
+                       var param = msignature.mparameter_by_name(name)
+                       if param == null then
+                               modelbuilder.error(e.n_id, "Error: no parameter `{name}` for `{mproperty}{msignature}`.")
+                               return null
+                       end
+                       var idx = msignature.mparameters.index_of(param)
+                       var prev = map.map.get_or_null(idx)
+                       if prev != null then
+                               modelbuilder.error(e, "Error: parameter `{name}` already associated with argument #{prev} for `{mproperty}{msignature}`.")
+                               return null
+                       end
+                       map.map[idx] = i
+                       e.mtype = self.visit_expr_subtype(e.n_expr, param.mtype)
+               end
+
+               # Number of minimum mandatory remaining parameters
+               var min_arity = 0
+
+               # Second, associate remaining parameters
                var vararg_decl = args.length - msignature.arity
+               var j = 0
                for i in [0..msignature.arity[ do
-                       var j = i
-                       if i == vararg_rank then continue # skip the vararg
-                       if i > vararg_rank then
-                               j = i + vararg_decl
+                       # Skip parameters associated by name
+                       if map.map.has_key(i) then continue
+
+                       var param = msignature.mparameters[i]
+
+                       # Search the next free argument: skip named arguments since they are already associated
+                       while j < nbargs and args[j] isa ANamedargExpr do j += 1
+                       if j >= nbargs then
+                               if not param.mtype isa MNullableType then
+                                       min_arity = j + 1
+                               end
+                               j += 1
+                               continue
+                       end
+                       var arg = args[j]
+                       map.map[i] = j
+                       j += 1
+
+                       if i == vararg_rank then
+                               j += vararg_decl
+                               continue # skip the vararg
                        end
-                       var paramtype = msignature.mparameters[i].mtype
-                       self.visit_expr_subtype(args[j], paramtype)
+
+                       var paramtype = param.mtype
+                       self.visit_expr_subtype(arg, paramtype)
                end
+
+               if min_arity > 0 then
+                       if last_is_padded then min_arity += 1
+                       if min_arity < msignature.arity then
+                               modelbuilder.error(node, "Error: expected at least {min_arity} argument(s) for `{mproperty}{msignature}`; got {args.length}. See introduction at `{mproperty.full_name}`.")
+                       else
+                               modelbuilder.error(node, "Error: expected {min_arity} argument(s) for `{mproperty}{msignature}`; got {args.length}. See introduction at `{mproperty.full_name}`.")
+                       end
+                       return null
+               end
+
+               # Third, check varargs
                if vararg_rank >= 0 then
                        var paramtype = msignature.mparameters[vararg_rank].mtype
                        var first = args[vararg_rank]
-                       if vararg_decl == 0 and first isa AVarargExpr then
+                       if vararg_decl == 0 then
                                var mclass = get_mclass(node, "Array")
-                               if mclass == null then return false # Forward error
+                               if mclass == null then return null # Forward error
                                var array_mtype = mclass.get_mtype([paramtype])
-                               self.visit_expr_subtype(first.n_expr, array_mtype)
-                               first.mtype  = first.n_expr.mtype
+                               if first isa AVarargExpr then
+                                       self.visit_expr_subtype(first.n_expr, array_mtype)
+                                       first.mtype  = first.n_expr.mtype
+                               else
+                                       # only one vararg, maybe `...` was forgot, so be gentle!
+                                       var t = visit_expr(first)
+                                       if t == null then return null # Forward error
+                                       if not is_subtype(t, paramtype) and is_subtype(t, array_mtype) then
+                                               # Not acceptable but could be a `...`
+                                               error(first, "Type Error: expected `{paramtype}`, got `{t}`. Is an ellipsis `...` missing on the argument?")
+                                               return null
+                                       end
+                                       # Standard valid vararg, finish the job
+                                       map.vararg_decl = 1
+                                       self.visit_expr_subtype(first, paramtype)
+                               end
                        else
-                               for j in [vararg_rank..vararg_rank+vararg_decl] do
-                                       self.visit_expr_subtype(args[j], paramtype)
+                               map.vararg_decl = vararg_decl + 1
+                               for i in [vararg_rank..vararg_rank+vararg_decl] do
+                                       self.visit_expr_subtype(args[i], paramtype)
                                end
                        end
                end
-               return true
+
+               return map
        end
 
        fun error(node: ANode, message: String)
@@ -444,11 +545,10 @@ private class TypeVisitor
 
        fun get_variable(node: AExpr, variable: Variable): nullable MType
        do
+               if not variable.is_adapted then return variable.declared_type
+
                var flow = node.after_flow_context
-               if flow == null then
-                       self.error(node, "No context!")
-                       return null
-               end
+               if flow == null then return null # skip error
 
                if flow.vars.has_key(variable) then
                        return flow.vars[variable]
@@ -456,7 +556,7 @@ private class TypeVisitor
                        #node.debug("*** START Collected for {variable}")
                        var mtypes = flow.collect_types(variable)
                        #node.debug("**** END Collected for {variable}")
-                       if mtypes == null or mtypes.length == 0 then
+                       if mtypes.length == 0 then
                                return variable.declared_type
                        else if mtypes.length == 1 then
                                return mtypes.first
@@ -468,12 +568,18 @@ private class TypeVisitor
                end
        end
 
+       # Some variables where type-adapted during the visit
+       var dirty = false
+
+       # Some loops had been visited during the visit
+       var has_loop = false
+
        fun set_variable(node: AExpr, variable: Variable, mtype: nullable MType)
        do
                var flow = node.after_flow_context
                assert flow != null
 
-               flow.set_var(variable, mtype)
+               flow.set_var(self, variable, mtype)
        end
 
        fun merge_types(node: ANode, col: Array[nullable MType]): nullable MType
@@ -499,10 +605,24 @@ private class TypeVisitor
        end
 end
 
+# Mapping between parameters and arguments in a call.
+#
+# Parameters and arguments are not stored in the class but referenced by their position (starting from 0)
+#
+# The point of this class is to help engine and other things to map arguments in the AST to parameters of the model.
+class SignatureMap
+       # Associate a parameter to an argument
+       var map = new ArrayMap[Int, Int]
+
+       # The length of the vararg sequence
+       # 0 if no vararg or if reverse vararg (cf `AVarargExpr`)
+       var vararg_decl: Int = 0
+end
+
 # A specific method call site with its associated informations.
 class CallSite
-       # The associated node for location
-       var node: ANode
+       # The associated location of the callsite
+       var location: Location
 
        # The static type of the receiver (possibly unresolved)
        var recv: MType
@@ -531,58 +651,73 @@ class CallSite
        # Is a implicit cast required on erasure typing policy?
        var erasure_cast: Bool
 
-       private fun check_signature(v: TypeVisitor, args: Array[AExpr]): Bool
+       # The mapping used on the call to associate arguments to parameters
+       # If null then no specific association is required.
+       var signaturemap: nullable SignatureMap = null
+
+       private fun check_signature(v: TypeVisitor, node: ANode, args: Array[AExpr]): Bool
        do
-               return v.check_signature(self.node, args, self.mproperty.name, self.msignature)
+               var map = v.check_signature(node, args, self.mproperty, self.msignature)
+               signaturemap = map
+               return map == null
        end
 end
 
 redef class Variable
        # The declared type of the variable
-       var declared_type: nullable MType
+       var declared_type: nullable MType is writable
+
+       # Was the variable type-adapted?
+       # This is used to speedup type retrieval while it remains `false`
+       private var is_adapted = false
 end
 
 redef class FlowContext
        # Store changes of types because of type evolution
        private var vars = new HashMap[Variable, nullable MType]
-       private var cache = new HashMap[Variable, nullable Array[nullable MType]]
 
        # Adapt the variable to a static type
        # Warning1: do not modify vars directly.
        # Warning2: sub-flow may have cached a unadapted variable
-       private fun set_var(variable: Variable, mtype: nullable MType)
+       private fun set_var(v: TypeVisitor, variable: Variable, mtype: nullable MType)
        do
+               if variable.declared_type == mtype and not variable.is_adapted then return
+               if vars.has_key(variable) and vars[variable] == mtype then return
                self.vars[variable] = mtype
-               self.cache.keys.remove(variable)
+               v.dirty = true
+               variable.is_adapted = true
+               #node.debug "set {variable} to {mtype or else "X"}"
        end
 
-       private fun collect_types(variable: Variable): nullable Array[nullable MType]
+       # Look in the flow and previous flow and collect all first reachable type adaptation of a local variable
+       private fun collect_types(variable: Variable): Array[nullable MType]
        do
-               if cache.has_key(variable) then
-                       return cache[variable]
-               end
-               var res: nullable Array[nullable MType] = null
-               if vars.has_key(variable) then
-                       var mtype = vars[variable]
-                       res = [mtype]
-               else if self.previous.is_empty then
-                       # Root flow
-                       res = [variable.declared_type]
-               else
-                       for flow in self.previous do
-                               if flow.is_unreachable then continue
-                               var r2 = flow.collect_types(variable)
-                               if r2 == null then continue
-                               if res == null then
-                                       res = r2.to_a
-                               else
-                                       for t in r2 do
-                                               if not res.has(t) then res.add(t)
-                                       end
+               #node.debug "flow for {variable}"
+               var res = new Array[nullable MType]
+
+               var todo = [self]
+               var seen = new HashSet[FlowContext]
+               while not todo.is_empty do
+                       var f = todo.pop
+                       if f.is_unreachable then continue
+                       if seen.has(f) then continue
+                       seen.add f
+
+                       if f.vars.has_key(variable) then
+                               # Found something. Collect it and do not process further on this path
+                               res.add f.vars[variable]
+                               #f.node.debug "process {variable}: got {f.vars[variable] or else "X"}"
+                       else
+                               todo.add_all f.previous
+                               todo.add_all f.loops
+                               if f.previous.is_empty then
+                                       # Root flowcontext mean a parameter or something related
+                                       res.add variable.declared_type
+                                       #f.node.debug "root process {variable}: got {variable.declared_type or else "X"}"
                                end
                        end
                end
-               cache[variable] = res
+               #self.node.debug "##### end flow for {variable}: {res.join(" ")}"
                return res
        end
 end
@@ -600,9 +735,6 @@ end
 redef class AMethPropdef
        redef fun do_typing(modelbuilder: ModelBuilder)
        do
-               var nblock = self.n_block
-               if nblock == null then return
-
                var mpropdef = self.mpropdef
                if mpropdef == null then return # skip error
 
@@ -623,29 +755,53 @@ redef class AMethPropdef
                        assert variable != null
                        variable.declared_type = mtype
                end
-               v.visit_stmt(nblock)
+
+               var nblock = self.n_block
+               if nblock == null then return
+
+               loop
+                       v.dirty = false
+                       v.visit_stmt(nblock)
+                       if not v.has_loop or not v.dirty then break
+               end
+
+               var post_visitor = new PostTypingVisitor(v)
+               post_visitor.enter_visit(self)
 
                if not nblock.after_flow_context.is_unreachable and msignature.return_mtype != null then
                        # We reach the end of the function without having a return, it is bad
-                       v.error(self, "Control error: Reached end of function (a 'return' with a value was expected).")
+                       v.error(self, "Error: reached end of function; expected `return` with a value.")
                end
        end
 end
 
+private class PostTypingVisitor
+       super Visitor
+       var type_visitor: TypeVisitor
+       redef fun visit(n) do
+               n.visit_all(self)
+               n.accept_post_typing(type_visitor)
+       end
+end
+
+redef class ANode
+       private fun accept_post_typing(v: TypeVisitor) do end
+end
+
 redef class AAttrPropdef
        redef fun do_typing(modelbuilder: ModelBuilder)
        do
                if not has_value then return
 
-               var mpropdef = self.mpropdef
-               if mpropdef == null then return # skip error
+               var mpropdef = self.mreadpropdef
+               if mpropdef == null or mpropdef.msignature == null then return # skip error
 
                var v = new TypeVisitor(modelbuilder, mpropdef.mclassdef.mmodule, mpropdef)
                self.selfvariable = v.selfvariable
 
                var nexpr = self.n_expr
                if nexpr != null then
-                       var mtype = self.mpropdef.static_mtype
+                       var mtype = self.mtype
                        v.visit_expr_subtype(nexpr, mtype)
                end
                var nblock = self.n_block
@@ -653,7 +809,7 @@ redef class AAttrPropdef
                        v.visit_stmt(nblock)
                        if not nblock.after_flow_context.is_unreachable then
                                # We reach the end of the init without having a return, it is bad
-                               v.error(self, "Control error: Reached end of block (a 'return' with a value was expected).")
+                               v.error(self, "Error: reached end of block; expected `return`.")
                        end
                end
        end
@@ -799,18 +955,11 @@ redef class AReassignFormExpr
        # Return the static type of the value to store.
        private fun resolve_reassignment(v: TypeVisitor, readtype, writetype: MType): nullable MType
        do
-               var reassign_name: String
-               if self.n_assign_op isa APlusAssignOp then
-                       reassign_name = "+"
-               else if self.n_assign_op isa AMinusAssignOp then
-                       reassign_name = "-"
-               else
-                       abort
-               end
+               var reassign_name = self.n_assign_op.operator
 
                self.read_type = readtype
 
-               var callsite = v.get_method(self, readtype, reassign_name, false)
+               var callsite = v.get_method(self.n_assign_op, readtype, reassign_name, false)
                if callsite == null then return null # Skip error
                self.reassign_callsite = callsite
 
@@ -821,7 +970,7 @@ redef class AReassignFormExpr
                var value_type = v.visit_expr_subtype(self.n_value, msignature.mparameters.first.mtype)
                if value_type == null then return null # Skip error
 
-               v.check_subtype(self, rettype, writetype)
+               v.check_subtype(self, rettype, writetype, false)
                return rettype
        end
 end
@@ -844,7 +993,7 @@ redef class AVarReassignExpr
 
                v.set_variable(self, variable, rettype)
 
-               self.is_typed = true
+               self.is_typed = rettype != null
        end
 end
 
@@ -889,10 +1038,12 @@ redef class AReturnExpr
                                v.visit_expr_subtype(nexpr, ret_type)
                        else
                                v.visit_expr(nexpr)
-                               v.error(self, "Error: Return with value in a procedure.")
+                               v.error(nexpr, "Error: `return` with value in a procedure.")
+                               return
                        end
                else if ret_type != null then
-                       v.error(self, "Error: Return without value in a function.")
+                       v.error(self, "Error: `return` without value in a function.")
+                       return
                end
                self.is_typed = true
        end
@@ -935,7 +1086,7 @@ redef class AIfexprExpr
 
                var t = v.merge_types(self, [t1, t2])
                if t == null then
-                       v.error(self, "Type Error: ambiguous type {t1} vs {t2}")
+                       v.error(self, "Type Error: ambiguous type `{t1}` vs `{t2}`.")
                end
                self.mtype = t
        end
@@ -952,8 +1103,8 @@ end
 redef class AWhileExpr
        redef fun accept_typing(v)
        do
+               v.has_loop = true
                v.visit_expr_bool(n_expr)
-
                v.visit_stmt(n_block)
                self.is_typed = true
        end
@@ -962,6 +1113,7 @@ end
 redef class ALoopExpr
        redef fun accept_typing(v)
        do
+               v.has_loop = true
                v.visit_stmt(n_block)
                self.is_typed = true
        end
@@ -983,7 +1135,7 @@ redef class AForExpr
        private fun do_type_iterator(v: TypeVisitor, mtype: MType)
        do
                if mtype isa MNullType then
-                       v.error(self, "Type error: 'for' cannot iterate over 'null'")
+                       v.error(self, "Type Error: `for` cannot iterate over `null`.")
                        return
                end
 
@@ -994,7 +1146,7 @@ redef class AForExpr
                # check iterator method
                var itdef = v.get_method(self, mtype, "iterator", n_expr isa ASelfExpr)
                if itdef == null then
-                       v.error(self, "Type Error: 'for' expects a type providing 'iterator' method, got '{mtype}'.")
+                       v.error(self, "Type Error: `for` expects a type providing an `iterator` method, got `{mtype}`.")
                        return
                end
                self.method_iterator = itdef
@@ -1002,7 +1154,7 @@ redef class AForExpr
                # check that iterator return something
                var ittype = itdef.msignature.return_mtype
                if ittype == null then
-                       v.error(self, "Type Error: 'for' expects method 'iterator' to return an 'Iterator' or 'MapIterator' type'.")
+                       v.error(self, "Type Error: `for` expects the method `iterator` to return an `Iterator` or `MapIterator` type.")
                        return
                end
 
@@ -1017,7 +1169,7 @@ redef class AForExpr
                        var coltype = ittype.supertype_to(v.mmodule, v.anchor, colit_cla)
                        var variables =  self.variables
                        if variables.length != 1 then
-                               v.error(self, "Type Error: 'for' expects only one variable when using 'Iterator'.")
+                               v.error(self, "Type Error: `for` expects only one variable when using `Iterator`.")
                        else
                                variables.first.declared_type = coltype.arguments.first
                        end
@@ -1029,7 +1181,7 @@ redef class AForExpr
                        var coltype = ittype.supertype_to(v.mmodule, v.anchor, mapit_cla)
                        var variables = self.variables
                        if variables.length != 2 then
-                               v.error(self, "Type Error: 'for' expects two variables when using 'MapIterator'.")
+                               v.error(self, "Type Error: `for` expects two variables when using `MapIterator`.")
                        else
                                variables[0].declared_type = coltype.arguments[0]
                                variables[1].declared_type = coltype.arguments[1]
@@ -1038,7 +1190,7 @@ redef class AForExpr
                end
 
                if not is_col and not is_map then
-                       v.error(self, "Type Error: 'for' expects method 'iterator' to return an 'Iterator' or 'MapIterator' type'.")
+                       v.error(self, "Type Error: `for` expects the method `iterator` to return an `Iterator` or `MapIterator` type.")
                        return
                end
 
@@ -1051,21 +1203,21 @@ redef class AForExpr
                # get methods is_ok, next, item
                var ikdef = v.get_method(self, ittype, "is_ok", false)
                if ikdef == null then
-                       v.error(self, "Type Error: 'for' expects a method 'is_ok' in 'Iterator' type {ittype}.")
+                       v.error(self, "Type Error: `for` expects a method `is_ok` in type `{ittype}`.")
                        return
                end
                self.method_is_ok = ikdef
 
                var itemdef = v.get_method(self, ittype, "item", false)
                if itemdef == null then
-                       v.error(self, "Type Error: 'for' expects a method 'item' in 'Iterator' type {ittype}.")
+                       v.error(self, "Type Error: `for` expects a method `item` in type `{ittype}`.")
                        return
                end
                self.method_item = itemdef
 
                var nextdef = v.get_method(self, ittype, "next", false)
                if nextdef == null then
-                       v.error(self, "Type Error: 'for' expects a method 'next' in 'Iterator' type {ittype}.")
+                       v.error(self, "Type Error: `for` expects a method `next` in type {ittype}.")
                        return
                end
                self.method_next = nextdef
@@ -1075,7 +1227,7 @@ redef class AForExpr
                if is_map then
                        var keydef = v.get_method(self, ittype, "key", false)
                        if keydef == null then
-                               v.error(self, "Type Error: 'for' expects a method 'key' in 'Iterator' type {ittype}.")
+                               v.error(self, "Type Error: `for` expects a method `key` in type `{ittype}`.")
                                return
                        end
                        self.method_key = keydef
@@ -1097,12 +1249,14 @@ redef class AForExpr
 
        redef fun accept_typing(v)
        do
+               v.has_loop = true
                var mtype = v.visit_expr(n_expr)
                if mtype == null then return
 
                self.do_type_iterator(v, mtype)
 
                v.visit_stmt(n_block)
+
                self.mtype = n_block.mtype
                self.is_typed = true
        end
@@ -1183,8 +1337,9 @@ redef class AOrElseExpr
                end
 
                if t1 isa MNullType then
-                       v.error(n_expr, "Type error: or else on null")
-               else if v.check_can_be_null(n_expr, t1) then
+                       self.mtype = t2
+                       return
+               else if v.can_be_null(t1) then
                        t1 = t1.as_notnull
                end
 
@@ -1200,6 +1355,16 @@ redef class AOrElseExpr
                end
                self.mtype = t
        end
+
+       redef fun accept_post_typing(v)
+       do
+               var t1 = n_expr.mtype
+               if t1 == null then
+                       return
+               else
+                       v.check_can_be_null(n_expr, t1)
+               end
+       end
 end
 
 redef class ATrueExpr
@@ -1216,10 +1381,25 @@ redef class AFalseExpr
        end
 end
 
-redef class AIntExpr
+redef class AIntegerExpr
        redef fun accept_typing(v)
        do
-               var mclass = v.get_mclass(self, "Int")
+               var mclass: nullable MClass = null
+               if value isa Byte then
+                       mclass = v.get_mclass(self, "Byte")
+               else if value isa Int then
+                       mclass = v.get_mclass(self, "Int")
+               else if value isa Int8 then
+                       mclass = v.get_mclass(self, "Int8")
+               else if value isa Int16 then
+                       mclass = v.get_mclass(self, "Int16")
+               else if value isa UInt16 then
+                       mclass = v.get_mclass(self, "UInt16")
+               else if value isa Int32 then
+                       mclass = v.get_mclass(self, "Int32")
+               else if value isa UInt32 then
+                       mclass = v.get_mclass(self, "UInt32")
+               end
                if mclass == null then return # Forward error
                self.mtype = mclass.mclass_type
        end
@@ -1310,13 +1490,15 @@ redef class AArrayExpr
                        end
                        set_comprehension(e)
                        if mtype != null then
-                               if v.check_subtype(e, t, mtype) == null then return # Skip error
+                               if v.check_subtype(e, t, mtype, false) == null then return # Forward error
                                if t == mtype then useless = true
                        else
                                mtypes.add(t)
                        end
                end
                if mtype == null then
+                       # Ensure monotony for type adaptation on loops
+                       if self.element_mtype != null then mtypes.add self.element_mtype
                        mtype = v.merge_types(self, mtypes)
                end
                if mtype == null or mtype isa MNullType then
@@ -1360,7 +1542,7 @@ redef class ARangeExpr
                else if v.is_subtype(t2, t1) then
                        mtype = mclass.get_mtype([t1])
                else
-                       v.error(self, "Type Error: Cannot create range: {t1} vs {t2}")
+                       v.error(self, "Type Error: cannot create range: `{t1}` vs `{t2}`.")
                        return
                end
 
@@ -1392,7 +1574,10 @@ redef class AIsaExpr
        var cast_type: nullable MType
        redef fun accept_typing(v)
        do
-               var mtype = v.visit_expr_cast(self, self.n_expr, self.n_type)
+               v.visit_expr(n_expr)
+
+               var mtype = v.resolve_mtype(n_type)
+
                self.cast_type = mtype
 
                var variable = self.n_expr.its_variable
@@ -1401,17 +1586,29 @@ redef class AIsaExpr
                        #var from = if orig != null then orig.to_s else "invalid"
                        #var to = if mtype != null then mtype.to_s else "invalid"
                        #debug("adapt {variable}: {from} -> {to}")
-                       self.after_flow_context.when_true.set_var(variable, mtype)
+                       self.after_flow_context.when_true.set_var(v, variable, mtype)
                end
 
                self.mtype = v.type_bool(self)
        end
+
+       redef fun accept_post_typing(v)
+       do
+               v.check_expr_cast(self, self.n_expr, self.n_type)
+       end
 end
 
 redef class AAsCastExpr
        redef fun accept_typing(v)
        do
-               self.mtype = v.visit_expr_cast(self, self.n_expr, self.n_type)
+               v.visit_expr(n_expr)
+
+               self.mtype = v.resolve_mtype(n_type)
+       end
+
+       redef fun accept_post_typing(v)
+       do
+               v.check_expr_cast(self, self.n_expr, self.n_type)
        end
 end
 
@@ -1422,16 +1619,23 @@ redef class AAsNotnullExpr
                if mtype == null then return # Forward error
 
                if mtype isa MNullType then
-                       v.error(self, "Type error: as(not null) on null")
+                       v.error(self, "Type Error: `as(not null)` on `null`.")
                        return
                end
 
-               if v.check_can_be_null(n_expr, mtype) then
+               if v.can_be_null(mtype) then
                        mtype = mtype.as_notnull
                end
 
                self.mtype = mtype
        end
+
+       redef fun accept_post_typing(v)
+       do
+               var mtype = n_expr.mtype
+               if mtype == null then return
+               v.check_can_be_null(n_expr, mtype)
+       end
 end
 
 redef class AParExpr
@@ -1453,7 +1657,7 @@ redef class ASelfExpr
        redef fun accept_typing(v)
        do
                if v.is_toplevel_context and not self isa AImplicitSelfExpr then
-                       v.error(self, "Error: self cannot be used in top-level method.")
+                       v.error(self, "Error: `self` cannot be used in top-level method.")
                end
                var variable = v.selfvariable
                self.its_variable = variable
@@ -1481,20 +1685,21 @@ redef class ASendExpr
                var nrecv = self.n_expr
                var recvtype = v.visit_expr(nrecv)
                var name = self.property_name
+               var node = self.property_node
 
                if recvtype == null then return # Forward error
 
                var callsite = null
                var unsafe_type = v.anchor_to(recvtype)
-               var mproperty = v.try_get_mproperty_by_name2(self, unsafe_type, name)
+               var mproperty = v.try_get_mproperty_by_name2(node, unsafe_type, name)
                if mproperty == null and nrecv isa AImplicitSelfExpr then
                        # Special fall-back search in `sys` when noting found in the implicit receiver.
-                       var sysclass = v.try_get_mclass(self, "Sys")
+                       var sysclass = v.try_get_mclass(node, "Sys")
                        if sysclass != null then
                                var systype = sysclass.mclass_type
-                               mproperty = v.try_get_mproperty_by_name2(self, systype, name)
+                               mproperty = v.try_get_mproperty_by_name2(node, systype, name)
                                if mproperty != null then
-                                       callsite = v.get_method(self, systype, name, false)
+                                       callsite = v.get_method(node, systype, name, false)
                                        if callsite == null then return # Forward error
                                        # Update information, we are looking at `sys` now, not `self`
                                        nrecv.is_sys = true
@@ -1506,7 +1711,7 @@ redef class ASendExpr
                end
                if callsite == null then
                        # If still nothing, just exit
-                       callsite = v.get_method(self, recvtype, name, nrecv isa ASelfExpr)
+                       callsite = v.get_method(node, recvtype, name, nrecv isa ASelfExpr)
                        if callsite == null then return
                end
 
@@ -1515,15 +1720,15 @@ redef class ASendExpr
 
                var args = compute_raw_arguments
 
-               callsite.check_signature(v, args)
+               callsite.check_signature(v, node, args)
 
                if callsite.mproperty.is_init then
                        var vmpropdef = v.mpropdef
                        if not (vmpropdef isa MMethodDef and vmpropdef.mproperty.is_init) then
-                               v.error(self, "Can call a init only in another init")
+                               v.error(node, "Error: an `init` can only be called from another `init`.")
                        end
                        if vmpropdef isa MMethodDef and vmpropdef.mproperty.is_root_init and not callsite.mproperty.is_root_init then
-                               v.error(self, "Error: {vmpropdef} cannot call a factory {callsite.mproperty}")
+                               v.error(node, "Error: `{vmpropdef}` cannot call a factory `{callsite.mproperty}`.")
                        end
                end
 
@@ -1539,6 +1744,11 @@ redef class ASendExpr
        # Each subclass simply provide the correct name.
        private fun property_name: String is abstract
 
+       # The node identifying the name (id, operator, etc) for messages.
+       #
+       # Is `self` by default
+       private fun property_node: ANode do return self
+
        # An array of all arguments (excluding self)
        fun raw_arguments: Array[AExpr] do return compute_raw_arguments
 
@@ -1547,76 +1757,45 @@ end
 
 redef class ABinopExpr
        redef fun compute_raw_arguments do return [n_expr2]
+       redef fun property_name do return operator
+       redef fun property_node do return n_op
 end
-redef class AEqExpr
-       redef fun property_name do return "=="
+
+redef class AEqFormExpr
        redef fun accept_typing(v)
        do
                super
                v.null_test(self)
        end
-end
-redef class ANeExpr
-       redef fun property_name do return "!="
-       redef fun accept_typing(v)
+
+       redef fun accept_post_typing(v)
        do
-               super
-               v.null_test(self)
+               var mtype = n_expr.mtype
+               var mtype2 = n_expr2.mtype
+
+               if mtype == null or mtype2 == null then return
+
+               if not mtype2 isa MNullType then return
+
+               v.check_can_be_null(n_expr, mtype)
        end
 end
-redef class ALtExpr
-       redef fun property_name do return "<"
-end
-redef class ALeExpr
-       redef fun property_name do return "<="
-end
-redef class ALlExpr
-       redef fun property_name do return "<<"
-end
-redef class AGtExpr
-       redef fun property_name do return ">"
-end
-redef class AGeExpr
-       redef fun property_name do return ">="
-end
-redef class AGgExpr
-       redef fun property_name do return ">>"
-end
-redef class APlusExpr
-       redef fun property_name do return "+"
-end
-redef class AMinusExpr
-       redef fun property_name do return "-"
-end
-redef class AStarshipExpr
-       redef fun property_name do return "<=>"
-end
-redef class AStarExpr
-       redef fun property_name do return "*"
-end
-redef class AStarstarExpr
-       redef fun property_name do return "**"
-end
-redef class ASlashExpr
-       redef fun property_name do return "/"
-end
-redef class APercentExpr
-       redef fun property_name do return "%"
-end
 
-redef class AUminusExpr
-       redef fun property_name do return "unary -"
+redef class AUnaryopExpr
+       redef fun property_name do return "unary {operator}"
        redef fun compute_raw_arguments do return new Array[AExpr]
 end
 
 
 redef class ACallExpr
-       redef fun property_name do return n_id.text
+       redef fun property_name do return n_qid.n_id.text
+       redef fun property_node do return n_qid
        redef fun compute_raw_arguments do return n_args.to_a
 end
 
 redef class ACallAssignExpr
-       redef fun property_name do return n_id.text + "="
+       redef fun property_name do return n_qid.n_id.text + "="
+       redef fun property_node do return n_qid
        redef fun compute_raw_arguments
        do
                var res = n_args.to_a
@@ -1648,26 +1827,27 @@ redef class ASendReassignFormExpr
        do
                var recvtype = v.visit_expr(self.n_expr)
                var name = self.property_name
+               var node = self.property_node
 
                if recvtype == null then return # Forward error
 
                var for_self = self.n_expr isa ASelfExpr
-               var callsite = v.get_method(self, recvtype, name, for_self)
+               var callsite = v.get_method(node, recvtype, name, for_self)
 
                if callsite == null then return
                self.callsite = callsite
 
                var args = compute_raw_arguments
 
-               callsite.check_signature(v, args)
+               callsite.check_signature(v, node, args)
 
                var readtype = callsite.msignature.return_mtype
                if readtype == null then
-                       v.error(self, "Error: {name} is not a function")
+                       v.error(node, "Error: `{name}` is not a function.")
                        return
                end
 
-               var wcallsite = v.get_method(self, recvtype, name + "=", self.n_expr isa ASelfExpr)
+               var wcallsite = v.get_method(node, recvtype, name + "=", self.n_expr isa ASelfExpr)
                if wcallsite == null then return
                self.write_callsite = wcallsite
 
@@ -1676,14 +1856,15 @@ redef class ASendReassignFormExpr
 
                args = args.to_a # duplicate so raw_arguments keeps only the getter args
                args.add(self.n_value)
-               wcallsite.check_signature(v, args)
+               wcallsite.check_signature(v, node, args)
 
                self.is_typed = true
        end
 end
 
 redef class ACallReassignExpr
-       redef fun property_name do return n_id.text
+       redef fun property_name do return n_qid.n_id.text
+       redef fun property_node do return n_qid.n_id
        redef fun compute_raw_arguments do return n_args.to_a
 end
 
@@ -1694,6 +1875,7 @@ end
 
 redef class AInitExpr
        redef fun property_name do return "init"
+       redef fun property_node do return n_kwinit
        redef fun compute_raw_arguments do return n_args.to_a
 end
 
@@ -1720,7 +1902,7 @@ redef class ASuperExpr
                assert recvtype != null
                var mproperty = v.mpropdef.mproperty
                if not mproperty isa MMethod then
-                       v.error(self, "Error: super only usable in a method")
+                       v.error(self, "Error: `super` only usable in a `method`.")
                        return
                end
                var superprops = mproperty.lookup_super_definitions(v.mmodule, anchor)
@@ -1729,7 +1911,7 @@ redef class ASuperExpr
                                process_superinit(v)
                                return
                        end
-                       v.error(self, "Error: No super method to call for {mproperty}.")
+                       v.error(self, "Error: no super method to call for `{mproperty}`.")
                        return
                end
                # FIXME: covariance of return type in linear extension?
@@ -1739,7 +1921,7 @@ redef class ASuperExpr
                msignature = v.resolve_for(msignature, recvtype, true).as(MSignature)
                var args = self.n_args.to_a
                if args.length > 0 then
-                       v.check_signature(self, args, mproperty.name, msignature)
+                       signaturemap = v.check_signature(self, args, mproperty, msignature)
                end
                self.mtype = msignature.return_mtype
                self.is_typed = true
@@ -1747,6 +1929,10 @@ redef class ASuperExpr
                mpropdef = v.mpropdef.as(MMethodDef)
        end
 
+       # The mapping used on the call to associate arguments to parameters.
+       # If null then no specific association is required.
+       var signaturemap: nullable SignatureMap
+
        private fun process_superinit(v: TypeVisitor)
        do
                var anchor = v.anchor
@@ -1769,7 +1955,7 @@ redef class ASuperExpr
                                continue
                        end
                        if superprop != null and superprop.mproperty != candidate and not superprop.mproperty.is_root_init then
-                               v.error(self, "Error: conflicting super constructor to call for {mproperty}: {candidate.full_name}, {superprop.mproperty.full_name}")
+                               v.error(self, "Error: conflicting super constructor to call for `{mproperty}`: `{candidate.full_name}`, `{superprop.mproperty.full_name}`")
                                return
                        end
                        var candidatedefs = candidate.lookup_definitions(v.mmodule, anchor)
@@ -1778,29 +1964,29 @@ redef class ASuperExpr
                                candidatedefs.add(superprop)
                        end
                        if candidatedefs.length > 1 then
-                               v.error(self, "Error: conflicting property definitions for property {mproperty} in {recvtype}: {candidatedefs.join(", ")}")
+                               v.error(self, "Error: conflicting property definitions for property `{mproperty}` in `{recvtype}`: {candidatedefs.join(", ")}")
                                return
                        end
                        superprop = candidatedefs.first
                end
                if superprop == null then
-                       v.error(self, "Error: No super method to call for {mproperty}.")
+                       v.error(self, "Error: no super method to call for `{mproperty}`.")
                        return
                end
 
                var msignature = superprop.new_msignature or else superprop.msignature.as(not null)
                msignature = v.resolve_for(msignature, recvtype, true).as(MSignature)
 
-               var callsite = new CallSite(self, recvtype, v.mmodule, v.anchor, true, superprop.mproperty, superprop, msignature, false)
+               var callsite = new CallSite(hot_location, recvtype, v.mmodule, v.anchor, true, superprop.mproperty, superprop, msignature, false)
                self.callsite = callsite
 
                var args = self.n_args.to_a
                if args.length > 0 then
-                       callsite.check_signature(v, args)
+                       callsite.check_signature(v, self, args)
                else
                        # Check there is at least enough parameters
                        if mpropdef.msignature.arity < msignature.arity then
-                               v.error(self, "Error: Not enough implicit arguments to pass. Got {mpropdef.msignature.arity}, expected at least {msignature.arity}. Signature is {msignature}")
+                               v.error(self, "Error: not enough implicit arguments to pass. Got `{mpropdef.msignature.arity}`, expected at least `{msignature.arity}`. Signature is `{msignature}`.")
                                return
                        end
                        # Check that each needed parameter is conform
@@ -1808,7 +1994,7 @@ redef class ASuperExpr
                        for sp in msignature.mparameters do
                                var p = mpropdef.msignature.mparameters[i]
                                if not v.is_subtype(p.mtype, sp.mtype) then
-                                       v.error(self, "Type error: expected argument #{i} of type {sp.mtype}, got implicit argument {p.name} of type {p.mtype}. Signature is {msignature}")
+                                       v.error(self, "Type Error: expected argument #{i} of type `{sp.mtype}`, got implicit argument `{p.name}` of type `{p.mtype}`. Signature is {msignature}")
                                        return
                                end
                                i += 1
@@ -1835,10 +2021,13 @@ redef class ANewExpr
 
                if not recvtype isa MClassType then
                        if recvtype isa MNullableType then
-                               v.error(self, "Type error: cannot instantiate the nullable type {recvtype}.")
+                               v.error(self, "Type Error: cannot instantiate the nullable type `{recvtype}`.")
+                               return
+                       else if recvtype isa MFormalType then
+                               v.error(self, "Type Error: cannot instantiate the formal type `{recvtype}`.")
                                return
                        else
-                               v.error(self, "Type error: cannot instantiate the formal type {recvtype}.")
+                               v.error(self, "Type Error: cannot instantiate the type `{recvtype}`.")
                                return
                        end
                end
@@ -1847,15 +2036,18 @@ redef class ANewExpr
                var kind = recvtype.mclass.kind
 
                var name: String
-               var nid = self.n_id
-               if nid != null then
-                       name = nid.text
+               var nqid = self.n_qid
+               var node: ANode
+               if nqid != null then
+                       name = nqid.n_id.text
+                       node = nqid
                else
                        name = "new"
+                       node = self.n_kwnew
                end
                if name == "intern" then
                        if kind != concrete_kind then
-                               v.error(self, "Type Error: Cannot instantiate {kind} {recvtype}.")
+                               v.error(self, "Type Error: cannot instantiate {kind} {recvtype}.")
                                return
                        end
                        if n_args.n_exprs.not_empty then
@@ -1867,12 +2059,12 @@ redef class ANewExpr
                        return
                end
 
-               var callsite = v.get_method(self, recvtype, name, false)
+               var callsite = v.get_method(node, recvtype, name, false)
                if callsite == null then return
 
                if not callsite.mproperty.is_new then
                        if kind != concrete_kind then
-                               v.error(self, "Type Error: Cannot instantiate {kind} {recvtype}.")
+                               v.error(self, "Type Error: cannot instantiate {kind} `{recvtype}`.")
                                return
                        end
                        self.mtype = recvtype
@@ -1884,39 +2076,40 @@ redef class ANewExpr
                self.callsite = callsite
 
                if not callsite.mproperty.is_init_for(recvtype.mclass) then
-                       v.error(self, "Error: {name} is not a constructor.")
+                       v.error(self, "Error: `{name}` is not a constructor.")
                        return
                end
 
                var args = n_args.to_a
-               callsite.check_signature(v, args)
+               callsite.check_signature(v, node, args)
        end
 end
 
 ####
 
 redef class AAttrFormExpr
-       # The attribute acceded.
+       # The attribute accessed.
        var mproperty: nullable MAttribute
 
        # The static type of the attribute.
        var attr_type: nullable MType
 
-       # Resolve the attribute acceded.
+       # Resolve the attribute accessed.
        private fun resolve_property(v: TypeVisitor)
        do
                var recvtype = v.visit_expr(self.n_expr)
                if recvtype == null then return # Skip error
-               var name = self.n_id.text
+               var node = self.n_id
+               var name = node.text
                if recvtype isa MNullType then
-                       v.error(self, "Error: Attribute '{name}' access on 'null'.")
+                       v.error(node, "Error: attribute `{name}` access on `null`.")
                        return
                end
 
                var unsafe_type = v.anchor_to(recvtype)
-               var mproperty = v.try_get_mproperty_by_name2(self, unsafe_type, name)
+               var mproperty = v.try_get_mproperty_by_name2(node, unsafe_type, name)
                if mproperty == null then
-                       v.modelbuilder.error(self, "Error: Attribute {name} doesn't exists in {recvtype}.")
+                       v.modelbuilder.error(node, "Error: attribute `{name}` does not exist in `{recvtype}`.")
                        return
                end
                assert mproperty isa MAttribute
@@ -1948,7 +2141,7 @@ redef class AAttrAssignExpr
                var mtype = self.attr_type
 
                v.visit_expr_subtype(self.n_value, mtype)
-               self.is_typed = true
+               self.is_typed = mtype != null
        end
 end
 
@@ -1959,9 +2152,9 @@ redef class AAttrReassignExpr
                var mtype = self.attr_type
                if mtype == null then return # Skip error
 
-               self.resolve_reassignment(v, mtype, mtype)
+               var rettype = self.resolve_reassignment(v, mtype, mtype)
 
-               self.is_typed = true
+               self.is_typed = rettype != null
        end
 end
 
@@ -1975,7 +2168,7 @@ redef class AIssetAttrExpr
                var recvtype = self.n_expr.mtype.as(not null)
                var bound = v.resolve_for(mtype, recvtype, false)
                if bound isa MNullableType then
-                       v.error(self, "Error: isset on a nullable attribute.")
+                       v.error(n_id, "Type Error: `isset` on a nullable attribute.")
                end
                self.mtype = v.type_bool(self)
        end
@@ -1987,7 +2180,7 @@ redef class AVarargExpr
                # This kind of pseudo-expression can be only processed trough a signature
                # See `check_signature`
                # Other cases are a syntax error.
-               v.error(self, "Syntax error: unexpected `...`")
+               v.error(self, "Syntax Error: unexpected `...`.")
        end
 end