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>
# 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(); `}
# 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); `}
# 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
# 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 {
# 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); `}
# 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(); `}
# 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);
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(); `}
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); `}
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],
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(); `}
# 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); `}
# 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
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]; `}
redef class NativeString
super NativeCArray
redef type E: Char
- redef type SELF: NativeString
redef fun +(offset) `{ return recv + offset; `}
end
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(); `}
extern class NativeFileInputStream in "Java" `{ java.io.FileInputStream `}
super JavaObject
- redef type SELF: NativeFileInputStream
fun available: Int in "Java" `{
try {
extern class NativeFileOutputStream in "Java" `{ java.io.FileOutputStream `}
super JavaObject
- redef type SELF: NativeFileOutputStream
fun close in "Java" `{
try {
extern class NativeFileDescriptor in "Java" `{ java.io.FileDescriptor `}
super JavaObject
- redef type SELF: NativeFileDescriptor
+
fun sync in "Java" `{
try{
recv.sync();
extern class NativeInputStream in "Java" `{ java.io.InputStream `}
super JavaObject
- redef type SELF: NativeInputStream
fun available: Int in "Java" `{
try {
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);
end
redef extern class JavaObject
- type SELF: JavaObject
# Returns a global reference to the Java object behind this reference
#
#
# 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.
class TreeNode[K: Comparable, E]
# TreeNode type
- type SELF: TreeNode[K, E]
+ type N: TreeNode[K, E]
# `key` for this node
var key: K
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 ""}\}"
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
# 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
# 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
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
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)
# 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
--- /dev/null
+# 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
--- /dev/null
+X
+X
+X
+Y
+Y
+Y
+Y
+A[Int]
+G[A[Int]]
+B[Bool]
+G[B[Bool]]
--- /dev/null
+alt/base_self_type_alt1.nit:25,25--29: Type error: expected SELF, got X
--- /dev/null
+X
+X
+X
+Y
+Y
+Y
+Y
+A[Int]
+G[A[Int]]
+B[Bool]
+G[B[Bool]]
--- /dev/null
+alt/base_self_type_alt3.nit:50,7: Type error: expected Y, got X
-../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`?
--- /dev/null
+X
+X
+X
+Y
+Y
+Y
+Y
+A
+G
+B
+G
--- /dev/null
+X
+X
+X
+Y
+Y
+Y
+Y
+A
+G
+B
+G
-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
-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
-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
-Runtime error: Assert failed (../lib/c.nit:64)
+Runtime error: Assert failed (../lib/c.nit:63)
-Runtime error: Assert failed (../lib/c.nit:57)
+Runtime error: Assert failed (../lib/c.nit:56)
0
-Runtime error: Assert failed (../lib/c.nit:57)
+Runtime error: Assert failed (../lib/c.nit:56)
0
-Runtime error: Assert failed (../lib/c.nit:56)
+Runtime error: Assert failed (../lib/c.nit:55)
0
0
1