Skip to content

Commit

Permalink
Generate static inline accessors in the outermost scope
Browse files Browse the repository at this point in the history
  • Loading branch information
nicolasstucki committed Feb 1, 2023
1 parent 3e21f03 commit d3f782e
Show file tree
Hide file tree
Showing 12 changed files with 77 additions and 11 deletions.
10 changes: 8 additions & 2 deletions compiler/src/dotty/tools/dotc/inlines/PrepareInlineable.scala
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,8 @@ object PrepareInlineable {
/** A tree map which inserts accessors for non-public term members accessed from inlined code.
*/
abstract class MakeInlineableMap(val inlineSym: Symbol) extends TreeMap with Insert {
def accessorNameOf(name: TermName, site: Symbol)(using Context): TermName =
def accessorNameOf(sym: Symbol, site: Symbol)(using Context): TermName =
val name = if sym.owner.isStaticOwner then sym.flatName.asTermName else sym.name.asTermName
val accName = InlineAccessorName(name)
if site.isExtensibleClass then accName.expandedName(site) else accName

Expand Down Expand Up @@ -107,7 +108,12 @@ object PrepareInlineable {
report.error("Implementation restriction: cannot use private constructors in inline methods", tree.srcPos)
tree // TODO: create a proper accessor for the private constructor
}
else useAccessor(tree)
else
val accessorClass =
if tree.symbol.owner.isStaticOwner then AccessProxies.staticHostForAccessorOf(tree.symbol, tree.srcPos)
else AccessProxies.hostForAccessorOf(tree.symbol)
// TODO generate old accessor for binary compat?
useAccessor(tree, accessorClass)
case _ =>
tree
}
Expand Down
32 changes: 24 additions & 8 deletions compiler/src/dotty/tools/dotc/transform/AccessProxies.scala
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import TypeUtils._
import Types._
import util.Spans.Span
import config.Printers.transforms
import dotty.tools.dotc.util.SrcPos

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

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

def ifNoHost(reference: RefTree)(using Context): Tree = {
Expand Down Expand Up @@ -130,13 +131,10 @@ abstract class AccessProxies {
* @param reference The original reference to the non-public symbol
* @param onLHS The reference is on the left-hand side of an assignment
*/
def useAccessor(reference: RefTree)(using Context): Tree = {
def useAccessor(reference: RefTree, accessorClass: Symbol)(using Context): Tree = {
val accessed = reference.symbol.asTerm
var accessorClass = hostForAccessorOf(accessed: Symbol)
if (accessorClass.exists) {
if accessorClass.is(Package) then
accessorClass = ctx.owner.topLevelClass
val accessorName = accessorNameOf(accessed.name, accessorClass)
val accessorName = accessorNameOf(accessed, accessorClass)
val accessorInfo =
accessed.info.ensureMethodic.asSeenFrom(accessorClass.thisType, accessed.owner)
val accessor = accessorSymbol(accessorClass, accessorName, accessorInfo, accessed)
Expand All @@ -152,7 +150,7 @@ abstract class AccessProxies {
report.error("Implementation restriction: cannot use private constructors in inlineable methods", tree.srcPos)
tree // TODO: create a proper accessor for the private constructor
}
else useAccessor(tree)
else useAccessor(tree, hostForAccessorOf(tree.symbol))
case _ =>
tree
}
Expand All @@ -171,4 +169,22 @@ object AccessProxies {
else recur(cls.owner)
recur(ctx.owner)
}

/** Where an accessor for the `accessed` symbol should be placed.
* This is the furthest enclosing class that has `accessed` as a member.
*/
def staticHostForAccessorOf(accessed: Symbol, srcPos: SrcPos)(using Context): Symbol = {
val outerAccessibleClass =
if accessed.privateWithin.maybeOwner.exists && !accessed.privateWithin.maybeOwner.isTopLevelClass then
accessed.privateWithin
else ctx.owner.topLevelClass
if outerAccessibleClass.is(Module) then
outerAccessibleClass
else if outerAccessibleClass.companionClass.exists then
outerAccessibleClass.companionClass
else
// Should we generate a static owner? Generate companion class of the current top level class.
report.error(em"Cannot create inline accessor for $accessed.\n\nThis can be fixed by creating a companion object for ${outerAccessibleClass}", srcPos)
NoSymbol
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ class ProtectedAccessors extends MiniPhase {

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

override def ifNoHost(reference: RefTree)(using Context): Tree = {
Expand Down
7 changes: 7 additions & 0 deletions tests/pos-macros/i15413/Macro_1.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import scala.quoted.*

class Macro:
inline def foo = ${ Macro.fooImpl }

object Macro:
private def fooImpl(using Quotes) = '{}
2 changes: 2 additions & 0 deletions tests/pos-macros/i15413/Test_2.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
def test =
new Macro().foo
5 changes: 5 additions & 0 deletions tests/pos-macros/i15413b/Macro_1.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import scala.quoted.*

inline def foo = ${ fooImpl }

private def fooImpl(using Quotes) = '{}
1 change: 1 addition & 0 deletions tests/pos-macros/i15413b/Test_2.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
def test = foo
2 changes: 2 additions & 0 deletions tests/run-macros/inline-macro-inner-object.check
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
A.f
A.B.f
A.B.C.f
A.B.D.f
A.B.E.f
14 changes: 14 additions & 0 deletions tests/run-macros/inline-macro-inner-object/Macro_1.scala
Original file line number Diff line number Diff line change
Expand Up @@ -18,5 +18,19 @@ object A {
'{println("A.B.C.f")}
}
}

object D {
inline def f: Unit = ${impl}
private[D] def impl(using Quotes): Expr[Unit] = {
'{println("A.B.D.f")}
}
}

object E {
inline def f: Unit = ${impl}
private[A] def impl(using Quotes): Expr[Unit] = {
'{println("A.B.E.f")}
}
}
}
}
2 changes: 2 additions & 0 deletions tests/run-macros/inline-macro-inner-object/Test_2.scala
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,7 @@ object Test {
A.f
A.B.f
A.B.C.f
A.B.D.f
A.B.E.f
}
}
1 change: 1 addition & 0 deletions tests/run/i13215.check
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
public foo.Baz$ foo.Bar$.inline$Baz()
10 changes: 10 additions & 0 deletions tests/run/i13215.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package foo {
trait Bar:
inline def baz = Baz
object Bar // TODO can this requirement be removed?

private[foo] object Baz
}

@main def Test: Unit =
foo.Bar.getClass.getMethods.filter(_.getName.contains("Baz")).foreach(println)

0 comments on commit d3f782e

Please sign in to comment.