summaryrefslogtreecommitdiff
path: root/src/library/scala/reflect/api/TypeTags.scala
blob: c58b0fcec22ee6072e8ee6148c2fd4de0a16d541 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
/* NSC -- new Scala compiler
 * Copyright 2005-2011 LAMP/EPFL
 * @author  Martin Odersky
 */

package scala.reflect
package api

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 five classes:
 * [[scala.reflect.ArrayTag]], [[scala.reflect.ErasureTag]], [[scala.reflect.ClassTag]],
 * [[scala.reflect.api.Universe#TypeTag]] and [[scala.reflect.api.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 Java 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.
 *
 * An [[scala.reflect.ErasureTag]] value wraps a Java class, which can be accessed via the `erasure` method.
 * This notion, previously embodied in a [[scala.reflect.ClassManifest]] together with the notion of array creation,
 * deserves a concept of itself. Quite often (e.g. for serialization or classloader introspection) it's useful to
 * know an erasure, and only it, so we've implemented this notion in [[scala.reflect.ErasureTag]].
 *
 * A [[scala.reflect.ClassTag]] is a standard implementation of both [[scala.reflect.ArrayTag]] and [[scala.reflect.ErasureTag]].
 * 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.api.Universe#TypeTag]] value wraps a full Scala type in its tpe field.
 * A [[scala.reflect.api.Universe#ConcreteTypeTag]] value is a [[scala.reflect.api.Universe#TypeTag]]
 * that is guaranteed not to contain any references to type parameters or abstract types.
 * Both flavors of TypeTags also carry an erasure, so [[scala.reflect.api.Universe#TypeTag]] is also an [[scala.reflect.ErasureTag]],
 * and [[scala.reflect.api.Universe#ConcreteTypeTag]] is additionally an [[scala.reflect.ArrayTag]] and a [[scala.reflect.ClassTag]]
 *
 * It is recommended to use the tag supertypes of to precisely express your intent, i.e.:
 * use ArrayTag when you want to construct arrays,
 * use ErasureTag when you need an erasure and don't mind it being generated for untagged abstract types,
 * use ClassTag only when you need an erasure of a type that doesn't refer to untagged abstract types.
 *
 * === 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.
 *
 * === ErasureTag vs ClassTag and TypeTag vs ConcreteTypeTag ===
 *
 * Be careful with ErasureTag and TypeTag, because they 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.api.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 core tags
 *    (defined in the corresponding companion objects) to find out whether it represents a primitive value class.
 *
 * 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.
 */
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.api.TypeTags]]
   */
  @annotation.implicitNotFound(msg = "No TypeTag available for ${T}")
  trait TypeTag[T] extends ErasureTag[T] with Equals with Serializable {

    def tpe: Type
    def sym: Symbol = tpe.typeSymbol

    def isConcrete: Boolean = tpe.isConcrete
    def notConcrete: Boolean = !isConcrete
    def toConcrete: ConcreteTypeTag[T] = ConcreteTypeTag[T](tpe, erasure)

    /** case class accessories */
    override def canEqual(x: Any) = x.isInstanceOf[TypeTag[_]]
    override def equals(x: Any) = x.isInstanceOf[TypeTag[_]] && this.tpe == x.asInstanceOf[TypeTag[_]].tpe
    override def hashCode = scala.runtime.ScalaRunTime.hash(tpe)
    override def toString = if (!self.isInstanceOf[DummyMirror]) (if (isConcrete) "*ConcreteTypeTag" else "TypeTag") + "[" + tpe + "]" else "TypeTag[?]"
  }

  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 AnyVal  : TypeTag[scala.AnyVal]     = ConcreteTypeTag.AnyVal
    val AnyRef  : TypeTag[scala.AnyRef]     = ConcreteTypeTag.AnyRef
    val Nothing : TypeTag[scala.Nothing]    = ConcreteTypeTag.Nothing
    val Null    : TypeTag[scala.Null]       = ConcreteTypeTag.Null
    val String  : TypeTag[java.lang.String] = ConcreteTypeTag.String

    // todo. uncomment after I redo the starr
    // def apply[T](tpe1: Type, erasure1: jClass[_]): TypeTag[T] =
    def apply[T](tpe1: Type, erasure1: jClass[_]): TypeTag[T] =
      tpe1 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 AnyValTpe  => TypeTag.AnyVal.asInstanceOf[TypeTag[T]]
        case AnyRefTpe  => TypeTag.AnyRef.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 TypeTag[T]{ def tpe = tpe1; def erasure = erasure1 }
      }

    def unapply[T](ttag: TypeTag[T]): Option[Type] = Some(ttag.tpe)
  }

  /**
   * 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.api.TypeTags]]
   */
  @annotation.implicitNotFound(msg = "No ConcreteTypeTag available for ${T}")
  trait ConcreteTypeTag[T] extends TypeTag[T] with ClassTag[T] with Equals with Serializable {
    if (!self.isInstanceOf[DummyMirror]) {
      if (notConcrete) throw new Error("%s (%s) is not concrete and cannot be used to construct a concrete type tag".format(tpe, tpe.kind))
    }

    /** case class accessories */
    override def canEqual(x: Any) = x.isInstanceOf[TypeTag[_]] // this is done on purpose. TypeTag(tpe) and ConcreteTypeTag(tpe) should be equal if tpe's are equal
    override def equals(x: Any) = x.isInstanceOf[TypeTag[_]] && this.tpe == x.asInstanceOf[TypeTag[_]].tpe
    override def hashCode = scala.runtime.ScalaRunTime.hash(tpe)
    override def toString = if (!self.isInstanceOf[DummyMirror]) "ConcreteTypeTag[" + tpe + "]" else "ConcreteTypeTag[?]"
  }

  object ConcreteTypeTag {
    val Byte    : ConcreteTypeTag[scala.Byte]       = new ConcreteTypeTag[scala.Byte]{ def tpe = ByteTpe; def erasure = ClassTag.Byte.erasure; private def readResolve() = ConcreteTypeTag.Byte }
    val Short   : ConcreteTypeTag[scala.Short]      = new ConcreteTypeTag[scala.Short]{ def tpe = ShortTpe; def erasure = ClassTag.Short.erasure; private def readResolve() = ConcreteTypeTag.Short }
    val Char    : ConcreteTypeTag[scala.Char]       = new ConcreteTypeTag[scala.Char]{ def tpe = CharTpe; def erasure = ClassTag.Char.erasure; private def readResolve() = ConcreteTypeTag.Char }
    val Int     : ConcreteTypeTag[scala.Int]        = new ConcreteTypeTag[scala.Int]{ def tpe = IntTpe; def erasure = ClassTag.Int.erasure; private def readResolve() = ConcreteTypeTag.Int }
    val Long    : ConcreteTypeTag[scala.Long]       = new ConcreteTypeTag[scala.Long]{ def tpe = LongTpe; def erasure = ClassTag.Long.erasure; private def readResolve() = ConcreteTypeTag.Long }
    val Float   : ConcreteTypeTag[scala.Float]      = new ConcreteTypeTag[scala.Float]{ def tpe = FloatTpe; def erasure = ClassTag.Float.erasure; private def readResolve() = ConcreteTypeTag.Float }
    val Double  : ConcreteTypeTag[scala.Double]     = new ConcreteTypeTag[scala.Double]{ def tpe = DoubleTpe; def erasure = ClassTag.Double.erasure; private def readResolve() = ConcreteTypeTag.Double }
    val Boolean : ConcreteTypeTag[scala.Boolean]    = new ConcreteTypeTag[scala.Boolean]{ def tpe = BooleanTpe; def erasure = ClassTag.Boolean.erasure; private def readResolve() = ConcreteTypeTag.Boolean }
    val Unit    : ConcreteTypeTag[scala.Unit]       = new ConcreteTypeTag[scala.Unit]{ def tpe = UnitTpe; def erasure = ClassTag.Unit.erasure; private def readResolve() = ConcreteTypeTag.Unit }
    val Any     : ConcreteTypeTag[scala.Any]        = new ConcreteTypeTag[scala.Any]{ def tpe = AnyTpe; def erasure = ClassTag.Any.erasure; private def readResolve() = ConcreteTypeTag.Any }
    val Object  : ConcreteTypeTag[java.lang.Object] = new ConcreteTypeTag[java.lang.Object]{ def tpe = ObjectTpe; def erasure = ClassTag.Object.erasure; private def readResolve() = ConcreteTypeTag.Object }
    val AnyVal  : ConcreteTypeTag[scala.AnyVal]     = new ConcreteTypeTag[scala.AnyVal]{ def tpe = AnyValTpe; def erasure = ClassTag.AnyVal.erasure; private def readResolve() = ConcreteTypeTag.AnyVal }
    val AnyRef  : ConcreteTypeTag[scala.AnyRef]     = new ConcreteTypeTag[scala.AnyRef]{ def tpe = AnyRefTpe; def erasure = ClassTag.AnyRef.erasure; private def readResolve() = ConcreteTypeTag.AnyRef }
    val Nothing : ConcreteTypeTag[scala.Nothing]    = new ConcreteTypeTag[scala.Nothing]{ def tpe = NothingTpe; def erasure = ClassTag.Nothing.erasure; private def readResolve() = ConcreteTypeTag.Nothing }
    val Null    : ConcreteTypeTag[scala.Null]       = new ConcreteTypeTag[scala.Null]{ def tpe = NullTpe; def erasure = ClassTag.Null.erasure; private def readResolve() = ConcreteTypeTag.Null }
    val String  : ConcreteTypeTag[java.lang.String] = new ConcreteTypeTag[java.lang.String]{ def tpe = StringTpe; def erasure = ClassTag.String.erasure; private def readResolve() = ConcreteTypeTag.String }

    // todo. uncomment after I redo the starr
    // def apply[T](tpe1: Type, erasure1: jClass[_]): ConcreteTypeTag[T] =
    def apply[T](tpe1: Type, erasure1: jClass[_] = null): ConcreteTypeTag[T] =
      tpe1 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 AnyValTpe  => ConcreteTypeTag.AnyVal.asInstanceOf[ConcreteTypeTag[T]]
        case AnyRefTpe  => ConcreteTypeTag.AnyRef.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 ConcreteTypeTag[T]{ def tpe = tpe1; def erasure = erasure1 }
      }

    def unapply[T](ttag: TypeTag[T]): Option[Type] = if (ttag.isConcrete) Some(ttag.tpe) else None
  }

  // incantations for summoning
  // moved to Context, since rm.tags have their own incantations in Predef, and these guys are only useful in macros
//  def tag[T](implicit ttag: TypeTag[T]) = ttag
//  def typeTag[T](implicit ttag: TypeTag[T]) = ttag
//  def concreteTag[T](implicit gttag: ConcreteTypeTag[T]) = cttag
//  def concreteTypeTag[T](implicit gttag: ConcreteTypeTag[T]) = cttag
}