Skip to content

Commit 4258d8e

Browse files
authored
Merge pull request #4468 from dotty-staging/fix-#4322
Fix #4322: Avoid generating multiple inline accessors for the same ac…
2 parents 3e1ce3b + 8d8ce45 commit 4258d8e

File tree

11 files changed

+241
-194
lines changed

11 files changed

+241
-194
lines changed

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

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
package dotty.tools
22
package dotc
33

4-
import dotty.tools.dotc.core.Types.Type // Do not remove me #3383
4+
import core.Types.Type // Do not remove me #3383
55
import util.SourceFile
66
import ast.{tpd, untpd}
7-
import dotty.tools.dotc.ast.tpd.{ Tree, TreeTraverser }
7+
import tpd.{ Tree, TreeTraverser }
8+
import typer.Inliner.InlineAccessors
89
import dotty.tools.dotc.core.Contexts.Context
910
import dotty.tools.dotc.core.SymDenotations.ClassDenotation
1011
import dotty.tools.dotc.core.Symbols._
@@ -27,6 +28,9 @@ class CompilationUnit(val source: SourceFile) {
2728
* is used in phase ReifyQuotes in order to avoid traversing a quote-less tree.
2829
*/
2930
var containsQuotesOrSplices: Boolean = false
31+
32+
/** A structure containing a temporary map for generating inline accessors */
33+
val inlineAccessors = new InlineAccessors
3034
}
3135

