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

Rework ElimByName #14295

Merged
merged 10 commits into from
Jan 26, 2022
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
15 changes: 8 additions & 7 deletions compiler/src/dotty/tools/dotc/Compiler.scala
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@ import typer.{TyperPhase, RefChecks}
import parsing.Parser
import Phases.Phase
import transform._
import dotty.tools.backend.jvm.{CollectSuperCalls, GenBCode}
import dotty.tools.backend
import dotty.tools.dotc.transform.localopt.StringInterpolatorOpt
import backend.jvm.{CollectSuperCalls, GenBCode}
import localopt.StringInterpolatorOpt

/** The central class of the dotc compiler. The job of a compiler is to create
* runs, which process given `phases` in a given `rootContext`.
Expand Down Expand Up @@ -67,23 +67,24 @@ class Compiler {
new CheckLoopingImplicits, // Check that implicit defs do not call themselves in an infinite loop
new BetaReduce, // Reduce closure applications
new InlineVals, // Check right hand-sides of an `inline val`s
new ExpandSAMs) :: // Expand single abstract method closures to anonymous classes
new ExpandSAMs, // Expand single abstract method closures to anonymous classes
new ElimRepeated, // Rewrite vararg parameters and arguments
new RefChecks) :: // Various checks mostly related to abstract members and overriding
List(new init.Checker) :: // Check initialization of objects
List(new ElimRepeated, // Rewrite vararg parameters and arguments
List(new CrossVersionChecks, // Check issues related to deprecated and experimental
new ProtectedAccessors, // Add accessors for protected members
new ExtensionMethods, // Expand methods of value classes with extension methods
new UncacheGivenAliases, // Avoid caching RHS of simple parameterless given aliases
new ByNameClosures, // Expand arguments to by-name parameters to closures
new ElimByName, // Map by-name parameters to functions
new HoistSuperArgs, // Hoist complex arguments of supercalls to enclosing scope
new ForwardDepChecks, // Check that there are no forward references to local vals
new SpecializeApplyMethods, // Adds specialized methods to FunctionN
new RefChecks, // Various checks mostly related to abstract members and overriding
new TryCatchPatterns, // Compile cases in try/catch
new PatternMatcher) :: // Compile pattern matches
List(new ElimOpaque, // Turn opaque into normal aliases
new sjs.ExplicitJSClasses, // Make all JS classes explicit (Scala.js only)
new ExplicitOuter, // Add accessors to outer classes from nested ones.
new ExplicitSelf, // Make references to non-trivial self types explicit as casts
new ElimByName, // Expand by-name parameter references
new StringInterpolatorOpt) :: // Optimizes raw and s string interpolators by rewriting them to string concatenations
List(new PruneErasedDefs, // Drop erased definitions from scopes and simplify erased expressions
new UninitializedDefs, // Replaces `compiletime.uninitialized` by `_`
Expand Down
29 changes: 22 additions & 7 deletions compiler/src/dotty/tools/dotc/core/Definitions.scala
Original file line number Diff line number Diff line change
Expand Up @@ -447,12 +447,6 @@ class Definitions {
@tu lazy val andType: TypeSymbol = enterBinaryAlias(tpnme.AND, AndType(_, _))
@tu lazy val orType: TypeSymbol = enterBinaryAlias(tpnme.OR, OrType(_, _, soft = false))

/** Marker method to indicate an argument to a call-by-name parameter.
* Created by byNameClosures and elimByName, eliminated by Erasure,
*/
@tu lazy val cbnArg: TermSymbol = enterPolyMethod(OpsPackageClass, nme.cbnArg, 1,
pt => MethodType(List(FunctionOf(Nil, pt.paramRefs(0))), pt.paramRefs(0)))

/** Method representing a throw */
@tu lazy val throwMethod: TermSymbol = enterMethod(OpsPackageClass, nme.THROWkw,
MethodType(List(ThrowableType), NothingType))
Expand Down Expand Up @@ -1082,6 +1076,24 @@ class Definitions {
}
}

object ByNameFunction:
def apply(tp: Type)(using Context): Type =
defn.ContextFunction0.typeRef.appliedTo(tp :: Nil)
def unapply(tp: Type)(using Context): Option[Type] = tp match
case tp @ AppliedType(tycon, arg :: Nil) if defn.isByNameFunctionClass(tycon.typeSymbol) =>
Some(arg)
case tp @ AnnotatedType(parent, _) =>
unapply(parent)
case _ =>
None

final def isByNameFunctionClass(sym: Symbol): Boolean =
sym eq ContextFunction0

def isByNameFunction(tp: Type)(using Context): Boolean = tp match
case ByNameFunction(_) => true
case _ => false

final def isCompiletime_S(sym: Symbol)(using Context): Boolean =
sym.name == tpnme.S && sym.owner == CompiletimeOpsIntModuleClass

Expand Down Expand Up @@ -1295,10 +1307,12 @@ class Definitions {
).symbol.asClass

@tu lazy val Function0_apply: Symbol = Function0.requiredMethod(nme.apply)
@tu lazy val ContextFunction0_apply: Symbol = ContextFunction0.requiredMethod(nme.apply)

@tu lazy val Function0: Symbol = FunctionClass(0)
@tu lazy val Function1: Symbol = FunctionClass(1)
@tu lazy val Function2: Symbol = FunctionClass(2)
@tu lazy val ContextFunction0: Symbol = FunctionClass(0, isContextual = true)

