compiler: Added prefix and suffix support for `Char`
[nit.git] / src / semantize / typing.nit
index 9b0d841..08f3d65 100644 (file)
@@ -117,6 +117,7 @@ private class TypeVisitor
                        #node.debug("Unsafe typing: expected {sup}, got {sub}")
                        return sup
                end
+               if sup isa MBottomType then return null # Skip error
                if sub.need_anchor then
                        var u = anchor_to(sub)
                        self.modelbuilder.error(node, "Type Error: expected `{sup}`, got `{sub}: {u}`.")
@@ -151,7 +152,11 @@ private class TypeVisitor
                        end
                        return null # forward error
                end
-               self.error(nexpr, "Error: expected an expression.")
+               var more_message = null
+               var p = nexpr.parent
+               if p != null then more_message = p.bad_expr_message(nexpr)
+               if more_message == null then more_message = "" else more_message = " " + more_message
+               self.error(nexpr, "Error: expected an expression{more_message}.")
                return null
        end
 
@@ -389,7 +394,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
 
@@ -489,8 +494,12 @@ private class TypeVisitor
                                continue # skip the vararg
                        end
 
-                       var paramtype = param.mtype
-                       self.visit_expr_subtype(arg, paramtype)
+                       if not param.is_vararg then
+                               var paramtype = param.mtype
+                               self.visit_expr_subtype(arg, paramtype)
+                       else
+                               check_one_vararg(arg, param)
+                       end
                end
 
                if min_arity > 0 then
@@ -508,27 +517,9 @@ private class TypeVisitor
                        var paramtype = msignature.mparameters[vararg_rank].mtype
                        var first = args[vararg_rank]
                        if vararg_decl == 0 then
-                               var mclass = get_mclass(node, "Array")
-                               if mclass == null then return null # Forward error
-                               var array_mtype = mclass.get_mtype([paramtype])
-                               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
+                               if not check_one_vararg(first, msignature.mparameters[vararg_rank]) then return null
                        else
-                               map.vararg_decl = vararg_decl + 1
+                               first.vararg_decl = vararg_decl + 1
                                for i in [vararg_rank..vararg_rank+vararg_decl] do
                                        self.visit_expr_subtype(args[i], paramtype)
                                end
@@ -538,9 +529,36 @@ private class TypeVisitor
                return map
        end
 
+       # Check an expression as a single vararg.
+       # The main point of the method if to handle the case of reversed vararg (see `AVarargExpr`)
+       fun check_one_vararg(arg: AExpr, param: MParameter): Bool
+       do
+               var paramtype = param.mtype
+               var mclass = get_mclass(arg, "Array")
+               if mclass == null then return false # Forward error
+               var array_mtype = mclass.get_mtype([paramtype])
+               if arg isa AVarargExpr then
+                       self.visit_expr_subtype(arg.n_expr, array_mtype)
+                       arg.mtype  = arg.n_expr.mtype
+               else
+                       # only one vararg, maybe `...` was forgot, so be gentle!
+                       var t = visit_expr(arg)
+                       if t == null then return false # Forward error
+                       if not is_subtype(t, paramtype) and is_subtype(t, array_mtype) then
+                               # Not acceptable but could be a `...`
+                               error(arg, "Type Error: expected `{paramtype}`, got `{t}`. Is an ellipsis `...` missing on the argument?")
+                               return false
+                       end
+                       # Standard valid vararg, finish the job
+                       arg.vararg_decl = 1
+                       self.visit_expr_subtype(arg, paramtype)
+               end
+               return true
+       end
+
        fun error(node: ANode, message: String)
        do
-               self.modelbuilder.toolcontext.error(node.hot_location, message)
+               self.modelbuilder.error(node, message)
        end
 
        fun get_variable(node: AExpr, variable: Variable): nullable MType
@@ -613,16 +631,14 @@ end
 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
+       super MEntity
+
+       # The associated location of the callsite
+       var location: Location
 
        # The static type of the receiver (possibly unresolved)
        var recv: MType
@@ -655,10 +671,11 @@ class CallSite
        # If null then no specific association is required.
        var signaturemap: nullable SignatureMap = null
 
-       private fun check_signature(v: TypeVisitor, args: Array[AExpr]): Bool
+       private fun check_signature(v: TypeVisitor, node: ANode, args: Array[AExpr]): Bool
        do
-               var map = v.check_signature(self.node, args, self.mproperty, self.msignature)
+               var map = v.check_signature(node, args, self.mproperty, self.msignature)
                signaturemap = map
