Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make FunctionN into functional interfaces #15

Closed
wants to merge 11 commits into from
3 changes: 2 additions & 1 deletion src/compiler/scala/tools/nsc/backend/icode/Members.scala
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ package backend
package icode

import scala.collection.{ mutable, immutable }
import scala.reflect.internal.Flags
import scala.reflect.internal.util.{ SourceFile, NoSourceFile }

trait ReferenceEquality {
Expand Down Expand Up @@ -217,7 +218,7 @@ trait Members {

/** Is this method deferred ('abstract' in Java sense)?
*/
def isAbstractMethod = symbol.isDeferred || symbol.owner.isInterface || native
def isAbstractMethod = symbol.isDeferred || (symbol.owner.isInterface && !symbol.hasFlag(Flags.DEFAULTMETHOD)) || native

def isStatic: Boolean = symbol.isStaticMember

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -635,7 +635,7 @@ abstract class BCodeBodyBuilder extends BCodeSkelBuilder {
case Apply(fun, args) if app.hasAttachment[delambdafy.LambdaMetaFactoryCapable] =>
val attachment = app.attachments.get[delambdafy.LambdaMetaFactoryCapable].get
genLoadArguments(args, paramTKs(app))
genInvokeDynamicLambda(attachment.target, attachment.arity, attachment.functionalInterface)
genInvokeDynamicLambda(attachment.target, attachment.arity, attachment.functionalInterface, attachment.samMethod)
generatedType = asmMethodType(fun.symbol).returnType

case Apply(fun @ _, List(expr)) if currentRun.runDefinitions.isBox(fun.symbol) =>
Expand Down Expand Up @@ -1284,7 +1284,7 @@ abstract class BCodeBodyBuilder extends BCodeSkelBuilder {
def genSynchronized(tree: Apply, expectedType: BType): BType
def genLoadTry(tree: Try): BType

def genInvokeDynamicLambda(lambdaTarget: Symbol, arity: Int, functionalInterface: Symbol) {
def genInvokeDynamicLambda(lambdaTarget: Symbol, arity: Int, functionalInterface: Symbol, samMethod: Symbol) {
val isStaticMethod = lambdaTarget.hasFlag(Flags.STATIC)
def asmType(sym: Symbol) = classBTypeFromSymbol(sym).toASMType

Expand All @@ -1299,9 +1299,8 @@ abstract class BCodeBodyBuilder extends BCodeSkelBuilder {
val invokedType = asm.Type.getMethodDescriptor(asmType(functionalInterface), (receiver ::: capturedParams).map(sym => toTypeKind(sym.info).toASMType): _*)

val constrainedType = new MethodBType(lambdaParams.map(p => toTypeKind(p.tpe)), toTypeKind(lambdaTarget.tpe.resultType)).toASMType
val sam = functionalInterface.info.decls.find(_.isDeferred).getOrElse(functionalInterface.info.member(nme.apply))
val samName = sam.name.toString
val samMethodType = asmMethodType(sam).toASMType
val samName = samMethod.name.toString
val samMethodType = asmMethodType(samMethod).toASMType

val flags = 3 // TODO 2.12.x Replace with LambdaMetafactory.FLAG_SERIALIZABLE | LambdaMetafactory.FLAG_MARKERS

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -587,10 +587,10 @@ abstract class BCodeSkelBuilder extends BCodeHelpers {
}

val isNative = methSymbol.hasAnnotation(definitions.NativeAttr)
val isAbstractMethod = (methSymbol.isDeferred || methSymbol.owner.isInterface)
val isAbstractMethod = (methSymbol.isDeferred || methSymbol.owner.isInterface) && !methSymbol.hasFlag(Flags.DEFAULTMETHOD)
val flags = GenBCode.mkFlags(
javaFlags(methSymbol),
if (claszSymbol.isInterface) asm.Opcodes.ACC_ABSTRACT else 0,
if (isAbstractMethod) asm.Opcodes.ACC_ABSTRACT else 0,
if (methSymbol.isStrictFP) asm.Opcodes.ACC_STRICT else 0,
if (isNative) asm.Opcodes.ACC_NATIVE else 0 // native methods of objects are generated in mirror classes
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -560,7 +560,7 @@ class BTypesFromSymbols[G <: Global](val global: G) extends BTypes {
import asm.Opcodes._
GenBCode.mkFlags(
if (privateFlag) ACC_PRIVATE else ACC_PUBLIC,
if (sym.isDeferred || sym.hasAbstractFlag) ACC_ABSTRACT else 0,
if ((sym.isDeferred && !sym.hasFlag(symtab.Flags.DEFAULTMETHOD))|| sym.hasAbstractFlag) ACC_ABSTRACT else 0,
if (sym.isInterface) ACC_INTERFACE else 0,
if (finalFlag && !sym.hasAbstractFlag) ACC_FINAL else 0,
if (sym.isStaticMember) ACC_STATIC else 0,
Expand Down
3 changes: 2 additions & 1 deletion src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1391,10 +1391,11 @@ abstract class GenASM extends SubComponent with BytecodeWriters { self =>
var resTpe: asm.Type = javaType(m.symbol.tpe.resultType)
if (m.symbol.isClassConstructor)
resTpe = asm.Type.VOID_TYPE
val isAbstractTraitMeth = isJInterface && !m.symbol.hasFlag(Flags.DEFAULTMETHOD)

val flags = mkFlags(
javaFlags(m.symbol),
if (isJInterface) asm.Opcodes.ACC_ABSTRACT else 0,
if (isAbstractTraitMeth) asm.Opcodes.ACC_ABSTRACT else 0,
if (m.symbol.isStrictFP) asm.Opcodes.ACC_STRICT else 0,
if (method.native) asm.Opcodes.ACC_NATIVE else 0, // native methods of objects are generated in mirror classes
if(isDeprecated(m.symbol)) asm.Opcodes.ACC_DEPRECATED else 0 // ASM pseudo access flag
Expand Down
29 changes: 15 additions & 14 deletions src/compiler/scala/tools/nsc/transform/Delambdafy.scala
Original file line number Diff line number Diff line change
Expand Up @@ -379,7 +379,7 @@ abstract class Delambdafy extends Transform with TypingTransformers with ast.Tre
}
val functionType = definitions.functionType(functionParamTypes, functionResultType)

val (functionalInterface, isSpecialized) = java8CompatFunctionalInterface(target, functionType)
val (functionalInterface, samMethod, isSpecialized) = java8CompatFunctionalInterface(target, functionType)
if (functionalInterface.exists) {
// Create a symbol representing a fictional lambda factory method that accepts the captured
// arguments and returns a Function.
Expand Down Expand Up @@ -407,7 +407,7 @@ abstract class Delambdafy extends Transform with TypingTransformers with ast.Tre

// The backend needs to know the target of the lambda and the functional interface in order
// to emit the invokedynamic instruction. We pass this information as tree attachment.
apply.updateAttachment(LambdaMetaFactoryCapable(lambdaTarget, arity, functionalInterface))
apply.updateAttachment(LambdaMetaFactoryCapable(lambdaTarget, arity, functionalInterface, samMethod))
InvokeDynamicLambda(apply)
} else {
val anonymousClassDef = makeAnonymousClass
Expand Down Expand Up @@ -566,24 +566,25 @@ abstract class Delambdafy extends Transform with TypingTransformers with ast.Tre
}
}

final case class LambdaMetaFactoryCapable(target: Symbol, arity: Int, functionalInterface: Symbol)
final case class LambdaMetaFactoryCapable(target: Symbol, arity: Int, functionalInterface: Symbol, samMethod: Symbol)

// The functional interface that can be used to adapt the lambda target method `target` to the
// given function type. Returns `NoSymbol` if the compiler settings are unsuitable.
private def java8CompatFunctionalInterface(target: Symbol, functionType: Type): (Symbol, Boolean) = {
private def java8CompatFunctionalInterface(target: Symbol, functionType: Type): (Symbol, Symbol, Boolean) = {
val canUseLambdaMetafactory = settings.isBCodeActive

val sym = functionType.typeSymbol
val pack = currentRun.runDefinitions.Scala_Java8_CompatPackage
val name1 = specializeTypes.specializedFunctionName(sym, functionType.typeArgs)
val paramTps :+ restpe = functionType.typeArgs
val arity = paramTps.length
val isSpecialized = name1.toTypeName != sym.name
val functionalInterface = if (!isSpecialized) {
currentRun.runDefinitions.Scala_Java8_CompatPackage_JFunction(arity)
val clazz = functionType.typeSymbol
val specializedFunction = specializeTypes.specializedFunctionClass(clazz, functionType.typeArgs)
val isSpecialized = specializedFunction != clazz
val genericApply = clazz.info.member(nme.apply)
val samMethod = if (isSpecialized) {
specializeTypes.specializedOverloaded(genericApply, functionType.typeArgs)
} else {
pack.info.decl(name1.toTypeName.prepend("J"))
genericApply
}
(if (canUseLambdaMetafactory) functionalInterface else NoSymbol, isSpecialized)
val x = if (canUseLambdaMetafactory) {
specializedFunction
} else NoSymbol
(x, samMethod, isSpecialized)
}
}
51 changes: 50 additions & 1 deletion src/compiler/scala/tools/nsc/transform/Mixin.scala
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ package scala.tools.nsc
package transform

import symtab._
import Flags._
import reflect.internal.Flags._
import scala.collection.{ mutable, immutable }

abstract class Mixin extends InfoTransform with ast.TreeDSL {
Expand Down Expand Up @@ -1060,6 +1060,21 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL {
isOverriddenAccessor(other.getterIn(other.owner), clazz.info.baseClasses)
}

if (isFunctionSymbol(clazz) && clazz.isSpecialized) {
val sym = clazz.info.decl(nme.apply)
// <default> def apply(v1: Object)Object = apply(v1.unbox).box
val functionClass = clazz.baseClasses(1)
val genericApply = functionClass.info.member(nme.apply)
val bridge = genericApply.cloneSymbol(clazz, /*BRIDGE |*/ METHOD | DEFAULTMETHOD | DEFERRED).setPos(sym.pos)
addDefDef(bridge,
Apply(gen.mkAttributedSelect(gen.mkAttributedThis(sym.owner), sym), bridge.paramss.head.map(p => gen.mkAttributedIdent(p))))

// <deferred> def apply$mcII$sp(v1: Int)Int
val specializedApply = specializeTypes.specializedOverloaded(genericApply, exitingSpecialize(clazz.info.baseType(functionClass).typeArgs))
val m2 = specializedApply.cloneSymbol(clazz, METHOD | DEFERRED).setPos(sym.pos)
addDefDef(m2.setPos(sym.pos))
}

// for all symbols `sym` in the class definition, which are mixed in:
for (sym <- clazz.info.decls ; if sym hasFlag MIXEDIN) {
// if current class is a trait interface, add an abstract method for accessor `sym`
Expand Down Expand Up @@ -1167,6 +1182,40 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL {
case Apply(TypeApply(Select(qual, _), targ :: _), _) if isCastSymbol(sym) && (qual.tpe <:< targ.tpe) =>
qual

case dd @ DefDef(_, _, _, vparamss, _, EmptyTree) if isFunctionSymbol(sym.owner) =>
val addDefault = enteringPhase(currentRun.erasurePhase.prev)(!sym.isDeferred) && sym.name != nme.toString_ // before lateDEFERRED
if (addDefault) {
def implSym = implClass(sym.owner).info.member(sym.name)
sym.setFlag(Flags.DEFAULTMETHOD)
val tree = Apply(staticRef(implSym), gen.mkAttributedThis(sym.owner) :: sym.paramss.head.map(gen.mkAttributedRef))
val app = typedPos(tree.pos)(tree)
copyDefDef(dd)(rhs = app)
} else if (sym.owner.isSpecialized && sym.name == nme.apply) {
val clazz = sym.owner
val functionClass = clazz.baseClasses(1)
val substitutedApply = clazz.info.decl(nme.apply)
val genericApply = functionClass.info.decl(nme.apply)
val specializedApply = specializeTypes.specializedOverloaded(genericApply, exitingSpecialize(clazz.info.baseType(functionClass).typeArgs))
val app = Apply(gen.mkAttributedSelect(gen.mkAttributedThis(clazz), specializedApply), vparamss.head.map(p => gen.mkAttributedIdent(p.symbol)))
dd.symbol.setFlag(Flags.DEFAULTMETHOD)
copyDefDef(dd)(rhs = typedPos(tree.pos)(app))
} else {
tree
}
case dd @ DefDef(_, _, _, vparamss, _, EmptyTree) if sym.owner.isTrait =>
val isDeferred = enteringPhase(currentRun.erasurePhase.prev)(sym.isDeferred) // before lateDEFERRED
// TODO Duplicated with isImplementedStatically, at this point sym.owner is still the trait so the other method returns false.
val isImplementedStatically = sym.isMethod && !sym.isModule && !sym.hasFlag(ACCESSOR | SUPERACCESSOR)
if (!isDeferred && isImplementedStatically) {
val implSym = exitingMixin(implClass(sym.owner).info.member(sym.name))
sym.setFlag(Flags.DEFAULTMETHOD)
val tree = Apply(staticRef(implSym), gen.mkAttributedCast(gen.mkAttributedThis(sym.owner), sym.owner.typeOfThis) :: vparamss.head.map(t => gen.mkAttributedRef(t.symbol)))
val app = exitingMixin(typedPos(tree.pos)(tree))
copyDefDef(dd)(rhs = app)
} else {
tree
}

case Apply(Select(qual, _), args) =>
/* Changes `qual.m(args)` where m refers to an implementation
* class method to Q.m(S, args) where Q is the implementation module of
Expand Down
14 changes: 7 additions & 7 deletions src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala
Original file line number Diff line number Diff line change
Expand Up @@ -303,15 +303,15 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers {
}
}

def specializedFunctionName(sym: Symbol, args: List[Type]) = exitingSpecialize {
def specializedFunctionClass(sym: Symbol, args: List[Type]): Symbol = exitingSpecialize {
require(isFunctionSymbol(sym), sym)
val env: TypeEnv = TypeEnv.fromSpecialization(sym, args)
specializedClass.get((sym, env)) match {
case Some(x) =>
x.name
case None =>
sym.name
}
specializedClass.getOrElse((sym, env), sym)
}

def specializedOverloaded(sym: Symbol, args: List[Type]): Symbol = exitingSpecialize {
val env: TypeEnv = TypeEnv.fromSpecialization(sym.owner, args)
overloads(sym).find(_.matchesEnv(env)).map(_.sym).getOrElse(NoSymbol)
}

/** Return the specialized name of 'sym' in the given environment. It
Expand Down
53 changes: 29 additions & 24 deletions src/compiler/scala/tools/nsc/typechecker/Contexts.scala
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@ trait Contexts { self: Analyzer =>

object NoContext
extends Context(EmptyTree, NoSymbol, EmptyScope, NoCompilationUnit,
null) { // We can't pass the uninitialized `this`. Instead, we treat null specially in `Context#outer`
// We can't pass the uninitialized `this`. Instead, we treat null specially in `Context#outer`
null) {
enclClass = this
enclMethod = this

Expand All @@ -48,12 +49,11 @@ trait Contexts { self: Analyzer =>
def ambiguousDefnAndImport(owner: Symbol, imp: ImportInfo) =
LookupAmbiguous(s"it is both defined in $owner and imported subsequently by \n$imp")

private lazy val startContext = {
NoContext.make(
private lazy val startContext = NoContext.make(
Template(List(), noSelfType, List()) setSymbol global.NoSymbol setType global.NoType,
rootMirror.RootClass,
rootMirror.RootClass.info.decls)
}
rootMirror.RootClass.info.decls
)

private lazy val allUsedSelectors =
mutable.Map[ImportInfo, Set[ImportSelector]]() withDefaultValue Set()
Expand Down Expand Up @@ -802,6 +802,14 @@ trait Contexts { self: Analyzer =>
(e ne null) && (e.owner == scope) && (!settings.isScala212 || e.sym.exists)
})

/** Do something with the symbols with name `name` imported via the import in `imp`,
* if any such symbol is accessible from this context and is a qualifying implicit.
*/
private def withQualifyingImplicitAlternatives(imp: ImportInfo, name: Name, pre: Type)(f: Symbol => Unit) = for {
sym <- importedAccessibleSymbol(imp, name, requireExplicit = false, record = false).alternatives
if isQualifyingImplicit(name, sym, pre, imported = true)
} f(sym)

private def collectImplicits(syms: Scope, pre: Type, imported: Boolean = false): List[ImplicitInfo] =
for (sym <- syms.toList if isQualifyingImplicit(sym.name, sym, pre, imported)) yield
new ImplicitInfo(sym.name, pre, sym)
Expand All @@ -819,9 +827,9 @@ trait Contexts { self: Analyzer =>
case ImportSelector(from, _, to, _) :: sels1 =>
var impls = collect(sels1) filter (info => info.name != from)
if (to != nme.WILDCARD) {
for (sym <- importedAccessibleSymbol(imp, to).alternatives)
if (isQualifyingImplicit(to, sym, pre, imported = true))
impls = new ImplicitInfo(to, pre, sym) :: impls
withQualifyingImplicitAlternatives(imp, to, pre) { sym =>
impls = new ImplicitInfo(to, pre, sym) :: impls
}
}
impls
}
Expand Down Expand Up @@ -946,11 +954,8 @@ trait Contexts { self: Analyzer =>
/** The symbol with name `name` imported via the import in `imp`,
* if any such symbol is accessible from this context.
*/
def importedAccessibleSymbol(imp: ImportInfo, name: Name): Symbol =
importedAccessibleSymbol(imp, name, requireExplicit = false)

private def importedAccessibleSymbol(imp: ImportInfo, name: Name, requireExplicit: Boolean): Symbol =
imp.importedSymbol(name, requireExplicit) filter (s => isAccessible(s, imp.qual.tpe, superAccess = false))
private def importedAccessibleSymbol(imp: ImportInfo, name: Name, requireExplicit: Boolean, record: Boolean): Symbol =
imp.importedSymbol(name, requireExplicit, record) filter (s => isAccessible(s, imp.qual.tpe, superAccess = false))

private def requiresQualifier(s: Symbol) = (
s.owner.isClass
Expand Down Expand Up @@ -1057,7 +1062,7 @@ trait Contexts { self: Analyzer =>
def imp2Explicit = imp2 isExplicitImport name

def lookupImport(imp: ImportInfo, requireExplicit: Boolean) =
importedAccessibleSymbol(imp, name, requireExplicit) filter qualifies
importedAccessibleSymbol(imp, name, requireExplicit, record = true) filter qualifies

// Java: A single-type-import declaration d in a compilation unit c of package p
// that imports a type named n shadows, throughout c, the declarations of:
Expand Down Expand Up @@ -1353,7 +1358,6 @@ trait Contexts { self: Analyzer =>
protected def handleError(pos: Position, msg: String): Unit = onTreeCheckerError(pos, msg)
}


class ImportInfo(val tree: Import, val depth: Int) {
def pos = tree.pos
def posOf(sel: ImportSelector) = tree.pos withPoint sel.namePos
Expand All @@ -1369,19 +1373,20 @@ trait Contexts { self: Analyzer =>
def isExplicitImport(name: Name): Boolean =
tree.selectors exists (_.rename == name.toTermName)

/** The symbol with name `name` imported from import clause `tree`.
*/
def importedSymbol(name: Name): Symbol = importedSymbol(name, requireExplicit = false)
/** The symbol with name `name` imported from import clause `tree`. */
def importedSymbol(name: Name): Symbol = importedSymbol(name, requireExplicit = false, record = true)

private def recordUsage(sel: ImportSelector, result: Symbol) {
def posstr = pos.source.file.name + ":" + posOf(sel).line
def resstr = if (tree.symbol.hasCompleteInfo) s"(qual=$qual, $result)" else s"(expr=${tree.expr}, ${result.fullLocationString})"
debuglog(s"In $this at $posstr, selector '${selectorString(sel)}' resolved to $resstr")
private def recordUsage(sel: ImportSelector, result: Symbol): Unit = {
debuglog(s"In $this at ${ pos.source.file.name }:${ posOf(sel).line }, selector '${ selectorString(sel)
}' resolved to ${
if (tree.symbol.hasCompleteInfo) s"(qual=$qual, $result)"
else s"(expr=${tree.expr}, ${result.fullLocationString})"
}")
allUsedSelectors(this) += sel
}

/** If requireExplicit is true, wildcard imports are not considered. */
def importedSymbol(name: Name, requireExplicit: Boolean): Symbol = {
def importedSymbol(name: Name, requireExplicit: Boolean, record: Boolean): Symbol = {
var result: Symbol = NoSymbol
var renamed = false
var selectors = tree.selectors
Expand All @@ -1398,7 +1403,7 @@ trait Contexts { self: Analyzer =>
if (result == NoSymbol)
selectors = selectors.tail
}
if (settings.warnUnusedImport && selectors.nonEmpty && result != NoSymbol && pos != NoPosition)
if (record && settings.warnUnusedImport && selectors.nonEmpty && result != NoSymbol && pos != NoPosition)
recordUsage(current, result)

// Harden against the fallout from bugs like SI-6745
Expand Down
2 changes: 1 addition & 1 deletion src/compiler/scala/tools/nsc/typechecker/Implicits.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1408,7 +1408,7 @@ trait Implicits {
}

if (result.isFailure && settings.debug) // debuglog is not inlined for some reason
log("no implicits found for "+pt+" "+pt.typeSymbol.info.baseClasses+" "+implicitsOfExpectedType)
log(s"no implicits found for ${pt} ${pt.typeSymbol.info.baseClasses} ${implicitsOfExpectedType}")

result
}
Expand Down
Loading