Skip to content

Clean up scala.runtime.function, refactor closure adaptation #10920

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

Merged
merged 5 commits into from
Dec 26, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 4 additions & 14 deletions compiler/src/dotty/tools/backend/jvm/BCodeBodyBuilder.scala
Original file line number Diff line number Diff line change
Expand Up @@ -318,20 +318,10 @@ trait BCodeBodyBuilder extends BCodeSkelBuilder {
abort(s"Unexpected New(${tpt.tpe.showSummary()}/$tpt) reached GenBCode.\n" +
" Call was genLoad" + ((tree, expectedType)))

case app: Closure =>
val env: List[Tree] = app.env
val call: Tree = app.meth
val functionalInterface: Symbol = {
val t = app.tpt.tpe.typeSymbol
if (t.exists) t
else {
val arity = app.meth.tpe.widenDealias.firstParamTypes.size - env.size
val returnsUnit = app.meth.tpe.widenDealias.resultType.classSymbol == defn.UnitClass
if (returnsUnit) requiredClass(("scala.runtime.function.JProcedure" + arity))
else if (arity <= 2) requiredClass(("scala.runtime.function.JFunction" + arity))
else requiredClass(("scala.Function" + arity))
}
}
case t @ Closure(env, call, tpt) =>
val functionalInterface: Symbol =
if !tpt.isEmpty then tpt.tpe.classSymbol
else t.tpe.classSymbol
val (fun, args) = call match {
case Apply(fun, args) => (fun, args)
case t @ DesugaredSelect(_, _) => (t, Nil) // TODO: use Select
Expand Down
4 changes: 2 additions & 2 deletions compiler/src/dotty/tools/backend/sjs/JSCodeGen.scala
Original file line number Diff line number Diff line change
Expand Up @@ -4135,7 +4135,7 @@ class JSCodeGen()(using genCtx: Context) {
}

private def computeJSNativeLoadSpecOfValDef(sym: Symbol): js.JSNativeLoadSpec = {
atPhase(picklerPhase.next) {
atPhaseBeforeTransforms {
computeJSNativeLoadSpecOfInPhase(sym)
}
}
Expand All @@ -4144,7 +4144,7 @@ class JSCodeGen()(using genCtx: Context) {
if (sym.is(Trait) || sym.hasAnnotation(jsdefn.JSGlobalScopeAnnot)) {
None
} else {
atPhase(picklerPhase.next) {
atPhaseBeforeTransforms {
if (sym.owner.isStaticOwner)
Some(computeJSNativeLoadSpecOfInPhase(sym))
else
Expand Down
1 change: 0 additions & 1 deletion compiler/src/dotty/tools/dotc/Compiler.scala
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,6 @@ class Compiler {
new CapturedVars) :: // Represent vars captured by closures as heap objects
List(new Constructors, // Collect initialization code in primary constructors
// Note: constructors changes decls in transformTemplate, no InfoTransformers should be added after it
new FunctionalInterfaces, // Rewrites closures to implement @specialized types of Functions.
new Instrumentation) :: // Count calls and allocations under -Yinstrument
List(new LambdaLift, // Lifts out nested functions to class scope, storing free variables in environments
// Note: in this mini-phase block scopes are incorrect. No phases that rely on scopes should be here
Expand Down
6 changes: 3 additions & 3 deletions compiler/src/dotty/tools/dotc/core/Annotations.scala
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ object Annotations {
mySym match {
case symFn: (Context ?=> Symbol) @unchecked =>
mySym = null
mySym = atPhaseNoLater(picklerPhase)(symFn)
mySym = atPhaseBeforeTransforms(symFn)
// We should always produce the same annotation tree, no matter when the
// annotation is evaluated. Setting the phase to a pre-transformation phase
// seems to be enough to ensure this (note that after erasure, `ctx.typer`
Expand All @@ -79,7 +79,7 @@ object Annotations {
myTree match {
case treeFn: (Context ?=> Tree) @unchecked =>
myTree = null
myTree = atPhaseNoLater(picklerPhase)(treeFn)
myTree = atPhaseBeforeTransforms(treeFn)
case _ =>
}
myTree.asInstanceOf[Tree]
Expand Down Expand Up @@ -112,7 +112,7 @@ object Annotations {
myTree match {
case treeFn: (Context ?=> Tree) @unchecked =>
myTree = null
myTree = atPhaseNoLater(picklerPhase)(treeFn)
myTree = atPhaseBeforeTransforms(treeFn)
case _ =>
}
myTree.asInstanceOf[Tree]
Expand Down
10 changes: 10 additions & 0 deletions compiler/src/dotty/tools/dotc/core/Contexts.scala
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,16 @@ object Contexts {
inline def atNextPhase[T](inline op: Context ?=> T)(using Context): T =
atPhase(ctx.phase.next)(op)

/** Execute `op` at the current phase if it's before the first transform phase,
* otherwise at the last phase before the first transform phase.
*
* Note: this should be used instead of `atPhaseNoLater(ctx.picklerPhase)`
* because the later won't work if the `Pickler` phase is not present (for example,
* when using `QuoteCompiler`).
*/
inline def atPhaseBeforeTransforms[T](inline op: Context ?=> T)(using Context): T =
atPhaseNoLater(firstTransformPhase.prev)(op)

inline def atPhaseNoLater[T](limit: Phase)(inline op: Context ?=> T)(using Context): T =
op(using if !limit.exists || ctx.phase <= limit then ctx else ctx.withPhase(limit))

Expand Down
7 changes: 6 additions & 1 deletion compiler/src/dotty/tools/dotc/core/Definitions.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1464,7 +1464,12 @@ class Definitions {
new PerRun(Function2SpecializedReturnTypes.map(_.symbol))

def isSpecializableFunction(cls: ClassSymbol, paramTypes: List[Type], retType: Type)(using Context): Boolean =
paramTypes.length <= 2 && cls.derivesFrom(FunctionClass(paramTypes.length)) && (paramTypes match {
paramTypes.length <= 2 && cls.derivesFrom(FunctionClass(paramTypes.length))
&& isSpecializableFunctionSAM(paramTypes, retType)

/** If the Single Abstract Method of a Function class has this type, is it specializable? */
def isSpecializableFunctionSAM(paramTypes: List[Type], retType: Type)(using Context): Boolean =
paramTypes.length <= 2 && (paramTypes match {
case Nil =>
Function0SpecializedReturnClasses().contains(retType.typeSymbol)
case List(paramType0) =>
Expand Down
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/core/Denotations.scala
Original file line number Diff line number Diff line change
Expand Up @@ -585,7 +585,7 @@ object Denotations {
try info.signature
catch { // !!! DEBUG
case scala.util.control.NonFatal(ex) =>
report.echo(s"cannot take signature of ${info.show}")
report.echo(s"cannot take signature of $info")
throw ex
}
case _ => Signature.NotAMethod
Expand Down
4 changes: 2 additions & 2 deletions compiler/src/dotty/tools/dotc/core/NameOps.scala
Original file line number Diff line number Diff line change
Expand Up @@ -300,15 +300,15 @@ object NameOps {
*
* `<return type><first type><second type><...>`
*/
def specializedFunction(ret: Type, args: List[Type])(using Context): Name =
def specializedFunction(ret: Type, args: List[Type])(using Context): N =
val sb = new StringBuilder
sb.append(name.toString)
sb.append(nme.specializedTypeNames.prefix.toString)
sb.append(nme.specializedTypeNames.separator)
sb.append(defn.typeTag(ret).toString)
args.foreach { arg => sb.append(defn.typeTag(arg)) }
sb.append(nme.specializedTypeNames.suffix)
termName(sb.toString)
likeSpacedN(termName(sb.toString))

/** If name length exceeds allowable limit, replace part of it by hash */
def compactified(using Context): TermName = termName(compactify(name.toString))
Expand Down
23 changes: 14 additions & 9 deletions compiler/src/dotty/tools/dotc/core/Phases.scala
Original file line number Diff line number Diff line change
Expand Up @@ -16,25 +16,26 @@ import Periods._
import typer.{FrontEnd, RefChecks}
import typer.ImportInfo.withRootImports
import ast.tpd
import scala.annotation.internal.sharable

object Phases {

inline def phaseOf(id: PhaseId)(using Context): Phase =
ctx.base.phases(id)

@sharable object NoPhase extends Phase {
override def exists: Boolean = false
def phaseName: String = "<no phase>"
def run(using Context): Unit = unsupported("run")
def transform(ref: SingleDenotation)(using Context): SingleDenotation = unsupported("transform")
}

trait PhasesBase {
this: ContextBase =>

// drop NoPhase at beginning
def allPhases: Array[Phase] = (if (fusedPhases.nonEmpty) fusedPhases else phases).tail

object NoPhase extends Phase {
override def exists: Boolean = false
def phaseName: String = "<no phase>"
def run(using Context): Unit = unsupported("run")
def transform(ref: SingleDenotation)(using Context): SingleDenotation = unsupported("transform")
}

object SomePhase extends Phase {
def phaseName: String = "<some phase>"
def run(using Context): Unit = unsupported("run")
Expand Down Expand Up @@ -198,6 +199,7 @@ object Phases {
private var mySbtExtractDependenciesPhase: Phase = _
private var myPicklerPhase: Phase = _
private var myPickleQuotesPhase: Phase = _
private var myFirstTransformPhase: Phase = _
private var myCollectNullableFieldsPhase: Phase = _
private var myRefChecksPhase: Phase = _
private var myPatmatPhase: Phase = _
Expand All @@ -217,6 +219,7 @@ object Phases {
final def sbtExtractDependenciesPhase: Phase = mySbtExtractDependenciesPhase
final def picklerPhase: Phase = myPicklerPhase
final def pickleQuotesPhase: Phase = myPickleQuotesPhase
final def firstTransformPhase: Phase = myFirstTransformPhase
final def collectNullableFieldsPhase: Phase = myCollectNullableFieldsPhase
final def refchecksPhase: Phase = myRefChecksPhase
final def patmatPhase: Phase = myPatmatPhase
Expand All @@ -239,6 +242,7 @@ object Phases {
mySbtExtractDependenciesPhase = phaseOfClass(classOf[sbt.ExtractDependencies])
myPicklerPhase = phaseOfClass(classOf[Pickler])
myPickleQuotesPhase = phaseOfClass(classOf[PickleQuotes])
myFirstTransformPhase = phaseOfClass(classOf[FirstTransform])
myCollectNullableFieldsPhase = phaseOfClass(classOf[CollectNullableFields])
myRefChecksPhase = phaseOfClass(classOf[RefChecks])
myElimRepeatedPhase = phaseOfClass(classOf[ElimRepeated])
Expand Down Expand Up @@ -385,10 +389,10 @@ object Phases {
exists && id <= that.id

final def prev: Phase =
if (id > FirstPhaseId) myBase.phases(start - 1) else myBase.NoPhase
if (id > FirstPhaseId) myBase.phases(start - 1) else NoPhase

final def next: Phase =
if (hasNext) myBase.phases(end + 1) else myBase.NoPhase
if (hasNext) myBase.phases(end + 1) else NoPhase

final def hasNext: Boolean = start >= FirstPhaseId && end + 1 < myBase.phases.length

Expand All @@ -403,6 +407,7 @@ object Phases {
def sbtExtractDependenciesPhase(using Context): Phase = ctx.base.sbtExtractDependenciesPhase
def picklerPhase(using Context): Phase = ctx.base.picklerPhase
def pickleQuotesPhase(using Context): Phase = ctx.base.pickleQuotesPhase
def firstTransformPhase(using Context): Phase = ctx.base.firstTransformPhase
def refchecksPhase(using Context): Phase = ctx.base.refchecksPhase
def elimRepeatedPhase(using Context): Phase = ctx.base.elimRepeatedPhase
def extensionMethodsPhase(using Context): Phase = ctx.base.extensionMethodsPhase
Expand Down
3 changes: 3 additions & 0 deletions compiler/src/dotty/tools/dotc/core/StdNames.scala
Original file line number Diff line number Diff line change
Expand Up @@ -830,6 +830,9 @@ object StdNames {
final val Conforms: TypeName = encode("<:<")

final val Uninstantiated: TypeName = "?$"

val JFunctionPrefix: Seq[TypeName] = (0 to 2).map(i => s"scala.runtime.java8.JFunction${i}")
val JProcedure: Seq[TypeName] = (0 to 22).map(i => s"scala.runtime.function.JProcedure${i}")
}

abstract class JavaNames[N <: Name] extends DefinedNames[N] {
Expand Down
4 changes: 2 additions & 2 deletions compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ class TreeUnpickler(reader: TastyReader,
val owner = ctx.owner
val source = ctx.source
def complete(denot: SymDenotation)(using Context): Unit =
treeAtAddr(currentAddr) = atPhaseNoLater(picklerPhase) {
treeAtAddr(currentAddr) = atPhaseBeforeTransforms {
new TreeReader(reader).readIndexedDef()(
using ctx.withOwner(owner).withSource(source))
}
Expand Down Expand Up @@ -1392,7 +1392,7 @@ class TreeUnpickler(reader: TastyReader,
op: TreeReader => Context ?=> T) extends Trees.Lazy[T] {
def complete(using Context): T = {
pickling.println(i"starting to read at ${reader.reader.currentAddr} with owner $owner")
atPhaseNoLater(picklerPhase) {
atPhaseBeforeTransforms {
op(reader)(using ctx
.withOwner(owner)
.withModeBits(mode)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -608,7 +608,7 @@ class Scala2Unpickler(bytes: Array[Byte], classRoot: ClassDenotation, moduleClas
}
atReadPos(startCoord(denot).toIndex,
() => withMode(Mode.Scala2Unpickling) {
atPhaseNoLater(picklerPhase) {
atPhaseBeforeTransforms {
parseToCompletion(denot)
}
})
Expand Down
Loading