summaryrefslogtreecommitdiff
path: root/src/library/scala/reflect/base/TypeTags.scala
diff options
context:
space:
mode:
authorEugene Burmako <xeno.by@gmail.com>2012-06-06 02:46:31 +0200
committerEugene Burmako <xeno.by@gmail.com>2012-06-08 15:31:33 +0200
commit6bb5975289c5b11cb8c88dd4629286956b5d3d27 (patch)
tree39b1f4bffc4c64c98ae3cb01cedae88cdc95d9b5 /src/library/scala/reflect/base/TypeTags.scala
parent8ce47873f2207a72d902e01cc54eef26f28d1213 (diff)
downloadscala-6bb5975289c5b11cb8c88dd4629286956b5d3d27.tar.gz
scala-6bb5975289c5b11cb8c88dd4629286956b5d3d27.tar.bz2
scala-6bb5975289c5b11cb8c88dd4629286956b5d3d27.zip
The new reflection
A must read: "SIP: Scala Reflection": https://docs.google.com/document/d/1Z1VhhNPplbUpaZPIYdc0_EUv5RiGQ2X4oqp0i-vz1qw/edit Highlights: * Architecture has undergone a dramatic rehash. * Universes and mirrors are now separate entities: universes host reflection artifacts (trees, symbols, types, etc), mirrors abstract loading of those artifacts (e.g. JavaMirror loads stuff using a classloader and annotation unpickler, while GlobalMirror uses internal compiler classreader to achieve the same goal). * No static reflection mirror is imposed on the user. One is free to choose between lightweight mirrors and full-blown classloader-based mirror (read below). * Public reflection API is split into scala.reflect.base and scala.reflect.api. The former represents a minimalistic snapshot that is exactly enough to build reified trees and types. To build, but not to analyze - everything smart (for example, getting a type signature) is implemented in scala.reflect.api. * Both reflection domains have their own universe: scala.reflect.basis and scala.reflect.runtime.universe. The former is super lightweight and doesn't involve any classloaders, while the latter represents a stripped down compiler. * Classloader problems from 2.10.0-M3 are solved. * Exprs and type tags are now bound to a mirror upon creation. * However there is an easy way to migrate exprs and type tags between mirrors and even between universes. * This means that no classloader is imposed on the user of type tags and exprs. If one doesn't like a classloader that's there (associated with tag's mirror), one can create a custom mirror and migrate the tag or the expr to it. * There is a shortcut that works in most cases. Requesting a type tag from a full-blown universe will create that tag in a mirror that corresponds to the callsite classloader aka `getClass.getClassLoader`. This imposes no obligations on the programmer, since Type construction is lazy, so one can always migrate a tag into a different mirror. Migration notes for 2.10.0-M3 users: * Incantations in Predef are gone, some of them have moved to scala.reflect. * Everything path-dependent requires implicit prefix (for example, to refer to a type tag, you need to explicitly specify the universe it belongs to, e.g. reflect.basis.TypeTag or reflect.runtime.universe.TypeTag). * ArrayTags have been removed, ConcreteTypeTag have been renamed to TypeTags, TypeTags have been renamed to AbsTypeTags. Look for the reasoning in the nearby children of this commit. Why not in this commit? Scroll this message to the very bottom to find out the reason. * Some of the functions have been renamed or moved around. The rule of thumb is to look for anything non-trivial in scala.reflect.api. Some of tree build utils have been moved to Universe.build. * staticModule and staticClass have been moved from universes to mirrors * ClassTag.erasure => ClassTag.runtimeClass * For the sake of purity, type tags no longer have erasures. Use multiple context bounds (e.g. def foo[T: ru.TypeTag : ClassTag](...) = ...) if you're interested in having both erasures and types for type parameters. * reify now rolls back macro applications. * Runtime evaluation is now explicit, requires import scala.tools.reflect.Eval and scala-compiler.jar on the classpath. * Macro context now has separate universe and mirror fields. * Most of the useful stuff is declared in c.universe, so be sure to change your "import c.universe._" to "import c.mirror._". * Due to the changes in expressions and type tags, their regular factories are now really difficult to use. We acknowledge that macro users need to frequently create exprs and tags, so we added old-style factories to context. Bottom line: almost always prepend Expr(...)/TypeTag(...) with "c.". * Expr.eval has been renamed to Expr.splice. * Expr.value no longer splices (it can still be used to express cross-stage path-dependent types as specified in SIP-16). * c.reifyTree now has a mirror parameter that lets one customize the initial mirror the resulting Expr will be bound to. If you provide EmptyTree, then the reifier will automatically pick a reasonable mirror (callsite classloader mirror for a full-blown universe and rootMirror for a basis universe). Bottom line: this parameter should be EmptyTree in 99% of cases. * c.reifyErasure => c.reifyRuntimeClass. Known issues: * API is really raw, need your feedback. * All reflection artifacts are now represented by abstract types. This means that pattern matching against them will emit unchecked warnings. Adriaan is working on a patch that will fix that. WARNING, FELLOW CODE EXPLORER! You have entered a turbulence zone. For this commit and its nearby parents and children tests are not guaranteed to work. Things get back to normal only after the "repairs the tests after the refactoring spree" commit. Why so weird? These twentish changesets were once parts of a humongous blob, which spanned 1200 files and 15 kLOC. I did my best to split up the blob, so that the individual parts of the code compile and make sense in isolation. However doing the same for tests would be too much work.
Diffstat (limited to 'src/library/scala/reflect/base/TypeTags.scala')
-rw-r--r--src/library/scala/reflect/base/TypeTags.scala253
1 files changed, 253 insertions, 0 deletions
diff --git a/src/library/scala/reflect/base/TypeTags.scala b/src/library/scala/reflect/base/TypeTags.scala
new file mode 100644
index 0000000000..5c55f45cf7
--- /dev/null
+++ b/src/library/scala/reflect/base/TypeTags.scala
@@ -0,0 +1,253 @@
+/* NSC -- new Scala compiler
+ * Copyright 2005-2011 LAMP/EPFL
+ * @author Martin Odersky
+ */
+
+package scala.reflect
+package base
+
+import java.lang.{ Class => jClass }
+import language.implicitConversions
+
+/**
+ * Type tags encapsulate a representation of type T.
+ * They are supposed to replace the pre-2.10 concept of a [[scala.reflect.Manifest]].
+ * TypeTags are much better integrated with reflection than manifests are, and are consequently much simpler.
+ *
+ * === Overview ===
+ *
+ * Type tags are organized in a hierarchy of four classes:
+ * [[scala.reflect.ArrayTag]], [[scala.reflect.ClassTag]],
+ * [[scala.reflect.base.Universe#TypeTag]] and [[scala.reflect.base.Universe#ConcreteTypeTag]].
+ *
+ * An [[scala.reflect.ArrayTag]] value carries knowledge about how to build an array of elements of type T.
+ * Typically such operation is performed by storing an erasure and instantiating arrays via reflection,
+ * but [[scala.reflect.ArrayTag]] only defines an interface, not an implementation, hence it only contains the factory methods
+ * `newArray` and `wrap` that can be used to build, correspondingly, single-dimensional and multi-dimensional arrays.
+ *
+ * A [[scala.reflect.ClassTag]] is a standard implementation of [[scala.reflect.ArrayTag]].
+ * It guarantees that the source type T did not to contain any references to type parameters or abstract types.
+ * [[scala.reflect.ClassTag]] corresponds to a previous notion of [[scala.reflect.ClassManifest]].
+ *
+ * A [[scala.reflect.base.Universe#TypeTag]] value wraps a full Scala type in its tpe field.
+ * A [[scala.reflect.base.Universe#ConcreteTypeTag]] value is a [[scala.reflect.base.Universe#TypeTag]]
+ * that is guaranteed not to contain any references to type parameters or abstract types.
+ *
+ * It is recommended to use the tag supertypes of to precisely express your intent, i.e.:
+ * use ArrayTag when you just want to construct arrays,
+ * use ClassTag only when you need an erasure, e.g. for serialization or pattern matching.
+ *
+ * [Eugene++] also mention sensitivity to prefixes, i.e. that rb.TypeTag is different from ru.TypeTag
+ * [Eugene++] migratability between mirrors and universes is also worth mentioning
+ *
+ * === Splicing ===
+ *
+ * Tags can be spliced, i.e. if compiler generates a tag for a type that contains references to tagged
+ * type parameters or abstract type members, it will retrieve the corresponding tag and embed it into the result.
+ * An example that illustrates the TypeTag embedding, consider the following function:
+ *
+ * import reflect.mirror._
+ * def f[T: TypeTag, U] = {
+ * type L = T => U
+ * implicitly[TypeTag[L]]
+ * }
+ *
+ * Then a call of f[String, Int] will yield a result of the form
+ *
+ * TypeTag(<[ String => U ]>).
+ *
+ * Note that T has been replaced by String, because it comes with a TypeTag in f, whereas U was left as a type parameter.
+ *
+ * === TypeTag vs ConcreteTypeTag ===
+ *
+ * Be careful with TypeTag, because it will reify types even if these types are abstract.
+ * This makes it easy to forget to tag one of the methods in the call chain and discover it much later in the runtime
+ * by getting cryptic errors far away from their source. For example, consider the following snippet:
+ *
+ * def bind[T: TypeTag](name: String, value: T): IR.Result = bind((name, value))
+ * def bind(p: NamedParam): IR.Result = bind(p.name, p.tpe, p.value)
+ * object NamedParam {
+ * implicit def namedValue[T: TypeTag](name: String, x: T): NamedParam = apply(name, x)
+ * def apply[T: TypeTag](name: String, x: T): NamedParam = new Typed[T](name, x)
+ * }
+ *
+ * This fragment of Scala REPL implementation defines a `bind` function that carries a named value along with its type
+ * into the heart of the REPL. Using a [[scala.reflect.base.Universe#TypeTag]] here is reasonable, because it is desirable
+ * to work with all types, even if they are type parameters or abstract type members.
+ *
+ * However if any of the three `TypeTag` context bounds is omitted, the resulting code will be incorrect,
+ * because the missing `TypeTag` will be transparently generated by the compiler, carrying meaningless information.
+ * Most likely, this problem will manifest itself elsewhere, making debugging complicated.
+ * If `TypeTag` context bounds were replaced with `ConcreteTypeTag`, then such errors would be reported statically.
+ * But in that case we wouldn't be able to use `bind` in arbitrary contexts.
+ *
+ * === Backward compatibility ===
+ *
+ * TypeTags correspond loosely to Manifests. More precisely:
+ * The previous notion of a [[scala.reflect.ClassManifest]] corresponds to a scala.reflect.ClassTag,
+ * The previous notion of a [[scala.reflect.Manifest]] corresponds to scala.reflect.mirror.ConcreteTypeTag,
+ * Whereas scala.reflect.mirror.TypeTag is approximated by the previous notion of [[scala.reflect.OptManifest]].
+ *
+ * In Scala 2.10, manifests are deprecated, so it's adviseable to migrate them to tags,
+ * because manifests might be removed in the next major release.
+ *
+ * In most cases it will be enough to replace ClassManifests with ClassTags and Manifests with ConcreteTypeTags,
+ * however there are a few caveats:
+ *
+ * 1) The notion of OptManifest is no longer supported. Tags can reify arbitrary types, so they are always available.
+ * // [Eugene++] it might be useful, though, to guard against abstractness of the incoming type.
+ *
+ * 2) There's no equivalent for AnyValManifest. Consider comparing your tag with one of the base tags
+ * (defined in the corresponding companion objects) to find out whether it represents a primitive value class.
+ * You can also use `<tag>.tpe.typeSymbol.isPrimitiveValueClass` for that purpose (requires scala-reflect.jar).
+ *
+ * 3) There's no replacement for factory methods defined in `ClassManifest` and `Manifest` companion objects.
+ * Consider assembling corresponding types using reflection API provided by Java (for classes) and Scala (for types).
+ *
+ * 4) Certain manifest functions (such as `<:<`, `>:>` and `typeArguments`) weren't included in the tag API.
+ * Consider using reflection API provided by Java (for classes) and Scala (for types) instead.
+ */
+// [Eugene++] implement serialization for typetags
+trait TypeTags { self: Universe =>
+
+ /**
+ * If an implicit value of type u.TypeTag[T] is required, the compiler will make one up on demand.
+ * The implicitly created value contains in its tpe field a value of type u.Type that is a reflective representation of T.
+ * In that value, any occurrences of type parameters or abstract types U
+ * which come themselves with a TypeTag are represented by the type referenced by that TypeTag.
+ *
+ * @see [[scala.reflect.base.TypeTags]]
+ */
+ @annotation.implicitNotFound(msg = "No TypeTag available for ${T}")
+ trait TypeTag[T] extends Equals with Serializable {
+ val mirror: Mirror
+ def in[U <: Universe with Singleton](otherMirror: MirrorOf[U]): U # TypeTag[T]
+ def tpe: Type
+
+ /** case class accessories */
+ override def canEqual(x: Any) = x.isInstanceOf[TypeTag[_]]
+ override def equals(x: Any) = x.isInstanceOf[TypeTag[_]] && this.mirror == x.asInstanceOf[TypeTag[_]].mirror && this.tpe == x.asInstanceOf[TypeTag[_]].tpe
+ override def hashCode = mirror.hashCode * 31 + tpe.hashCode
+ override def toString = "TypeTag[" + tpe + "]"
+ }
+
+ object TypeTag {
+ val Byte : TypeTag[scala.Byte] = ConcreteTypeTag.Byte
+ val Short : TypeTag[scala.Short] = ConcreteTypeTag.Short
+ val Char : TypeTag[scala.Char] = ConcreteTypeTag.Char
+ val Int : TypeTag[scala.Int] = ConcreteTypeTag.Int
+ val Long : TypeTag[scala.Long] = ConcreteTypeTag.Long
+ val Float : TypeTag[scala.Float] = ConcreteTypeTag.Float
+ val Double : TypeTag[scala.Double] = ConcreteTypeTag.Double
+ val Boolean : TypeTag[scala.Boolean] = ConcreteTypeTag.Boolean
+ val Unit : TypeTag[scala.Unit] = ConcreteTypeTag.Unit
+ val Any : TypeTag[scala.Any] = ConcreteTypeTag.Any
+ val Object : TypeTag[java.lang.Object] = ConcreteTypeTag.Object
+ val Nothing : TypeTag[scala.Nothing] = ConcreteTypeTag.Nothing
+ val Null : TypeTag[scala.Null] = ConcreteTypeTag.Null
+ val String : TypeTag[java.lang.String] = ConcreteTypeTag.String
+
+ def apply[T](mirror1: MirrorOf[self.type], tpec1: TypeCreator): TypeTag[T] =
+ tpec1(mirror1) match {
+ case ByteTpe => TypeTag.Byte.asInstanceOf[TypeTag[T]]
+ case ShortTpe => TypeTag.Short.asInstanceOf[TypeTag[T]]
+ case CharTpe => TypeTag.Char.asInstanceOf[TypeTag[T]]
+ case IntTpe => TypeTag.Int.asInstanceOf[TypeTag[T]]
+ case LongTpe => TypeTag.Long.asInstanceOf[TypeTag[T]]
+ case FloatTpe => TypeTag.Float.asInstanceOf[TypeTag[T]]
+ case DoubleTpe => TypeTag.Double.asInstanceOf[TypeTag[T]]
+ case BooleanTpe => TypeTag.Boolean.asInstanceOf[TypeTag[T]]
+ case UnitTpe => TypeTag.Unit.asInstanceOf[TypeTag[T]]
+ case AnyTpe => TypeTag.Any.asInstanceOf[TypeTag[T]]
+ case ObjectTpe => TypeTag.Object.asInstanceOf[TypeTag[T]]
+ case NothingTpe => TypeTag.Nothing.asInstanceOf[TypeTag[T]]
+ case NullTpe => TypeTag.Null.asInstanceOf[TypeTag[T]]
+ case StringTpe => TypeTag.String.asInstanceOf[TypeTag[T]]
+ case _ => new TypeTagImpl[T](mirror1.asInstanceOf[Mirror], tpec1)
+ }
+
+ def unapply[T](ttag: TypeTag[T]): Option[Type] = Some(ttag.tpe)
+ }
+
+ private class TypeTagImpl[T](val mirror: Mirror, val tpec: TypeCreator) extends TypeTag[T] {
+ lazy val tpe: Type = tpec[self.type](mirror)
+ def in[U <: Universe with Singleton](otherMirror: MirrorOf[U]): U # TypeTag[T] = {
+ val otherMirror1 = otherMirror.asInstanceOf[MirrorOf[otherMirror.universe.type]]
+ otherMirror.universe.TypeTag[T](otherMirror1, tpec)
+ }
+ }
+
+ /**
+ * If an implicit value of type u.ConcreteTypeTag[T] is required, the compiler will make one up on demand following the same procedure as for TypeTags.
+ * However, if the resulting type still contains references to type parameters or abstract types, a static error results.
+ *
+ * @see [[scala.reflect.base.TypeTags]]
+ */
+ @annotation.implicitNotFound(msg = "No ConcreteTypeTag available for ${T}")
+ trait ConcreteTypeTag[T] extends TypeTag[T] with Equals with Serializable {
+ /** case class accessories */
+ override def canEqual(x: Any) = x.isInstanceOf[ConcreteTypeTag[_]]
+ override def equals(x: Any) = x.isInstanceOf[ConcreteTypeTag[_]] && this.mirror == x.asInstanceOf[ConcreteTypeTag[_]].mirror && this.tpe == x.asInstanceOf[ConcreteTypeTag[_]].tpe
+ override def hashCode = mirror.hashCode * 31 + tpe.hashCode
+ override def toString = "ConcreteTypeTag[" + tpe + "]"
+ }
+
+ object ConcreteTypeTag {
+ val Byte: ConcreteTypeTag[scala.Byte] = new PredefConcreteTypeTag[scala.Byte] (ByteTpe, _.ConcreteTypeTag.Byte)
+ val Short: ConcreteTypeTag[scala.Short] = new PredefConcreteTypeTag[scala.Short] (ShortTpe, _.ConcreteTypeTag.Short)
+ val Char: ConcreteTypeTag[scala.Char] = new PredefConcreteTypeTag[scala.Char] (CharTpe, _.ConcreteTypeTag.Char)
+ val Int: ConcreteTypeTag[scala.Int] = new PredefConcreteTypeTag[scala.Int] (IntTpe, _.ConcreteTypeTag.Int)
+ val Long: ConcreteTypeTag[scala.Long] = new PredefConcreteTypeTag[scala.Long] (LongTpe, _.ConcreteTypeTag.Long)
+ val Float: ConcreteTypeTag[scala.Float] = new PredefConcreteTypeTag[scala.Float] (FloatTpe, _.ConcreteTypeTag.Float)
+ val Double: ConcreteTypeTag[scala.Double] = new PredefConcreteTypeTag[scala.Double] (DoubleTpe, _.ConcreteTypeTag.Double)
+ val Boolean: ConcreteTypeTag[scala.Boolean] = new PredefConcreteTypeTag[scala.Boolean] (BooleanTpe, _.ConcreteTypeTag.Boolean)
+ val Unit: ConcreteTypeTag[scala.Unit] = new PredefConcreteTypeTag[scala.Unit] (UnitTpe, _.ConcreteTypeTag.Unit)
+ val Any: ConcreteTypeTag[scala.Any] = new PredefConcreteTypeTag[scala.Any] (AnyTpe, _.ConcreteTypeTag.Any)
+ val Object: ConcreteTypeTag[java.lang.Object] = new PredefConcreteTypeTag[java.lang.Object] (ObjectTpe, _.ConcreteTypeTag.Object)
+ val Nothing: ConcreteTypeTag[scala.Nothing] = new PredefConcreteTypeTag[scala.Nothing] (NothingTpe, _.ConcreteTypeTag.Nothing)
+ val Null: ConcreteTypeTag[scala.Null] = new PredefConcreteTypeTag[scala.Null] (NullTpe, _.ConcreteTypeTag.Null)
+ val String: ConcreteTypeTag[java.lang.String] = new PredefConcreteTypeTag[java.lang.String] (StringTpe, _.ConcreteTypeTag.String)
+
+ def apply[T](mirror1: MirrorOf[self.type], tpec1: TypeCreator): ConcreteTypeTag[T] =
+ tpec1(mirror1) match {
+ case ByteTpe => ConcreteTypeTag.Byte.asInstanceOf[ConcreteTypeTag[T]]
+ case ShortTpe => ConcreteTypeTag.Short.asInstanceOf[ConcreteTypeTag[T]]
+ case CharTpe => ConcreteTypeTag.Char.asInstanceOf[ConcreteTypeTag[T]]
+ case IntTpe => ConcreteTypeTag.Int.asInstanceOf[ConcreteTypeTag[T]]
+ case LongTpe => ConcreteTypeTag.Long.asInstanceOf[ConcreteTypeTag[T]]
+ case FloatTpe => ConcreteTypeTag.Float.asInstanceOf[ConcreteTypeTag[T]]
+ case DoubleTpe => ConcreteTypeTag.Double.asInstanceOf[ConcreteTypeTag[T]]
+ case BooleanTpe => ConcreteTypeTag.Boolean.asInstanceOf[ConcreteTypeTag[T]]
+ case UnitTpe => ConcreteTypeTag.Unit.asInstanceOf[ConcreteTypeTag[T]]
+ case AnyTpe => ConcreteTypeTag.Any.asInstanceOf[ConcreteTypeTag[T]]
+ case ObjectTpe => ConcreteTypeTag.Object.asInstanceOf[ConcreteTypeTag[T]]
+ case NothingTpe => ConcreteTypeTag.Nothing.asInstanceOf[ConcreteTypeTag[T]]
+ case NullTpe => ConcreteTypeTag.Null.asInstanceOf[ConcreteTypeTag[T]]
+ case StringTpe => ConcreteTypeTag.String.asInstanceOf[ConcreteTypeTag[T]]
+ case _ => new ConcreteTypeTagImpl[T](mirror1.asInstanceOf[Mirror], tpec1)
+ }
+
+ def unapply[T](ttag: ConcreteTypeTag[T]): Option[Type] = Some(ttag.tpe)
+ }
+
+ private class ConcreteTypeTagImpl[T](mirror: Mirror, tpec: TypeCreator) extends TypeTagImpl[T](mirror, tpec) with ConcreteTypeTag[T] {
+ override def in[U <: Universe with Singleton](otherMirror: MirrorOf[U]): U # TypeTag[T] = {
+ val otherMirror1 = otherMirror.asInstanceOf[MirrorOf[otherMirror.universe.type]]
+ otherMirror.universe.ConcreteTypeTag[T](otherMirror1, tpec)
+ }
+ }
+
+ private class PredefConcreteTypeTag[T](_tpe: Type, copyIn: Universe => Universe # TypeTag[T]) extends ConcreteTypeTagImpl[T](rootMirror, null) {
+ override lazy val tpe: Type = _tpe
+ override def in[U <: Universe with Singleton](otherMirror: MirrorOf[U]): U # TypeTag[T] =
+ copyIn(otherMirror.universe).asInstanceOf[U # TypeTag[T]]
+ private def readResolve() = copyIn(self)
+ }
+
+ // incantations
+ def typeTag[T](implicit ttag: TypeTag[T]) = ttag
+ def concreteTypeTag[T](implicit cttag: ConcreteTypeTag[T]) = cttag
+
+ // big thanks to Viktor Klang for this brilliant idea!
+ def typeOf[T](implicit ttag: TypeTag[T]): Type = ttag.tpe
+}