From: Jean Privat Date: Tue, 25 Nov 2014 01:04:13 +0000 (-0500) Subject: Merge: SELF type X-Git-Tag: v0.6.11~20 X-Git-Url: http://nitlanguage.org?hp=550eb90522ae1b8c0c2b611b0fae5f7bf60739d7 Merge: SELF type 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 Reviewed-by: Etienne M. Gagnon Reviewed-by: Lucas Bajolet Reviewed-by: Alexandre Terrasa --- diff --git a/lib/android/assets_and_resources.nit b/lib/android/assets_and_resources.nit index 9470dd8..82ae3d1 100644 --- a/lib/android/assets_and_resources.nit +++ b/lib/android/assets_and_resources.nit @@ -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 { diff --git a/lib/android/audio.nit b/lib/android/audio.nit index e640621..366c7d4 100644 --- a/lib/android/audio.nit +++ b/lib/android/audio.nit @@ -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); diff --git a/lib/android/bundle/bundle.nit b/lib/android/bundle/bundle.nit index 9528e05..5a083d2 100644 --- a/lib/android/bundle/bundle.nit +++ b/lib/android/bundle/bundle.nit @@ -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(); `} diff --git a/lib/android/intent/intent_api10.nit b/lib/android/intent/intent_api10.nit index d07a817..20c85dc 100644 --- a/lib/android/intent/intent_api10.nit +++ b/lib/android/intent/intent_api10.nit @@ -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); `} diff --git a/lib/android/shared_preferences/shared_preferences_api10.nit b/lib/android/shared_preferences/shared_preferences_api10.nit index 8635f86..404aaea 100644 --- a/lib/android/shared_preferences/shared_preferences_api10.nit +++ b/lib/android/shared_preferences/shared_preferences_api10.nit @@ -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(); `} diff --git a/lib/android/vibration.nit b/lib/android/vibration.nit index 6226285..f56dbef 100644 --- a/lib/android/vibration.nit +++ b/lib/android/vibration.nit @@ -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); `} diff --git a/lib/c.nit b/lib/c.nit index fdc7e99..6c843f3 100644 --- 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 diff --git a/lib/java/io.nit b/lib/java/io.nit index 04245a5..4ab4eab 100644 --- a/lib/java/io.nit +++ b/lib/java/io.nit @@ -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 { diff --git a/lib/java/java.nit b/lib/java/java.nit index 0b6a652..c3db430 100644 --- a/lib/java/java.nit +++ b/lib/java/java.nit @@ -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 # diff --git a/lib/standard/kernel.nit b/lib/standard/kernel.nit index 2f2df4b..a179e9a 100644 --- a/lib/standard/kernel.nit +++ b/lib/standard/kernel.nit @@ -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. diff --git a/lib/trees/abstract_tree.nit b/lib/trees/abstract_tree.nit index d05663e..6626036 100644 --- a/lib/trees/abstract_tree.nit +++ b/lib/trees/abstract_tree.nit @@ -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 ""}\}" diff --git a/lib/trees/bintree.nit b/lib/trees/bintree.nit index b689700..4c647b3 100644 --- a/lib/trees/bintree.nit +++ b/lib/trees/bintree.nit @@ -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 diff --git a/lib/trees/rbtree.nit b/lib/trees/rbtree.nit index ce17feb..e1fe413 100644 --- a/lib/trees/rbtree.nit +++ b/lib/trees/rbtree.nit @@ -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 diff --git a/src/modelize/modelize_property.nit b/src/modelize/modelize_property.nit index cb56d38..81b3245 100644 --- a/src/modelize/modelize_property.nit +++ b/src/modelize/modelize_property.nit @@ -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 index 0000000..7c5d204 --- /dev/null +++ b/tests/base_self_type.nit @@ -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 index 0000000..4c75853 --- /dev/null +++ b/tests/sav/base_self_type.res @@ -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 index 0000000..467d315 --- /dev/null +++ b/tests/sav/base_self_type_alt1.res @@ -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 index 0000000..4c75853 --- /dev/null +++ b/tests/sav/base_self_type_alt2.res @@ -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 index 0000000..78888be --- /dev/null +++ b/tests/sav/base_self_type_alt3.res @@ -0,0 +1 @@ +alt/base_self_type_alt3.nit:50,7: Type error: expected Y, got X diff --git a/tests/sav/error_class_glob.res b/tests/sav/error_class_glob.res index 30a32a5..695948f 100644 --- a/tests/sav/error_class_glob.res +++ b/tests/sav/error_class_glob.res @@ -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 index 0000000..3c20b42 --- /dev/null +++ b/tests/sav/nitg-e/base_self_type.res @@ -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 index 0000000..3c20b42 --- /dev/null +++ b/tests/sav/nitg-e/base_self_type_alt2.res @@ -0,0 +1,11 @@ +X +X +X +Y +Y +Y +Y +A +G +B +G diff --git a/tests/sav/nitg-e/fixme/base_gen_reassign_alt4.res b/tests/sav/nitg-e/fixme/base_gen_reassign_alt4.res index 8229d05..980ce17 100644 --- a/tests/sav/nitg-e/fixme/base_gen_reassign_alt4.res +++ b/tests/sav/nitg-e/fixme/base_gen_reassign_alt4.res @@ -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 diff --git a/tests/sav/nitg-e/fixme/base_gen_reassign_alt5.res b/tests/sav/nitg-e/fixme/base_gen_reassign_alt5.res index 8229d05..980ce17 100644 --- a/tests/sav/nitg-e/fixme/base_gen_reassign_alt5.res +++ b/tests/sav/nitg-e/fixme/base_gen_reassign_alt5.res @@ -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 diff --git a/tests/sav/nitg-e/fixme/base_gen_reassign_alt6.res b/tests/sav/nitg-e/fixme/base_gen_reassign_alt6.res index 8229d05..980ce17 100644 --- a/tests/sav/nitg-e/fixme/base_gen_reassign_alt6.res +++ b/tests/sav/nitg-e/fixme/base_gen_reassign_alt6.res @@ -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 diff --git a/tests/sav/test_c_alt1.res b/tests/sav/test_c_alt1.res index aa38be4..9533b69 100644 --- a/tests/sav/test_c_alt1.res +++ b/tests/sav/test_c_alt1.res @@ -1 +1 @@ -Runtime error: Assert failed (../lib/c.nit:64) +Runtime error: Assert failed (../lib/c.nit:63) diff --git a/tests/sav/test_c_alt2.res b/tests/sav/test_c_alt2.res index 95d7a74..17a3dc5 100644 --- a/tests/sav/test_c_alt2.res +++ b/tests/sav/test_c_alt2.res @@ -1,2 +1,2 @@ -Runtime error: Assert failed (../lib/c.nit:57) +Runtime error: Assert failed (../lib/c.nit:56) 0 diff --git a/tests/sav/test_c_alt3.res b/tests/sav/test_c_alt3.res index 95d7a74..17a3dc5 100644 --- a/tests/sav/test_c_alt3.res +++ b/tests/sav/test_c_alt3.res @@ -1,2 +1,2 @@ -Runtime error: Assert failed (../lib/c.nit:57) +Runtime error: Assert failed (../lib/c.nit:56) 0 diff --git a/tests/sav/test_c_alt4.res b/tests/sav/test_c_alt4.res index 3bb984c..fdc9a45 100644 --- a/tests/sav/test_c_alt4.res +++ b/tests/sav/test_c_alt4.res @@ -1,4 +1,4 @@ -Runtime error: Assert failed (../lib/c.nit:56) +Runtime error: Assert failed (../lib/c.nit:55) 0 0 1