syntax: Update check_conform_multiexpr with nullables
[nit.git] / src / syntax / syntax_base.nit
index deefdad..ab1ced0 100644 (file)
@@ -29,9 +29,9 @@ special MMModule
        # Concrete NIT source local classs by name
        readable attr _src_local_classes: Map[Symbol, MMSrcLocalClass]
 
-       init(c: MMContext, source: AModule, dir: MMDirectory, name: Symbol)
+       init(c: MMContext, source: AModule, dir: MMDirectory, name: Symbol, filename: String)
        do
-               super(name, dir, c)
+               super(name, dir, c, filename)
                _node = source
                _src_local_classes = new HashMap[Symbol, MMSrcLocalClass]
        end
@@ -68,9 +68,9 @@ special MMConcreteClass
        # Concrete NIT source properties by name
        readable attr _src_local_properties: Map[Symbol, MMLocalProperty] 
 
-       init(n: Symbol, cla: PClassdef, a: Int)
+       init(mod: MMSrcModule, n: Symbol, cla: PClassdef, a: Int)
        do
-               super(n, a)
+               super(mod, n, a)
                _nodes = [cla]
                _src_local_properties = new HashMap[Symbol, MMLocalProperty]
        end
@@ -102,11 +102,13 @@ end
 redef class MMLocalProperty
        # The attached node (if any)
        meth node: PNode do return null
+
+       # Is the concrete method defined as init
+       meth is_init: Bool do return false
 end
 
 # Concrete NIT source attribute
 class MMSrcAttribute
-special MMLocalProperty
 special MMAttribute
        redef readable attr _node: AAttrPropdef
        init(name: Symbol, cla: MMLocalClass, n: AAttrPropdef)
@@ -118,7 +120,6 @@ end
 
 # Concrete NIT source method
 class MMSrcMethod
-special MMLocalProperty
 special MMMethod
 end
 
@@ -126,33 +127,35 @@ end
 class MMAttrImplementationMethod
 special MMSrcMethod
        redef readable attr _node: AAttrPropdef
+       init(name: Symbol, cla: MMLocalClass, n: AAttrPropdef)
+       do
+               super(name, cla)
+               _node = n
+       end
 end
 
 # Concrete NIT source method for an automatic read accesor
 class MMReadImplementationMethod
 special MMAttrImplementationMethod
-
        init(name: Symbol, cla: MMLocalClass, n: AAttrPropdef)
        do
-               super(name, cla)
-               _node = n
+               super(name, cla, n)
        end
 end
 
 # Concrete NIT source method for an automatic write accesor
 class MMWriteImplementationMethod
 special MMAttrImplementationMethod
-
        init(name: Symbol, cla: MMLocalClass, n: AAttrPropdef)
        do
-               super(name, cla)
-               _node = n
+               super(name, cla, n)
        end
 end
 
 # Concrete NIT source method for an explicit method
 class MMMethSrcMethod
 special MMSrcMethod
+       redef meth is_init do return _node isa AConcreteInitPropdef
        redef readable attr _node: AMethPropdef
        init(name: Symbol, cla: MMLocalClass, n: AMethPropdef)
        do
@@ -173,9 +176,22 @@ special MMTypeProperty
        end
 end
 
+# Concrete NIT implicit constructor
+class MMImplicitInit
+special MMMethSrcMethod
+       redef meth is_init do return true
+       readable attr _unassigned_attributes: Array[MMSrcAttribute]
+       readable attr _super_inits: Array[MMLocalProperty]
+       init(cla: MMLocalClass, unassigned_attributes: Array[MMSrcAttribute], super_inits: Array[MMLocalProperty])
+       do
+               super(once "init".to_symbol, cla, null)
+               _unassigned_attributes = unassigned_attributes
+               _super_inits = super_inits
+       end
+end
 
-# Local variable and method parameter
-class Variable
+# Local variables
+abstract class Variable
        # Name of the variable
        readable attr _name: Symbol 
 
@@ -187,20 +203,65 @@ class Variable
 
        redef meth to_s do return _name.to_s
 
+       meth kind: String is abstract
+
        init(n: Symbol, d: PNode)
        do
-               assert n != null
-               assert d != null
+               #assert n != null
+               #assert d != null
                _name = n
                _decl = d
        end
 end
 
