diff options
Diffstat (limited to 'src/compiler/scala/tools/nsc/transform/TypeAdaptingTransformer.scala')
-rw-r--r-- | src/compiler/scala/tools/nsc/transform/TypeAdaptingTransformer.scala | 177 |
1 files changed, 70 insertions, 107 deletions
diff --git a/src/compiler/scala/tools/nsc/transform/TypeAdaptingTransformer.scala b/src/compiler/scala/tools/nsc/transform/TypeAdaptingTransformer.scala index 3b23306386..52d7c0b897 100644 --- a/src/compiler/scala/tools/nsc/transform/TypeAdaptingTransformer.scala +++ b/src/compiler/scala/tools/nsc/transform/TypeAdaptingTransformer.scala @@ -1,90 +1,64 @@ package scala.tools.nsc package transform +import scala.annotation.tailrec import scala.tools.nsc.ast.TreeDSL -import scala.tools.nsc.Global /** * A trait usable by transforms that need to adapt trees of one type to another type */ -trait TypeAdaptingTransformer { - self: TreeDSL => - - val analyzer: typechecker.Analyzer { val global: self.global.type } - - trait TypeAdapter { - val typer: analyzer.Typer +trait TypeAdaptingTransformer { self: TreeDSL => + abstract class TypeAdapter { import global._ import definitions._ - import CODE._ - def isMethodTypeWithEmptyParams(tpe: Type) = tpe match { - case MethodType(Nil, _) => true - case _ => false - } + 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 == definitions.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) - - private def isErasedValueType(tpe: Type) = tpe.isInstanceOf[ErasedValueType] - - private def isDifferentErasedValueType(tpe: Type, other: Type) = - isErasedValueType(tpe) && (tpe ne other) + private def isPrimitiveValueType(tpe: Type) = isPrimitiveValueClass(tpe.typeSymbol) + final def isPrimitiveValueMember(sym: Symbol) = isPrimitiveValueClass(sym.owner) + final def isMethodTypeWithEmptyParams(tpe: Type) = tpe.isInstanceOf[MethodType] && tpe.params.isEmpty + final def applyMethodWithEmptyParams(qual: Tree) = Apply(qual, List()) setPos qual.pos setType qual.tpe.resultType - def isPrimitiveValueMember(sym: Symbol) = isPrimitiveValueClass(sym.owner) - - @inline def box(tree: Tree, target: => String): Tree = { - val result = box1(tree) - if (tree.tpe =:= UnitTpe) () - else log(s"boxing ${tree.summaryString}: ${tree.tpe} into $target: ${result.tpe}") - result - } + import CODE._ /** Box `tree` of unboxed type */ - private def box1(tree: Tree): Tree = tree match { + final def box(tree: Tree): Tree = tree match { case LabelDef(_, _, _) => - val ldef = deriveLabelDef(tree)(box1) + val ldef = deriveLabelDef(tree)(box) ldef setType ldef.rhs.tpe case _ => val tree1 = tree.tpe match { - case ErasedValueType(clazz, _) => - New(clazz, cast(tree, underlyingOfValueClass(clazz))) - case _ => - tree.tpe.typeSymbol match { - case UnitClass => - if (treeInfo isExprSafeToInline tree) REF(BoxedUnit_UNIT) - else BLOCK(tree, REF(BoxedUnit_UNIT)) - case NothingClass => tree // a non-terminating expression doesn't need boxing - 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 - } - } + case ErasedValueType(clazz, _) => New(clazz, cast(tree, underlyingOfValueClass(clazz))) + case _ => tree.tpe.typeSymbol match { + case UnitClass => + if (treeInfo isExprSafeToInline tree) REF(BoxedUnit_UNIT) + else BLOCK(tree, REF(BoxedUnit_UNIT)) + case NothingClass => tree // a non-terminating expression doesn't need boxing + case x => + assert(x != ArrayClass) + tree match { + case Apply(boxFun, List(arg)) if isSafelyRemovableUnbox(tree, arg) => + arg + case _ => + (REF(currentRun.runDefinitions.boxMethod(x)) APPLY tree) setPos (tree.pos) setType ObjectTpe + } + } } - typer.typedPos(tree.pos)(tree1) - } - - def unbox(tree: Tree, pt: Type): Tree = { - val result = unbox1(tree, pt) - log(s"unboxing ${tree.shortClass}: ${tree.tpe} as a ${result.tpe}") - result + typedPos(tree.pos)(tree1) } /** Unbox `tree` of boxed type to expected type `pt`. @@ -93,27 +67,13 @@ trait TypeAdaptingTransformer { * @param pt the expected type. * @return the unboxed tree */ - private def unbox1(tree: Tree, pt: Type): Tree = tree match { -/* - case Boxed(unboxed) => - println("unbox shorten: "+tree) // this never seems to kick in during build and test; therefore disabled. - adaptToType(unboxed, pt) - */ + final def unbox(tree: Tree, pt: Type): Tree = tree match { case LabelDef(_, _, _) => val ldef = deriveLabelDef(tree)(unbox(_, pt)) ldef setType ldef.rhs.tpe case _ => val tree1 = pt match { - case ErasedValueType(clazz, underlying) => - val tree0 = - if (tree.tpe.typeSymbol == NullClass && - isPrimitiveValueClass(underlying.typeSymbol)) { - // convert `null` directly to underlying type, as going - // via the unboxed type would yield a NPE (see SI-5866) - unbox1(tree, underlying) - } else - Apply(Select(adaptToType(tree, clazz.tpe), clazz.derivedValueClassUnbox), List()) - cast(tree0, pt) + case ErasedValueType(clazz, underlying) => cast(unboxValueClass(tree, clazz, underlying), pt) case _ => pt.typeSymbol match { case UnitClass => @@ -125,21 +85,28 @@ trait TypeAdaptingTransformer { Apply(currentRun.runDefinitions.unboxMethod(pt.typeSymbol), tree) } } - typer.typedPos(tree.pos)(tree1) + typedPos(tree.pos)(tree1) } + final def unboxValueClass(tree: Tree, clazz: Symbol, underlying: Type): Tree = + if (tree.tpe.typeSymbol == NullClass && isPrimitiveValueClass(underlying.typeSymbol)) { + // convert `null` directly to underlying type, as going via the unboxed type would yield a NPE (see SI-5866) + unbox(tree, underlying) + } else + Apply(Select(adaptToType(tree, clazz.tpe), clazz.derivedValueClassUnbox), List()) + /** Generate a synthetic cast operation from tree.tpe to pt. - * @pre pt eq pt.normalize + * + * @pre pt eq pt.normalize */ - def cast(tree: Tree, pt: Type): Tree = { - if ((tree.tpe ne null) && !(tree.tpe =:= ObjectTpe)) { - def word = ( + final def cast(tree: Tree, pt: Type): Tree = { + if (settings.debug && (tree.tpe ne null) && !(tree.tpe =:= ObjectTpe)) { + def word = if (tree.tpe <:< pt) "upcast" else if (pt <:< tree.tpe) "downcast" else if (pt weak_<:< tree.tpe) "coerce" else if (tree.tpe weak_<:< pt) "widen" else "cast" - ) log(s"erasure ${word}s from ${tree.tpe} to $pt") } if (pt =:= UnitTpe) { @@ -160,27 +127,23 @@ trait TypeAdaptingTransformer { * @param pt the expected type * @return the adapted tree */ - def adaptToType(tree: Tree, pt: Type): Tree = { - if (settings.debug && pt != WildcardType) - log("adapting " + tree + ":" + tree.tpe + " : " + tree.tpe.parents + " to " + pt)//debug - if (tree.tpe <:< pt) - tree - else if (isDifferentErasedValueType(tree.tpe, pt)) - adaptToType(box(tree, pt.toString), pt) - else if (isDifferentErasedValueType(pt, tree.tpe)) - adaptToType(unbox(tree, pt), pt) - else if (isPrimitiveValueType(tree.tpe) && !isPrimitiveValueType(pt)) { - adaptToType(box(tree, pt.toString), pt) - } else if (isMethodTypeWithEmptyParams(tree.tpe)) { - // [H] this assert fails when trying to typecheck tree !(SomeClass.this.bitmap) for single lazy val - //assert(tree.symbol.isStable, "adapt "+tree+":"+tree.tpe+" to "+pt) - adaptToType(Apply(tree, List()) setPos tree.pos setType tree.tpe.resultType, pt) -// } else if (pt <:< tree.tpe) -// cast(tree, pt) - } else if (isPrimitiveValueType(pt) && !isPrimitiveValueType(tree.tpe)) - adaptToType(unbox(tree, pt), pt) - else - cast(tree, pt) + @tailrec final def adaptToType(tree: Tree, pt: Type): Tree = { + val tpe = tree.tpe + + if ((tpe eq pt) || tpe <:< pt) tree + else if (tpe.isInstanceOf[ErasedValueType]) adaptToType(box(tree), pt) // what if pt is an erased value type? + else if (pt.isInstanceOf[ErasedValueType]) adaptToType(unbox(tree, pt), pt) + // See corresponding case in `Eraser`'s `adaptMember` + // [H] this does not hold here, however: `assert(tree.symbol.isStable)` (when typechecking !(SomeClass.this.bitmap) for single lazy val) + else if (isMethodTypeWithEmptyParams(tpe)) adaptToType(applyMethodWithEmptyParams(tree), pt) + else { + val gotPrimitiveVC = isPrimitiveValueType(tpe) + val expectedPrimitiveVC = isPrimitiveValueType(pt) + + if (gotPrimitiveVC && !expectedPrimitiveVC) adaptToType(box(tree), pt) + else if (!gotPrimitiveVC && expectedPrimitiveVC) adaptToType(unbox(tree, pt), pt) + else cast(tree, pt) + } } } } |