Skip to content

Commit 907caeb

Browse files
committed
Fix #6909: Use memo to cache given aliases
1 parent c90b200 commit 907caeb

File tree

8 files changed

+70
-117
lines changed

8 files changed

+70
-117
lines changed

compiler/src/dotty/tools/dotc/Compiler.scala

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,6 @@ class Compiler {
6363
new ExpandSAMs, // Expand single abstract method closures to anonymous classes
6464
new ProtectedAccessors, // Add accessors for protected members
6565
new ExtensionMethods, // Expand methods of value classes with extension methods
66-
new CacheAliasImplicits, // Cache RHS of parameterless alias implicits
6766
new ShortcutImplicits, // Allow implicit functions without creating closures
6867
new ByNameClosures, // Expand arguments to by-name parameters to closures
6968
new HoistSuperArgs, // Hoist complex arguments of supercalls to enclosing scope

compiler/src/dotty/tools/dotc/ast/Desugar.scala

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -887,7 +887,7 @@ object desugar {
887887
/** The normalized name of `mdef`. This means
888888
* 1. Check that the name does not redefine a Scala core class.
889889
* If it does redefine, issue an error and return a mangled name instead of the original one.
890-
* 2. If the name is missing (this can be the case for instance definitions), invent one instead.
890+
* 2. If the name is missing (this can be the case for given instance definitions), invent one instead.
891891
*/
892892
def normalizeName(mdef: MemberDef, impl: Tree)(implicit ctx: Context): Name = {
893893
var name = mdef.name
@@ -900,7 +900,7 @@ object desugar {
900900
name
901901
}
902902

903-
/** Invent a name for an anonymous instance with template `impl`.
903+
/** Invent a name for an anonymous given instance with template `impl`.
904904
*/
905905
private def inventName(impl: Tree)(implicit ctx: Context): String = impl match {
906906
case impl: Template =>
@@ -912,7 +912,7 @@ object desugar {
912912
case Some(DefDef(name, _, (vparam :: _) :: _, _, _)) =>
913913
s"${name}_of_${inventTypeName(vparam.tpt)}"
914914
case _ =>
915-
ctx.error(i"anonymous instance must have `for` part or must define at least one extension method", impl.sourcePos)
915+
ctx.error(i"anonymous given must have `as` part or must define at least one extension method", impl.sourcePos)
916916
nme.ERROR.toString
917917
}
918918
else

compiler/src/dotty/tools/dotc/core/NameKinds.scala

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -363,7 +363,6 @@ object NameKinds {
363363
val InlineAccessorName: PrefixNameKind = new PrefixNameKind(INLINEACCESSOR, "inline$")
364364

365365
val AvoidClashName: SuffixNameKind = new SuffixNameKind(AVOIDCLASH, "$_avoid_name_clash_$")
366-
val CacheName = new SuffixNameKind(CACHE, "$_cache")
367366
val DirectMethodName: SuffixNameKind = new SuffixNameKind(DIRECT, "$direct") { override def definesNewName = true }
368367
val FieldName: SuffixNameKind = new SuffixNameKind(FIELD, "$$local") {
369368
override def mkString(underlying: TermName, info: ThisInfo) = underlying.toString

compiler/src/dotty/tools/dotc/core/NameTags.scala

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,6 @@ object NameTags extends TastyFormat.NameTags {
3636
final val IMPLMETH = 32 // Used to define methods in implementation classes
3737
// (can probably be removed).
3838

39-
final val CACHE = 33 // Used as a cache for the rhs of an alias implicit.
40-
4139
def nameTagToString(tag: Int): String = tag match {
4240
case UTF8 => "UTF8"
4341
case QUALIFIED => "QUALIFIED"

compiler/src/dotty/tools/dotc/parsing/Parsers.scala

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2334,7 +2334,7 @@ object Parsers {
23342334
* given C ...
23352335
* we know that `given` must start a parameter list. It cannot be a new given` definition.
23362336
*/
2337-
def followingIsInstanceDef =
2337+
def followingIsGivenDef =
23382338
(ofClass || ofInstance) && {
23392339
val lookahead = in.lookaheadScanner // skips newline on startup
23402340
lookahead.nextToken() // skip the `given`
@@ -2358,7 +2358,7 @@ object Parsers {
23582358
var initialMods = EmptyModifiers
23592359
val isNewLine = in.token == NEWLINE
23602360
newLineOptWhenFollowedBy(LPAREN)
2361-
if (in.token == NEWLINE && in.next.token == GIVEN && !followingIsInstanceDef)
2361+
if (in.token == NEWLINE && in.next.token == GIVEN && !followingIsGivenDef)
23622362
in.nextToken()
23632363
if (in.token == GIVEN) {
23642364
in.nextToken()
@@ -2761,7 +2761,7 @@ object Parsers {
27612761
case ENUM =>
27622762
enumDef(start, posMods(start, mods | Enum))
27632763
case IMPLIED | GIVEN =>
2764-
instanceDef(in.token == GIVEN, start, mods, atSpan(in.skipToken()) { Mod.Given() })
2764+
givenDef(in.token == GIVEN, start, mods, atSpan(in.skipToken()) { Mod.Given() })
27652765
case _ =>
27662766
syntaxErrorOrIncomplete(ExpectedStartOfTopLevelDefinition())
27672767
EmptyTree
@@ -2857,7 +2857,7 @@ object Parsers {
28572857
* GivenBody ::= [‘as ConstrApp {‘,’ ConstrApp }] {GivenParamClause} [TemplateBody]
28582858
* | ‘as’ Type {GivenParamClause} ‘=’ Expr
28592859
*/
2860-
def instanceDef(newStyle: Boolean, start: Offset, mods: Modifiers, instanceMod: Mod) = atSpan(start, nameStart) {
2860+
def givenDef(newStyle: Boolean, start: Offset, mods: Modifiers, instanceMod: Mod) = atSpan(start, nameStart) {
28612861
var mods1 = addMod(mods, instanceMod)
28622862
val name = if (isIdent && (!newStyle || in.name != nme.as)) ident() else EmptyTermName
28632863
val tparams = typeParamClauseOpt(ParamOwner.Def)
@@ -3174,7 +3174,7 @@ object Parsers {
31743174
val mods = modifiers(closureMods)
31753175
mods.mods match {
31763176
case givenMod :: Nil if !isBindingIntro =>
3177-
stats += instanceDef(true, start, EmptyModifiers, Mod.Given().withSpan(givenMod.span))
3177+
stats += givenDef(true, start, EmptyModifiers, Mod.Given().withSpan(givenMod.span))
31783178
case _ =>
31793179
stats += implicitClosure(in.offset, Location.InBlock, mods)
31803180
}

compiler/src/dotty/tools/dotc/transform/CacheAliasImplicits.scala

Lines changed: 0 additions & 104 deletions
This file was deleted.
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
package dotty.tools.dotc
2+
package typer
3+
4+
import core.Symbols._
5+
import core.Contexts._
6+
import core.Types._
7+
import core.Flags._
8+
import core.Decorators._
9+
import ast.{untpd, tpd}
10+
11+
object MemoizeGivenAliases {
12+
13+
/** Flags that disable caching */
14+
val NoCacheFlags =
15+
StableRealizable | // It's a simple forwarder, leave it as one
16+
Exported // Export forwarders are never cached
17+
}
18+
19+
trait MemoizeGivenAliases { this: Typer =>
20+
import tpd._
21+
22+
/** Ensure that the right hand side of a parameterless given alias
23+
* is cached. This applies to all given aliases that have neither type parameters
24+
* nor a given clause. Example: The given alias
25+
*
26+
* given a as TC = rhs
27+
*
28+
* is desugared to
29+
*
30+
* given def a: TC = rhs
31+
*
32+
* It is then expanded as follows:
33+
*
34+
* 1. If `rhs` is a simple name `x` (possibly with a `this.` prefix), leave the definition as is.
35+
* 2. Otherwise, wrap `rhs` in a call to `scala.compiletime.memo`.
36+
*/
37+
def memoizeGivenAlias(rhs: Tree, meth: Symbol) given Context : Tree = meth.info match {
38+
case ExprType(rhsType) if meth.is(Given, butNot = MemoizeGivenAliases.NoCacheFlags) =>
39+
// If rhs is a simple stable TermRef, leave as is.
40+
val needsMemo = rhs.tpe match {
41+
case rhsTpe @ TermRef(pre, _) if rhsTpe.isStable =>
42+
pre match {
43+
case NoPrefix => false
44+
case pre: ThisType => pre.cls != meth.owner.enclosingClass
45+
}
46+
case _ => true
47+
}
48+
if (needsMemo) {
49+
val memoized =
50+
untpd.Apply(untpd.ref(defn.Compiletime_memo.termRef), untpd.TypedSplice(rhs) :: Nil)
51+
typed(memoized, rhsType)
52+
}
53+
else rhs
54+
case _ => rhs
55+
}
56+
}
57+
58+
59+

compiler/src/dotty/tools/dotc/typer/Typer.scala

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@ class Typer extends Namer
8888
with Dynamic
8989
with Checking
9090
with QuotesAndSplices
91+
with MemoizeGivenAliases
9192
with Deriving {
9293

9394
import Typer._
@@ -1538,7 +1539,8 @@ class Typer extends Namer
15381539
}
15391540

15401541
if (sym.isInlineMethod) rhsCtx.addMode(Mode.InlineableBody)
1541-
val rhs1 = typedExpr(ddef.rhs, tpt1.tpe.widenExpr)(rhsCtx)
1542+
val rhs0 = typedExpr(ddef.rhs, tpt1.tpe.widenExpr)(rhsCtx)
1543+
val rhs1 = memoizeGivenAlias(rhs0, sym)
15421544

15431545
if (sym.isInlineMethod) {
15441546
PrepareInlineable.checkInlineMacro(sym, rhs1, ddef.sourcePos)

0 commit comments

Comments
 (0)