+# Variable declared with 'var'
+class VarVariable
+special Variable
+       redef meth kind do return once "variable"
+       init(n: Symbol, d: PNode) do super
+end
+
+# Parameter of method (declared in signature)
+class ParamVariable
+special Variable
+       redef meth kind do return once "parameter"
+       init(n: Symbol, d: PNode) do super
+end
+
+# Automatic variable (like in the 'for' statement)
+class AutoVariable
+special Variable
+       redef meth kind do return once "automatic variable"
+       init(n: Symbol, d: PNode) do super
+end
+
+# False variable corresponding to closures declared in signatures
+# Lives in the same namespace than variables
+class ClosureVariable
+special Variable
+       redef meth kind do return once "closure"
+
+       # The signature of the closure
+       readable attr _closure: MMClosure
+
+       init(n: Symbol, d: PNode, c: MMClosure)
+       do
+               super(n, d)
+               _closure = c
+       end
+end
+
 ###############################################################################
 
 # Visitor used during the syntax analysis
 class AbsSyntaxVisitor
 special Visitor
+       # The root type Object
+       meth type_object: MMType
+       do
+               return _module.class_by_name(once ("Object".to_symbol)).get_type
+       end
+
        # The primitive type Bool
        meth type_bool: MMType
        do
@@ -276,13 +337,20 @@ special Visitor
        # Display an error for a given syntax node
        meth error(n: PNode, s: String)
        do
-               _tc.error("{n.locate}: {s}")
+               _tc.error("{locate(n)}: {s}")
        end
 
        # Display a warning for a given syntax node
        meth warning(n: PNode, s: String)
        do
-               _tc.warning("{n.locate}: {s}")
+               _tc.warning("{locate(n)}: {s}")
+       end
+
+       #
+       meth locate(n: PNode): String
+       do
+               if n != null then return n.locate
+               return _module.filename
        end
 
        # Check conformity and display error
@@ -294,12 +362,79 @@ special Visitor
                if subtype < stype then
                        return true
                end
-               #error(n, "Type error: expected {stype}'{stype.module}, got {subtype}'{subtype.module}")
-               #abort
+               # Do not enforce nullable subtype rules yet
+               if subtype isa MMTypeNone or subtype.as_notnull < stype.as_notnull then
+                       warning(n, "Nullable type warning: expected {stype}, got {subtype}")
+                       return true
+               end
                error(n, "Type error: expected {stype}, got {subtype}")
                return false
        end
 
+       # Check that an expression has a static type and that 
+       # Display an error and return false if n is a statement
+       # Require that the static type of n is known
+       meth check_expr(n: PExpr): Bool
+       do
+               if not n.is_typed then
+                       if tc.error_count == 0 then
+                               print("{n.locate} not typed but not error")
+                               abort
+                       end
+                       # An error occured in a sub node,
+                       # sillently cascade fail
+                       return false
+               else if n.is_statement then
+                       error(n, "Type error: expected expression.")
+                       return false
+               end
+               return true
+       end
+
+       # Combine check_conform and check_expr
+       meth check_conform_expr(n: PExpr, stype: MMType): Bool
+       do
+               if check_expr(n) then return check_conform(n, n.stype, stype) else return false
+       end
+
+       # Check conformance between multiple expressions and a static type
+       # Conformance is granted if among them there is a most general type
+       # Return the most general type if a conformance is found
+       # Display an error and return null if no conformance is found
+       # The only allowed combinaison is with the nullable marker
+       # @param stype is a possible additional type (without node)
+       # Examples:
+       #   Int, Int, Object => return Object
+       #   Int, Float => display error, return null
+       #   nullable Int, Object => return nullable Object
+       meth check_conform_multiexpr(stype: MMType, nodes: Collection[PExpr]): MMType
+       do
+               var node: PExpr = null # candidate node
+               for n in nodes do
+                       if not check_expr(n) then return null
+                       var ntype = n.stype
+                       if stype != null and stype.is_nullable != ntype.is_nullable then
+                               # nullable combinaison: if one of them is nulable, considers that both are
+                               stype = stype.as_nullable
+                               ntype = ntype.as_nullable
+                       end
+                       if stype == null or (ntype != null and stype < ntype) then
+                               stype = ntype
+                               node = n
+                       end
+               end
+               for n in nodes do
+                       if not n.stype < stype then
+                               if node == null then
+                                       error(n, "Type error: no most general type. Got {n.stype} and {stype}.")
+                               else
+                                       error(n, "Type error: no most general type. Got {n.stype} and {stype} at {node.locate}.")
+                               end
+                               return null
+                       end
+               end
+               return stype
+       end
 
        protected init(tc: ToolContext, module: MMSrcModule)
        do
@@ -349,6 +484,9 @@ end
 redef class AMethPropdef
        # Associated method (MM entity)
        meth method: MMMethSrcMethod is abstract
+
+       # Associated 'self' variable
+       meth self_var: ParamVariable is abstract
 end
 
 redef class ATypePropdef
