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>

18 files changed:
src/model/model.nit
src/modelize_property.nit
tests/base_isa_formal_type.nit
tests/base_isa_vt_ft.nit
tests/base_isa_vt_gen1.nit
tests/base_isa_vt_gen3.nit
tests/base_virtual_type_fixed.nit [new file with mode: 0644]
tests/base_virtual_type_redef.nit
tests/sav/base_isa_vt_gen1.res
tests/sav/base_virtual_type4.res
tests/sav/base_virtual_type4_alt1.res
tests/sav/base_virtual_type_fixed.res [new file with mode: 0644]
tests/sav/base_virtual_type_fixed_alt1.res [new file with mode: 0644]
tests/sav/base_virtual_type_redef.res
tests/sav/base_virtual_type_redef_alt1.res [new file with mode: 0644]
tests/sav/base_virtual_type_redef_alt2.res [new file with mode: 0644]
tests/sav/nitg-e/base_isa_vt_gen3.res [deleted file]
tests/sav/opengles2_hello_triangle.res [new file with mode: 0644]

index 7568e9b..8f92a87 100644 (file)
@@ -1153,6 +1153,20 @@ class MVirtualType
                abort
        end
 
+       # Is the virtual type fixed for a given resolved_receiver?
+       fun is_fixed(mmodule: MModule, resolved_receiver: MType): Bool
+       do
+               assert not resolved_receiver.need_anchor
+               var props = self.mproperty.lookup_definitions(mmodule, resolved_receiver)
+               if props.is_empty then
+                       abort
+               end
+               for p in props do
+                       if p.as(MVirtualTypeDef).is_fixed then return true
+               end
+               return false
+       end
+
        redef fun resolve_for(mtype, anchor, mmodule, cleanup_virtual)
        do
                assert can_resolve_for(mtype, anchor, mmodule)
@@ -1181,6 +1195,8 @@ class MVirtualType
                if resolved_reciever.as(MClassType).mclass.kind == enum_kind then return res
                # If the resolved type isa MVirtualType, it means that self was bound to it, and cannot be unbound. self is just fixed. so return the resolution.
                if res isa MVirtualType then return res
+               # If we are final, just return the resolution
+               if is_fixed(mmodule, resolved_reciever) then return res
                # It the resolved type isa intern class, then there is no possible valid redefinition is any potentiel subclass. self is just fixed. so simply return the resolution
                if res isa MClassType and res.mclass.kind == enum_kind then return res
                # TODO: Add 'fixed' virtual type in the specification.
@@ -1945,6 +1961,9 @@ class MVirtualTypeDef
 
        # The bound of the virtual type
        var bound: nullable MType writable = null
+
+       # Is the bound fixed?
+       var is_fixed writable = false
 end
 
 # A kind of class.
index fb21a65..cc11d50 100644 (file)
@@ -1001,6 +1001,11 @@ redef class ATypePropdef
                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)
@@ -1029,25 +1034,44 @@ redef class ATypePropdef
 
                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
index 855298f..abbf631 100644 (file)
@@ -15,7 +15,7 @@
 import kernel
 
 class A[T]
-       type U: Object
+       type U: nullable Object
        fun testT(o: Object): Bool do return o isa T
        fun testU(o: Object): Bool do return o isa U
 
index c0de90b..6dab4d6 100644 (file)
@@ -24,7 +24,7 @@ enum Bool
 end
 
 class A[X]
-       type T: Object
+       type T: nullable Object
 
        fun foo(o: Object): Bool do
                return o isa T
@@ -43,7 +43,7 @@ end
 
 class C[X, Y]
        super B[X]
-       redef type T: Y
+       redef type T: X
 end
 
 var a = new A[Object]
@@ -57,7 +57,7 @@ assert not b.bar(new B[Object])
 assert b.bar(true)
 
 var c = new C[Object, B[Object]]
-assert not c.foo(new A[Object])
+assert c.foo(new A[Object])
 assert c.foo(new B[Object])
 assert c.foo(new C[Object, B[Object]])
 
index 7d823dc..869650b 100644 (file)
@@ -23,7 +23,7 @@ class Triple[X, Y, Z]
 end
 
 class A[T, U]
-       type V: Object
+       type V: nullable Object
 
        fun foo: Triple[T, U, V] do
                var triple = new Triple[T, U, V]
@@ -51,7 +51,7 @@ end
 
 
 var a = new A[String, Int]
-assert a.foo isa Triple[String, Int, Object]
+assert a.foo isa Triple[String, Int, nullable Object]
 
 var b = new B[String]
 assert b.foo isa Triple[String, String, String]
index 1aec20d..d6c2f0b 100644 (file)
@@ -17,7 +17,7 @@
 import base_minimal
 
 class A[X]
-       type T: Object
+       type T: nullable Object
 
        fun foo(o: Object): Bool do
                return o isa T
@@ -31,7 +31,7 @@ end
 
 class C[X]
        super B[X]
-       redef type T: C[B[X]]
+       redef type T: C[X]
 end
 
 var a = new A[Object]
@@ -45,7 +45,7 @@ assert b.foo(new C[Object])
 var c = new C[Object]
 assert not c.foo(new A[Object])
 assert not c.foo(new B[Object])
-assert not c.foo(new C[Object])
+assert c.foo(new C[Object])
 assert c.foo(new C[B[Object]])
 assert c.foo(new C[B[Bool]])
 
