Merge: Not null types
[nit.git] / src / modelize / modelize_property.nit
index 468852b..c224b29 100644 (file)
@@ -86,7 +86,8 @@ redef class ModelBuilder
                # Force building recursively
                if nclassdef.build_properties_is_done then return
                nclassdef.build_properties_is_done = true
-               var mclassdef = nclassdef.mclassdef.as(not null)
+               var mclassdef = nclassdef.mclassdef
+               if mclassdef == null then return # skip error
                if mclassdef.in_hierarchy == null then return # Skip error
                for superclassdef in mclassdef.in_hierarchy.direct_greaters do
                        if not mclassdef2nclassdef.has_key(superclassdef) then continue
@@ -102,6 +103,17 @@ redef class ModelBuilder
                                npropdef.build_signature(self)
                        end
                        for npropdef in nclassdef2.n_propdefs do
+                               if not npropdef isa ATypePropdef then continue
+                               # Check circularity
+                               var mpropdef = npropdef.mpropdef
+                               if mpropdef == null then continue
+                               if mpropdef.bound == null then continue
+                               if not check_virtual_types_circularity(npropdef, mpropdef.mproperty, mclassdef.bound_mtype, mclassdef.mmodule) then
+                                       # Invalidate the bound
+                                       mpropdef.bound = mclassdef.mmodule.model.null_type
+                               end
+                       end
+                       for npropdef in nclassdef2.n_propdefs do
                                # Check ATypePropdef first since they may be required for the other properties
                                if not npropdef isa ATypePropdef then continue
                                npropdef.check_signature(self)
@@ -303,6 +315,7 @@ redef class ModelBuilder
                                        for p in spd.initializers do
                                                if p != longest.initializers[i] then
                                                        self.error(nclassdef, "Error: conflict for inherited inits {spd}({spd.initializers.join(", ")}) and {longest}({longest.initializers.join(", ")})")
+                                                       # TODO: invalidate the initializer to avoid more errors
                                                        return
                                                end
                                                i += 1
@@ -357,7 +370,7 @@ redef class ModelBuilder
                # It is a case-by case
                var vis_type: nullable MVisibility = null # The own visibility of the type
                var mmodule_type: nullable MModule = null # The original module of the type
-               mtype = mtype.as_notnullable
+               mtype = mtype.undecorate
                if mtype isa MClassType then
                        vis_type = mtype.mclass.visibility
                        mmodule_type = mtype.mclass.intro.mmodule
@@ -366,6 +379,8 @@ redef class ModelBuilder
                        mmodule_type = mtype.mproperty.intro_mclassdef.mmodule
                else if mtype isa MParameterType then
                        # nothing, always visible
+               else if mtype isa MNullType then
+                       # nothing to do.
                else
                        node.debug "Unexpected type {mtype}"
                        abort
@@ -394,6 +409,72 @@ redef class ModelBuilder
                        for t in mtype.arguments do check_visibility(node, t, mpropdef)
                end
        end
+
+       # Detect circularity errors for virtual types.
+       fun check_virtual_types_circularity(node: ANode, mproperty: MVirtualTypeProp, recv: MType, mmodule: MModule): Bool
+       do
+               # Check circularity
+               # Slow case: progress on each resolution until we visit all without getting a loop
+
+               # The graph used to detect loops
+               var mtype = mproperty.mvirtualtype
+               var poset = new POSet[MType]
+
+               # The work-list of types to resolve
+               var todo = new List[MType]
+               todo.add mtype
+
+               while not todo.is_empty do
+                       # The visited type
+                       var t = todo.pop
+
+                       if not t.need_anchor then continue
+
+                       # Get the types derived of `t` (subtypes and bounds)
+                       var nexts
+                       if t isa MNullableType then
+                               nexts = [t.mtype]
+                       else if t isa MGenericType then
+                               nexts = t.arguments
+                       else if t isa MVirtualType then
+                               var vt = t.mproperty
+                               # Because `vt` is possibly unchecked, we have to do the bound-lookup manually
+                               var defs = vt.lookup_definitions(mmodule, recv)
+                               # TODO something to manage correctly bound conflicts
+                               assert not defs.is_empty
+                               nexts = new Array[MType]
+                               for d in defs do
+                                       var next = defs.first.bound
+                                       if next == null then return false
+                                       nexts.add next
+                               end
+                       else if t isa MClassType then
+                               # Basic type, nothing to to
+                               continue
+                       else if t isa MParameterType then
+                               # Parameter types cannot depend on virtual types, so nothing to do
+                               continue
+                       else
+                               abort
+                       end
+
+                       # For each one
+                       for next in nexts do
+                               if poset.has_edge(next, t) then
+                                       if mtype == next then
+                                               error(node, "Error: circularity of virtual type definition: {next} <-> {t}")
+                                       else
+                                               error(node, "Error: circularity of virtual type definition: {mtype} -> {next} <-> {t}")
+                                       end
+                                       return false
+                               else
+                                       poset.add_edge(t, next)
+                                       todo.add next
+                               end
+                       end
+               end
+               return true
+       end
 end
 
 redef class MPropDef