@@ -361,7 +499,12 @@ redef class PParam
        meth position: Int is abstract
 
        # Associated local variable
-       meth variable: Variable is abstract 
+       meth variable: ParamVariable is abstract 
+end
+
+redef class PClosureDecl
+       # Associated closure variable
+       meth variable: ClosureVariable is abstract
 end
 
 redef class PType
@@ -388,7 +531,7 @@ end
 
 redef class AType
        attr _stype_cache: MMType
-       attr _stype_cached: Bool
+       attr _stype_cached: Bool = false
 
        redef meth get_local_class(v)
        do
@@ -421,15 +564,17 @@ redef class AType
                var name = n_id.to_symbol
                var mod = v.module
                var cla = v.local_class
+               var t: MMType
 
                if cla.formal_dict.has_key(name) then
                        if n_types.length > 0 then
                                v.error(self, "Type error: formal type {name} cannot have formal parameters.")
                                return null
                        end
-                       var formal = cla.formal_dict[name]
-                       _stype_cache = formal
-                       return formal
+                       t = cla.formal_dict[name]
+                       if n_kwnullable != null then t = t.as_nullable
+                       _stype_cache = t
+                       return t
                end
 
                if cla.global_properties != null and cla.has_global_property_by_name(name) then
@@ -437,11 +582,12 @@ redef class AType
                                v.error(self, "Type error: formal type {name} cannot have formal parameters.")
                                return null
                        end
-                       var t = cla.get_type.local_class.select_virtual_type(name).stype_for(cla.get_type)
+                       t = cla.get_type.local_class.select_virtual_type(name).stype_for(cla.get_type)
                        if t == null then
                                v.error(self, "Type error: circular definition in formal type {name}.")
                                return null
                        end
+                       if n_kwnullable != null then t = t.as_nullable
                        _stype_cache = t
                        return t
                end
@@ -460,20 +606,21 @@ redef class AType
                        for p in n_types do
                                tab.add(p.get_unchecked_stype(v))
                        end
-                       var t = local_class.get_instantiate_type(tab)
-                       _stype_cache = t
-                       return t
+                       t = local_class.get_instantiate_type(tab)
                else
-                       var t = local_class.get_type
-                       _stype_cache = t
-                       return t
+                       t = local_class.get_type
                end
+               if n_kwnullable != null then t = t.as_nullable
+               _stype_cache = t
+               return t
        end
        
        redef meth get_stype(v)
        do
                var t = get_unchecked_stype(v)
-               if t != null then check_conform(v)
+               if t == null then return null
+               if not t.is_valid then return null
+               check_conform(v)
                return t
        end
 
@@ -487,8 +634,9 @@ redef class AType
                        for i in [0..arity[ do
                                var p = n_types[i]
                                var pt = p.get_stype(v)
-                               var bt = local_class.get_formal(i).bound
-                               if bt == null then return
+                               var b = local_class.get_formal(i)
+                               if not b.is_valid then return
+                               var bt = b.bound
                                bt = bt.adapt_to(st) # We need to abapt because of F-genericity
                                v.check_conform(p, pt, bt)
                        end
@@ -497,19 +645,33 @@ redef class AType
 end
 
 redef class PExpr
-       # Static type
-       # Is null for statement and for erronus expression
+       # Is the expression node correcly typed
+       # Return false if typed was not yet computed or
+       # if an error occured during the typing computation
+       meth is_typed: Bool is abstract
+
+       # Is the expression node a statement? (ie has no return value)
+       # require: is_typed
+       meth is_statement: Bool is abstract
+
+       # The static type of the expression
+       # require: is_typed and not is_statement
        meth stype: MMType is abstract
 end
 
 redef class AVardeclExpr
        # Assiociated local variable
-       readable writable attr _variable: Variable
+       readable writable attr _variable: VarVariable
 end
 
-redef class AForVardeclExpr
+redef class AForExpr
        # Associated automatic local variable
-       readable writable attr _variable: Variable
+       readable writable attr _variable: AutoVariable
+end
+
+redef class ASelfExpr
+       # Associated local variable
+       readable writable attr _variable: ParamVariable 
 end
 
 redef class AVarFormExpr
@@ -517,3 +679,15 @@ redef class AVarFormExpr
        readable writable attr _variable: Variable 
 end
 
+redef class AClosureCallExpr
+       # Associated closure variable
+       readable writable attr _variable: ClosureVariable
+end
+
+redef class PClosureDef
+       # Associated closure
+       readable writable attr _closure: MMClosure
+
+       # Automatic variables
+       readable writable attr _variables: Array[AutoVariable]
+end