Merge: SELF type
authorJean Privat <jean@pryen.org>
Tue, 25 Nov 2014 01:04:13 +0000 (20:04 -0500)
committerJean Privat <jean@pryen.org>
Tue, 25 Nov 2014 01:04:13 +0000 (20:04 -0500)
If Object defines a virtual type SELF, redefines it automatically in all subclasses. Also checks for some conditions on the declaration of SELF: it must be public, it must not be fixed and it must be declared on Object (or not declared at all). These conditions ensures that its refinements are valid.

The error messages when trying to redefining SELF could be improved. Maybe adding a special case preventing any redef of SELF?

Pull-Request: #902
Reviewed-by: Jean Privat <jean@pryen.org>
Reviewed-by: Etienne M. Gagnon <egagnon@j-meg.com>
Reviewed-by: Lucas Bajolet <r4pass@hotmail.com>
Reviewed-by: Alexandre Terrasa <alexandre@moz-code.org>

29 files changed:
lib/android/assets_and_resources.nit
lib/android/audio.nit
lib/android/bundle/bundle.nit
lib/android/intent/intent_api10.nit
lib/android/shared_preferences/shared_preferences_api10.nit
lib/android/vibration.nit
lib/c.nit
lib/java/io.nit
lib/java/java.nit
lib/standard/kernel.nit
lib/trees/abstract_tree.nit
lib/trees/bintree.nit
lib/trees/rbtree.nit
src/modelize/modelize_property.nit
tests/base_self_type.nit [new file with mode: 0644]
tests/sav/base_self_type.res [new file with mode: 0644]
tests/sav/base_self_type_alt1.res [new file with mode: 0644]
tests/sav/base_self_type_alt2.res [new file with mode: 0644]
tests/sav/base_self_type_alt3.res [new file with mode: 0644]
tests/sav/error_class_glob.res
tests/sav/nitg-e/base_self_type.res [new file with mode: 0644]
tests/sav/nitg-e/base_self_type_alt2.res [new file with mode: 0644]
tests/sav/nitg-e/fixme/base_gen_reassign_alt4.res
tests/sav/nitg-e/fixme/base_gen_reassign_alt5.res
tests/sav/nitg-e/fixme/base_gen_reassign_alt6.res
tests/sav/test_c_alt1.res
tests/sav/test_c_alt2.res
tests/sav/test_c_alt3.res
tests/sav/test_c_alt4.res

