@@ -15,7 +15,7 @@ import StdNames.nme
1515import Contexts .Context
1616import Names .{Name , TermName , EmptyTermName }
1717import NameOps ._
18- import NameKinds .{ClassifiedNameKind , InlineAccessorName }
18+ import NameKinds .{ClassifiedNameKind , InlineAccessorName , UniqueInlineName }
1919import ProtoTypes .selectionProto
2020import SymDenotations .SymDenotation
2121import Annotations ._
@@ -33,10 +33,18 @@ object Inliner {
3333
3434 class InlineAccessors extends AccessProxies {
3535
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 {
4048 def accessorNameKind = InlineAccessorName
4149
4250 /** A definition needs an accessor if it is private, protected, or qualified private
@@ -48,18 +56,126 @@ object Inliner {
4856 (sym.is(AccessFlags ) || sym.privateWithin.exists) &&
4957 ! sym.isContainedIn(inlineSym)
5058
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+ }
5167
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.
5668 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+ }
63179 }
64180
65181 /** Adds accessors for all non-public term members accessed
@@ -76,7 +192,9 @@ object Inliner {
76192 // Inline methods in local scopes can only be called in the scope they are defined,
77193 // so no accessors are needed for them.
78194 tree
79- else new MakeInlineable (inlineSym).transform(tree)
195+ else
196+ new MakeInlineablePassing (inlineSym).transform(
197+ new MakeInlineableDirect (inlineSym).transform(tree))
80198 }
81199 }
82200
0 commit comments