3236
object CompilationUnit {

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -275,7 +275,6 @@ object NameKinds {
275275
}
276276

277277
/** Other unique names */
278-
val InlineAccessorName = new UniqueNameKind("$_inlineAccessor_$")
279278
val TempResultName = new UniqueNameKind("ev$")
280279
val EvidenceParamName = new UniqueNameKind("evidence$")
281280
val DepParamName = new UniqueNameKind("(param)")
@@ -356,6 +355,9 @@ object NameKinds {
356355
val InitializerName = new PrefixNameKind(INITIALIZER, "initial$")
357356
val ProtectedAccessorName = new PrefixNameKind(PROTECTEDACCESSOR, "protected$")
358357
val ProtectedSetterName = new PrefixNameKind(PROTECTEDSETTER, "protected$set") // dubious encoding, kept for Scala2 compatibility
358+
val InlineGetterName = new PrefixNameKind(INLINEGETTER, "inline_get$")
359+
val InlineSetterName = new PrefixNameKind(INLINESETTER, "inline_set$")
360+
359361
val AvoidClashName = new SuffixNameKind(AVOIDCLASH, "$_avoid_name_clash_$")
360362
val DirectMethodName = new SuffixNameKind(DIRECT, "$direct") { override def definesNewName = true }
361363
val FieldName = new SuffixNameKind(FIELD, "$$local") {

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

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15,23 +15,23 @@ object NameTags extends TastyFormat.NameTags {
1515
// outer accessor that will be filled in by ExplicitOuter.
1616
// <num> indicates the number of hops needed to select the outer field.
1717

18-
final val INITIALIZER = 24 // A mixin initializer method
18+
final val INITIALIZER = 26 // A mixin initializer method
1919

20-
final val AVOIDCLASH = 25 // Adds a suffix to avoid a name clash;
20+
final val AVOIDCLASH = 27 // Adds a suffix to avoid a name clash;
2121
// Used in FirstTransform for synthesized companion objects of classes
2222
// if they would clash with another value.
2323

24-
final val DIRECT = 26 // Used by ShortCutImplicits for the name of methods that
24+
final val DIRECT = 28 // Used by ShortCutImplicits for the name of methods that
2525
// implement implicit function result types directly.
2626

27-
final val FIELD = 27 // Used by Memoize to tag the name of a class member field.
27+
final val FIELD = 29 // Used by Memoize to tag the name of a class member field.
2828

29-
final val EXTMETH = 28 // Used by ExtensionMethods for the name of an extension method
29+
final val EXTMETH = 30 // Used by ExtensionMethods for the name of an extension method
3030
// implementing a value class method.
3131

32-
final val ADAPTEDCLOSURE = 29 // Used in Erasure to adapt closures over primitive types.
32+
final val ADAPTEDCLOSURE = 31 // Used in Erasure to adapt closures over primitive types.
3333

34-
final val IMPLMETH = 30 // Used to define methods in implementation classes
34+
final val IMPLMETH = 32 // Used to define methods in implementation classes
3535
// (can probably be removed).
3636

3737
def nameTagToString(tag: Int): String = tag match {

compiler/src/dotty/tools/dotc/core/tasty/TastyFormat.scala

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -227,7 +227,7 @@ object TastyFormat {
227227

228228
final val header = Array(0x5C, 0xA1, 0xAB, 0x1F)
229229
val MajorVersion = 7
230-
val MinorVersion = 0
230+
val MinorVersion = 1
231231

232232
/** Tags used to serialize names */
233233
class NameTags {
@@ -260,6 +260,10 @@ object TastyFormat {
260260

261261
final val OBJECTCLASS = 23 // The name of an object class (or: module class) `<name>$`.
262262

263+
final val INLINEGETTER = 24 // The name of an inline getter `inline_get$name`
264+
265+
final val INLINESETTER = 25 // The name of an inline setter `inline_set$name`
266+
263267
final val SIGNED = 63 // A pair of a name and a signature, used to idenitfy
264268
// possibly overloaded methods.
265269
}
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
package dotty.tools.dotc
2+
package transform
3+
4+
import core._
5+
import Contexts.Context
6+
import Symbols._
7+
import Flags._
8+
import Names._
9+
import Decorators._
10+
import TypeUtils._
11+
import Annotations.Annotation
12+
import Types._
13+
import NameKinds.ClassifiedNameKind
14+
import ast.Trees._
15+
import util.Property
16+
import util.Positions.Position
17+
18+
/** A utility class for generating access proxies. Currently used for
19+
* inline accessors and protected accessors.
20+
*/
21+
abstract class AccessProxies {
22+
import ast.tpd._
23+
24+
def getterName: ClassifiedNameKind
25+
def setterName: ClassifiedNameKind
26+
27+
/** accessor -> accessed */
28+
private val accessedBy = newMutableSymbolMap[Symbol]
29+
30+
/** The accessor definitions that need to be added to class `cls`
31+
* As a side-effect, this method removes entries from the `accessedBy` map.
32+
* So a second call of the same method will yield the empty list.
33+
*/
34+
private def accessorDefs(cls: Symbol)(implicit ctx: Context): Iterator[DefDef] =
35+
for (accessor <- cls.info.decls.iterator; accessed <- accessedBy.remove(accessor)) yield
36+
polyDefDef(accessor.asTerm, tps => argss => {
37+
val accessRef = ref(TermRef(cls.thisType, accessed))
38+
val rhs =
39+
if (accessor.name.is(setterName) &&
40+
argss.nonEmpty && argss.head.nonEmpty) // defensive conditions
41+
accessRef.becomes(argss.head.head)
42+
else
43+
accessRef.appliedToTypes(tps).appliedToArgss(argss)
44+
rhs.withPos(accessed.pos)
45+
})
46+
47+
/** Add all needed accessors to the `body` of class `cls` */
48+
def addAccessorDefs(cls: Symbol, body: List[Tree])(implicit ctx: Context): List[Tree] = {
49+
val accDefs = accessorDefs(cls)
50+
if (accDefs.isEmpty) body else body ++ accDefs
51+
}
52+
53+
trait Insert {
54+
import ast.tpd._
55+
56+
def needsAccessor(sym: Symbol)(implicit ctx: Context): Boolean
57+
58+
/** A fresh accessor symbol */
59+
def newAccessorSymbol(accessed: Symbol, name: TermName, info: Type)(implicit ctx: Context): TermSymbol =
60+
ctx.newSymbol(accessed.owner.enclosingSubClass, name, Synthetic | Method,
61+
info, coord = accessed.pos).entered
62+
63+
/** Create an accessor unless one exists already, and replace the original
64+
* access with a reference to the accessor.
65+
*
66+
* @param reference The original reference to the non-public symbol
67+
* @param onLHS The reference is on the left-hand side of an assignment
68+
*/
69+
def useAccessor(reference: RefTree, onLHS: Boolean)(implicit ctx: Context): Tree = {
70+
71+
def nameKind = if (onLHS) setterName else getterName
72+
val accessed = reference.symbol.asTerm
73+
74+
def refersToAccessed(sym: Symbol) = accessedBy.get(sym) == Some(accessed)
75+
76+
val accessorInfo =
77+
if (onLHS) MethodType(accessed.info :: Nil, defn.UnitType)
78+
else accessed.info.ensureMethodic
79+
val accessorName = nameKind(accessed.name)
80+
val accessorSymbol =
81+
accessed.owner.info.decl(accessorName).suchThat(refersToAccessed).symbol
82+
.orElse {
83+
val acc = newAccessorSymbol(accessed, accessorName, accessorInfo)
84+
accessedBy(acc) = accessed
85+
acc
86+
}
87+
88+
{ reference match {
89+
case Select(qual, _) => qual.select(accessorSymbol)
90+
case Ident(name) => ref(accessorSymbol)
91+
}
92+
}.withPos(reference.pos)
93+
}
94+
95+
/** Replace tree with a reference to an accessor if needed */
96+
def accessorIfNeeded(tree: Tree)(implicit ctx: Context): Tree = tree match {
97+
case tree: RefTree if needsAccessor(tree.symbol) =>
98+
if (tree.symbol.isConstructor) {
99+
ctx.error("Implementation restriction: cannot use private constructors in inline methods", tree.pos)
100+
tree // TODO: create a proper accessor for the private constructor
101+
}
102+
else useAccessor(tree, onLHS = false)
103+
case Assign(lhs: RefTree, rhs) if needsAccessor(lhs.symbol) =>
104+
cpy.Apply(tree)(useAccessor(lhs, onLHS = true), List(rhs))
105+
case _ =>
106+
tree
107+
}
108+
}
109+
}

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

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import typer.ProtoTypes._
2020
import typer.ErrorReporting._
2121
import core.TypeErasure._
2222
import core.Decorators._
23+
import core.NameKinds._
2324
import dotty.tools.dotc.ast.{Trees, tpd, untpd}
2425
import ast.Trees._
2526
import scala.collection.mutable.ListBuffer
@@ -30,7 +31,6 @@ import ExplicitOuter._
3031
import core.Mode
3132
import reporting.trace
3233

33-
3434
class Erasure extends Phase with DenotTransformer {
3535

3636
override def phaseName: String = Erasure.name
@@ -315,6 +315,10 @@ object Erasure {
315315
}
316316
}
317317

318+
/** The erasure typer.
319+
* Also inserts protected accessors where needed. This logic is placed here
320+
* since it is most naturally done in a macro transform.
321+
*/
318322
class Typer extends typer.ReTyper with NoChecking {
319323
import Boxing._
320324

@@ -323,6 +327,23 @@ object Erasure {
323327
if (tree.isTerm) erasedRef(tp) else valueErasure(tp)
324328
}
325329

330+
object ProtectedAccessors extends AccessProxies {
331+
def getterName = ProtectedAccessorName
332+
def setterName = ProtectedSetterName
333+
334+
val insert = new Insert {
335+
def needsAccessor(sym: Symbol)(implicit ctx: Context): Boolean =
336+
false &&
337+
sym.isTerm && sym.is(Flags.Protected) &&
338+
ctx.owner.enclosingPackageClass != sym.enclosingPackageClass &&
339+
!ctx.owner.enclosingClass.derivesFrom(sym.owner) &&
340+
{ println(i"need protected acc $sym accessed from ${ctx.owner}"); assert(false); false }
341+
}
342+
}
343+
344+
override def addAccessorDefs(cls: Symbol, body: List[Tree])(implicit ctx: Context): List[Tree] =
345+
ProtectedAccessors.addAccessorDefs(cls, body)
346+
326347
override def promote(tree: untpd.Tree)(implicit ctx: Context): tree.ThisTree[Type] = {
327348
assert(tree.hasType)
328349
val erasedTp = erasedType(tree)
@@ -357,6 +378,9 @@ object Erasure {
357378
else
358379
super.typedLiteral(tree)
359380

381+
override def typedIdent(tree: untpd.Ident, pt: Type)(implicit ctx: Context): Tree =
382+
ProtectedAccessors.insert.accessorIfNeeded(super.typedIdent(tree, pt))
383+
360384
/** Type check select nodes, applying the following rewritings exhaustively
361385
* on selections `e.m`, where `OT` is the type of the owner of `m` and `ET`
362386
* is the erased type of the selection's original qualifier expression.
@@ -437,9 +461,12 @@ object Erasure {
437461
}
438462
}
439463

440-
recur(typed(tree.qualifier, AnySelectionProto))
464+
ProtectedAccessors.insert.accessorIfNeeded(recur(typed(tree.qualifier, AnySelectionProto)))
441465
}
442466

467+
override def typedAssign(tree: untpd.Assign, pt: Type)(implicit ctx: Context): Tree =
468+
ProtectedAccessors.insert.accessorIfNeeded(super.typedAssign(tree, pt))
469+
443470
override def typedThis(tree: untpd.This)(implicit ctx: Context): Tree =
444471
if (tree.symbol == ctx.owner.lexicallyEnclosingClass || tree.symbol.isStaticOwner) promote(tree)
445472
else {

0 commit comments

Comments
 (0)