diff --git a/tests/base_virtual_type_fixed.nit b/tests/base_virtual_type_fixed.nit
new file mode 100644 (file)
index 0000000..cc0248b
--- /dev/null
@@ -0,0 +1,38 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import kernel
+
+interface A
+       type V1: Object
+       fun v1(v: V1): V1 do return v
+       type V2: Object
+       fun v2(v: V2): V2 do return v
+end
+interface B
+       super A
+       redef type V1: Discrete
+       redef type V2: Discrete is fixed
+end
+class C
+       super B
+
+       # Because of the workaround, there is no simple way to see the subtype relation
+       #alt1#redef fun v1(v: Discrete): Discrete do return v
+       redef fun v2(v: Discrete): Discrete do return v
+end
+
+var c: A = new C
+c.v1(1).output
+c.v1(2).output
index 837bd46..e8feb60 100644 (file)
 import kernel
 
 interface Number
-       type OTHER: Number
+       type OTHE: Number
+       type OTHE2: Int
+       type OTHE3: Object is fixed
 end
-
 redef class Int
        super Number
 
-       redef type OTHER: Int
+       redef type OTHE: Int
+       #alt1#redef type OTHE2: Number
+       #alt2#redef type OTHE3: Int
 end
 
index 6aa8394..032236e 100644 (file)
@@ -1,5 +1,5 @@
 base_isa_vt_gen1.nit:30,10--34: Warning: Expression is already a Triple[A#0, A#1, V].
-base_isa_vt_gen1.nit:54,8--43: Warning: Expression is already a Triple[String, Int, Object].
+base_isa_vt_gen1.nit:54,8--52: Warning: Expression is already a Triple[String, Int, nullable Object].
 base_isa_vt_gen1.nit:57,8--46: Warning: Expression is already a Triple[String, String, String].
 base_isa_vt_gen1.nit:60,8--48: Warning: Expression is already a Triple[String, String, B[String]].
 base_isa_vt_gen1.nit:63,8--46: Warning: Expression is already a Triple[String, String, String].
index d00491f..13dcd34 100644 (file)
@@ -1 +1,2 @@
+base_virtual_type4.nit:20,16: Redef Error: a virtual type cannot be refined.
 1
index d72eb64..39bff1c 100644 (file)
@@ -1 +1,2 @@
+alt/base_virtual_type4_alt1.nit:20,16: Redef Error: a virtual type cannot be refined.
 alt/base_virtual_type4_alt1.nit:24,7--11: Type error: expected nullable U, got T
diff --git a/tests/sav/base_virtual_type_fixed.res b/tests/sav/base_virtual_type_fixed.res
new file mode 100644 (file)
index 0000000..1191247
--- /dev/null
@@ -0,0 +1,2 @@
+1
+2
diff --git a/tests/sav/base_virtual_type_fixed_alt1.res b/tests/sav/base_virtual_type_fixed_alt1.res
new file mode 100644 (file)
index 0000000..e1f51ca
--- /dev/null
@@ -0,0 +1,2 @@
+alt/base_virtual_type_fixed_alt1.nit:32,15--25: Redef Error: Wrong type for parameter `v'. found Discrete, expected V1 as in base_virtual_type_fixed_alt1#A#v1.
+alt/base_virtual_type_fixed_alt1.nit:32,29--36: Redef Error: Wrong return type. found Discrete, expected V1 as in base_virtual_type_fixed_alt1#A#v1.
index a5ebee7..e69de29 100644 (file)
@@ -1 +0,0 @@
-base_virtual_type_redef.nit:24,13--17: Ambigous property name 'OTHER' for Int; conflict between standard::kernel::Comparable::OTHER and base_virtual_type_redef::Number::OTHER
diff --git a/tests/sav/base_virtual_type_redef_alt1.res b/tests/sav/base_virtual_type_redef_alt1.res
new file mode 100644 (file)
index 0000000..f6fa7dd
--- /dev/null
@@ -0,0 +1 @@
+alt/base_virtual_type_redef_alt1.nit:26,20--25: Redef Error: Wrong bound type. Found Number, expected a subtype of Int, as in base_virtual_type_redef_alt1#Number#OTHE2.
diff --git a/tests/sav/base_virtual_type_redef_alt2.res b/tests/sav/base_virtual_type_redef_alt2.res
new file mode 100644 (file)
index 0000000..cf677e6
--- /dev/null
@@ -0,0 +1 @@
+alt/base_virtual_type_redef_alt2.nit:26,2--27,22: Redef Error: Virtual type OTHE3 is fixed in super-class Number
diff --git a/tests/sav/nitg-e/base_isa_vt_gen3.res b/tests/sav/nitg-e/base_isa_vt_gen3.res
deleted file mode 100644 (file)
index 4eb1aea..0000000
+++ /dev/null
@@ -1 +0,0 @@
-Runtime error: Assert failed (base_isa_vt_gen3.nit:48)
diff --git a/tests/sav/opengles2_hello_triangle.res b/tests/sav/opengles2_hello_triangle.res
new file mode 100644 (file)
index 0000000..92405d4
--- /dev/null
@@ -0,0 +1,2 @@
+../lib/mnit_linux/linux_app.nit:28,16--31: Redef Error: a virtual type cannot be refined.
+../lib/mnit_linux/linux_app.nit:29,16--29: Redef Error: a virtual type cannot be refined.