diff options
author | Adriaan Moors <adriaan.moors@typesafe.com> | 2016-03-23 10:52:57 -0700 |
---|---|---|
committer | Adriaan Moors <adriaan.moors@typesafe.com> | 2016-03-26 22:55:10 -0700 |
commit | 608ac2c2b9e3f6f46489e20830d8949ee7d506cf (patch) | |
tree | 8d995139a8a66f56fed90bf65f7ece5bf26d55d7 /src | |
parent | 878e20a5243383300d3b4990146d260409bf5dfd (diff) | |
download | scala-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')
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_* |