@@ -15,7 +15,7 @@ import StdNames.nme
15
15
import Contexts .Context
16
16
import Names .{Name , TermName , EmptyTermName }
17
17
import NameOps ._
18
- import NameKinds .{ClassifiedNameKind , InlineAccessorName }
18
+ import NameKinds .{ClassifiedNameKind , InlineAccessorName , UniqueInlineName }
19
19
import ProtoTypes .selectionProto
20
20
import SymDenotations .SymDenotation
21
21
import Annotations ._
@@ -33,10 +33,18 @@ object Inliner {
33
33
34
34
class InlineAccessors extends AccessProxies {
35
35
36
- /** A tree map which inserts accessors for all non-public term members accessed
37
- * from inlined code. Accessors are collected in the `accessors` buffer.
38
- */
39
- class MakeInlineable (inlineSym : Symbol ) extends TreeMap with Insert {
36
+ /** If an inline accessor name wraps a unique inline name, this is taken as indication
37
+ * that the inline accessor takes its receiver as first parameter. Such accessors
38
+ * are created by MakeInlineablePassing.
39
+ */
40
+ override def passReceiverAsArg (name : Name )(implicit ctx : Context ) = name match {
41
+ case InlineAccessorName (UniqueInlineName (_, _)) => true
42
+ case _ => false
43
+ }
44
+
45
+ /** A tree map which inserts accessors for non-public term members accessed from inlined code.
46
+ */
47
+ abstract class MakeInlineableMap (val inlineSym : Symbol ) extends TreeMap with Insert {
40
48
def accessorNameKind = InlineAccessorName
41
49
42
50
/** A definition needs an accessor if it is private, protected, or qualified private
@@ -48,18 +56,126 @@ object Inliner {
48
56
(sym.is(AccessFlags ) || sym.privateWithin.exists) &&
49
57
! sym.isContainedIn(inlineSym)
50
58
59
+ def preTransform (tree : Tree )(implicit ctx : Context ): Tree
60
+
61
+ def postTransform (tree : Tree )(implicit ctx : Context ) = tree match {
62
+ case Assign (lhs, rhs) if lhs.symbol.name.is(InlineAccessorName ) =>
63
+ cpy.Apply (tree)(useSetter(lhs), rhs :: Nil )
64
+ case _ =>
65
+ tree
66
+ }
51
67
52
- // TODO: Also handle references to non-public types.
53
- // This is quite tricky, as such types can appear anywhere, including as parts
54
- // of types of other things. For the moment we do nothing and complain
55
- // at the implicit expansion site if there's a reference to an inaccessible type.
56
68
override def transform (tree : Tree )(implicit ctx : Context ): Tree =
57
- super .transform(accessorIfNeeded(tree)) match {
58
- case tree1 @ Assign (lhs : RefTree , rhs) if lhs.symbol.name.is(InlineAccessorName ) =>
59
- cpy.Apply (tree1)(useSetter(lhs), rhs :: Nil )
60
- case tree1 =>
61
- tree1
62
- }
69
+ postTransform(super .transform(preTransform(tree)))
70
+ }
71
+
72
+ /** Direct approach: place the accessor with the accessed symbol. This has the
73
+ * advantage that we can re-use the receiver as is. But it is only
74
+ * possible if the receiver is essentially this or an outer this, which is indicated
75
+ * by the test that we can find a host for the accessor.
76
+ */
77
+ class MakeInlineableDirect (inlineSym : Symbol ) extends MakeInlineableMap (inlineSym) {
78
+ def preTransform (tree : Tree )(implicit ctx : Context ): Tree = tree match {
79
+ case tree : RefTree if needsAccessor(tree.symbol) =>
80
+ if (tree.symbol.isConstructor) {
81
+ ctx.error(" Implementation restriction: cannot use private constructors in inline methods" , tree.pos)
82
+ tree // TODO: create a proper accessor for the private constructor
83
+ }
84
+ else if (AccessProxies .hostForAccessorOf(tree.symbol).exists) useAccessor(tree)
85
+ else tree
86
+ case _ =>
87
+ tree
88
+ }
89
+ }
90
+
91
+ /** Fallback approach if the direct approach does not work: Place the accessor method
92
+ * in the same class as the inlined method, and let it take the receiver as parameter.
93
+ * This is tricky, since we have to find a suitable type for the parameter, which might
94
+ * require additional type parameters for the inline accessor. An example is in the
95
+ * `TestPassing` class in test `run/inline/inlines_1`:
96
+ *
97
+ * class C[T](x: T) {
98
+ * private[inlines] def next[U](y: U): (T, U) = (x, y)
99
+ * }
100
+ * class TestPassing {
101
+ * inline def foo[A](x: A): (A, Int) = {
102
+ * val c = new C[A](x)
103
+ * c.next(1)
104
+ * }
105
+ * inline def bar[A](x: A): (A, String) = {
106
+ * val c = new C[A](x)
107
+ * c.next("")
108
+ * }
109
+ *
110
+ * `C` could be compiled separately, so we cannot place the inline accessor in it.
111
+ * Instead, the inline accessor goes into `TestPassing` and takes the actual receiver
112
+ * type as argument:
113
+ *
114
+ * def inline$next$i1[A, U](x$0: C[A])(y: U): (A, U) =
115
+ * x$0.next[U](y)
116
+ *
117
+ * Since different calls might have different receiver types, we need to generate one
118
+ * such accessor per call, so they need to have unique names.
119
+ */
120
+ class MakeInlineablePassing (inlineSym : Symbol ) extends MakeInlineableMap (inlineSym) {
121
+
122
+ def preTransform (tree : Tree )(implicit ctx : Context ): Tree = tree match {
123
+ case _ : Apply | _ : TypeApply | _ : RefTree
124
+ if needsAccessor(tree.symbol) && tree.isTerm && ! tree.symbol.isConstructor =>
125
+ val (refPart, targs, argss) = decomposeCall(tree)
126
+ val qual = qualifier(refPart)
127
+ inlining.println(i " adding receiver passing inline accessor for $tree -> ( ${qual.tpe}, $refPart: ${refPart.getClass}, [ $targs%, %], ( $argss%, %)) " )
128
+
129
+ // Need to dealias in order to cagtch all possible references to abstracted over types in
130
+ // substitutions
131
+ val dealiasMap = new TypeMap {
132
+ def apply (t : Type ) = mapOver(t.dealias)
133
+ }
134
+ val qualType = dealiasMap(qual.tpe.widen)
135
+
136
+ // The types that are local to the inlined method, and that therefore have
137
+ // to be abstracted out in the accessor, which is external to the inlined method
138
+ val localRefs = qualType.namedPartsWith(ref =>
139
+ ref.isType && ref.symbol.isContainedIn(inlineSym)).toList
140
+
141
+ // Add qualifier type as leading method argument to argument `tp`
142
+ def addQualType (tp : Type ): Type = tp match {
143
+ case tp : PolyType => tp.derivedLambdaType(tp.paramNames, tp.paramInfos, addQualType(tp.resultType))
144
+ case tp : ExprType => addQualType(tp.resultType)
145
+ case tp => MethodType (qualType :: Nil , tp)
146
+ }
147
+
148
+ // Abstract accessed type over local refs
149
+ def abstractQualType (mtpe : Type ): Type =
150
+ if (localRefs.isEmpty) mtpe
151
+ else PolyType .fromParams(localRefs.map(_.symbol.asType), mtpe)
152
+ .asInstanceOf [PolyType ].flatten
153
+
154
+ val accessed = refPart.symbol.asTerm
155
+ val accessedType = refPart.tpe.widen
156
+ val accessor = accessorSymbol(
157
+ owner = inlineSym.owner,
158
+ accessorName = InlineAccessorName (UniqueInlineName .fresh(accessed.name)),
159
+ accessorInfo = abstractQualType(addQualType(dealiasMap(accessedType))),
160
+ accessed = accessed)
161
+
162
+ ref(accessor)
163
+ .appliedToTypeTrees(localRefs.map(TypeTree (_)) ++ targs)
164
+ .appliedToArgss((qual :: Nil ) :: argss)
165
+ .withPos(tree.pos)
166
+
167
+ // TODO: Handle references to non-public types.
168
+ // This is quite tricky, as such types can appear anywhere, including as parts
169
+ // of types of other things. For the moment we do nothing and complain
170
+ // at the implicit expansion site if there's a reference to an inaccessible type.
171
+ // Draft code (incomplete):
172
+ //
173
+ // val accessor = accessorSymbol(tree, TypeAlias(tree.tpe)).asType
174
+ // myAccessors += TypeDef(accessor).withPos(tree.pos.focus)
175
+ // ref(accessor).withPos(tree.pos)
176
+ //
177
+ case _ => tree
178
+ }
63
179
}
64
180
65
181
/** Adds accessors for all non-public term members accessed
@@ -76,7 +192,9 @@ object Inliner {
76
192
// Inline methods in local scopes can only be called in the scope they are defined,
77
193
// so no accessors are needed for them.
78
194
tree
79
- else new MakeInlineable (inlineSym).transform(tree)
195
+ else
196
+ new MakeInlineablePassing (inlineSym).transform(
197
+ new MakeInlineableDirect (inlineSym).transform(tree))
80
198
}
81
199
}
82
200
0 commit comments