def FunctionType(n: Int, isContextual: Boolean = false, isErased: Boolean = false)(using Context): TypeRef =
FunctionClass(n, isContextual && !ctx.erasedTypes, isErased).typeRef
Expand Down Expand Up @@ -1545,7 +1559,8 @@ 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.length <= 2
&& (cls.derivesFrom(FunctionClass(paramTypes.length)) || isByNameFunctionClass(cls))
&& isSpecializableFunctionSAM(paramTypes, retType)

/** If the Single Abstract Method of a Function class has this type, is it specializable? */
Expand Down
4 changes: 4 additions & 0 deletions compiler/src/dotty/tools/dotc/core/Phases.scala
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,7 @@ object Phases {
private var myRefChecksPhase: Phase = _
private var myPatmatPhase: Phase = _
private var myElimRepeatedPhase: Phase = _
private var myElimByNamePhase: Phase = _
private var myExtensionMethodsPhase: Phase = _
private var myExplicitOuterPhase: Phase = _
private var myGettersPhase: Phase = _
Expand All @@ -229,6 +230,7 @@ object Phases {
final def refchecksPhase: Phase = myRefChecksPhase
final def patmatPhase: Phase = myPatmatPhase
final def elimRepeatedPhase: Phase = myElimRepeatedPhase
final def elimByNamePhase: Phase = myElimByNamePhase
final def extensionMethodsPhase: Phase = myExtensionMethodsPhase
final def explicitOuterPhase: Phase = myExplicitOuterPhase
final def gettersPhase: Phase = myGettersPhase
Expand All @@ -253,6 +255,7 @@ object Phases {
myCollectNullableFieldsPhase = phaseOfClass(classOf[CollectNullableFields])
myRefChecksPhase = phaseOfClass(classOf[RefChecks])
myElimRepeatedPhase = phaseOfClass(classOf[ElimRepeated])
myElimByNamePhase = phaseOfClass(classOf[ElimByName])
myExtensionMethodsPhase = phaseOfClass(classOf[ExtensionMethods])
myErasurePhase = phaseOfClass(classOf[Erasure])
myElimErasedValueTypePhase = phaseOfClass(classOf[ElimErasedValueType])
Expand Down Expand Up @@ -427,6 +430,7 @@ object Phases {
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 elimByNamePhase(using Context): Phase = ctx.base.elimByNamePhase
def extensionMethodsPhase(using Context): Phase = ctx.base.extensionMethodsPhase
def explicitOuterPhase(using Context): Phase = ctx.base.explicitOuterPhase
def gettersPhase(using Context): Phase = ctx.base.gettersPhase
Expand Down
1 change: 0 additions & 1 deletion compiler/src/dotty/tools/dotc/core/StdNames.scala
Original file line number Diff line number Diff line change
Expand Up @@ -445,7 +445,6 @@ object StdNames {
val bytes: N = "bytes"
val canEqual_ : N = "canEqual"
val canEqualAny : N = "canEqualAny"
val cbnArg: N = "<cbn-arg>"
val checkInitialized: N = "checkInitialized"
val ClassManifestFactory: N = "ClassManifestFactory"
val classOf: N = "classOf"
Expand Down
20 changes: 14 additions & 6 deletions compiler/src/dotty/tools/dotc/core/TypeComparer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ package core

import Types._, Contexts._, Symbols._, Flags._, Names._, NameOps._, Denotations._
import Decorators._
import Phases.gettersPhase
import Phases.{gettersPhase, elimByNamePhase}
import StdNames.nme
import TypeOps.refineUsingParent
import collection.mutable
Expand Down Expand Up @@ -846,7 +846,7 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling
case _ => tp2.isAnyRef
}
compareJavaArray
case tp1: ExprType if ctx.phase.id > gettersPhase.id =>
case tp1: ExprType if ctx.phaseId > gettersPhase.id =>
// getters might have converted T to => T, need to compensate.
recur(tp1.widenExpr, tp2)
case _ =>
Expand Down Expand Up @@ -1499,7 +1499,7 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling
false
}

def isSubArg(arg1: Type, arg2: Type): Boolean = arg2 match {
def isSubArg(arg1: Type, arg2: Type): Boolean = arg2 match
case arg2: TypeBounds =>
val arg1norm = arg1 match {
case arg1: TypeBounds =>
Expand All @@ -1510,15 +1510,23 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling
case _ => arg1
}
arg2.contains(arg1norm)
case ExprType(arg2res)
if ctx.phaseId > elimByNamePhase.id && !ctx.erasedTypes
&& defn.isByNameFunction(arg1.dealias) =>
// ElimByName maps `=> T` to `()? => T`, but only in method parameters. It leaves
// embedded `=> T` arguments alone. This clause needs to compensate for that.
isSubArg(arg1.dealias.argInfos.head, arg2res)
case _ =>
arg1 match {
arg1 match
case arg1: TypeBounds =>
compareCaptured(arg1, arg2)
case ExprType(arg1res)
if ctx.phaseId > elimByNamePhase.id && !ctx.erasedTypes
&& defn.isByNameFunction(arg2.dealias) =>
isSubArg(arg1res, arg2.argInfos.head)
case _ =>
(v > 0 || isSubType(arg2, arg1)) &&
(v < 0 || isSubType(arg1, arg2))
}
}

isSubArg(args1.head, args2.head)
} && recurArgs(args1.tail, args2.tail, tparams2.tail)
Expand Down
40 changes: 0 additions & 40 deletions compiler/src/dotty/tools/dotc/transform/ByNameClosures.scala

This file was deleted.

Loading