summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAdriaan Moors <adriaan.moors@typesafe.com>2016-03-16 16:37:59 -0700
committerAdriaan Moors <adriaan.moors@typesafe.com>2016-03-26 22:49:15 -0700
commit1acfc70b038b09018d3ba1cc4b8c968e2a93d33c (patch)
tree02dc96cbe07eb98c94e5ca74631c3f0124be9711
parentad90614c391328bdc9d13795d26f339f67e7f4d5 (diff)
downloadscala-1acfc70b038b09018d3ba1cc4b8c968e2a93d33c.tar.gz
scala-1acfc70b038b09018d3ba1cc4b8c968e2a93d33c.tar.bz2
scala-1acfc70b038b09018d3ba1cc4b8c968e2a93d33c.zip
Set the scene for Sammy.
Go beyond refactoring and introduce some hooks and patch some holes that will become acute when we set Sammy loose. Expanding sam requires class as first parent: `addObjectParent`. (Tested in pos/sam_ctor_arg.scala, coming next.)
-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