Merge: Add annotation `fixed` for virtual types
authorJean Privat <jean@pryen.org>
Wed, 23 Jul 2014 18:22:52 +0000 (14:22 -0400)
committerJean Privat <jean@pryen.org>
Wed, 23 Jul 2014 18:22:52 +0000 (14:22 -0400)
Fixed virtual types prevent redefinition is subclasses

~~~
class A
   type F: Foo is fixed
end
class B
   super A
   redef type F: Foo # Static error
end
~~~

Currently, there is no really apparent benefit of fixed virtual types because the hack of `typing::check_subtype`. Thus this is only a step toward the resolution of #298.

Note: the first commits introduce the basic verification on redefinitons of virtual types that is missing; but nobody saw that there was no checks.

Pull-Request: #611
Reviewed-by: Alexandre Terrasa <alexandre@moz-code.org>

1  2 
src/modelize_property.nit

@@@ -645,20 -645,10 +645,20 @@@ redef class AAttrPropde
        # Is the node tagged `noinit`?
        var noinit = false
  
 +      # Is the node taggeg lazy?
 +      var is_lazy = false
 +
 +      # The guard associated to a lasy attribute.
 +      # Because some engines does not have a working `isset`,
 +      # this additionnal attribute is used to guard the lazy initialization.
 +      # TODO: to remove once isset is correctly implemented
 +      var mlazypropdef: nullable MAttributeDef
 +
        # The associated getter (read accessor) if any
        var mreadpropdef: nullable MMethodDef writable
        # The associated setter (write accessor) if any
        var mwritepropdef: nullable MMethodDef writable
 +
        redef fun build_property(modelbuilder, mclassdef)
        do
                var mclass = mclassdef.mclass
                        modelbuilder.mpropdef2npropdef[mreadpropdef] = self
                        mreadpropdef.mdoc = mpropdef.mdoc
  
 +                      var atlazy = self.get_single_annotation("lazy", modelbuilder)
 +                      if atlazy != null then
 +                              if n_expr == null then
 +                                      modelbuilder.error(atlazy, "Error: a lazy attribute needs a value")
 +                              end
 +                              is_lazy = true
 +                              var mlazyprop = new MAttribute(mclassdef, "lazy _" + name, none_visibility)
 +                              var mlazypropdef = new MAttributeDef(mclassdef, mlazyprop, self.location)
 +                              self.mlazypropdef = mlazypropdef
 +                      end
 +
                        var atreadonly = self.get_single_annotation("readonly", modelbuilder)
                        if atreadonly != null then
                                if n_expr == null then
                        mreadpropdef.msignature = msignature
                end
  
 -              var msritepropdef = self.mwritepropdef
 +              var mwritepropdef = self.mwritepropdef
                if mwritepropdef != null then
                        var name: String
                        if n_id != null then
                        var msignature = new MSignature([mparameter], null)
                        mwritepropdef.msignature = msignature
                end
 +
 +              var mlazypropdef = self.mlazypropdef
 +              if mlazypropdef != null then
 +                      mlazypropdef.static_mtype = modelbuilder.model.get_mclasses_by_name("Bool").first.mclass_type
 +              end
        end
  
        redef fun check_signature(modelbuilder)
@@@ -1001,6 -975,11 +1001,11 @@@ redef class ATypePropde
                self.mpropdef = mpropdef
                modelbuilder.mpropdef2npropdef[mpropdef] = self
                set_doc(mpropdef)
+               var atfixed = get_single_annotation("fixed", modelbuilder)
+               if atfixed != null then
+                       mpropdef.is_fixed = true
+               end
        end
  
        redef fun build_signature(modelbuilder)
  
                modelbuilder.check_visibility(n_type, bound, mpropdef)
  
-               # Fast case: the bound is not a formal type
-               if not bound isa MVirtualType then return
                var mclassdef = mpropdef.mclassdef
                var mmodule = mclassdef.mmodule
                var anchor = mclassdef.bound_mtype
  
-               # 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
+               # 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)
-                               modelbuilder.error(self, "Error: circularity of virtual type definition: {seen.join(" -> ")}")
-                               return
+                               var next = bound.lookup_bound(mmodule, anchor)
+                               if not next isa MVirtualType then break
+                               bound = next
+                       end
+               end
+               # Check redefinitions
+               bound = mpropdef.bound.as(not null)
+               for p in mpropdef.mproperty.lookup_super_definitions(mmodule, anchor) do
+                       var supbound = p.bound.as(not null)
+                       if p.is_fixed then
+                               modelbuilder.error(self, "Redef Error: Virtual type {mpropdef.mproperty} is fixed in super-class {p.mclassdef.mclass}")
+                               break
+                       end
+                       if p.mclassdef.mclass == mclassdef.mclass then
+                               # Still a warning to pass existing bad code
+                               modelbuilder.warning(n_type, "Redef Error: a virtual type cannot be refined.")
+                               break
+                       end
+                       if not bound.is_subtype(mmodule, anchor, supbound) then
+                               modelbuilder.error(n_type, "Redef Error: Wrong bound type. Found {bound}, expected a subtype of {supbound}, as in {p}.")
+                               break
                        end
-                       seen.add(bound)
-                       var next = bound.lookup_bound(mmodule, anchor)
-                       if not next isa MVirtualType then break
-                       bound = next
                end
        end
  end