+               if map == null then is_broken = true
                return map == null
        end
 end
@@ -781,11 +798,20 @@ private class PostTypingVisitor
        redef fun visit(n) do
                n.visit_all(self)
                n.accept_post_typing(type_visitor)
+               if n isa AExpr and n.mtype == null and not n.is_typed then
+                       n.is_broken = true
+               end
        end
 end
 
 redef class ANode
        private fun accept_post_typing(v: TypeVisitor) do end
+
+       # An additional information message to explain the role of a child expression.
+       #
+       # The point of the method is to allow some kind of double dispatch so the parent
+       # choose how to describe its children.
+       private fun bad_expr_message(child: AExpr): nullable String do return null
 end
 
 redef class AAttrPropdef
@@ -846,6 +872,15 @@ redef class AExpr
        # The result of the evaluation of `self` must be
        # stored inside the designated array (there is an implicit `push`)
        var comprehension: nullable AArrayExpr = null
+
+       # It indicates the number of arguments collected as a vararg.
+       #
+       # When 0, the argument is used as is, without transformation.
+       # When 1, the argument is transformed into an singleton array.
+       # Above 1, the arguments and the next ones are transformed into a common array.
+       #
+       # This attribute is meaning less on expressions not used as attributes.
+       var vararg_decl: Int = 0
 end
 
 redef class ABlockExpr
@@ -1120,6 +1155,25 @@ redef class ALoopExpr
 end
 
 redef class AForExpr
+       redef fun accept_typing(v)
+       do
+               v.has_loop = true
+
+               for g in n_groups do
+                       var mtype = v.visit_expr(g.n_expr)
+                       if mtype == null then return
+                       g.do_type_iterator(v, mtype)
+                       if g.is_broken then is_broken = true
+               end
+
+               v.visit_stmt(n_block)
+
+               self.mtype = n_block.mtype
+               self.is_typed = true
+       end
+end
+
+redef class AForGroup
        var coltype: nullable MClassType
 
        var method_iterator: nullable CallSite
@@ -1246,20 +1300,6 @@ redef class AForExpr
                        self.method_successor = v.get_method(self, vtype, "successor", false)
                end
        end
-
-       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
 end
 
 redef class AWithExpr
@@ -1389,6 +1429,16 @@ redef class AIntegerExpr
                        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
@@ -1405,9 +1455,15 @@ redef class AFloatExpr
 end
 
 redef class ACharExpr
-       redef fun accept_typing(v)
-       do
-               var mclass = v.get_mclass(self, "Char")
+       redef fun accept_typing(v) do
+               var mclass: nullable MClass = null
+               if is_ascii then
+                       mclass = v.get_mclass(self, "Byte")
+               else if is_code_point then
+                       mclass = v.get_mclass(self, "Int")
+               else
+                       mclass = v.get_mclass(self, "Char")
+               end
                if mclass == null then return # Forward error
                self.mtype = mclass.mclass_type
        end
@@ -1670,6 +1726,14 @@ redef class ASendExpr
        # The property invoked by the send.
        var callsite: nullable CallSite
 
+       redef fun bad_expr_message(child)
+       do
+               if child == self.n_expr then
+                       return "to be the receiver of `{self.property_name}`"
+               end
+               return null
+       end
+
        redef fun accept_typing(v)
        do
                var nrecv = self.n_expr
@@ -1710,7 +1774,7 @@ 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
@@ -1778,14 +1842,14 @@ end
 
 
 redef class ACallExpr
-       redef fun property_name do return n_id.text
-       redef fun property_node do return n_id
+       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_node do return n_id
+       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
@@ -1829,7 +1893,7 @@ redef class ASendReassignFormExpr
 
                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
@@ -1846,15 +1910,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_node do return n_id
+       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
 
@@ -1967,12 +2031,12 @@ redef class ASuperExpr
                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
@@ -2026,11 +2090,11 @@ redef class ANewExpr
                var kind = recvtype.mclass.kind
 
                var name: String
-               var nid = self.n_id
+               var nqid = self.n_qid
                var node: ANode
-               if nid != null then
-                       name = nid.text
-                       node = nid
+               if nqid != null then
+                       name = nqid.n_id.text
+                       node = nqid
                else
                        name = "new"
                        node = self.n_kwnew
@@ -2071,7 +2135,7 @@ redef class ANewExpr
                end
 
                var args = n_args.to_a
-               callsite.check_signature(v, args)
+               callsite.check_signature(v, node, args)
        end
 end