Skip to content

Commit

Permalink
Redesign primitive numeric types in the IR.
Browse files Browse the repository at this point in the history
* Add types for `char`, `byte` and `short` in the IR.
* Remove the subtyping relationships between primitive types, for
  example `int <: double`.

Instead of the subtyping relationships, we introduce explicit
widening conversions as `UnaryOp`s. In order to avoid widening
`int`s to `double`s for the purpose of comparing them, we separate
`Num_<` and friends into `Int_<` and `Double_<`. For floats, we do
introduce the conversions in that case.

There is one subtlety for `char` (`CharType`): it is *not* a
subtype of `any`. That's because `char`s must always be boxed in
`j.l.Character` when they are assigned to an `any`.

This fixes scala-js#3085, and provides a cleaner solution to the old
issues scala-js#2184 and scala-js#2780.
  • Loading branch information
sjrd committed Aug 13, 2017
1 parent 69f0d84 commit 9890ee7
Show file tree
Hide file tree
Showing 20 changed files with 1,125 additions and 740 deletions.
489 changes: 227 additions & 262 deletions compiler/src/main/scala/org/scalajs/core/compiler/GenJSCode.scala

Large diffs are not rendered by default.

14 changes: 10 additions & 4 deletions compiler/src/main/scala/org/scalajs/core/compiler/TypeKinds.scala
Expand Up @@ -91,7 +91,12 @@ trait TypeKinds extends SubComponent { this: GenJSCode =>

/** Integer number (Byte, Short, Char or Int). */
case class INT private[TypeKinds] (typeSymbol: Symbol) extends ValueTypeKind {
def toIRType: Types.IntType.type = Types.IntType
val toIRType: Types.Type = typeSymbol match {
case CharClass => Types.CharType
case ByteClass => Types.ByteType
case ShortClass => Types.ShortType
case IntClass => Types.IntType
}
}

/** Long */
Expand All @@ -102,9 +107,10 @@ trait TypeKinds extends SubComponent { this: GenJSCode =>

/** 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
val toIRType: Types.Type = typeSymbol match {
case FloatClass => Types.FloatType
case DoubleClass => Types.DoubleType
}
}

/** Boolean */
Expand Down
24 changes: 24 additions & 0 deletions ir/src/main/scala/org/scalajs/core/ir/Hashers.scala
Expand Up @@ -395,6 +395,18 @@ object Hashers {
mixTag(TagBooleanLiteral)
mixBoolean(value)

case CharLiteral(value) =>
mixTag(TagCharLiteral)
mixChar(value)

case ByteLiteral(value) =>
mixTag(TagByteLiteral)
mixByte(value)

case ShortLiteral(value) =>
mixTag(TagShortLiteral)
mixShort(value)

case IntLiteral(value) =>
mixTag(TagIntLiteral)
mixInt(value)
Expand Down Expand Up @@ -459,6 +471,9 @@ object Hashers {
case NothingType => mixTag(TagNothingType)
case UndefType => mixTag(TagUndefType)
case BooleanType => mixTag(TagBooleanType)
case CharType => mixTag(TagCharType)
case ByteType => mixTag(TagByteType)
case ShortType => mixTag(TagShortType)
case IntType => mixTag(TagIntType)
case LongType => mixTag(TagLongType)
case FloatType => mixTag(TagFloatType)
Expand Down Expand Up @@ -521,6 +536,15 @@ object Hashers {
@inline
final def mixString(str: String): Unit = digestStream.writeUTF(str)

@inline
final def mixChar(c: Char): Unit = digestStream.writeChar(c)

@inline
final def mixByte(b: Byte): Unit = digestStream.writeByte(b)

@inline
final def mixShort(s: Short): Unit = digestStream.writeShort(s)

@inline
final def mixInt(i: Int): Unit = digestStream.writeInt(i)

Expand Down
100 changes: 73 additions & 27 deletions ir/src/main/scala/org/scalajs/core/ir/Printers.scala
Expand Up @@ -329,11 +329,22 @@ object Printers {
import UnaryOp._
print('(')
print((op: @switch) match {
case Boolean_! => "!"
case IntToLong | DoubleToLong => "(long)"
case DoubleToInt | LongToInt => "(int)"
case DoubleToFloat => "(float)"
case LongToDouble => "(double)"
case Boolean_! =>
"!"
case IntToChar =>
"(char)"
case IntToByte =>
"(byte)"
case IntToShort =>
"(short)"
case CharToInt | ByteToInt | ShortToInt | LongToInt | DoubleToInt =>
"(int)"
case IntToLong | DoubleToLong =>
"(long)"
case DoubleToFloat =>
"(float)"
case IntToDouble | LongToDouble | FloatToDouble =>
"(double)"
})
print(lhs)
print(')')
Expand Down Expand Up @@ -380,6 +391,11 @@ object Printers {

case String_+ => "+[string]"

case Boolean_== => "==[bool]"
case Boolean_!= => "!=[bool]"
case Boolean_| => "|[bool]"
case Boolean_& => "&[bool]"

case Int_+ => "+[int]"
case Int_- => "-[int]"
case Int_* => "*[int]"
Expand All @@ -393,24 +409,12 @@ object Printers {
case Int_>>> => ">>>[int]"
case Int_>> => ">>[int]"

case Float_+ => "+[float]"
case Float_- => "-[float]"
case Float_* => "*[float]"
case Float_/ => "/[float]"
case Float_% => "%[float]"

case Double_+ => "+[double]"
case Double_- => "-[double]"
case Double_* => "*[double]"
case Double_/ => "/[double]"
case Double_% => "%[double]"

case Num_== => "=="
case Num_!= => "!="
case Num_< => "<"
case Num_<= => "<="
case Num_> => ">"
case Num_>= => ">="
case Int_== => "==[int]"
case Int_!= => "!=[int]"
case Int_< => "<[int]"
case Int_<= => "<=[int]"
case Int_> => ">[int]"
case Int_>= => ">=[int]"

case Long_+ => "+[long]"
case Long_- => "-[long]"
Expand All @@ -432,10 +436,24 @@ object Printers {
case Long_> => ">[long]"
case Long_>= => ">=[long]"

case Boolean_== => "==[bool]"
case Boolean_!= => "!=[bool]"
case Boolean_| => "|[bool]"
case Boolean_& => "&[bool]"
case Float_+ => "+[float]"
case Float_- => "-[float]"
case Float_* => "*[float]"
case Float_/ => "/[float]"
case Float_% => "%[float]"

case Double_+ => "+[double]"
case Double_- => "-[double]"
case Double_* => "*[double]"
case Double_/ => "/[double]"
case Double_% => "%[double]"

case Double_== => "==[double]"
case Double_!= => "!=[double]"
case Double_< => "<[double]"
case Double_<= => "<=[double]"
case Double_> => ">[double]"
case Double_>= => ">=[double]"
})
print(' ')
print(rhs)
Expand Down Expand Up @@ -688,6 +706,31 @@ object Printers {
case BooleanLiteral(value) =>
print(if (value) "true" else "false")

case CharLiteral(value) =>
print('\'')
printEscapeJS(value.toString(), out)
print('\'')

case ByteLiteral(value) =>
if (value >= 0) {
print(value.toString)
print("_b")
} else {
print('(')
print(value.toString)
print("_b)")
}

case ShortLiteral(value) =>
if (value >= 0) {
print(value.toString)
print("_s")
} else {
print('(')
print(value.toString)
print("_s)")
}

case IntLiteral(value) =>
if (value >= 0) {
print(value.toString)
Expand Down Expand Up @@ -906,6 +949,9 @@ object Printers {
case NothingType => print("nothing")
case UndefType => print("void")
case BooleanType => print("boolean")
case CharType => print("char")
case ByteType => print("byte")
case ShortType => print("short")
case IntType => print("int")
case LongType => print("long")
case FloatType => print("float")
Expand Down
21 changes: 21 additions & 0 deletions ir/src/main/scala/org/scalajs/core/ir/Serializers.scala
Expand Up @@ -375,6 +375,18 @@ object Serializers {
writeByte(TagBooleanLiteral)
writeBoolean(value)

case CharLiteral(value) =>
writeByte(TagCharLiteral)
writeChar(value)

case ByteLiteral(value) =>
writeByte(TagByteLiteral)
writeByte(value)

case ShortLiteral(value) =>
writeByte(TagShortLiteral)
writeShort(value)

case IntLiteral(value) =>
writeByte(TagIntLiteral)
writeInt(value)
Expand Down Expand Up @@ -572,6 +584,9 @@ object Serializers {
case NothingType => buffer.write(TagNothingType)
case UndefType => buffer.write(TagUndefType)
case BooleanType => buffer.write(TagBooleanType)
case CharType => buffer.write(TagCharType)
case ByteType => buffer.write(TagByteType)
case ShortType => buffer.write(TagShortType)
case IntType => buffer.write(TagIntType)
case LongType => buffer.write(TagLongType)
case FloatType => buffer.write(TagFloatType)
Expand Down Expand Up @@ -863,6 +878,9 @@ object Serializers {
case TagUndefined => Undefined()
case TagNull => Null()
case TagBooleanLiteral => BooleanLiteral(readBoolean())
case TagCharLiteral => CharLiteral(readChar())
case TagByteLiteral => ByteLiteral(readByte())
case TagShortLiteral => ShortLiteral(readShort())
case TagIntLiteral => IntLiteral(readInt())
case TagLongLiteral => LongLiteral(readLong())
case TagFloatLiteral => FloatLiteral(readFloat())
Expand Down Expand Up @@ -989,6 +1007,9 @@ object Serializers {
case TagNothingType => NothingType
case TagUndefType => UndefType
case TagBooleanType => BooleanType
case TagCharType => CharType
case TagByteType => ByteType
case TagShortType => ShortType
case TagIntType => IntType
case TagLongType => LongType
case TagFloatType => FloatType
Expand Down
10 changes: 8 additions & 2 deletions ir/src/main/scala/org/scalajs/core/ir/Tags.scala
Expand Up @@ -78,7 +78,10 @@ private[ir] object Tags {
final val TagUndefined = TagJSLinkingInfo + 1
final val TagNull = TagUndefined + 1
final val TagBooleanLiteral = TagNull + 1
final val TagIntLiteral = TagBooleanLiteral + 1
final val TagCharLiteral = TagBooleanLiteral + 1
final val TagByteLiteral = TagCharLiteral + 1
final val TagShortLiteral = TagByteLiteral + 1
final val TagIntLiteral = TagShortLiteral + 1
final val TagLongLiteral = TagIntLiteral + 1
final val TagFloatLiteral = TagLongLiteral + 1
final val TagDoubleLiteral = TagFloatLiteral + 1
Expand Down Expand Up @@ -109,7 +112,10 @@ private[ir] object Tags {
final val TagNothingType = TagAnyType + 1
final val TagUndefType = TagNothingType + 1
final val TagBooleanType = TagUndefType + 1
final val TagIntType = TagBooleanType + 1
final val TagCharType = TagBooleanType + 1
final val TagByteType = TagCharType + 1
final val TagShortType = TagByteType + 1
final val TagIntType = TagShortType + 1
final val TagLongType = TagIntType + 1
final val TagFloatType = TagLongType + 1
final val TagDoubleType = TagFloatType + 1
Expand Down

0 comments on commit 9890ee7

Please sign in to comment.