Skip to content

Commit c33c967

Browse files
committed
SIP-23 asInstanceOf checks equality for singleton types
``` scala> val x = "a" x: "a" = a scala> x.asInstanceOf["a"] res1: "a" = a scala> "1".asInstanceOf["2"] x asInstanceOf Throwable java.lang.ClassCastException ... 33 elided scala> "1".asInstanceOf["1"] res3: "1" = 1 scala> 1.asInstanceOf[1] res4: 1 = 1 scala> 1.asInstanceOf[2] x asInstanceOf Throwable java.lang.ClassCastException ... 33 elided ```
1 parent ee5b81e commit c33c967

File tree

1 file changed

+37
-11
lines changed

1 file changed

+37
-11
lines changed

src/compiler/scala/tools/nsc/transform/Erasure.scala

Lines changed: 37 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -869,6 +869,18 @@ abstract class Erasure extends AddInterfaces
869869
* - Reset all other type attributes to null, thus enforcing a retyping.
870870
*/
871871
private val preTransformer = new TypingTransformer(unit) {
872+
// TODO: since the spec defines instanceOf checks in terms of pattern matching,
873+
// this extractor should share code with TypeTestTreeMaker
874+
object SingletonInstanceCheck {
875+
def unapply(pt: Type): Option[(TermSymbol, Tree)] = pt match {
876+
case SingleType(_, _) | LiteralType(_) | ThisType(_) | SuperType(_, _) =>
877+
val cmpOp = if (pt <:< AnyValTpe) Any_equals else Object_eq
878+
val cmpArg = gen.mkAttributedQualifier(pt)
879+
Some((cmpOp, cmpArg))
880+
case _ =>
881+
None
882+
}
883+
}
872884

873885
private def preEraseNormalApply(tree: Apply) = {
874886
val fn = tree.fun
@@ -878,19 +890,36 @@ abstract class Erasure extends AddInterfaces
878890
case Select(qual, _) => qual
879891
case TypeApply(Select(qual, _), _) => qual
880892
}
893+
894+
// TODO SPEC: this should share logic with TypeTestTreeMaker in the pattern matcher,
895+
// since `x.asInstanceOf[T]` is specified as the pattern match
896+
// `x match { case x: T => x case null => null case _ => throw new ClassCastException }` (why is the null case needed?)
881897
def preEraseAsInstanceOf = {
882898
(fn: @unchecked) match {
883899
case TypeApply(Select(qual, _), List(targ)) =>
884-
if (qual.tpe <:< targ.tpe)
885-
atPos(tree.pos) { Typed(qual, TypeTree(targ.tpe)) }
886-
else if (isNumericValueClass(qual.tpe.typeSymbol) && isNumericValueClass(targ.tpe.typeSymbol))
887-
atPos(tree.pos)(numericConversion(qual, targ.tpe.typeSymbol))
888-
else
889-
tree
900+
targ.tpe match {
901+
case argTp@SingletonInstanceCheck(cmpOp, cmpArg) if sip23 => // compiler has an unsound asInstanceOf[global.type]...
902+
atPos(tree.pos) {
903+
gen.evalOnce(qual, currentOwner, currentUnit) { qual =>
904+
If(Apply(Select(qual(), cmpOp), List(cmpArg)),
905+
Typed(qual(), TypeTree(argTp)),
906+
Throw(ClassCastExceptionClass.tpe_*))
907+
}
908+
}
909+
case argTp if qual.tpe <:< argTp =>
910+
atPos(tree.pos) { Typed(qual, TypeTree(argTp)) }
911+
case argTp if isNumericValueClass(qual.tpe.typeSymbol) && isNumericValueClass(argTp.typeSymbol) =>
912+
atPos(tree.pos)(numericConversion(qual, argTp.typeSymbol))
913+
case _ =>
914+
tree
915+
}
890916
}
891917
// todo: also handle the case where the singleton type is buried in a compound
892918
}
893919

920+
// TODO SPEC: this should share logic with TypeTestTreeMaker in the pattern matcher,
921+
// since `x.isInstanceOf[T]` is specified as the pattern match
922+
// `x match { case _: T => true case _ => false }` (modulo numeric conversion)
894923
def preEraseIsInstanceOf = {
895924
fn match {
896925
case TypeApply(sel @ Select(qual, name), List(targ)) =>
@@ -904,11 +933,8 @@ abstract class Erasure extends AddInterfaces
904933
List(TypeTree(tp) setPos targ.pos)) setPos fn.pos,
905934
List()) setPos tree.pos
906935
targ.tpe match {
907-
case SingleType(_, _) | LiteralType(_) | ThisType(_) | SuperType(_, _) =>
908-
val cmpOp = if (targ.tpe <:< AnyValTpe) Any_equals else Object_eq
909-
atPos(tree.pos) {
910-
Apply(Select(qual, cmpOp), List(gen.mkAttributedQualifier(targ.tpe)))
911-
}
936+
case SingletonInstanceCheck(cmpOp, cmpArg) =>
937+
atPos(tree.pos) { Apply(Select(qual, cmpOp), List(cmpArg)) }
912938
case RefinedType(parents, decls) if (parents.length >= 2) =>
913939
gen.evalOnce(qual, currentOwner, unit) { q =>
914940
// Optimization: don't generate isInstanceOf tests if the static type

0 commit comments

Comments
 (0)