summaryrefslogtreecommitdiff
path: root/ir/src/main/scala/scala/scalajs/ir/Types.scala
diff options
context:
space:
mode:
Diffstat (limited to 'ir/src/main/scala/scala/scalajs/ir/Types.scala')
-rw-r--r--ir/src/main/scala/scala/scalajs/ir/Types.scala182
1 files changed, 182 insertions, 0 deletions
diff --git a/ir/src/main/scala/scala/scalajs/ir/Types.scala b/ir/src/main/scala/scala/scalajs/ir/Types.scala
new file mode 100644
index 0000000..4af493a
--- /dev/null
+++ b/ir/src/main/scala/scala/scalajs/ir/Types.scala
@@ -0,0 +1,182 @@
+/* __ *\
+** ________ ___ / / ___ __ ____ Scala.js IR **
+** / __/ __// _ | / / / _ | __ / // __/ (c) 2014, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ **
+** /____/\___/_/ |_/____/_/ | |__/ /____/ **
+** |/____/ **
+\* */
+
+
+package scala.scalajs.ir
+
+import scala.annotation.tailrec
+
+object Types {
+
+ /** Type of an expression in the IR. */
+ abstract sealed class Type {
+ def show(): String = {
+ val writer = new java.io.StringWriter
+ val printer = new Printers.IRTreePrinter(writer)
+ printer.printType(this)
+ writer.toString()
+ }
+ }
+
+ /** Any type (the top type of this type system).
+ * A variable of this type can contain any value, including `undefined`
+ * and `null` and any raw JS value. This type supports a very limited set
+ * of Scala operations, the ones common to all values. Basically only
+ * reference equality tests and instance tests. It also supports all
+ * JavaScript operations, since all Scala objects are also genuine
+ * JavaScript objects.
+ * The type java.lang.Object in the back-end maps to [[AnyType]] because it
+ * can hold raw JS values (not only instances of Scala.js classes).
+ */
+ case object AnyType extends Type
+
+ /** Nothing type (the bottom type of this type system).
+ * Expressions from which one can never come back are typed as [[Nothing]].
+ * For example, `throw` and `return`.
+ */
+ case object NothingType extends Type
+
+ /** The type of `undefined`. */
+ case object UndefType extends Type
+
+ /** Boolean type.
+ * It does not accept `null` nor `undefined`.
+ */
+ case object BooleanType extends Type
+
+ /** 32-bit signed integer type.
+ * It does not accept `null` nor `undefined`.
+ */
+ case object IntType extends Type
+
+ /** 64-bit signed integer type.
+ * It does not accept `null` nor `undefined`.
+ */
+ case object LongType extends Type
+
+ /** Float type (32-bit).
+ * It does not accept `null` nor `undefined`.
+ */
+ case object FloatType extends Type
+
+ /** Double type (64-bit).
+ * It does not accept `null` nor `undefined`.
+ */
+ case object DoubleType extends Type
+
+ /** String type.
+ * It does not accept `null` nor `undefined`.
+ */
+ case object StringType extends Type
+
+ /** The type of `null`.
+ * It does not accept `undefined`.
+ * The null type is a subtype of all class types and array types.
+ */
+ case object NullType extends Type
+
+ /** Reference types (allowed for classOf[], is/asInstanceOf[]). */
+ sealed abstract class ReferenceType extends Type
+
+ /** Class (or interface) type. */
+ final case class ClassType(className: String) extends ReferenceType
+
+ /** Array type. */
+ final case class ArrayType(baseClassName: String, dimensions: Int) extends ReferenceType
+
+ object ArrayType {
+ def apply(innerType: ReferenceType): ArrayType = innerType match {
+ case ClassType(className) => ArrayType(className, 1)
+ case ArrayType(className, dim) => ArrayType(className, dim + 1)
+ }
+ }
+
+ /** Record type.
+ * Used by the optimizer to inline classes as records with multiple fields.
+ * They are desugared as several local variables by JSDesugaring.
+ * Record types cannot cross method boundaries, so they cannot appear as
+ * the type of fields or parameters, nor as result types of methods.
+ * The compiler itself never generates record types.
+ */
+ final case class RecordType(fields: List[RecordType.Field]) extends Type {
+ def findField(name: String): RecordType.Field =
+ fields.find(_.name == name).get
+ }
+
+ object RecordType {
+ final case class Field(name: String, originalName: Option[String],
+ tpe: Type, mutable: Boolean)
+ }
+
+ /** No type. */
+ case object NoType extends Type
+
+ /** Tests whether a type `lhs` is a subtype of `rhs` (or equal).
+ * [[NoType]] is never a subtype or supertype of anything (including
+ * itself). All other types are subtypes of themselves.
+ * @param isSubclass A function testing whether a class/interface is a
+ * subclass of another class/interface.
+ */
+ def isSubtype(lhs: Type, rhs: Type)(
+ isSubclass: (String, String) => Boolean): Boolean = {
+ import Definitions._
+
+ (lhs != NoType && rhs != NoType) && {
+ (lhs == rhs) ||
+ ((lhs, rhs) match {
+ case (_, AnyType) => true
+ case (NothingType, _) => true
+
+ case (ClassType(lhsClass), ClassType(rhsClass)) =>
+ isSubclass(lhsClass, rhsClass)
+
+ case (NullType, ClassType(_)) => true
+ case (NullType, ArrayType(_, _)) => true
+
+ case (UndefType, ClassType(cls)) =>
+ isSubclass(BoxedUnitClass, cls)
+ case (BooleanType, ClassType(cls)) =>
+ isSubclass(BoxedBooleanClass, cls)
+ case (IntType, ClassType(cls)) =>
+ isSubclass(BoxedIntegerClass, cls) ||
+ cls == BoxedByteClass ||
+ cls == BoxedShortClass ||
+ cls == BoxedDoubleClass
+ case (LongType, ClassType(cls)) =>
+ isSubclass(BoxedLongClass, cls)
+ case (FloatType, ClassType(cls)) =>
+ isSubclass(BoxedFloatClass, cls) ||
+ cls == BoxedDoubleClass
+ case (DoubleType, ClassType(cls)) =>
+ isSubclass(BoxedDoubleClass, cls)
+ case (StringType, ClassType(cls)) =>
+ isSubclass(StringClass, cls)
+
+ case (IntType, DoubleType) => true
+ case (FloatType, DoubleType) => true
+
+ case (ArrayType(lhsBase, lhsDims), ArrayType(rhsBase, rhsDims)) =>
+ if (lhsDims < rhsDims) {
+ false // because Array[A] </: Array[Array[A]]
+ } else if (lhsDims > rhsDims) {
+ rhsBase == ObjectClass // because Array[Array[A]] <: Array[Object]
+ } else { // lhsDims == rhsDims
+ // lhsBase must be <: rhsBase
+ if (isPrimitiveClass(lhsBase) || isPrimitiveClass(rhsBase)) {
+ lhsBase == rhsBase
+ } else {
+ isSubclass(lhsBase, rhsBase)
+ }
+ }
+
+ case _ =>
+ false
+ })
+ }
+ }
+}