@@ -905,6 +986,7 @@ redef class AMethPropdef
                        var ret_type = mysignature.return_mtype
                        if ret_type != null and precursor_ret_type == null then
                                modelbuilder.error(nsig.n_type.as(not null), "Redef Error: {mpropdef.mproperty} is a procedure, not a function.")
+                               self.mpropdef.msignature = null
                                return
                        end
 
@@ -916,6 +998,7 @@ redef class AMethPropdef
                                        var node = nsig.n_params[i]
                                        if not modelbuilder.check_sametype(node, mmodule, mclassdef.bound_mtype, myt, prt) then
                                                modelbuilder.error(node, "Redef Error: Wrong type for parameter `{mysignature.mparameters[i].name}'. found {myt}, expected {prt} as in {mpropdef.mproperty.intro}.")
+                                               self.mpropdef.msignature = null
                                        end
                                end
                        end
@@ -928,6 +1011,7 @@ redef class AMethPropdef
                                        ret_type = precursor_ret_type
                                else if not modelbuilder.check_subtype(node, mmodule, mclassdef.bound_mtype, ret_type, precursor_ret_type) then
                                        modelbuilder.error(node, "Redef Error: Wrong return type. found {ret_type}, expected {precursor_ret_type} as in {mpropdef.mproperty.intro}.")
+                                       self.mpropdef.msignature = null
                                end
                        end
                end
@@ -1043,6 +1127,7 @@ redef class AAttrPropdef
                                else if atautoinit != null then
                                        modelbuilder.error(atautoinit, "Error: a autoinit attribute needs a value")
                                end
+                               has_value = true
                                return
                        end
                        is_lazy = true
@@ -1354,7 +1439,7 @@ redef class ATypePropdef
                var mpropdef = self.mpropdef
                if mpropdef == null then return # Error thus skipped
 
-               var bound = self.mpropdef.bound
+               var bound = mpropdef.bound
                if bound == null then return # Error thus skipped
 
                modelbuilder.check_visibility(n_type, bound, mpropdef)
@@ -1363,23 +1448,6 @@ redef class ATypePropdef
                var mmodule = mclassdef.mmodule
                var anchor = mclassdef.bound_mtype
 
-               # Check circularity
-               if bound isa MVirtualType then
-                       # Slow case: progress on each resolution until: (i) we loop, or (ii) we found a non formal type
-                       var seen = [self.mpropdef.mproperty.mvirtualtype]
-                       loop
-                               if seen.has(bound) then
-                                       seen.add(bound)
-                                       modelbuilder.error(self, "Error: circularity of virtual type definition: {seen.join(" -> ")}")
-                                       return
-                               end
-                               seen.add(bound)
-                               var next = bound.lookup_bound(mmodule, anchor)
-                               if not next isa MVirtualType then break
-                               bound = next
-                       end
-               end
-
                var ntype = self.n_type
                if modelbuilder.resolve_mtype(mmodule, mclassdef, ntype) == null then
                        mpropdef.bound = null
@@ -1387,7 +1455,6 @@ redef class ATypePropdef
                end
 
                # Check redefinitions
-               bound = mpropdef.bound.as(not null)
                for p in mpropdef.mproperty.lookup_super_definitions(mmodule, anchor) do
                        var supbound = p.bound
                        if supbound == null then break # broken super bound, skip error