summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/compiler/scala/tools/nsc/ast/TreeGen.scala8
-rw-r--r--src/compiler/scala/tools/nsc/transform/Delambdafy.scala2
-rw-r--r--src/compiler/scala/tools/nsc/transform/Erasure.scala7
-rw-r--r--src/compiler/scala/tools/nsc/transform/TypeAdaptingTransformer.scala103
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Infer.scala4
-rw-r--r--src/reflect/scala/reflect/internal/Definitions.scala18
-rw-r--r--src/reflect/scala/reflect/internal/Types.scala5
7 files changed, 71 insertions, 76 deletions
diff --git a/src/compiler/scala/tools/nsc/ast/TreeGen.scala b/src/compiler/scala/tools/nsc/ast/TreeGen.scala
index 7b0216a6d7..b2a54e16d3 100644
--- a/src/compiler/scala/tools/nsc/ast/TreeGen.scala
+++ b/src/compiler/scala/tools/nsc/ast/TreeGen.scala
@@ -300,7 +300,7 @@ abstract class TreeGen extends scala.reflect.internal.TreeGen with TreeDSL {
abstractFunctionType(fun.vparams.map(_.symbol.tpe), fun.tpe.typeArgs.last.deconst) // TODO: use `fun.body.tpe.deconst` -- preserving wrong status quo because refactoring-only
def expandFunction(localTyper: analyzer.Typer)(fun: Function, inConstructorFlag: Long): Tree = {
- val parents = addSerializable(functionClassType(fun))
+ val parents = addObjectParent(addSerializable(functionClassType(fun)))
val anonClass = fun.symbol.owner newAnonymousFunctionClass(fun.pos, inConstructorFlag) addAnnotation SerialVersionUIDAnnotation
// The original owner is used in the backend for the EnclosingMethod attribute. If fun is
@@ -309,12 +309,12 @@ abstract class TreeGen extends scala.reflect.internal.TreeGen with TreeDSL {
defineOriginalOwner(anonClass, fun.symbol.originalOwner)
anonClass setInfo ClassInfoType(parents, newScope, anonClass)
- val applyMethodDef = mkMethodFromFunction(localTyper)(anonClass, fun)
- anonClass.info.decls enter applyMethodDef.symbol
+ val samDef = mkMethodFromFunction(localTyper)(anonClass, fun)
+ anonClass.info.decls enter samDef.symbol
localTyper.typedPos(fun.pos) {
Block(
- ClassDef(anonClass, NoMods, ListOfNil, List(applyMethodDef), fun.pos),
+ ClassDef(anonClass, NoMods, ListOfNil, List(samDef), fun.pos),
Typed(New(anonClass.tpe), TypeTree(fun.tpe)))
}
}
diff --git a/src/compiler/scala/tools/nsc/transform/Delambdafy.scala b/src/compiler/scala/tools/nsc/transform/Delambdafy.scala
index 43d24be4d5..254bf81999 100644
--- a/src/compiler/scala/tools/nsc/transform/Delambdafy.scala
+++ b/src/compiler/scala/tools/nsc/transform/Delambdafy.scala
@@ -145,7 +145,7 @@ abstract class Delambdafy extends Transform with TypingTransformers with ast.Tre
gen.mkMethodCall(newTarget, args)
}
val body1 = if (enteringErasure(functionResultType.typeSymbol.isDerivedValueClass))
- adaptToType(box(body.setType(ErasedValueType(functionResultType.typeSymbol, body.tpe)), "boxing lambda target"), bridgeResultType)
+ adaptToType(box(body.setType(ErasedValueType(functionResultType.typeSymbol, body.tpe))), bridgeResultType)
else adaptToType(body, bridgeResultType)
val methDef0 = DefDef(methSym, List(bridgeParamTrees), body1)
val bridge = postErasure.newTransformer(unit).transform(methDef0).asInstanceOf[DefDef]
diff --git a/src/compiler/scala/tools/nsc/transform/Erasure.scala b/src/compiler/scala/tools/nsc/transform/Erasure.scala
index 41f22e5669..58be6b53a0 100644
--- a/src/compiler/scala/tools/nsc/transform/Erasure.scala
+++ b/src/compiler/scala/tools/nsc/transform/Erasure.scala
@@ -647,7 +647,7 @@ abstract class Erasure extends AddInterfaces
var qual1 = typedQualifier(qual)
if ((isPrimitiveValueType(qual1.tpe) && !isPrimitiveValueMember(tree.symbol)) ||
isErasedValueType(qual1.tpe))
- qual1 = box(qual1, "owner "+tree.symbol.owner)
+ qual1 = box(qual1)
else if (!isPrimitiveValueType(qual1.tpe) && isPrimitiveValueMember(tree.symbol))
qual1 = unbox(qual1, tree.symbol.owner.tpe)
@@ -656,10 +656,9 @@ abstract class Erasure extends AddInterfaces
if (isPrimitiveValueMember(tree.symbol) && !isPrimitiveValueType(qual1.tpe)) {
tree.symbol = NoSymbol
selectFrom(qual1)
- } else if (isMethodTypeWithEmptyParams(qual1.tpe)) {
+ } else if (isMethodTypeWithEmptyParams(qual1.tpe)) { // see also adaptToType in TypeAdapter
assert(qual1.symbol.isStable, qual1.symbol)
- val applied = Apply(qual1, List()) setPos qual1.pos setType qual1.tpe.resultType
- adaptMember(selectFrom(applied))
+ adaptMember(selectFrom(applyMethodWithEmptyParams(qual1)))
} else if (!(qual1.isInstanceOf[Super] || (qual1.tpe.typeSymbol isSubClass tree.symbol.owner))) {
assert(tree.symbol.owner != ArrayClass)
selectFrom(cast(qual1, tree.symbol.owner.tpe.resultType))
diff --git a/src/compiler/scala/tools/nsc/transform/TypeAdaptingTransformer.scala b/src/compiler/scala/tools/nsc/transform/TypeAdaptingTransformer.scala
index 1ed728247b..246d9ebf3f 100644
--- a/src/compiler/scala/tools/nsc/transform/TypeAdaptingTransformer.scala
+++ b/src/compiler/scala/tools/nsc/transform/TypeAdaptingTransformer.scala
@@ -1,6 +1,7 @@
package scala.tools.nsc
package transform
+import scala.annotation.tailrec
import scala.tools.nsc.ast.TreeDSL
/**
@@ -17,11 +18,6 @@ trait TypeAdaptingTransformer {
import definitions._
import CODE._
- def isMethodTypeWithEmptyParams(tpe: Type) = tpe match {
- case MethodType(Nil, _) => true
- case _ => false
- }
-
private def isSafelyRemovableUnbox(fn: Tree, arg: Tree): Boolean = {
currentRun.runDefinitions.isUnbox(fn.symbol) && {
val cls = arg.tpe.typeSymbol
@@ -30,25 +26,14 @@ trait TypeAdaptingTransformer {
}
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)
-
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
- }
+ def isMethodTypeWithEmptyParams(tpe: Type) = tpe.isInstanceOf[MethodType] && tpe.params.isEmpty
+ def applyMethodWithEmptyParams(qual: Tree) = Apply(qual, List()) setPos qual.pos setType qual.tpe.resultType
/** Box `tree` of unboxed type */
- private def box1(tree: Tree): Tree = tree match {
+ 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 {
@@ -80,39 +65,19 @@ trait TypeAdaptingTransformer {
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
- }
-
/** Unbox `tree` of boxed type to expected type `pt`.
*
* @param tree the given tree
* @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)
- */
+ 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 =>
@@ -127,11 +92,19 @@ trait TypeAdaptingTransformer {
typer.typedPos(tree.pos)(tree1)
}
+ 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)) {
+ 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"
@@ -159,27 +132,25 @@ 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 if (samMatchingFunction(tree, pt).exists) {
+ tree setType pt
+ } else cast(tree, pt)
+ }
}
}
}
diff --git a/src/compiler/scala/tools/nsc/typechecker/Infer.scala b/src/compiler/scala/tools/nsc/typechecker/Infer.scala
index 84cf3c6475..9b42841eb8 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Infer.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Infer.scala
@@ -164,7 +164,9 @@ trait Infer extends Checkable {
| was: $restpe
| now""")(normalize(restpe))
case mt @ MethodType(_, restpe) if mt.isImplicit => normalize(restpe)
- case mt @ MethodType(_, restpe) if !mt.isDependentMethodType => functionType(mt.paramTypes, normalize(restpe))
+ case mt @ MethodType(_, restpe) if !mt.isDependentMethodType =>
+ if (phase.erasedTypes) FunctionClass(mt.params.length).tpe
+ else functionType(mt.paramTypes, normalize(restpe))
case NullaryMethodType(restpe) => normalize(restpe)
case ExistentialType(tparams, qtpe) => newExistentialType(tparams, normalize(qtpe))
case _ => tp // @MAT aliases already handled by subtyping
diff --git a/src/reflect/scala/reflect/internal/Definitions.scala b/src/reflect/scala/reflect/internal/Definitions.scala
index 9a212fcbcb..2fa39bc453 100644
--- a/src/reflect/scala/reflect/internal/Definitions.scala
+++ b/src/reflect/scala/reflect/internal/Definitions.scala
@@ -686,11 +686,29 @@ trait Definitions extends api.StandardDefinitions {
}
}
+ // the result type of a function or corresponding SAM type
+ def functionResultType(tp: Type): Type = {
+ val dealiased = tp.dealiasWiden
+ if (isFunctionTypeDirect(dealiased)) dealiased.typeArgs.last
+ else samOf(tp) match {
+ case samSym if samSym.exists => tp.memberInfo(samSym).resultType.deconst
+ case _ => NoType
+ }
+ }
+
// the SAM's parameters and the Function's formals must have the same length
// (varargs etc don't come into play, as we're comparing signatures, not checking an application)
def samMatchesFunctionBasedOnArity(sam: Symbol, formals: List[Any]): Boolean =
sam.exists && sameLength(sam.info.params, formals)
+ def samMatchingFunction(tree: Tree, pt: Type): Symbol = {
+ if (tree.isInstanceOf[Function] && !isFunctionType(pt)) {
+ val sam = samOf(pt)
+ if (samMatchesFunctionBasedOnArity(sam, tree.asInstanceOf[Function].vparams)) sam
+ else NoSymbol
+ } else NoSymbol
+ }
+
def isTupleType(tp: Type) = isTupleTypeDirect(tp.dealiasWiden)
def tupleComponents(tp: Type) = tp.dealiasWiden.typeArgs
diff --git a/src/reflect/scala/reflect/internal/Types.scala b/src/reflect/scala/reflect/internal/Types.scala
index f385ca08c9..00df55f044 100644
--- a/src/reflect/scala/reflect/internal/Types.scala
+++ b/src/reflect/scala/reflect/internal/Types.scala
@@ -4553,6 +4553,11 @@ trait Types
else (ps :+ SerializableTpe).toList
)
+ def addObjectParent(tps: List[Type]) = tps match {
+ case hd :: _ if hd.typeSymbol.isTrait => ObjectTpe :: tps
+ case _ => tps
+ }
+
/** Adds the @uncheckedBound annotation if the given `tp` has type arguments */
final def uncheckedBounds(tp: Type): Type = {
if (tp.typeArgs.isEmpty || UncheckedBoundsClass == NoSymbol) tp // second condition for backwards compatibility with older scala-reflect.jar