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