modelbuilder: detect invalid generic type (non conform with bounds)
authorJean Privat <jean@pryen.org>
Fri, 9 Nov 2012 06:39:37 +0000 (01:39 -0500)
committerJean Privat <jean@pryen.org>
Fri, 9 Nov 2012 06:39:37 +0000 (01:39 -0500)
Signed-off-by: Jean Privat <jean@pryen.org>

19 files changed:
src/modelbuilder.nit
tests/base_gen_bound.nit [new file with mode: 0644]
tests/sav/base_gen_bound.sav [new file with mode: 0644]
tests/sav/base_gen_bound_alt1.res [new file with mode: 0644]
tests/sav/base_gen_bound_alt1.sav [new file with mode: 0644]
tests/sav/base_gen_bound_alt2.res [new file with mode: 0644]
tests/sav/base_gen_bound_alt2.sav [new file with mode: 0644]
tests/sav/base_gen_bound_alt3.res [new file with mode: 0644]
tests/sav/base_gen_bound_alt3.sav [new file with mode: 0644]
tests/sav/base_gen_bound_alt4.res [new file with mode: 0644]
tests/sav/base_gen_bound_alt4.sav [new file with mode: 0644]
tests/sav/base_gen_bound_alt5.res [new file with mode: 0644]
tests/sav/base_gen_bound_alt5.sav [new file with mode: 0644]
tests/sav/base_gen_bound_alt6.res [new file with mode: 0644]
tests/sav/base_gen_bound_alt6.sav [new file with mode: 0644]
tests/sav/base_gen_bound_alt7.res [new file with mode: 0644]
tests/sav/base_gen_bound_alt7.sav [new file with mode: 0644]
tests/sav/base_gen_bound_alt8.res [new file with mode: 0644]
tests/sav/base_gen_bound_alt9.res [new file with mode: 0644]

index ca6ed92..1180ff6 100644 (file)
@@ -504,7 +504,7 @@ class ModelBuilder
                                var nfd = nclassdef.n_formaldefs[i]
                                var nfdt = nfd.n_type
                                if nfdt != null then
-                                       var bound = resolve_mtype(nclassdef, nfdt)
+                                       var bound = resolve_mtype_unchecked(nclassdef, nfdt)
                                        if bound == null then return # Forward error
                                        if bound.need_anchor then
                                                # No F-bounds!
@@ -548,7 +548,7 @@ class ModelBuilder
                        for nsc in nclassdef.n_superclasses do
                                specobject = false
                                var ntype = nsc.n_type
-                               var mtype = resolve_mtype(nclassdef, ntype)
+                               var mtype = resolve_mtype_unchecked(nclassdef, ntype)
                                if mtype == null then continue # Skip because of error
                                if not mtype isa MClassType then
                                        error(ntype, "Error: supertypes cannot be a formal type")
@@ -614,6 +614,29 @@ class ModelBuilder
                        mclassdef.add_in_hierarchy
                end
 
+               # Check unchecked ntypes
+               for nclassdef in nmodule.n_classdefs do
+                       if nclassdef isa AStdClassdef then
+                               # check bound of formal parameter
+                               for nfd in  nclassdef.n_formaldefs do
+                                       var nfdt = nfd.n_type
+                                       if nfdt != null and nfdt.mtype != null then
+                                               var bound = resolve_mtype(nclassdef, nfdt)
+                                               if bound == null then return # Forward error
+                                       end
+                               end
+                               # check declared super types
+                               for nsc in nclassdef.n_superclasses do
+                                       var ntype = nsc.n_type
+                                       if ntype.mtype != null then
+                                               var mtype = resolve_mtype(nclassdef, ntype)
+                                               if mtype == null then return # Forward error
+                                       end
+                               end
+                       end
+
+               end
+
                # TODO: Check that the super-class is not intrusive
 
                # TODO: Check that the super-class is not already known (by transitivity)
@@ -751,7 +774,7 @@ class ModelBuilder
        # The mmodule used as context is `nclassdef.mmodule'
        # In case of problem, an error is displayed on `ntype' and null is returned.
        # FIXME: the name "resolve_mtype" is awful
-       fun resolve_mtype(nclassdef: AClassdef, ntype: AType): nullable MType
+       fun resolve_mtype_unchecked(nclassdef: AClassdef, ntype: AType): nullable MType
        do
                var name = ntype.n_id.text
                var mclassdef = nclassdef.mclassdef
@@ -767,6 +790,7 @@ class ModelBuilder
                                end
                                res = prop.mvirtualtype
                                if ntype.n_kwnullable != null then res = res.as_nullable
+                               ntype.mtype = res
                                return res
                        end
                end
@@ -780,6 +804,7 @@ class ModelBuilder
                                if mclassdef.parameter_names[i] == name then
                                        res = mclassdef.mclass.mclass_type.arguments[i]
                                        if ntype.n_kwnullable != null then res = res.as_nullable
