summaryrefslogtreecommitdiff
path: root/src/compiler/scala/reflect/reify/codegen/GenTypes.scala
blob: d007df75e38cf1b8ea680d6b8b14389f6ad216c0 (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
package scala.reflect.reify
package codegen

trait GenTypes {
  self: Reifier =>

  import global._
  import definitions._
  private val runDefinitions = currentRun.runDefinitions
  import runDefinitions.{ReflectRuntimeUniverse, ReflectRuntimeCurrentMirror, _}

  /**
   *  Reify a type.
   *  For internal use only, use `reified` instead.
   */
  def reifyType(tpe: Type): Tree = {
    assert(tpe != null, "tpe is null")

    if (tpe.isErroneous)
      CannotReifyErroneousReifee(tpe)
    if (tpe.isLocalToReifee)
      CannotReifyType(tpe)

    // this is a very special case. see the comments below for more info.
    if (isSemiConcreteTypeMember(tpe))
      return reifySemiConcreteTypeMember(tpe)

    // SI-6242: splicing might violate type bounds
    val spliced = spliceType(tpe)
    if (spliced != EmptyTree)
      return spliced

    val tsym = tpe.typeSymbolDirect
    if (tsym.isClass && tpe == tsym.typeConstructor && tsym.isStatic)
      Select(Select(reify(tsym), nme.asType), nme.toTypeConstructor)
    else tpe match {
      case tpe : NoType.type =>
        reifyMirrorObject(tpe)
      case tpe : NoPrefix.type =>
        reifyMirrorObject(tpe)
      case tpe @ ThisType(root) if root.isRoot =>
        mirrorBuildCall(nme.thisPrefix, mirrorMirrorSelect(nme.RootClass))
      case tpe @ ThisType(empty) if empty.isEmptyPackageClass =>
        mirrorBuildCall(nme.thisPrefix, mirrorMirrorSelect(nme.EmptyPackageClass))
      case tpe @ ThisType(clazz) if clazz.isModuleClass && clazz.isStatic =>
        val module = reify(clazz.sourceModule)
        val moduleClass = Select(Select(module, nme.asModule), nme.moduleClass)
        mirrorBuildCall(nme.ThisType, moduleClass)
      case tpe @ ThisType(sym) =>
        reifyBuildCall(nme.ThisType, sym)
      case tpe @ SuperType(thistpe, supertpe) =>
        reifyBuildCall(nme.SuperType, thistpe, supertpe)
      case tpe @ SingleType(pre, sym) =>
        reifyBuildCall(nme.SingleType, pre, sym)
      case tpe @ ConstantType(value) =>
        mirrorBuildCall(nme.ConstantType, reifyProduct(value))
      case tpe @ TypeRef(pre, sym, args) =>
        reifyBuildCall(nme.TypeRef, pre, sym, args)
      case tpe @ TypeBounds(lo, hi) =>
        reifyBuildCall(nme.TypeBounds, lo, hi)
      case tpe @ NullaryMethodType(restpe) =>
        reifyBuildCall(nme.NullaryMethodType, restpe)
      case tpe @ AnnotatedType(anns, underlying) =>
        reifyAnnotatedType(tpe)
      case _ =>
        reifyToughType(tpe)
    }
  }

  /** Keeps track of whether this reification contains abstract type parameters */
  def reificationIsConcrete: Boolean = state.reificationIsConcrete

  def spliceType(tpe: Type): Tree = {
    if (tpe.isSpliceable && !(boundSymbolsInCallstack contains tpe.typeSymbol)) {
      if (reifyDebug) println("splicing " + tpe)

      val tagFlavor = if (concrete) tpnme.TypeTag.toString else tpnme.WeakTypeTag.toString
      // if this fails, it might produce the dreaded "erroneous or inaccessible type" error
      // to find out the whereabouts of the error run scalac with -Ydebug
      if (reifyDebug) println("launching implicit search for %s.%s[%s]".format(universe, tagFlavor, tpe))
      val result =
        typer.resolveTypeTag(defaultErrorPosition, universe.tpe, tpe, concrete = concrete, allowMaterialization = false) match {
          case failure if failure.isEmpty =>
            if (reifyDebug) println("implicit search was fruitless")
            if (reifyDebug) println("trying to splice as manifest")
            val splicedAsManifest = spliceAsManifest(tpe)
            if (splicedAsManifest.isEmpty) {
              if (reifyDebug) println("no manifest in scope")
              EmptyTree
            } else {
              if (reifyDebug) println("successfully spliced as manifest: " + splicedAsManifest)
              splicedAsManifest
            }
          case success =>
            if (reifyDebug) println("implicit search has produced a result: " + success)
            state.reificationIsConcrete &= concrete || success.tpe <:< TypeTagClass.toTypeConstructor
            Select(Apply(Select(success, nme.in), List(Ident(nme.MIRROR_SHORT))), nme.tpe)
        }
      if (result != EmptyTree) return result
      state.reificationIsConcrete = false
    }

    EmptyTree
  }

  private def spliceAsManifest(tpe: Type): Tree = {
    def isSynthetic(manifest: Tree) = manifest exists (sub => sub.symbol != null && (sub.symbol == FullManifestModule || sub.symbol.owner == FullManifestModule))
    def searchForManifest(typer: analyzer.Typer): Tree =
      analyzer.inferImplicit(
        EmptyTree,
        appliedType(FullManifestClass.toTypeConstructor, List(tpe)),
        reportAmbiguous = false,
        isView = false,
        context = typer.context,
        saveAmbiguousDivergent = false,
        pos = defaultErrorPosition) match {
          case success if !success.tree.isEmpty && !isSynthetic(success.tree) =>
            val manifestInScope = success.tree
            // todo. write a test for this
            if (ReflectRuntimeUniverse == NoSymbol) CannotConvertManifestToTagWithoutScalaReflect(tpe, manifestInScope)
            val cm = typer.typed(Ident(ReflectRuntimeCurrentMirror))
            val internal = gen.mkAttributedSelect(gen.mkAttributedRef(ReflectRuntimeUniverse), UniverseInternal)
            val tagTree = gen.mkMethodCall(Select(internal, nme.manifestToTypeTag), List(tpe), List(cm, manifestInScope))
            Select(Apply(Select(tagTree, nme.in), List(Ident(nme.MIRROR_SHORT))), nme.tpe)
          case _ =>
            EmptyTree
        }
    val result = typer.silent(silentTyper => silentTyper.context.withMacrosDisabled(searchForManifest(silentTyper)))
    result match {
      case analyzer.SilentResultValue(result) => result
      case analyzer.SilentTypeError(_) => EmptyTree
    }
  }

  /** Reify a semi-concrete type member.
   *
   *  This is a VERY special case to deal with stuff like `typeOf[ru.Type]`.
   *  In that case `Type`, which is an abstract type member of scala.reflect.api.Universe, is not a free type.
   *  Why? Because we know its prefix, and it unambiguously determines the type.
   *
   *  Here is a different view on this question that supports this suggestion.
   *  Say, you reify a tree. Iff it doesn't contain free types, it can be successfully compiled and run.
   *  For example, if you reify `tpe.asInstanceOf[T]` taken from `def foo[T]`, then you won't be able to compile the result.
   *  Fair enough, you don't know the `T`, so the compiler will choke.
   *  This fact is captured by reification result having a free type T (this can be inspected by calling `tree.freeTypes`).
   *  Now imagine you reify the following tree: `tpe.asInstanceOf[ru.Type]`.
   *  To the contrast with the previous example, that's totally not a problem.
   *
   *  Okay, so we figured out that `ru.Type` is not a free type.
   *  However, in our reification framework, this type would be treated a free type.
   *  Why? Because `tpe.isSpliceable` will return true.
   *  Hence we intervene and handle this situation in a special way.
   *
   *  By the way, we cannot change the definition of `isSpliceable`, because class tags also depend on it.
   *  And, you know, class tags don't care whether we select a type member from a concrete instance or get it from scope (as with type parameters).
   *  The type itself still remains not concrete, in the sense that we don't know its erasure.
   *  I.e. we can compile the code that involves `ru.Type`, but we cannot serialize an instance of `ru.Type`.
   */
  private def reifySemiConcreteTypeMember(tpe: Type): Tree = tpe match {
    case tpe @ TypeRef(pre @ SingleType(prepre, presym), sym, args) if sym.isAbstractType && !sym.isExistential =>
      mirrorBuildCall(nme.TypeRef, reify(pre), mirrorBuildCall(nme.selectType, reify(sym.owner), reify(sym.name.toString)), reify(args))
  }

  /** Reify an annotated type, i.e. the one that makes us deal with AnnotationInfos */
  private def reifyAnnotatedType(tpe: AnnotatedType): Tree = {
    val AnnotatedType(anns, underlying) = tpe
    mirrorBuildCall(nme.AnnotatedType, mkList(anns map reifyAnnotationInfo), reify(underlying))
  }

  /** Reify a tough type, i.e. the one that leads to creation of auxiliary symbols */
  private def reifyToughType(tpe: Type): Tree = {
    if (reifyDebug) println("tough type: %s (%s)".format(tpe, tpe.kind))

    def reifyScope(scope: Scope): Tree = {
      scope foreach reifySymDef
      mirrorBuildCall(nme.newScopeWith, scope.toList map reify: _*)
    }

    tpe match {
      case tpe @ RefinedType(parents, decls) =>
        reifySymDef(tpe.typeSymbol)
        mirrorBuildCall(nme.RefinedType, reify(parents), reifyScope(decls), reify(tpe.typeSymbol))
      case tpe @ ExistentialType(tparams, underlying) =>
        tparams foreach reifySymDef
        reifyBuildCall(nme.ExistentialType, tparams, underlying)
      case tpe @ ClassInfoType(parents, decls, clazz) =>
        reifySymDef(clazz)
        mirrorBuildCall(nme.ClassInfoType, reify(parents), reifyScope(decls), reify(tpe.typeSymbol))
      case tpe @ MethodType(params, restpe) =>
        params foreach reifySymDef
        reifyBuildCall(nme.MethodType, params, restpe)
      case tpe @ PolyType(tparams, underlying) =>
        tparams foreach reifySymDef
        reifyBuildCall(nme.PolyType, tparams, underlying)
      case _ =>
        throw new Error("internal error: %s (%s) is not supported".format(tpe, tpe.kind))
    }
  }
}