diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/compiler/scala/tools/nsc/transform/Erasure.scala | 32 | ||||
-rw-r--r-- | src/compiler/scala/tools/nsc/transform/UnCurry.scala | 21 |
2 files changed, 38 insertions, 15 deletions
diff --git a/src/compiler/scala/tools/nsc/transform/Erasure.scala b/src/compiler/scala/tools/nsc/transform/Erasure.scala index 2df4265573..cdf3e18b5a 100644 --- a/src/compiler/scala/tools/nsc/transform/Erasure.scala +++ b/src/compiler/scala/tools/nsc/transform/Erasure.scala @@ -1187,5 +1187,37 @@ abstract class Erasure extends AddInterfaces bridge.resetFlag(BRIDGE) } + /** Does this symbol compile to the underlying platform's notion of an interface, + * without requiring compiler magic before it can be instantiated? + * + * More specifically, we're interested in whether LambdaMetaFactory can instantiate this type, + * assuming it has a single abstract method. In other words, if we were to mix this + * trait into a class, it should not result in any compiler-generated members having to be + * implemented in ("mixed in to") this class (except for the SAM). + * + * Thus, the type must erase to a java interface, either by virtue of being defined as one, + * or by being a trait that: + * - is static (explicitouter or lambdalift may add disqualifying members) + * - extends only other traits that compile to pure interfaces (except for Any) + * - has no val/var members + * + * TODO: can we speed this up using the INTERFACE flag, or set it correctly by construction? + */ + final def compilesToPureInterface(tpSym: Symbol): Boolean = { + def ok(sym: Symbol) = + sym.isJavaInterface || + sym.isTrait && + // Unless sym.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. + sym.isStatic && + (sym.isInterface || sym.info.decls.forall(mem => mem.isMethod || mem.isType)) // TODO OPT: && {sym setFlag INTERFACE; true}) + + // we still need to check our ancestors even if the INTERFACE flag is set, as it doesn't take inheritance into account + ok(tpSym) && tpSym.ancestors.forall(sym => (sym eq AnyClass) || (sym eq ObjectClass) || ok(sym)) + } + private class TypeRefAttachment(val tpe: TypeRef) } diff --git a/src/compiler/scala/tools/nsc/transform/UnCurry.scala b/src/compiler/scala/tools/nsc/transform/UnCurry.scala index 9c4b125fc1..50e413fba2 100644 --- a/src/compiler/scala/tools/nsc/transform/UnCurry.scala +++ b/src/compiler/scala/tools/nsc/transform/UnCurry.scala @@ -79,21 +79,12 @@ abstract class UnCurry extends InfoTransform // (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] match { case Some(SAMFunction(userDefinedSamTp, sam)) => - 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) - // specialization and LMF are at odds, since LMF implements the single abstract method, - // but that's the one that specialization leaves generic, whereas we need to implement the specialized one to avoid boxing - && !specializeTypes.isSpecializedIn(sam, userDefinedSamTp) - ) + // LambdaMetaFactory cannot mix in trait members for us, or instantiate classes -- only pure interfaces need apply + erasure.compilesToPureInterface(erasure.javaErasure(userDefinedSamTp).typeSymbol) && + // 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) + // specialization and LMF are at odds, since LMF implements the single abstract method, + // but that's the one that specialization leaves generic, whereas we need to implement the specialized one to avoid boxing + !specializeTypes.isSpecializedIn(sam, userDefinedSamTp) case _ => true // our built-in FunctionN's are suitable for LambdaMetaFactory by construction }) |