+                                       ntype.mtype = res
                                        return res
                                end
                        end
@@ -803,16 +828,18 @@ class ModelBuilder
                        if arity == 0 then
                                res = mclass.mclass_type
                                if ntype.n_kwnullable != null then res = res.as_nullable
+                               ntype.mtype = res
                                return res
                        else
                                var mtypes = new Array[MType]
                                for nt in ntype.n_types do
-                                       var mt = resolve_mtype(nclassdef, nt)
+                                       var mt = resolve_mtype_unchecked(nclassdef, nt)
                                        if mt == null then return null # Forward error
                                        mtypes.add(mt)
                                end
                                res = mclass.get_mtype(mtypes)
                                if ntype.n_kwnullable != null then res = res.as_nullable
+                               ntype.mtype = res
                                return res
                        end
                end
@@ -822,6 +849,37 @@ class ModelBuilder
                return null
        end
 
+       # Return the static type associated to the node `ntype'.
+       # `classdef' is the context where the call is made (used to understand formal types)
+       # The mmodule used as context is `nclassdef.mmodule'
+       # In case of problem, an error is displayed on `ntype' and null is returned.
+       # FIXME: the name "resolve_mtype" is awful
+       fun resolve_mtype(nclassdef: AClassdef, ntype: AType): nullable MType
+       do
+               var mtype = ntype.mtype
+               if mtype == null then mtype = resolve_mtype_unchecked(nclassdef, ntype)
+               if mtype == null then return null # Forward error
+
+               if ntype.checked_mtype then return mtype
+               if mtype isa MGenericType then
+                       var mmodule = nclassdef.parent.as(AModule).mmodule.as(not null)
+                       var mclassdef = nclassdef.mclassdef
+                       var mclass = mtype.mclass
+                       for i in [0..mclass.arity[ do
+                               var bound = mclass.intro.bound_mtype.arguments[i]
+                               var nt = ntype.n_types[i]
+                               var mt = resolve_mtype(nclassdef, nt)
+                               if mt == null then return null # forward error
+                               if not mt.is_subtype(mmodule, mclassdef.bound_mtype, bound) then
+                                       error(nt, "Type error: expected {bound}, got {mt}")
+                                       return null
+                               end
+                       end
+               end
+               ntype.checked_mtype = true
+               return mtype
+       end
+
        # Helper function to display an error on a node.
        # Alias for `self.toolcontext.error(n.hot_location, text)'
        fun error(n: ANode, text: String)
@@ -922,6 +980,13 @@ redef class APrivateVisibility
        redef fun mvisibility do return private_visibility
 end
 
+redef class AType
+       # The mtype associated to the node
+       var mtype: nullable MType = null
+
+       # Is the mtype a valid one?
+       var checked_mtype: Bool = false
+end
 
 #
 
diff --git a/tests/base_gen_bound.nit b/tests/base_gen_bound.nit
new file mode 100644 (file)
index 0000000..e581710
--- /dev/null
@@ -0,0 +1,53 @@
+# 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 end
+
+interface Object
+end
+
+class A
+end
+
+class B
+       super A
+end
+
+class G[E:B]
+end
+
+class H[F:A]
+       #alt1 super G[F]
+       #alt2 var a: G[F]
+       #alt3 fun b: G[F] is abstract
+       #alt4 fun c(x: G[F]) is abstract
+end
+
+class I
+       type V: A
+       #alt5 var a: G[V]
+       #alt6 fun b: G[V] is abstract
+       #alt7 fun c(x: G[V]) is abstract
+end
+
+#alt8 class J[FF:G[A]]
+#alt8 end
+
+var a = new A
+var b = new B
+#alt9 var ga = new G[A]
+var gb = new G[B]
+var ha = new H[A]
+var hb = new H[B]
+var i = new I
diff --git a/tests/sav/base_gen_bound.sav b/tests/sav/base_gen_bound.sav
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/tests/sav/base_gen_bound_alt1.res b/tests/sav/base_gen_bound_alt1.res
new file mode 100644 (file)
index 0000000..342698c
--- /dev/null
@@ -0,0 +1 @@
+alt/base_gen_bound_alt1.nit:31,10: Type error: expected B, got H#0
diff --git a/tests/sav/base_gen_bound_alt1.sav b/tests/sav/base_gen_bound_alt1.sav
new file mode 100644 (file)
index 0000000..c7948e5
--- /dev/null
@@ -0,0 +1 @@
+alt/base_gen_bound_alt1.nit:31,10: Type error: expected B, got F
diff --git a/tests/sav/base_gen_bound_alt2.res b/tests/sav/base_gen_bound_alt2.res
new file mode 100644 (file)
index 0000000..79521ed
--- /dev/null
@@ -0,0 +1 @@
+alt/base_gen_bound_alt2.nit:32,11: Type error: expected B, got H#0
diff --git a/tests/sav/base_gen_bound_alt2.sav b/tests/sav/base_gen_bound_alt2.sav
new file mode 100644 (file)
index 0000000..4a85ac7
--- /dev/null
@@ -0,0 +1,2 @@
+alt/base_gen_bound_alt2.nit:32,11: Type error: expected B, got F
+alt/base_gen_bound_alt2.nit:32,11: Type error: expected B, got F
diff --git a/tests/sav/base_gen_bound_alt3.res b/tests/sav/base_gen_bound_alt3.res
new file mode 100644 (file)
index 0000000..26c07bc
--- /dev/null
@@ -0,0 +1 @@
+alt/base_gen_bound_alt3.nit:33,11: Type error: expected B, got H#0
diff --git a/tests/sav/base_gen_bound_alt3.sav b/tests/sav/base_gen_bound_alt3.sav
new file mode 100644 (file)
index 0000000..386030f
--- /dev/null
@@ -0,0 +1,2 @@
+alt/base_gen_bound_alt3.nit:33,11: Type error: expected B, got F
+alt/base_gen_bound_alt3.nit:33,11: Type error: expected B, got F
diff --git a/tests/sav/base_gen_bound_alt4.res b/tests/sav/base_gen_bound_alt4.res
new file mode 100644 (file)
index 0000000..b7fca30
--- /dev/null
@@ -0,0 +1 @@
+alt/base_gen_bound_alt4.nit:34,13: Type error: expected B, got H#0
diff --git a/tests/sav/base_gen_bound_alt4.sav b/tests/sav/base_gen_bound_alt4.sav
new file mode 100644 (file)
index 0000000..53b7e1c
--- /dev/null
@@ -0,0 +1,2 @@
+alt/base_gen_bound_alt4.nit:34,13: Type error: expected B, got F
+alt/base_gen_bound_alt4.nit:34,13: Type error: expected B, got F
diff --git a/tests/sav/base_gen_bound_alt5.res b/tests/sav/base_gen_bound_alt5.res
new file mode 100644 (file)
index 0000000..2b07879
--- /dev/null
@@ -0,0 +1 @@
+alt/base_gen_bound_alt5.nit:39,11: Type error: expected B, got V
diff --git a/tests/sav/base_gen_bound_alt5.sav b/tests/sav/base_gen_bound_alt5.sav
new file mode 100644 (file)
index 0000000..476afef
--- /dev/null
@@ -0,0 +1,2 @@
+alt/base_gen_bound_alt5.nit:39,11: Type error: expected B, got V
+alt/base_gen_bound_alt5.nit:39,11: Type error: expected B, got V
diff --git a/tests/sav/base_gen_bound_alt6.res b/tests/sav/base_gen_bound_alt6.res
new file mode 100644 (file)
index 0000000..719aea7
--- /dev/null
@@ -0,0 +1 @@
+alt/base_gen_bound_alt6.nit:40,11: Type error: expected B, got V
diff --git a/tests/sav/base_gen_bound_alt6.sav b/tests/sav/base_gen_bound_alt6.sav
new file mode 100644 (file)
index 0000000..95a9168
--- /dev/null
@@ -0,0 +1,2 @@
+alt/base_gen_bound_alt6.nit:40,11: Type error: expected B, got V
+alt/base_gen_bound_alt6.nit:40,11: Type error: expected B, got V
diff --git a/tests/sav/base_gen_bound_alt7.res b/tests/sav/base_gen_bound_alt7.res
new file mode 100644 (file)
index 0000000..071a71e
--- /dev/null
@@ -0,0 +1 @@
+alt/base_gen_bound_alt7.nit:41,13: Type error: expected B, got V
diff --git a/tests/sav/base_gen_bound_alt7.sav b/tests/sav/base_gen_bound_alt7.sav
new file mode 100644 (file)
index 0000000..acb2b8d
--- /dev/null
@@ -0,0 +1,2 @@
+alt/base_gen_bound_alt7.nit:41,13: Type error: expected B, got V
+alt/base_gen_bound_alt7.nit:41,13: Type error: expected B, got V
diff --git a/tests/sav/base_gen_bound_alt8.res b/tests/sav/base_gen_bound_alt8.res
new file mode 100644 (file)
index 0000000..f565456
--- /dev/null
@@ -0,0 +1 @@
+alt/base_gen_bound_alt8.nit:44,14: Type error: expected B, got A
diff --git a/tests/sav/base_gen_bound_alt9.res b/tests/sav/base_gen_bound_alt9.res
new file mode 100644 (file)
index 0000000..6aca3a9
--- /dev/null
@@ -0,0 +1 @@
+alt/base_gen_bound_alt9.nit:49,16: Type error: expected B, got A