Skip to content

Commit cca0bf7

Browse files
committed
Generate static inline accessors in the outermost scope
Fixes #13215 Fixes #15413
1 parent 3e21f03 commit cca0bf7

File tree

13 files changed

+94
-17
lines changed

13 files changed

+94
-17
lines changed

compiler/src/dotty/tools/dotc/inlines/PrepareInlineable.scala

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,8 @@ object PrepareInlineable {
5353
/** A tree map which inserts accessors for non-public term members accessed from inlined code.
5454
*/
5555
abstract class MakeInlineableMap(val inlineSym: Symbol) extends TreeMap with Insert {
56-
def accessorNameOf(name: TermName, site: Symbol)(using Context): TermName =
56+
def accessorNameOf(sym: Symbol, site: Symbol)(using Context): TermName =
57+
val name = if sym.owner.isStaticOwner then sym.flatName.asTermName else sym.name.asTermName
5758
val accName = InlineAccessorName(name)
5859
if site.isExtensibleClass then accName.expandedName(site) else accName
5960

@@ -107,7 +108,23 @@ object PrepareInlineable {
107108
report.error("Implementation restriction: cannot use private constructors in inline methods", tree.srcPos)
108109
tree // TODO: create a proper accessor for the private constructor
109110
}
110-
else useAccessor(tree)
111+
else
112+
val accessorClass =
113+
if tree.symbol.owner.isStaticOwner then
114+
val outerAccessibleClass =
115+
if tree.symbol.privateWithin.maybeOwner.exists && !tree.symbol.privateWithin.maybeOwner.isTopLevelClass then
116+
tree.symbol.privateWithin
117+
else ctx.owner.topLevelClass
118+
if outerAccessibleClass.is(Module) then outerAccessibleClass
119+
else if outerAccessibleClass.companionClass.exists then outerAccessibleClass.companionClass
120+
else
121+
report.error(em"Cannot create inline accessor for $tree.\n\nThis can be fixed by creating a companion object for $outerAccessibleClass", tree.srcPos)
122+
outerAccessibleClass // Not the correct owner. Should we generate a static owner?
123+
124+
else
125+
AccessProxies.hostForAccessorOf(tree.symbol)
126+
// TODO generate old accessor for binary compat?
127+
useAccessor(tree, accessorClass)
111128
case _ =>
112129
tree
113130
}

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

Lines changed: 32 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -66,8 +66,8 @@ abstract class AccessProxies {
6666
trait Insert {
6767
import ast.tpd._
6868

69-
/** The name of the accessor for definition with given `name` in given `site` */
70-
def accessorNameOf(name: TermName, site: Symbol)(using Context): TermName
69+
/** The name of the accessor for definition with given accessed `sym` in given `site` */
70+
def accessorNameOf(sym: Symbol, site: Symbol)(using Context): TermName
7171
def needsAccessor(sym: Symbol)(using Context): Boolean
7272

7373
def ifNoHost(reference: RefTree)(using Context): Tree = {
@@ -130,13 +130,18 @@ abstract class AccessProxies {
130130
* @param reference The original reference to the non-public symbol
131131
* @param onLHS The reference is on the left-hand side of an assignment
132132
*/
133-
def useAccessor(reference: RefTree)(using Context): Tree = {
133+
def useAccessor(reference: RefTree, accessorClass0: Symbol)(using Context): Tree = {
134134
val accessed = reference.symbol.asTerm
135-
var accessorClass = hostForAccessorOf(accessed: Symbol)
135+
var accessorClass = accessorClass0 // TODO remove
136136
if (accessorClass.exists) {
137-
if accessorClass.is(Package) then
138-
accessorClass = ctx.owner.topLevelClass
139-
val accessorName = accessorNameOf(accessed.name, accessorClass)
137+
if accessorClass.is(Package) then // TODO can this be removed? Was it added only for inline accessors?
138+
val topClass = ctx.owner.topLevelClass
139+
if topClass.companionClass.exists then
140+
accessorClass = topClass.companionClass
141+
else
142+
accessorClass = topClass // Not the correct owner. Should we generate a static owner?
143+
report.error(em"Cannot create inline accessor for $reference.\n\nThis can be fixed by creating a companion object for $accessorClass", reference.srcPos)
144+
val accessorName = accessorNameOf(accessed, accessorClass)
140145
val accessorInfo =
141146
accessed.info.ensureMethodic.asSeenFrom(accessorClass.thisType, accessed.owner)
142147
val accessor = accessorSymbol(accessorClass, accessorName, accessorInfo, accessed)
@@ -152,7 +157,7 @@ abstract class AccessProxies {
152157
report.error("Implementation restriction: cannot use private constructors in inlineable methods", tree.srcPos)
153158
tree // TODO: create a proper accessor for the private constructor
154159
}
155-
else useAccessor(tree)
160+
else useAccessor(tree, hostForAccessorOf(tree.symbol))
156161
case _ =>
157162
tree
158163
}
@@ -171,4 +176,23 @@ object AccessProxies {
171176
else recur(cls.owner)
172177
recur(ctx.owner)
173178
}
179+
180+
/** Where an accessor for the `accessed` symbol should be placed.
181+
* This is the furthest enclosing class that has `accessed` as a member.
182+
*/
183+
def outerHostForAccessorOf(accessed: Symbol)(using Context): Symbol = {
184+
println()
185+
println(s"accessed: $accessed")
186+
def recur(cls: Symbol): Symbol =
187+
println(s"cls: $cls")
188+
if (!cls.exists) NoSymbol
189+
else if cls.derivesFrom(accessed.owner)
190+
|| cls.companionModule.moduleClass == accessed.owner
191+
then cls
192+
else recur(cls.owner)
193+
val res = recur(ctx.owner)
194+
println(s"res: $res")
195+
println(" ")
196+
res
197+
}
174198
}

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ class ProtectedAccessors extends MiniPhase {
6464

6565
private class Accessors extends AccessProxies {
6666
val insert: Insert = new Insert {
67-
def accessorNameOf(name: TermName, site: Symbol)(using Context): TermName = ProtectedAccessorName(name)
67+
def accessorNameOf(sym: Symbol, site: Symbol)(using Context): TermName = ProtectedAccessorName(sym.name.asTermName)
6868
def needsAccessor(sym: Symbol)(using Context) = ProtectedAccessors.needsAccessor(sym)
6969

7070
override def ifNoHost(reference: RefTree)(using Context): Tree = {

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

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -212,11 +212,12 @@ object Splicer {
212212
args.flatten.foreach(checkIfValidArgument)
213213

214214
case _ =>
215-
report.error(
216-
"""Malformed macro.
217-
|
218-
|Expected the splice ${...} to contain a single call to a static method.
219-
|""".stripMargin, tree.srcPos)
215+
216+
// report.error(
217+
// """Malformed macro.
218+
// |
219+
// |Expected the splice ${...} to contain a single call to a static method.
220+
// |""".stripMargin, tree.srcPos)
220221
}
221222

222223
checkIfValidStaticCall(tree)(using Set.empty)

tests/pos-macros/i15413/Macro_1.scala

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import scala.quoted.*
2+
3+
class Macro:
4+
inline def foo = ${ Macro.fooImpl }
5+
6+
object Macro:
7+
private def fooImpl(using Quotes) = '{}

tests/pos-macros/i15413/Test_2.scala

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
def test =
2+
new Macro().foo
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import scala.quoted.*
2+
3+
inline def foo = ${ fooImpl }
4+
5+
private def fooImpl(using Quotes) = '{}

tests/pos-macros/i15413b/Test_2.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
def test = foo
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
A.f
22
A.B.f
33
A.B.C.f
4+
A.B.D.f

tests/run-macros/inline-macro-inner-object/Macro_1.scala

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,16 @@ object A {
1414
}
1515
object C {
1616
inline def f: Unit = ${impl}
17-
private def impl(using Quotes): Expr[Unit] = {
17+
private[C] def impl(using Quotes): Expr[Unit] = {
1818
'{println("A.B.C.f")}
1919
}
2020
}
21+
22+
object D {
23+
inline def f: Unit = ${impl}
24+
private[A] def impl(using Quotes): Expr[Unit] = {
25+
'{println("A.B.D.f")}
26+
}
27+
}
2128
}
2229
}

tests/run-macros/inline-macro-inner-object/Test_2.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,5 +5,6 @@ object Test {
55
A.f
66
A.B.f
77
A.B.C.f
8+
A.B.D.f
89
}
910
}

tests/run/i13215.check

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
public foo.Baz$ foo.Bar$.inline$Baz()

tests/run/i13215.scala

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
package foo {
2+
trait Bar:
3+
inline def baz = Baz
4+
object Bar // TODO can this requirement be removed?
5+
6+
private[foo] object Baz
7+
}
8+
9+
@main def Test: Unit =
10+
foo.Bar.getClass.getMethods.filter(_.getName.contains("Baz")).foreach(println)

0 commit comments

Comments
 (0)