diff options
author | Lukas Rytz <lukas.rytz@typesafe.com> | 2016-05-19 08:33:31 +0200 |
---|---|---|
committer | Lukas Rytz <lukas.rytz@typesafe.com> | 2016-05-19 08:33:31 +0200 |
commit | 960e4d76a382ff009c66346289db10b2ee598aa5 (patch) | |
tree | d15ddddd1bd1cea4fe6afc811a0771e9113eb24a /src/compiler | |
parent | 41c9a17e4f211fc24a931949a0819a0474cc004a (diff) | |
parent | a5fab1f588a6042ca924a78d225e85d0acddf5db (diff) | |
download | scala-960e4d76a382ff009c66346289db10b2ee598aa5.tar.gz scala-960e4d76a382ff009c66346289db10b2ee598aa5.tar.bz2 scala-960e4d76a382ff009c66346289db10b2ee598aa5.zip |
Merge pull request #5176 from lrytz/t9671
SI-9671, SI-7397 fix null.asInstanceOf[Int] when pt erases to Object
Diffstat (limited to 'src/compiler')
3 files changed, 33 insertions, 20 deletions
diff --git a/src/compiler/scala/tools/nsc/transform/Erasure.scala b/src/compiler/scala/tools/nsc/transform/Erasure.scala index bc614dfc31..5e903946c1 100644 --- a/src/compiler/scala/tools/nsc/transform/Erasure.scala +++ b/src/compiler/scala/tools/nsc/transform/Erasure.scala @@ -606,10 +606,8 @@ abstract class Erasure extends AddInterfaces // !!! Make pending/run/t5866b.scala work. The fix might be here and/or in unbox1. if (isPrimitiveValueType(targ.tpe) || isErasedValueType(targ.tpe)) { val noNullCheckNeeded = targ.tpe match { - case ErasedValueType(_, underlying) => - isPrimitiveValueClass(underlying.typeSymbol) - case _ => - true + case ErasedValueType(_, underlying) => isPrimitiveValueType(underlying) + case _ => true } if (noNullCheckNeeded) unbox(qual1, targ.tpe) else { @@ -1143,6 +1141,10 @@ abstract class Erasure extends AddInterfaces else { val tree1 = preErase(tree) tree1 match { + case TypeApply(fun, targs @ List(targ)) if fun.symbol == Any_asInstanceOf && targ.tpe == UnitTpe => + // SI-9066 prevent transforming `o.asInstanceOf[Unit]` to `o.asInstanceOf[BoxedUnit]`. + // adaptMember will then replace the call by a reference to BoxedUnit.UNIT. + treeCopy.TypeApply(tree1, transform(fun), targs).clearType() case EmptyTree | TypeTree() => tree1 setType specialScalaErasure(tree1.tpe) case ArrayValue(elemtpt, trees) => diff --git a/src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala b/src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala index 4b1f1efee4..e894c58b1a 100644 --- a/src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala +++ b/src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala @@ -1911,8 +1911,7 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { /** Forward to the generic class constructor. If the current class initializes * specialized fields corresponding to parameters, it passes null to the superclass - * constructor. This saves the boxing cost for initializing generic fields that are - * never used. + * constructor. * * For example: * {{{ @@ -1926,7 +1925,17 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { * super.this(null.asInstanceOf[Int], null.asInstanceOf[Int]) * } * } - * }} + * }}} + * + * Note that erasure first transforms `null.asInstanceOf[Int]` to `unbox(null)`, which is 0. + * Then it adapts the argument `unbox(null)` of type Int to the erased parameter type of Tuple2, + * which is Object, so it inserts a `box` call and we get `box(unbox(null))`, which is + * `new Integer(0)` (not `null`). + * + * However it does not make sense to create an Integer instance to be stored in the generic field + * of the superclass: that field is never used. Therefore we mark the `null` tree with the + * [[SpecializedSuperConstructorCallArgument]] attachment and special-case erasure to replace + * `box(unbox(null))` by `null` in this case. */ private def forwardCtorCall(pos: scala.reflect.internal.util.Position, receiver: Tree, paramss: List[List[ValDef]], clazz: Symbol): Tree = { log(s"forwardCtorCall($pos, $receiver, $paramss, $clazz)") @@ -1945,7 +1954,7 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { val argss = mmap(paramss)(x => if (initializesSpecializedField(x.symbol)) - gen.mkAsInstanceOf(Literal(Constant(null)), x.symbol.tpe) + gen.mkAsInstanceOf(Literal(Constant(null)).updateAttachment(SpecializedSuperConstructorCallArgument), x.symbol.tpe) else Ident(x.symbol) ) @@ -1989,5 +1998,7 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { } resultTree - } } + } + } + object SpecializedSuperConstructorCallArgument } diff --git a/src/compiler/scala/tools/nsc/transform/TypeAdaptingTransformer.scala b/src/compiler/scala/tools/nsc/transform/TypeAdaptingTransformer.scala index afafdedce7..52d7c0b897 100644 --- a/src/compiler/scala/tools/nsc/transform/TypeAdaptingTransformer.scala +++ b/src/compiler/scala/tools/nsc/transform/TypeAdaptingTransformer.scala @@ -14,11 +14,18 @@ trait TypeAdaptingTransformer { self: TreeDSL => def typedPos(pos: Position)(tree: Tree): Tree + /** + * SI-4148: can't always replace box(unbox(x)) by x because + * - unboxing x may lead to throwing an exception, e.g. in "aah".asInstanceOf[Int] + * - box(unbox(null)) is not `null` but the box of zero + */ private def isSafelyRemovableUnbox(fn: Tree, arg: Tree): Boolean = { - currentRun.runDefinitions.isUnbox(fn.symbol) && { - val cls = arg.tpe.typeSymbol - (cls == NullClass) || isBoxedValueClass(cls) - } + currentRun.runDefinitions.isUnbox(fn.symbol) && { + // replace box(unbox(null)) by null when passed to the super constructor in a specialized + // class, see comment in SpecializeTypes.forwardCtorCall. + arg.hasAttachment[specializeTypes.SpecializedSuperConstructorCallArgument.type] || + isBoxedValueClass(arg.tpe.typeSymbol) + } } private def isPrimitiveValueType(tpe: Type) = isPrimitiveValueClass(tpe.typeSymbol) @@ -44,14 +51,7 @@ trait TypeAdaptingTransformer { self: TreeDSL => case x => assert(x != ArrayClass) tree match { - /* Can't always remove a Box(Unbox(x)) combination because the process of boxing x - * may lead to throwing an exception. - * - * This is important for specialization: calls to the super constructor should not box/unbox specialized - * fields (see TupleX). (ID) - */ case Apply(boxFun, List(arg)) if isSafelyRemovableUnbox(tree, arg) => - log(s"boxing an unbox: ${tree.symbol} -> ${arg.tpe}") arg case _ => (REF(currentRun.runDefinitions.boxMethod(x)) APPLY tree) setPos (tree.pos) setType ObjectTpe |