summaryrefslogblamecommitdiff
path: root/examples/scala-js/compiler/src/main/scala/scala/scalajs/compiler/TypeKinds.scala
blob: 774be68ca4b52d339cd6a9f99181a9b229e56fc9 (plain) (tree)



























































































































































































































































                                                                                         
/* 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))
}