summaryrefslogtreecommitdiff
path: root/examples/scala-js/compiler/src/main/scala/scala/scalajs/compiler/TypeKinds.scala
blob: 774be68ca4b52d339cd6a9f99181a9b229e56fc9 (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
/* Scala.js compiler
 * Copyright 2013 LAMP/EPFL
 * @author  Sébastien Doeraene
 */

package scala.scalajs.compiler

import scala.tools.nsc._

import scala.scalajs.ir
import ir.{Definitions, Types}

/** Glue representation of types as seen from the IR but still with a
 *  reference to the Symbols.
 *
 *  @author Sébastien Doeraene
 */
trait TypeKinds extends SubComponent { this: GenJSCode =>
  import global._
  import jsAddons._
  import definitions._

  lazy val ObjectReference = REFERENCE(definitions.ObjectClass)

  lazy val VoidKind    = VOID
  lazy val BooleanKind = BOOL
  lazy val CharKind    = INT(CharClass)
  lazy val ByteKind    = INT(ByteClass)
  lazy val ShortKind   = INT(ShortClass)
  lazy val IntKind     = INT(IntClass)
  lazy val LongKind    = LONG
  lazy val FloatKind   = FLOAT(FloatClass)
  lazy val DoubleKind  = FLOAT(DoubleClass)

  /** TypeKinds for Scala primitive types. */
  lazy val primitiveTypeMap: Map[Symbol, TypeKind] = {
    import definitions._
    Map(
      UnitClass    -> VoidKind,
      BooleanClass -> BooleanKind,
      CharClass    -> CharKind,
      ByteClass    -> ByteKind,
      ShortClass   -> ShortKind,
      IntClass     -> IntKind,
      LongClass    -> LongKind,
      FloatClass   -> FloatKind,
      DoubleClass  -> DoubleKind
    )
  }

  /** Glue representation of types as seen from the IR but still with a
   *  reference to the Symbols.
   */
  sealed abstract class TypeKind {
    def isReferenceType = false
    def isArrayType = false
    def isValueType = false

    def toIRType: Types.Type
    def toReferenceType: Types.ReferenceType
  }

  sealed abstract class TypeKindButArray extends TypeKind {
    protected def typeSymbol: Symbol

    override def toReferenceType: Types.ClassType =
      Types.ClassType(encodeClassFullName(typeSymbol))
  }

  /** The void, for trees that can only appear in statement position. */
  case object VOID extends TypeKindButArray {
    protected def typeSymbol = UnitClass
    def toIRType: Types.NoType.type = Types.NoType
  }

  sealed abstract class ValueTypeKind extends TypeKindButArray {
    override def isValueType = true

    val primitiveCharCode: Char = typeSymbol match {
      case BooleanClass  => 'Z'
      case CharClass     => 'C'
      case ByteClass     => 'B'
      case ShortClass    => 'S'
      case IntClass      => 'I'
      case LongClass     => 'J'
      case FloatClass    => 'F'
      case DoubleClass   => 'D'
      case x => abort("Unknown primitive type: " + x.fullName)
    }
  }

  /** Integer number (Byte, Short, Char or Int). */
  case class INT private[TypeKinds] (typeSymbol: Symbol) extends ValueTypeKind {
    def toIRType: Types.IntType.type = Types.IntType
  }

  /** Long */
  case object LONG extends ValueTypeKind {
    protected def typeSymbol = definitions.LongClass
    def toIRType: Types.LongType.type = Types.LongType
  }

  /** Floating-point number (Float or Double). */
  case class FLOAT private[TypeKinds] (typeSymbol: Symbol) extends ValueTypeKind {
    def toIRType: Types.Type =
      if (typeSymbol == FloatClass) Types.FloatType
      else Types.DoubleType
  }

  /** Boolean */
  case object BOOL extends ValueTypeKind {
    protected def typeSymbol = definitions.BooleanClass
    def toIRType: Types.BooleanType.type = Types.BooleanType
  }

  /** Nothing */
  case object NOTHING extends TypeKindButArray {
    protected def typeSymbol = definitions.NothingClass
    def toIRType: Types.NothingType.type = Types.NothingType
    override def toReferenceType: Types.ClassType =
      Types.ClassType(Definitions.RuntimeNothingClass)
  }

  /** Null */
  case object NULL extends TypeKindButArray {
    protected def typeSymbol = definitions.NullClass
    def toIRType: Types.NullType.type = Types.NullType
    override def toReferenceType: Types.ClassType =
      Types.ClassType(Definitions.RuntimeNullClass)
  }

  /** An object */
  case class REFERENCE private[TypeKinds] (typeSymbol: Symbol) extends TypeKindButArray {
    override def toString(): String = "REFERENCE(" + typeSymbol.fullName + ")"
    override def isReferenceType = true

    def toIRType: Types.Type = encodeClassType(typeSymbol)
  }

  /** An array */
  case class ARRAY private[TypeKinds] (elem: TypeKind) extends TypeKind {
    override def toString = "ARRAY[" + elem + "]"
    override def isArrayType = true

    def dimensions: Int = elem match {
      case a: ARRAY => a.dimensions + 1
      case _        => 1
    }

    override def toIRType: Types.ArrayType = toReferenceType

    override def toReferenceType: Types.ArrayType = {
      Types.ArrayType(
          elementKind.toReferenceType.className,
          dimensions)
    }

    /** The ultimate element type of this array. */
    def elementKind: TypeKindButArray = elem match {
      case a: ARRAY            => a.elementKind
      case k: TypeKindButArray => k
    }
  }

  ////////////////// Conversions //////////////////////////////

  def toIRType(t: Type): Types.Type =
    toTypeKind(t).toIRType

  def toReferenceType(t: Type): Types.ReferenceType =
    toTypeKind(t).toReferenceType

  // The following code is a hard copy-and-paste from backend.icode.TypeKinds

  /** Return the TypeKind of the given type
   *
   *  Call to .normalize fixes #3003 (follow type aliases). Otherwise,
   *  arrayOrClassType below would return ObjectReference.
   */
  def toTypeKind(t: Type): TypeKind = t.normalize match {
    case ThisType(ArrayClass)            => ObjectReference
    case ThisType(sym)                   => newReference(sym)
    case SingleType(_, sym)              => primitiveOrRefType(sym)
    case ConstantType(_)                 => toTypeKind(t.underlying)
    case TypeRef(_, sym, args)           => primitiveOrClassType(sym, args)
    case ClassInfoType(_, _, ArrayClass) => abort("ClassInfoType to ArrayClass!")
    case ClassInfoType(_, _, sym)        => primitiveOrRefType(sym)

    // !!! Iulian says types which make no sense after erasure should not reach here,
    // which includes the ExistentialType, AnnotatedType, RefinedType.  I don't know
    // if the first two cases exist because they do or as a defensive measure, but
    // at the time I added it, RefinedTypes were indeed reaching here.
    // !!! Removed in JavaScript backend because I do not know what to do with lub
    //case ExistentialType(_, t)           => toTypeKind(t)
    // Apparently, this case does occur (see pos/CustomGlobal.scala)
    case t: AnnotatedType                => toTypeKind(t.underlying)
    //case RefinedType(parents, _)         => parents map toTypeKind reduceLeft lub

    /* This case is not in scalac. We need it for the test
     * run/valueclasses-classtag-existential. I have no idea how icode does
     * not fail this test: we do everything the same as icode up to here.
     */
    case tpe: ErasedValueType            => newReference(tpe.valueClazz)

    // For sure WildcardTypes shouldn't reach here either, but when
    // debugging such situations this may come in handy.
    // case WildcardType                    => REFERENCE(ObjectClass)
    case norm => abort(
      "Unknown type: %s, %s [%s, %s] TypeRef? %s".format(
        t, norm, t.getClass, norm.getClass, t.isInstanceOf[TypeRef]
      )
    )
  }

  /** Return the type kind of a class, possibly an array type.
   */
  private def arrayOrClassType(sym: Symbol, targs: List[Type]) = sym match {
    case ArrayClass       => ARRAY(toTypeKind(targs.head))
    case _ if sym.isClass => newReference(sym)
    case _                =>
      assert(sym.isType, sym) // it must be compiling Array[a]
      ObjectReference
  }

  /** Interfaces have to be handled delicately to avoid introducing
   *  spurious errors, but if we treat them all as AnyRef we lose too
   *  much information.
   */
  private def newReference(sym: Symbol): TypeKind = sym match {
    case NothingClass => NOTHING
    case NullClass    => NULL
    case _ =>
      // Can't call .toInterface (at this phase) or we trip an assertion.
      // See PackratParser#grow for a method which fails with an apparent mismatch
      // between "object PackratParsers$class" and "trait PackratParsers"
      if (sym.isImplClass) {
        // pos/spec-List.scala is the sole failure if we don't check for NoSymbol
        val traitSym = sym.owner.info.decl(tpnme.interfaceName(sym.name))
        if (traitSym != NoSymbol)
          REFERENCE(traitSym)
        else
          REFERENCE(sym)
      } else {
        REFERENCE(sym)
      }
  }

  private def primitiveOrRefType(sym: Symbol) =
    primitiveTypeMap.getOrElse(sym, newReference(sym))
  private def primitiveOrClassType(sym: Symbol, targs: List[Type]) =
    primitiveTypeMap.getOrElse(sym, arrayOrClassType(sym, targs))
}