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+ }
0 commit comments