Skip to content

Commit d3f782e

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

File tree

12 files changed

+77
-11
lines changed

12 files changed

+77
-11
lines changed

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

+8-2
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,12 @@ 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 AccessProxies.staticHostForAccessorOf(tree.symbol, tree.srcPos)
114+
else AccessProxies.hostForAccessorOf(tree.symbol)
115+
// TODO generate old accessor for binary compat?
116+
useAccessor(tree, accessorClass)
111117
case _ =>
112118
tree
113119
}

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

+24-8
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import TypeUtils._
1313
import Types._
1414
import util.Spans.Span
1515
import config.Printers.transforms
16+
import dotty.tools.dotc.util.SrcPos
1617

1718
/** A utility class for generating access proxies. Currently used for
1819
* inline accessors and protected accessors.
@@ -66,8 +67,8 @@ abstract class AccessProxies {
6667
trait Insert {
6768
import ast.tpd._
6869

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

7374
def ifNoHost(reference: RefTree)(using Context): Tree = {
@@ -130,13 +131,10 @@ abstract class AccessProxies {
130131
* @param reference The original reference to the non-public symbol
131132
* @param onLHS The reference is on the left-hand side of an assignment
132133
*/
133-
def useAccessor(reference: RefTree)(using Context): Tree = {
134+
def useAccessor(reference: RefTree, accessorClass: Symbol)(using Context): Tree = {
134135
val accessed = reference.symbol.asTerm
135-
var accessorClass = hostForAccessorOf(accessed: Symbol)
136136
if (accessorClass.exists) {
137-
if accessorClass.is(Package) then
138-
accessorClass = ctx.owner.topLevelClass
139-
val accessorName = accessorNameOf(accessed.name, accessorClass)
137+
val accessorName = accessorNameOf(accessed, accessorClass)
140138
val accessorInfo =
141139
accessed.info.ensureMethodic.asSeenFrom(accessorClass.thisType, accessed.owner)
142140
val accessor = accessorSymbol(accessorClass, accessorName, accessorInfo, accessed)
@@ -152,7 +150,7 @@ abstract class AccessProxies {
152150
report.error("Implementation restriction: cannot use private constructors in inlineable methods", tree.srcPos)
153151
tree // TODO: create a proper accessor for the private constructor
154152
}
155-
else useAccessor(tree)
153+
else useAccessor(tree, hostForAccessorOf(tree.symbol))
156154
case _ =>
157155
tree
158156
}
@@ -171,4 +169,22 @@ object AccessProxies {
171169
else recur(cls.owner)
172170
recur(ctx.owner)
173171
}
172+
173+
/** Where an accessor for the `accessed` symbol should be placed.
174+
* This is the furthest enclosing class that has `accessed` as a member.
175+
*/
176+
def staticHostForAccessorOf(accessed: Symbol, srcPos: SrcPos)(using Context): Symbol = {
177+
val outerAccessibleClass =
178+
if accessed.privateWithin.maybeOwner.exists && !accessed.privateWithin.maybeOwner.isTopLevelClass then
179+
accessed.privateWithin
180+
else ctx.owner.topLevelClass
181+
if outerAccessibleClass.is(Module) then
182+
outerAccessibleClass
183+
else if outerAccessibleClass.companionClass.exists then
184+
outerAccessibleClass.companionClass
185+
else
186+
// Should we generate a static owner? Generate companion class of the current top level class.
187+
report.error(em"Cannot create inline accessor for $accessed.\n\nThis can be fixed by creating a companion object for ${outerAccessibleClass}", srcPos)
188+
NoSymbol
189+
}
174190
}

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

+1-1
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 = {

tests/pos-macros/i15413/Macro_1.scala

+7
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

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
def test =
2+
new Macro().foo
+5
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

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
def test = foo
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
11
A.f
22
A.B.f
33
A.B.C.f
4+
A.B.D.f
5+
A.B.E.f

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

+14
Original file line numberDiff line numberDiff line change
@@ -18,5 +18,19 @@ object A {
1818
'{println("A.B.C.f")}
1919
}
2020
}
21+
22+
object D {
23+
inline def f: Unit = ${impl}
24+
private[D] def impl(using Quotes): Expr[Unit] = {
25+
'{println("A.B.D.f")}
26+
}
27+
}
28+
29+
object E {
30+
inline def f: Unit = ${impl}
31+
private[A] def impl(using Quotes): Expr[Unit] = {
32+
'{println("A.B.E.f")}
33+
}
34+
}
2135
}
2236
}

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

+2
Original file line numberDiff line numberDiff line change
@@ -5,5 +5,7 @@ object Test {
55
A.f
66
A.B.f
77
A.B.C.f
8+
A.B.D.f
9+
A.B.E.f
810
}
911
}

tests/run/i13215.check

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
public foo.Baz$ foo.Bar$.inline$Baz()

tests/run/i13215.scala

+10
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)