8000 Optimizations in Namer and Typer by retronym · Pull Request #5875 · scala/scala · GitHub
[go: up one dir, main page]

Skip to content

Optimizations in Namer and Typer #5875

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 12 commits into from
May 25, 2017
Merged
Original file line number Diff line number Diff line change
Expand Up @@ -451,7 +451,7 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers {
)

private def hasUnspecializableAnnotation(sym: Symbol): Boolean =
sym.ownerChain.exists(_ hasAnnotation UnspecializedClass)
sym.ownersIterator.exists(_ hasAnnotation UnspecializedClass)

def isNormalizedMember(m: Symbol) = m.isSpecialized && (info get m exists {
case NormalizedMember(_) => true
Expand Down
13 changes: 9 additions & 4 deletions src/compiler/scala/tools/nsc/typechecker/Implicits.scala
Original file line number Diff line number Diff line change
Expand Up @@ -357,6 +357,10 @@ trait Implicits {
/** The type parameters to instantiate */
val undetParams = if (isView) Nil else context.outer.undetparams
val wildPt = approximate(pt)
private val ptFunctionArity: Int = {
val dealiased = pt.dealiasWiden
if (isFunctionTypeDirect(dealiased)) dealiased.typeArgs.length - 1 else -1
}

private val stableRunDefsForImport = currentRun.runDefinitions
import stableRunDefsForImport._
Expand Down Expand Up @@ -543,9 +547,7 @@ trait Implicits {
if (sym.isAliasType) loop(tp, pt.dealias)
else if (sym.isAbstractType) loop(tp, pt.bounds.lo)
else {
val len = args.length - 1
hasLength(params, len) &&
sym == FunctionClass(len) && {
ptFunctionArity > 0 && hasLength(params, ptFunctionArity) && {
var ps = params
var as = args
if (fast) {
Expand Down Expand Up @@ -588,9 +590,12 @@ trait Implicits {
// We can only rule out a subtype relationship if the left hand
// side is a class, else we may not know enough.
case tr1 @ TypeRef(_, sym1, _) if sym1.isClass =>
def typeRefHasMember(tp: TypeRef, name: Name) = {
tp.baseClasses.exists(_.info.decls.lookupEntry(name) != null)
}
tp2.dealiasWiden match {
case TypeRef(_, sym2, _) => ((sym1 eq ByNameParamClass) != (sym2 eq ByNameParamClass)) || (sym2.isClass && !(sym1 isWeakSubClass sym2))
case RefinedType(parents, decls) => decls.nonEmpty && tr1.member(decls.head.name) == NoSymbol
case RefinedType(parents, decls) => decls.nonEmpty && !typeRefHasMember(tr1, decls.head.name) // opt avoid full call to .member
case _ => false
}
case _ => false
Expand Down
5 changes: 0 additions & 5 deletions src/compiler/scala/tools/nsc/typechecker/Namers.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1374,11 +1374,6 @@ trait Namers extends MethodSynthesis {
if (mexists(vparamss)(_.symbol.hasDefault) || mexists(overridden.paramss)(_.hasDefault))
addDefaultGetters(meth, ddef, vparamss, tparams, overridden)

// fast track macros, i.e. macros defined inside the compiler, are hardcoded
// hence we make use of that and let them have whatever right-hand side they need
// (either "macro ???" as they used to or just "???" to maximally simplify their compilation)
if (fastTrack contains meth) meth setFlag MACRO

// macro defs need to be typechecked in advance
// because @macroImpl annotation only gets assigned during typechecking
// otherwise macro defs wouldn't be able to robustly coexist with their clients
Expand Down
30 changes: 16 additions & 14 deletions src/compiler/scala/tools/nsc/typechecker/Typers.scala
Original file line number Diff line number Diff line change
Expand Up @@ -254,10 +254,6 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
var context = context0
def context1 = context

// for use with silent type checking to when we can't have results with undetermined type params
// note that this captures the context var
val isMonoContext = (_: Any) => context.undetparams.isEmpty

def dropExistential(tp: Type): Type = tp match {
case ExistentialType(tparams, tpe) =>
new SubstWildcardMap(tparams).apply(tp)
Expand Down Expand Up @@ -2425,7 +2421,7 @ 8000 @ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
block match {
case Block(List(classDef @ ClassDef(_, _, _, _)), Apply(Select(New(_), _), _)) =>
val classDecls = classDef.symbol.info.decls
val visibleMembers = pt match {
lazy val visibleMembers = pt match {
case WildcardType => classDecls.toList
case BoundedWildcardType(TypeBounds(lo, _)) => lo.members
case _ => pt.members
Expand Down Expand Up @@ -2972,7 +2968,11 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
// We're looking for a method (as indicated by FUNmode in the silent typed below),
// so let's make sure our expected type is a MethodType
val methArgs = NoSymbol.newSyntheticValueParams(argpts map { case NoType => WildcardType case tp => tp })
silent(_.typed(meth, mode.forFunMode, MethodType(methArgs, respt))) filter (isMonoContext) map { methTyped =>

val result = silent(_.typed(meth, mode.forFunMode, MethodType(methArgs, respt)))
// we can't have results with undetermined type params
val resultMono = result filter (_ => context.undetparams.isEmpty)
resultMono map { methTyped =>
// if context.undetparams is not empty, the method was polymorphic,
// so we need the missing arguments to infer its type. See #871
val funPt = normalize(methTyped.tpe) baseType FunctionClass(numVparams)
Expand Down Expand Up @@ -4875,17 +4875,16 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
dyna.wrapErrors(t, (_.typed1(t, mode, pt)))
}

val sym = tree.symbol orElse member(qual, name) orElse inCompanionForJavaStatic(qual.tpe.prefix, qual.symbol, name) orElse {
val sym = tree.symbol orElse member(qual, name) orElse inCompanionForJavaStatic(qual.tpe.prefix, qual.symbol, name)
if ((sym eq NoSymbol) && name != nme.CONSTRUCTOR && mode.inAny(EXPRmode | PATTERNmode)) {
// symbol not found? --> try to convert implicitly to a type that does have the required
// member. Added `| PATTERNmode` to allow enrichment in patterns (so we can add e.g., an
// xml member to StringContext, which in turn has an unapply[Seq] method)
if (name != nme.CONSTRUCTOR && mode.inAny(EXPRmode | PATTERNmode)) {
val qual1 = adaptToMemberWithArgs(tree, qual, name, mode)
if ((qual1 ne qual) && !qual1.isErrorTyped)
return typed(treeCopy.Select(tree, qual1, name), mode, pt)
}
NoSymbol
val qual1 = adaptToMemberWithArgs(tree, qual, name, mode)
if ((qual1 ne qual) && !qual1.isErrorTyped)
return typed(treeCopy.Select(tree, qual1, name), mode, pt)
}

if (phase.erasedTypes && qual.isInstanceOf[Super] && tree.symbol != NoSymbol)
qual setType tree.symbol.owner.tpe

Expand Down Expand Up @@ -5050,7 +5049,10 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
}
else {
val pre1 = if (sym.isTopLevel) sym.owner.thisType else if (qual == EmptyTree) NoPrefix else qual.tpe
val tree1 = if (qual == EmptyTree) tree else atPos(tree.pos)(Select(atPos(tree.pos.focusStart)(qual), name))
val tree1 = if (qual == EmptyTree) tree else {
val pos = tree.pos
Select(atPos(pos.focusStart)(qual), name).setPos(pos)
}
val (tree2, pre2) = makeAccessible(tree1, sym, pre1, qual)
// scala/bug#5967 Important to replace param type A* with Seq[A] when seen from from a reference, to avoid
// inference errors in pattern matching.
Expand Down
5 changes: 3 additions & 2 deletions src/compiler/scala/tools/nsc/typechecker/Unapplies.scala
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,9 @@ trait Unapplies extends ast.TreeDSL {
}

private def constrParamss(cdef: ClassDef): List[List[ValDef]] = {
val ClassDef(_, _, _, Template(_, _, body)) = resetAttrs(cdef.duplicate)
val DefDef(_, _, _, vparamss, _, _) = treeInfo firstConstructor body
val prunedClassDef = deriveClassDef(cdef)(tmpl => deriveTemplate(tmpl)(stats => treeInfo.firstConstructor(stats).duplicate :: Nil))
val ClassDef(_, _, _, Template(_, _, firstConstructor :: Nil)) = resetAttrs(prunedClassDef)
val DefDef(_, _, _, vparamss, _, _) = firstConstructor
vparamss
}

Expand Down
20 changes: 13 additions & 7 deletions src/reflect/scala/reflect/internal/SymbolTable.scala
Original file line number Diff line number Diff line change
Expand Up @@ -171,11 +171,12 @@ abstract class SymbolTable extends macros.Universe
final val NoRunId = 0

// sigh, this has to be public or enteringPhase doesn't inline.
var phStack: List[Phase] = Nil
var phStack: Array[Phase] = new Array(128)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nitpick] Only store phase name?

Copy link
Member Author
@retronym retronym May 2, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Storage wise, there isn't a difference, both are just already allocated Object refs.

If we store the phase name, we have to eagerly call the virtual Phase.name in phase_=, even though under normal circumstances we don't use the result.

In general, It would be good to avoid retaining Phase-s as fields in Global so they are eligible for GC after the Run that spawned them has been collected. But we know we'll end up with an empty phase stack here at the end of the run, so it doesn't make a practical difference.

var phStackIndex = 0
private[this] var ph: Phase = NoPhase
private[this] var per = NoPeriod

final def atPhaseStack: List[Phase] = phStack
final def atPhaseStack: List[Phase] = List.tabulate(phStackIndex)(i => phStack(i))
final def phase: Phase = {
if (Statistics.hotEnabled)
Statistics.incCounter(SymbolTableStats.phaseCounter)
Expand All @@ -196,11 +197,13 @@ abstract class SymbolTable extends macros.Universe
final def pushPhase(ph: Phase): Phase = {
val current = phase
phase = ph
phStack ::= ph
phStack(phStackIndex) = ph
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

bounds check, just in case?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was happy to let an ArrayIndexOutOfBoundException occur in that case rather than adding explicit code here to handle it.

Copy link
Member Author
@retronym retronym May 2, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The alternative would be to use an ArrayBuffer, which doesn't require a hard limit to the depth of recursive atPhase-s. But I was being a bit paranoid with performance.

phStackIndex += 1
current
}
final def popPhase(ph: Phase) {
phStack = phStack.tail
phStack(phStackIndex) = null
phStackIndex -= 1
phase = ph
}

Expand Down Expand Up @@ -231,9 +234,12 @@ abstract class SymbolTable extends macros.Universe

/** Perform given operation at given phase. */
@inline final def enteringPhase[T](ph: Phase)(op: => T): T = {
val saved = pushPhase(ph)
try op
finally popPhase(saved)
if (ph eq phase) op // opt
else {
val saved = pushPhase(ph)
try op
finally popPhase(saved)
}
}

final def findPhaseWithName(phaseName: String): Phase = {
Expand Down
0