index 9470dd8..82ae3d1 100644 (file)
@@ -46,7 +46,6 @@ in "Java" `{
 # This is a low-level class, use `AssetManager` instead
 extern class NativeAssetManager in "Java" `{ android.content.res.AssetManager `}
        super JavaObject
-       redef type SELF: NativeAssetManager
 
        fun close in "Java" `{ recv.close(); `}
 
@@ -172,7 +171,6 @@ end
 # This is a low-level class, use `ResourcesManager` instead
 extern class NativeResources in "Java" `{ android.content.res.Resources `}
        super JavaObject
-       redef type SELF: NativeResources
 
        fun get_assets:NativeAssetManager in "Java" `{ return recv.getAssets(); `}
        fun get_color(id: Int): Int in "Java" `{ return recv.getColor((int)id); `}
@@ -283,7 +281,6 @@ end
 # An android Bitmap, get an instance using the AssetManager or the ResourceManager
 extern class NativeBitmap in "Java" `{ android.graphics.Bitmap `}
        super JavaObject
-       redef type SELF: NativeBitmap
 
        # Create a NativeBitmap from a NativeInputStream retrieved with `open` function of the AssetManager
        # Called by the AssetManager
@@ -299,7 +296,6 @@ end
 # Android AssetFileDescriptor, can be retrieve by AssetManager and used to load a sound in a SoundPool
 extern class NativeAssetFileDescriptor in "Java" `{ android.content.res.AssetFileDescriptor `}
        super JavaObject
-       redef type SELF: NativeAssetFileDescriptor
 
        fun close in "Java" `{
                try {
index e640621..366c7d4 100644 (file)
@@ -38,7 +38,6 @@ in "Java" `{
 # AudioManager of the application, used to manage the audio mode
 extern class NativeAudioManager in "Java" `{ android.media.AudioManager `}
        super JavaObject
-       redef type SELF: NativeAudioManager
 
        fun mode: Int in "Java" `{ return recv.getMode(); `}
        fun mode=(i: Int) in "Java" `{ recv.setMode((int)i); `}
@@ -61,7 +60,6 @@ end
 # This is a low-level class, use `MediaPlater` instead
 extern class NativeMediaPlayer in "Java" `{ android.media.MediaPlayer `}
        super JavaObject
-       redef type SELF: NativeMediaPlayer
 
        new in "Java" `{ return new MediaPlayer(); `}
        fun start in "Java" `{ recv.start(); `}
@@ -108,7 +106,6 @@ end
 # This is a low-level class, use `SoundPool`instead
 extern class NativeSoundPool in "Java" `{ android.media.SoundPool `}
        super JavaObject
-       redef type SELF: NativeSoundPool
 
        new(max_streams, stream_type, src_quality: Int) in "Java" `{
                return new SoundPool((int)max_streams, (int)stream_type, (int)src_quality);
index 9528e05..5a083d2 100644 (file)
@@ -31,7 +31,6 @@ in "Java" `{
 
 extern class NativeBundle in "Java" `{ android.os.Bundle `}
        super JavaObject
-       redef type SELF: NativeBundle
 
        fun clone: JavaObject in "Java" `{ return recv.clone(); `}
        fun size: Int in "Java" `{ return recv.size(); `}
index d07a817..20c85dc 100644 (file)
@@ -33,7 +33,6 @@ in "Java" `{
 
 extern class NativeIntent in "Java" `{ android.content.Intent `}
        super JavaObject
-       redef type SELF: NativeIntent
 
        fun add_category(category: JavaString) in "Java" `{ recv.addCategory(category); `}
        fun add_flags(flags: Int) in "Java" `{ recv.addFlags((int)flags); `}
index 8635f86..404aaea 100644 (file)
@@ -33,7 +33,6 @@ in "Java" `{
 
 extern class NativeSharedPreferences in "Java" `{ android.content.SharedPreferences `}
        super JavaObject
-       redef type SELF: NativeSharedPreferences
        
        fun contains(key: JavaString): Bool in "Java" `{ return recv.contains(key); `}
        fun get_all: HashMap[JavaString, JavaObject] import HashMap[JavaString, JavaObject],
@@ -113,7 +112,6 @@ end
 
 extern class NativeSharedPreferencesEditor in "Java" `{ android.content.SharedPreferences$Editor `}
        super JavaObject
-       redef type SELF: NativeSharedPreferencesEditor
 
        fun clear: NativeSharedPreferencesEditor in "Java" `{ return recv.clear(); `}
        fun commit: Bool in "Java" `{ return recv.commit(); `}
index 6226285..f56dbef 100644 (file)
@@ -26,7 +26,6 @@ import native_app_glue
 # Handle to an Android vibrator
 extern class Vibrator in "Java" `{ android.os.Vibrator `}
        super JavaObject
-       redef type SELF: Vibrator
 
        # Vibrate for `n` miliseconds
        fun vibrate(n: Int) in "Java" `{ recv.vibrate(n); `}
index fdc7e99..6c843f3 100644 (file)
--- a/lib/c.nit
+++ b/lib/c.nit
@@ -29,7 +29,6 @@ end
 # A native C array, as in a pointer to the first element of the array
 extern class NativeCArray `{ void * `}
        type E: nullable Object
-       type SELF: NativeCArray
 
        fun [](index: E): E is abstract
        fun []=(index: E, val: E) is abstract
@@ -79,7 +78,6 @@ end
 extern class NativeCIntArray `{ int* `}
        super NativeCArray
        redef type E: Int
-       redef type SELF: NativeCIntArray
 
        new(size: Int) `{ return calloc(size, sizeof(int)); `}
        redef fun [](index) `{ return recv[index]; `}
@@ -91,7 +89,6 @@ end
 redef class NativeString
        super NativeCArray
        redef type E: Char
-       redef type SELF: NativeString
 
        redef fun +(offset) `{ return recv + offset; `}
 end
index 04245a5..4ab4eab 100644 (file)
@@ -32,7 +32,6 @@ in "Java" `{
 
 extern class NativeFile in "Java" `{ java.io.File `}
        super JavaObject
-       redef type SELF: NativeFile
 
        fun can_execute: Bool in "Java" `{ return recv.canExecute(); `}
        fun can_read: Bool in "Java" `{ return recv.canRead(); `}
@@ -77,7 +76,6 @@ end
 
 extern class NativeFileInputStream in "Java" `{ java.io.FileInputStream `}
        super JavaObject
-       redef type SELF: NativeFileInputStream
 
        fun available: Int in "Java" `{
                try {
@@ -114,7 +112,6 @@ end
 
 extern class NativeFileOutputStream in "Java" `{ java.io.FileOutputStream `}
        super JavaObject
-       redef type SELF: NativeFileOutputStream
 
        fun close in "Java" `{
                try {
@@ -141,7 +138,7 @@ end
 
 extern class NativeFileDescriptor in "Java" `{ java.io.FileDescriptor `}
        super JavaObject
-       redef type SELF: NativeFileDescriptor
+
        fun sync in "Java" `{
                try{
                        recv.sync();
@@ -154,7 +151,6 @@ end
 
 extern class NativeInputStream in "Java" `{ java.io.InputStream `}
        super JavaObject
-       redef type SELF: NativeInputStream
 
        fun available: Int in "Java" `{
                try {
index 0b6a652..c3db430 100644 (file)
@@ -98,8 +98,6 @@ end
 extern class JavaString in "Java" `{ java.lang.String `}
        super JavaObject
 
-       redef type SELF: JavaString
-
        # Get the string from Java and copy it to Nit memory
        fun to_cstring: NativeString import sys, Sys.jni_env `{
                Sys sys = JavaString_sys(recv);
@@ -140,7 +138,6 @@ redef class Text
 end
 
 redef extern class JavaObject
-       type SELF: JavaObject
 
        # Returns a global reference to the Java object behind this reference
        #
index 2f2df4b..a179e9a 100644 (file)
@@ -30,6 +30,28 @@ import end # Mark this module is a top level one. (must be only one)
 #
 # Currently, Object is also used to collect all top-level methods.
 interface Object
+       # Type of this instance, automatically specialized in every class
+       #
+       # A common use case of the virtual type `SELF` is to type an attribute and
+       # store another instance of the same type as `self`. It can also be used as as
+       # return type to a method producing a copy of `self` or returning an instance
+       # expected to be the exact same type as self.
+       #
+       # This virtual type must be used with caution as it can hinder specialization.
+       # In fact, it imposes strict restrictions on all sub-classes and their usage.
+       # For example, using `SELF` as a return type of a method `foo`
+       # forces all subclasses to ensure that `foo` returns the correct and updated
+       # type.
+       # A dangerous usage take the form of a method typed by `SELF` which creates
+       # and returns a new instance.
+       # If not correctly specialized, this method would break when invoked on a
+       # sub-class.
+       #
+       # A general rule for safe usage of `SELF` is to ensure that inputs typed
+       # `SELF` are stored in attributes typed `SELF` and returned by methods typed
+       # `SELF`, pretty much the same things as you would do with parameter types.
+       type SELF: Object
+
        # The unique object identifier in the class.
        # Unless specific code, you should not use this method.
        # The identifier is used internally to provide a hash value.
index d05663e..6626036 100644 (file)
@@ -42,7 +42,7 @@ end
 class TreeNode[K: Comparable, E]
 
        # TreeNode type
-       type SELF: TreeNode[K, E]
+       type N: TreeNode[K, E]
 
        # `key` for this node
        var key: K
@@ -51,7 +51,7 @@ class TreeNode[K: Comparable, E]
        var value: E
 
        # Direct parent of this node (null if the node is root)
-       var parent: nullable SELF = null is writable
+       var parent: nullable N = null is writable
 
        redef fun to_s do return "\{{value or else ""}\}"
 
index b689700..4c647b3 100644 (file)
@@ -370,38 +370,38 @@ class BinTreeNode[K: Comparable, E]
        private var prev: nullable BinTreeNode[K, E]
        private var next: nullable BinTreeNode[K, E]
 
-       redef type SELF: BinTreeNode[K, E]
+       redef type N: BinTreeNode[K, E]
 
        init(key: K, item: E) do
                super(key, item)
        end
 
-       private var left_node: nullable SELF = null
+       private var left_node: nullable N = null
 
        # `left` tree node child (null if node has no left child)
-       fun left: nullable SELF do return left_node
+       fun left: nullable N do return left_node
 
        # set `left` child for this node (or null if left no child)
        # ENSURE: node.key < key (only if node != null)
-       fun left=(node: nullable SELF) do
+       fun left=(node: nullable N) do
                #assert node != null implies node.key < key
                left_node = node
        end
 
-       private var right_node: nullable SELF = null
+       private var right_node: nullable N = null
 
        # `right` tree node child (null if node has no right child)
-       fun right: nullable SELF do return right_node
+       fun right: nullable N do return right_node
 
        # set `right` child for this node (or null if right no child)
        # ENSURE: node.key < key (only if node != null)
-       fun right=(node: nullable SELF) do
+       fun right=(node: nullable N) do
                #assert node != null implies node.key > key
                right_node = node
        end
 
        # `parent` of the `parent` of this node (null if root)
-       fun grandparent: nullable SELF do
+       fun grandparent: nullable N do
                if parent == null then
                        return null
                else
@@ -411,7 +411,7 @@ class BinTreeNode[K: Comparable, E]
 
        # Other child of the `grandparent`
        # `left` or `right` depends on the position of the current node against its parent
-       fun uncle: nullable SELF do
+       fun uncle: nullable N do
                var g = grandparent
                if g == null then
                        return null
@@ -426,7 +426,7 @@ class BinTreeNode[K: Comparable, E]
 
        # Other child of the parent
        # `left` or `right` depends on the position of the current node against its parent
-       fun sibling: nullable SELF do
+       fun sibling: nullable N do
                if parent == null then
                        return null
                else if self == parent.left then
index ce17feb..e1fe413 100644 (file)
@@ -130,7 +130,7 @@ end
 class RBTreeNode[K: Comparable, E]
        super BinTreeNode[K, E]
 
-       redef type SELF: RBTreeNode[K, E]
+       redef type N: RBTreeNode[K, E]
 
        # Is the node red?
        private var is_red = true
index cb56d38..81b3245 100644 (file)
@@ -54,6 +54,7 @@ redef class ModelBuilder
                        build_properties(mclassdef2nclassdef[superclassdef])
                end
 
+               mclassdef.build_self_type(self, nclassdef)
                for nclassdef2 in nclassdef.all_defs do
                        for npropdef in nclassdef2.n_propdefs do
                                npropdef.build_property(self, mclassdef)
@@ -310,6 +311,50 @@ redef class MClassDef
        # What is the `APropdef` associated to a `MProperty`?
        # Used to check multiple definition of a property.
        var mprop2npropdef: Map[MProperty, APropdef] = new HashMap[MProperty, APropdef]
+
+       # Build the virtual type `SELF` only for introduction `MClassDef`
+       fun build_self_type(modelbuilder: ModelBuilder, nclassdef: AClassdef)
+       do
+               if not is_intro then return
+
+               var name = "SELF"
+               var mprop = modelbuilder.try_get_mproperty_by_name(nclassdef, self, name)
+
+               # If SELF type is declared nowherer?
+               if mprop == null then return
+
+               # SELF is not a virtual type? it is weird but we ignore it
+               if not mprop isa MVirtualTypeProp then return
+
+               # Is this the intro of SELF in the library?
+               var intro = mprop.intro
+               var intro_mclassdef = intro.mclassdef
+               if intro_mclassdef == self then
+                       var nintro = modelbuilder.mpropdef2npropdef[intro]
+
+                       # SELF must be declared in Object, otherwise this will create conflicts
+                       if intro_mclassdef.mclass.name != "Object" then
+                               modelbuilder.error(nintro, "Error: the virtual type SELF must be declared in Object.")
+                       end
+
+                       # SELF must be public
+                       if mprop.visibility != public_visibility then
+                               modelbuilder.error(nintro, "Error: the virtual type SELF must be public.")
+                       end
+
+                       # SELF must not be fixed
+                       if intro.is_fixed then
+                               modelbuilder.error(nintro, "Error: the virtual type SELF cannot be fixed.")
+                       end
+
+                       return
+               end
+
+               # This class introduction inherits a SELF
+               # We insert an artificial property to update it
+               var mpropdef = new MVirtualTypeDef(self, mprop, self.location)
+               mpropdef.bound = mclass.mclass_type
+       end
 end
 
 redef class APropdef
diff --git a/tests/base_self_type.nit b/tests/base_self_type.nit
new file mode 100644 (file)
index 0000000..7c5d204
--- /dev/null
@@ -0,0 +1,58 @@
+# 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
+
+class X
+       fun foo: SELF do return self
+       fun bar(o: SELF) do o.output_class_name
+end
+
+class Y
+       super X
+
+#alt1# redef fun foo do return new X
+#alt2# redef fun foo do return new Y
+end
+
+class A[E]
+       fun foo: Object do return new G[SELF]
+end
+
+class B[F]
+       super A[F]
+end
+
+class G[E:A[nullable Object]]
+end
+
+var x = new X
+x.output_class_name
+x.foo.output_class_name
+x.bar x
+
+var y = new Y
+y.output_class_name
+y.foo.output_class_name
+x.bar y
+y.bar y
+#alt3# y.bar x
+
+var a = new A[Int]
+a.output_class_name
+a.foo.output_class_name
+
+var b = new B[Bool]
+b.output_class_name
+b.foo.output_class_name
diff --git a/tests/sav/base_self_type.res b/tests/sav/base_self_type.res
new file mode 100644 (file)
index 0000000..4c75853
--- /dev/null
@@ -0,0 +1,11 @@
+X
+X
+X
+Y
+Y
+Y
+Y
+A[Int]
+G[A[Int]]
+B[Bool]
+G[B[Bool]]
diff --git a/tests/sav/base_self_type_alt1.res b/tests/sav/base_self_type_alt1.res
new file mode 100644 (file)
index 0000000..467d315
--- /dev/null
@@ -0,0 +1 @@
+alt/base_self_type_alt1.nit:25,25--29: Type error: expected SELF, got X
diff --git a/tests/sav/base_self_type_alt2.res b/tests/sav/base_self_type_alt2.res
new file mode 100644 (file)
index 0000000..4c75853
--- /dev/null
@@ -0,0 +1,11 @@
+X
+X
+X
+Y
+Y
+Y
+Y
+A[Int]
+G[A[Int]]
+B[Bool]
+G[B[Bool]]
diff --git a/tests/sav/base_self_type_alt3.res b/tests/sav/base_self_type_alt3.res
new file mode 100644 (file)
index 0000000..78888be
--- /dev/null
@@ -0,0 +1 @@
+alt/base_self_type_alt3.nit:50,7: Type error: expected Y, got X
index 30a32a5..695948f 100644 (file)
@@ -1 +1 @@
-../lib/standard/kernel.nit:79,1--95,3: Fatal error: kernel#Sys does not specialize module_0#Object. Possible duplication of the root class `Object`?
+../lib/standard/kernel.nit:101,1--117,3: Fatal error: kernel#Sys does not specialize module_0#Object. Possible duplication of the root class `Object`?
diff --git a/tests/sav/nitg-e/base_self_type.res b/tests/sav/nitg-e/base_self_type.res
new file mode 100644 (file)
index 0000000..3c20b42
--- /dev/null
@@ -0,0 +1,11 @@
+X
+X
+X
+Y
+Y
+Y
+Y
+A
+G
+B
+G
diff --git a/tests/sav/nitg-e/base_self_type_alt2.res b/tests/sav/nitg-e/base_self_type_alt2.res
new file mode 100644 (file)
index 0000000..3c20b42
--- /dev/null
@@ -0,0 +1,11 @@
+X
+X
+X
+Y
+Y
+Y
+Y
+A
+G
+B
+G
index 8229d05..980ce17 100644 (file)
@@ -1,4 +1,4 @@
-Runtime error: Cast failed. Expected `OTHER`, got `Float` (../lib/standard/kernel.nit:389)
+Runtime error: Cast failed. Expected `OTHER`, got `Float` (../lib/standard/kernel.nit:411)
 11
 21
 31
index 8229d05..980ce17 100644 (file)
@@ -1,4 +1,4 @@
-Runtime error: Cast failed. Expected `OTHER`, got `Float` (../lib/standard/kernel.nit:389)
+Runtime error: Cast failed. Expected `OTHER`, got `Float` (../lib/standard/kernel.nit:411)
 11
 21
 31
index 8229d05..980ce17 100644 (file)
@@ -1,4 +1,4 @@
-Runtime error: Cast failed. Expected `OTHER`, got `Float` (../lib/standard/kernel.nit:389)
+Runtime error: Cast failed. Expected `OTHER`, got `Float` (../lib/standard/kernel.nit:411)
 11
 21
 31
index aa38be4..9533b69 100644 (file)
@@ -1 +1 @@
-Runtime error: Assert failed (../lib/c.nit:64)
+Runtime error: Assert failed (../lib/c.nit:63)
index 95d7a74..17a3dc5 100644 (file)
@@ -1,2 +1,2 @@
-Runtime error: Assert failed (../lib/c.nit:57)
+Runtime error: Assert failed (../lib/c.nit:56)
 0
index 95d7a74..17a3dc5 100644 (file)
@@ -1,2 +1,2 @@
-Runtime error: Assert failed (../lib/c.nit:57)
+Runtime error: Assert failed (../lib/c.nit:56)
 0
index 3bb984c..fdc9a45 100644 (file)
@@ -1,4 +1,4 @@
-Runtime error: Assert failed (../lib/c.nit:56)
+Runtime error: Assert failed (../lib/c.nit:55)
 0
 0
 1