Skip to content

Fix #6909: Use memo to cache given aliases #6939

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

Closed
wants to merge 18 commits into from
Closed
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
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 @@ -63,7 +63,6 @@ class Compiler {
new ExpandSAMs, // Expand single abstract method closures to anonymous classes
new ProtectedAccessors, // Add accessors for protected members
new ExtensionMethods, // Expand methods of value classes with extension methods
new CacheAliasImplicits, // Cache RHS of parameterless alias implicits
new ShortcutImplicits, // Allow implicit functions without creating closures
new ByNameClosures, // Expand arguments to by-name parameters to closures
new HoistSuperArgs, // Hoist complex arguments of supercalls to enclosing scope
Expand Down
20 changes: 8 additions & 12 deletions compiler/src/dotty/tools/dotc/ast/Desugar.scala
Original file line number Diff line number Diff line change
Expand Up @@ -144,21 +144,17 @@ object desugar {

// ----- Desugar methods -------------------------------------------------

def setterNeeded(flags: FlagSet, owner: Symbol) given Context =
flags.is(Mutable) && owner.isClass && (!flags.isAllOf(PrivateLocal) || owner.is(Trait))

/** var x: Int = expr
* ==>
* def x: Int = expr
* def x_=($1: <TypeTree()>): Unit = ()
*/
def valDef(vdef0: ValDef)(implicit ctx: Context): Tree = {
val vdef @ ValDef(name, tpt, rhs) = transformQuotedPatternName(vdef0)
val mods = vdef.mods
val setterNeeded =
mods.is(Mutable) && ctx.owner.isClass && (!mods.isAllOf(PrivateLocal) || ctx.owner.is(Trait))
if (setterNeeded) {
// TODO: copy of vdef as getter needed?
// val getter = ValDef(mods, name, tpt, rhs) withPos vdef.pos?
// right now vdef maps via expandedTree to a thicket which concerns itself.
// I don't see a problem with that but if there is one we can avoid it by making a copy here.
if (setterNeeded(vdef.mods.flags, ctx.owner)) {
val setterParam = makeSyntheticParameter(tpt = SetterParamTree().watching(vdef))
// The rhs gets filled in later, when field is generated and getter has parameters (see Memoize miniphase)
val setterRhs = if (vdef.rhs.isEmpty) EmptyTree else unitLiteral
Expand All @@ -168,7 +164,7 @@ object desugar {
vparamss = (setterParam :: Nil) :: Nil,
tpt = TypeTree(defn.UnitType),
rhs = setterRhs
).withMods((mods | Accessor) &~ (CaseAccessor | GivenOrImplicit | Lazy))
).withMods((vdef.mods | Accessor) &~ (CaseAccessor | GivenOrImplicit | Lazy))
Thicket(vdef, setter)
}
else vdef
Expand Down Expand Up @@ -891,7 +887,7 @@ object desugar {
/** The normalized name of `mdef`. This means
* 1. Check that the name does not redefine a Scala core class.
* If it does redefine, issue an error and return a mangled name instead of the original one.
* 2. If the name is missing (this can be the case for instance definitions), invent one instead.
* 2. If the name is missing (this can be the case for given instance definitions), invent one instead.
*/
def normalizeName(mdef: MemberDef, impl: Tree)(implicit ctx: Context): Name = {
var name = mdef.name
Expand All @@ -904,7 +900,7 @@ object desugar {
name
}

/** Invent a name for an anonymous instance with template `impl`.
/** Invent a name for an anonymous given instance with template `impl`.
*/
private def inventName(impl: Tree)(implicit ctx: Context): String = impl match {
case impl: Template =>
Expand All @@ -916,7 +912,7 @@ object desugar {
case Some(DefDef(name, _, (vparam :: _) :: _, _, _)) =>
s"${name}_of_${inventTypeName(vparam.tpt)}"
case _ =>
ctx.error(i"anonymous instance must have `for` part or must define at least one extension method", impl.sourcePos)
ctx.error(i"anonymous given must have `as` part or must define at least one extension method", impl.sourcePos)
nme.ERROR.toString
}
else
Expand Down
12 changes: 11 additions & 1 deletion compiler/src/dotty/tools/dotc/ast/TreeInfo.scala
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ package dotc
package ast

import core._
import Flags._, Trees._, Types._, Contexts._
import Flags._, Trees._, Types._, Contexts._, Constants.Constant
import Names._, StdNames._, NameOps._, Symbols._
import typer.ConstFold
import reporting.trace
Expand Down Expand Up @@ -856,6 +856,16 @@ trait TypedTreeInfo extends TreeInfo[Type] { self: Trees.Instance[Type] =>
case _ => None
}
}

object SplicedRHS {
/** Extracts splices, possibly wrapped in blocks or type ascriptions */
def unapply(tree: tpd.Tree)(implicit ctx: Context): Option[tpd.Tree] = tree match {
case Block(stat :: Nil, Literal(Constant(()))) => unapply(stat)
case Block(Nil, expr) => unapply(expr)
case Typed(expr, _) => unapply(expr)
case _ => Spliced.unapply(tree)
}
}
}

object TreeInfo {
Expand Down
1 change: 1 addition & 0 deletions compiler/src/dotty/tools/dotc/core/Definitions.scala
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,7 @@ class Definitions {
@threadUnsafe lazy val Compiletime_constValue : SymbolPerRun = perRunSym(CompiletimePackageObject.requiredMethodRef("constValue"))
@threadUnsafe lazy val Compiletime_constValueOpt: SymbolPerRun = perRunSym(CompiletimePackageObject.requiredMethodRef("constValueOpt"))
@threadUnsafe lazy val Compiletime_code : SymbolPerRun = perRunSym(CompiletimePackageObject.requiredMethodRef("code"))
@threadUnsafe lazy val Compiletime_memo : SymbolPerRun = perRunSym(CompiletimePackageObject.requiredMethodRef("memo"))

/** The `scalaShadowing` package is used to safely modify classes and
* objects in scala so that they can be used from dotty. They will
Expand Down
5 changes: 4 additions & 1 deletion compiler/src/dotty/tools/dotc/core/NameKinds.scala
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,9 @@ object NameKinds {
safePrefix + info.num
}

def currentCount(prefix: TermName = EmptyTermName) given (ctx: Context): Int =
ctx.freshNames.currentCount(prefix, this)

/** Generate fresh unique term name of this kind with given prefix name */
def fresh(prefix: TermName = EmptyTermName)(implicit ctx: Context): TermName =
ctx.freshNames.newName(prefix, this)
Expand Down Expand Up @@ -296,6 +299,7 @@ object NameKinds {
val UniqueInlineName: UniqueNameKind = new UniqueNameKind("$i")
val InlineScrutineeName: UniqueNameKind = new UniqueNameKind("$scrutinee")
val InlineBinderName: UniqueNameKind = new UniqueNameKind("$elem")
val MemoCacheName: UniqueNameKind = new UniqueNameKind("$cache")

/** A kind of unique extension methods; Unlike other unique names, these can be
* unmangled.
Expand Down Expand Up @@ -359,7 +363,6 @@ object NameKinds {
val InlineAccessorName: PrefixNameKind = new PrefixNameKind(INLINEACCESSOR, "inline$")

val AvoidClashName: SuffixNameKind = new SuffixNameKind(AVOIDCLASH, "$_avoid_name_clash_$")
val CacheName = new SuffixNameKind(CACHE, "$_cache")
val DirectMethodName: SuffixNameKind = new SuffixNameKind(DIRECT, "$direct") { override def definesNewName = true }
val FieldName: SuffixNameKind = new SuffixNameKind(FIELD, "$$local") {
override def mkString(underlying: TermName, info: ThisInfo) = underlying.toString
Expand Down
2 changes: 0 additions & 2 deletions compiler/src/dotty/tools/dotc/core/NameTags.scala
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,6 @@ object NameTags extends TastyFormat.NameTags {
final val IMPLMETH = 32 // Used to define methods in implementation classes
// (can probably be removed).

final val CACHE = 33 // Used as a cache for the rhs of an alias implicit.

def nameTagToString(tag: Int): String = tag match {
case UTF8 => "UTF8"
case QUALIFIED => "QUALIFIED"
Expand Down
1 change: 1 addition & 0 deletions compiler/src/dotty/tools/dotc/core/StdNames.scala
Original file line number Diff line number Diff line change
Expand Up @@ -482,6 +482,7 @@ object StdNames {
val materializeClassTag: N = "materializeClassTag"
val materializeWeakTypeTag: N = "materializeWeakTypeTag"
val materializeTypeTag: N = "materializeTypeTag"
val memo: N = "memo"
val mirror : N = "mirror"
val moduleClass : N = "moduleClass"
val name: N = "name"
Expand Down
8 changes: 6 additions & 2 deletions compiler/src/dotty/tools/dotc/core/SymDenotations.scala
Original file line number Diff line number Diff line change
Expand Up @@ -102,8 +102,12 @@ trait SymDenotations { this: Context =>
}
}

/** Configurable: Accept stale symbol with warning if in IDE */
def staleOK: Boolean = Config.ignoreStaleInIDE && mode.is(Mode.Interactive)
/** Configurable: Accept stale symbol with warning if in IDE
* Always accept stale symbols when testing pickling.
*/
def staleOK: Boolean =
Config.ignoreStaleInIDE && mode.is(Mode.Interactive) ||
settings.YtestPickler.value

/** Possibly accept stale symbol with warning if in IDE */
def acceptStale(denot: SingleDenotation): Boolean =
Expand Down
25 changes: 14 additions & 11 deletions compiler/src/dotty/tools/dotc/core/Symbols.scala
Original file line number Diff line number Diff line change
Expand Up @@ -553,9 +553,10 @@ object Symbols {

/** This symbol entered into owner's scope (owner must be a class). */
final def entered(implicit ctx: Context): this.type = {
assert(this.owner.isClass, s"symbol ($this) entered the scope of non-class owner ${this.owner}") // !!! DEBUG
this.owner.asClass.enter(this)
if (this.is(Module)) this.owner.asClass.enter(this.moduleClass)
if (this.owner.isClass) {
this.owner.asClass.enter(this)
if (this.is(Module)) this.owner.asClass.enter(this.moduleClass)
}
this
}

Expand All @@ -566,14 +567,16 @@ object Symbols {
*/
def enteredAfter(phase: DenotTransformer)(implicit ctx: Context): this.type =
if (ctx.phaseId != phase.next.id) enteredAfter(phase)(ctx.withPhase(phase.next))
else {
if (this.owner.is(Package)) {
denot.validFor |= InitialPeriod
if (this.is(Module)) this.moduleClass.validFor |= InitialPeriod
}
else this.owner.asClass.ensureFreshScopeAfter(phase)
assert(isPrivate || phase.changesMembers, i"$this entered in ${this.owner} at undeclared phase $phase")
entered
else this.owner match {
case owner: ClassSymbol =>
if (owner.is(Package)) {
denot.validFor |= InitialPeriod
if (this.is(Module)) this.moduleClass.validFor |= InitialPeriod
}
else owner.ensureFreshScopeAfter(phase)
assert(isPrivate || phase.changesMembers, i"$this entered in $owner at undeclared phase $phase")
entered
case _ => this
}

/** Remove symbol from scope of owning class */
Expand Down
10 changes: 5 additions & 5 deletions compiler/src/dotty/tools/dotc/parsing/Parsers.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2334,7 +2334,7 @@ object Parsers {
* given C ...
* we know that `given` must start a parameter list. It cannot be a new given` definition.
*/
def followingIsInstanceDef =
def followingIsGivenDef =
(ofClass || ofInstance) && {
val lookahead = in.lookaheadScanner // skips newline on startup
lookahead.nextToken() // skip the `given`
Expand Down Expand Up @@ -2362,7 +2362,7 @@ object Parsers {
var initialMods = EmptyModifiers
val isNewLine = in.token == NEWLINE
newLineOptWhenFollowedBy(LPAREN)
if (in.token == NEWLINE && in.next.token == GIVEN && !followingIsInstanceDef)
if (in.token == NEWLINE && in.next.token == GIVEN && !followingIsGivenDef)
in.nextToken()
if (in.token == GIVEN) {
in.nextToken()
Expand Down Expand Up @@ -2765,7 +2765,7 @@ object Parsers {
case ENUM =>
enumDef(start, posMods(start, mods | Enum))
case IMPLIED | GIVEN =>
instanceDef(in.token == GIVEN, start, mods, atSpan(in.skipToken()) { Mod.Given() })
givenDef(in.token == GIVEN, start, mods, atSpan(in.skipToken()) { Mod.Given() })
case _ =>
syntaxErrorOrIncomplete(ExpectedStartOfTopLevelDefinition())
EmptyTree
Expand Down Expand Up @@ -2861,7 +2861,7 @@ object Parsers {
* GivenBody ::= [‘as ConstrApp {‘,’ ConstrApp }] {GivenParamClause} [TemplateBody]
* | ‘as’ Type {GivenParamClause} ‘=’ Expr
*/
def instanceDef(newStyle: Boolean, start: Offset, mods: Modifiers, instanceMod: Mod) = atSpan(start, nameStart) {
def givenDef(newStyle: Boolean, start: Offset, mods: Modifiers, instanceMod: Mod) = atSpan(start, nameStart) {
var mods1 = addMod(mods, instanceMod)
val name = if (isIdent && (!newStyle || in.name != nme.as)) ident() else EmptyTermName
val tparams = typeParamClauseOpt(ParamOwner.Def)
Expand Down Expand Up @@ -3178,7 +3178,7 @@ object Parsers {
val mods = modifiers(closureMods)
mods.mods match {
case givenMod :: Nil if !isBindingIntro =>
stats += instanceDef(true, start, EmptyModifiers, Mod.Given().withSpan(givenMod.span))
stats += givenDef(true, start, EmptyModifiers, Mod.Given().withSpan(givenMod.span))
case _ =>
stats += implicitClosure(in.offset, Location.InBlock, mods)
}
Expand Down
104 changes: 0 additions & 104 deletions compiler/src/dotty/tools/dotc/transform/CacheAliasImplicits.scala

This file was deleted.

2 changes: 2 additions & 0 deletions compiler/src/dotty/tools/dotc/transform/CheckReentrant.scala
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import Flags._
import Contexts.Context
import Symbols._
import Decorators._
import NameKinds.MemoCacheName


/** A no-op transform that checks whether the compiled sources are re-entrant.
Expand Down Expand Up @@ -44,6 +45,7 @@ class CheckReentrant extends MiniPhase {
def isIgnored(sym: Symbol)(implicit ctx: Context): Boolean =
sym.hasAnnotation(sharableAnnot()) ||
sym.hasAnnotation(unsharedAnnot()) ||
sym.name.is(MemoCacheName) ||
sym.owner == defn.EnumValuesClass
// enum values are initialized eagerly before use
// in the long run, we should make them vals
Expand Down
6 changes: 3 additions & 3 deletions compiler/src/dotty/tools/dotc/transform/HoistSuperArgs.scala
Original file line number Diff line number Diff line change
Expand Up @@ -91,13 +91,13 @@ class HoistSuperArgs extends MiniPhase with IdentityDenotTransformer { thisPhase
val argTypeWrtConstr = argType.subst(origParams, allParamRefs(constr.info))
// argType with references to paramRefs of the primary constructor instead of
// local parameter accessors
val meth = ctx.newSymbol(
ctx.newSymbol(
owner = methOwner,
name = SuperArgName.fresh(cls.name.toTermName),
flags = Synthetic | Private | Method | staticFlag,
info = replaceResult(constr.info, argTypeWrtConstr),
coord = constr.coord)
if (methOwner.isClass) meth.enteredAfter(thisPhase) else meth
coord = constr.coord
).enteredAfter(thisPhase)
}

/** Type of a reference implies that it needs to be hoisted */
Expand Down
5 changes: 3 additions & 2 deletions compiler/src/dotty/tools/dotc/transform/LambdaLift.scala
Original file line number Diff line number Diff line change
Expand Up @@ -298,8 +298,9 @@ object LambdaLift {
proxyMap(owner) = {
for (fv <- freeValues.toList) yield {
val proxyName = newName(fv)
val proxy = ctx.newSymbol(owner, proxyName.asTermName, newFlags, fv.info, coord = fv.coord)
if (owner.isClass) proxy.enteredAfter(thisPhase)
val proxy =
ctx.newSymbol(owner, proxyName.asTermName, newFlags, fv.info, coord = fv.coord)
.enteredAfter(thisPhase)
(fv, proxy)
}
}.toMap
Expand Down
Loading