summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorAdriaan Moors <adriaan.moors@typesafe.com>2016-03-23 10:52:57 -0700
committerAdriaan Moors <adriaan.moors@typesafe.com>2016-03-26 22:55:10 -0700
commit608ac2c2b9e3f6f46489e20830d8949ee7d506cf (patch)
tree8d995139a8a66f56fed90bf65f7ece5bf26d55d7 /src
parent878e20a5243383300d3b4990146d260409bf5dfd (diff)
downloadscala-608ac2c2b9e3f6f46489e20830d8949ee7d506cf.tar.gz
scala-608ac2c2b9e3f6f46489e20830d8949ee7d506cf.tar.bz2
scala-608ac2c2b9e3f6f46489e20830d8949ee7d506cf.zip
Soften sam restrictions
Some of the earlier proposals were too strongly linked to the requirements of the Java 8 platform, which was problematic for scala.js & friends. Instead of ruling out SAM types that we can't compile to use LambdaMetaFactory, expand those during compilation to anonymous subclasses, instead of invokedynamic + LMF. Also, self types rear their ugly heads again. Align `hasSelfType` with the implementation suggested in `thisSym`'s docs.
Diffstat (limited to 'src')
-rw-r--r--src/compiler/scala/tools/nsc/transform/Delambdafy.scala2
-rw-r--r--src/compiler/scala/tools/nsc/transform/UnCurry.scala27
-rw-r--r--src/reflect/scala/reflect/internal/Definitions.scala24
-rw-r--r--src/reflect/scala/reflect/internal/Symbols.scala2
4 files changed, 36 insertions, 19 deletions
diff --git a/src/compiler/scala/tools/nsc/transform/Delambdafy.scala b/src/compiler/scala/tools/nsc/transform/Delambdafy.scala
index 32ab52203e..7ccaec2f50 100644
--- a/src/compiler/scala/tools/nsc/transform/Delambdafy.scala
+++ b/src/compiler/scala/tools/nsc/transform/Delambdafy.scala
@@ -239,7 +239,7 @@ abstract class Delambdafy extends Transform with TypingTransformers with ast.Tre
exitingErasure(target.info.paramTypes).map(reboxValueClass) :+ reboxValueClass(exitingErasure(target.info.resultType))).toTypeName
val isSpecialized = specializedName != funSym.name
- val functionalInterface =
+ val functionalInterface = // TODO: this is no longer needed, right? we can just use the regular function classes
if (isSpecialized) currentRun.runDefinitions.Scala_Java8_CompatPackage.info.decl(specializedName.prepend("J"))
else currentRun.runDefinitions.Scala_Java8_CompatPackage_JFunction(originalFunction.vparams.length)
diff --git a/src/compiler/scala/tools/nsc/transform/UnCurry.scala b/src/compiler/scala/tools/nsc/transform/UnCurry.scala
index 7e9e0e2a92..afa0fc92ff 100644
--- a/src/compiler/scala/tools/nsc/transform/UnCurry.scala
+++ b/src/compiler/scala/tools/nsc/transform/UnCurry.scala
@@ -74,7 +74,30 @@ abstract class UnCurry extends InfoTransform
private val newMembers = mutable.Map[Symbol, mutable.Buffer[Tree]]()
// Expand `Function`s in constructors to class instance creation (SI-6666, SI-8363)
- private def mustExpandFunction = forceExpandFunction || inConstructorFlag != 0
+ // We use Java's LambdaMetaFactory (LMF), which requires an interface for the sam's owner
+ private def mustExpandFunction(fun: Function) = forceExpandFunction || {
+ // (TODO: Can't use isInterface, yet, as it hasn't been updated for the new trait encoding)
+ val canUseLambdaMetaFactory = inConstructorFlag == 0 && (fun.attachments.get[SAMFunction].map(_.samTp) match {
+ case Some(userDefinedSamTp) =>
+ val tpSym = erasure.javaErasure(userDefinedSamTp).typeSymbol // we only care about what ends up in the bytecode
+ (
+ // LMF only supports interfaces
+ (tpSym.isJavaInterface || tpSym.isTrait)
+ // Unless tpSym.isStatic, even if the constructor is zero-argument now, it may acquire arguments in explicit outer or lambdalift.
+ // This is an impl restriction to simplify the decision of whether to expand the SAM during uncurry
+ // (when we don't yet know whether it will receive an outer pointer in explicit outer or whether lambda lift will add proxies for captures).
+ // When we delay sam expansion until after explicit outer & lambda lift, we could decide there whether
+ // to expand sam at compile time or use LMF, and this implementation restriction could be lifted.
+ && tpSym.isStatic
+ // impl restriction -- we currently use the boxed apply, so not really useful to allow specialized sam types (https://github.com/scala/scala/pull/4971#issuecomment-198119167)
+ && !tpSym.isSpecialized
+ )
+
+ case _ => true // our built-in FunctionN's are suitable for LambdaMetaFactory by construction
+ })
+
+ !canUseLambdaMetaFactory
+ }
/** Add a new synthetic member for `currentOwner` */
private def addNewMember(t: Tree): Unit =
@@ -191,7 +214,7 @@ abstract class UnCurry extends InfoTransform
def transformFunction(fun: Function): Tree =
// Undo eta expansion for parameterless and nullary methods
if (fun.vparams.isEmpty && isByNameRef(fun.body)) { noApply += fun.body ; fun.body }
- else if (mustExpandFunction) gen.expandFunction(localTyper)(fun, inConstructorFlag)
+ else if (mustExpandFunction(fun)) gen.expandFunction(localTyper)(fun, inConstructorFlag)
else {
// method definition with the same arguments, return type, and body as the original lambda
val liftedMethod = gen.mkLiftedFunctionBodyMethod(localTyper)(fun.symbol.owner, fun)
diff --git a/src/reflect/scala/reflect/internal/Definitions.scala b/src/reflect/scala/reflect/internal/Definitions.scala
index ef9a76f9c4..81071e763d 100644
--- a/src/reflect/scala/reflect/internal/Definitions.scala
+++ b/src/reflect/scala/reflect/internal/Definitions.scala
@@ -848,21 +848,15 @@ trait Definitions extends api.StandardDefinitions {
// (e.g., an alias type or intersection type is fine as long as the intersection dominator compiles to an interface)
val tpSym = erasure.javaErasure(tp).typeSymbol
- if (tpSym.exists
- // We use Java's MetaLambdaFactory, which requires an interface for the sam's owner
- // (TODO: Can't use isInterface, yet, as it hasn't been updated for the new trait encoding)
- && (tpSym.isJavaInterface || tpSym.isTrait)
- // explicit outer precludes no-arg ctor
- && tpSym.isStatic
- // impl restriction -- we currently use the boxed apply, so not really useful to allow specialized sam types (https://github.com/scala/scala/pull/4971#issuecomment-198119167)
- && !tpSym.isSpecialized) {
-
- // this does not apply yet, since traits don't have constructors during type checking
- // if tp has a constructor, it must be public and must not take any arguments
- // (not even an implicit argument list -- to keep it simple for now)
- // && { val ctor = tpSym.primaryConstructor
- // !ctor.exists || (!ctor.isOverloaded && ctor.isPublic && ctor.info.params.isEmpty && ctor.info.paramSectionCount <= 1)
- // }
+ if (tpSym.exists && tpSym.isClass
+ // if tp has a constructor (its class is not a trait), it must be public and must not take any arguments
+ // (implementation restriction: implicit argument lists are excluded to simplify type inference in adaptToSAM)
+ && { val ctor = tpSym.primaryConstructor
+ !ctor.exists || (!ctor.isOverloaded && ctor.isPublic && ctor.info.params.isEmpty && ctor.info.paramSectionCount <= 1)}
+ // we won't be able to create an instance of tp if it doesn't correspond to its self type
+ // (checking conformance gets complicated when tp is not fully defined, so let's just rule out self types entirely)
+ && !tpSym.hasSelfType
+ ) {
// find the single abstract member, if there is one
// don't go out requiring DEFERRED members, as you will get them even if there's a concrete override:
diff --git a/src/reflect/scala/reflect/internal/Symbols.scala b/src/reflect/scala/reflect/internal/Symbols.scala
index 0cbb45d12d..ee763df849 100644
--- a/src/reflect/scala/reflect/internal/Symbols.scala
+++ b/src/reflect/scala/reflect/internal/Symbols.scala
@@ -2000,7 +2000,7 @@ trait Symbols extends api.Symbols { self: SymbolTable =>
*/
def thisSym: Symbol = this
- def hasSelfType = thisSym.tpeHK != this.tpeHK
+ def hasSelfType = (thisSym ne this) && (typeOfThis.typeConstructor ne typeConstructor)
/** The type of `this` in a class, or else the type of the symbol itself. */
def typeOfThis = thisSym.tpe_*