diff --git a/compiler/src/dotty/tools/dotc/core/StdNames.scala b/compiler/src/dotty/tools/dotc/core/StdNames.scala index 92f2e55a49bf..1049a23887c5 100644 --- a/compiler/src/dotty/tools/dotc/core/StdNames.scala +++ b/compiler/src/dotty/tools/dotc/core/StdNames.scala @@ -21,6 +21,7 @@ object StdNames { inline val AVOID_CLASH_SUFFIX = "$_avoid_name_clash_$" inline val MODULE_SUFFIX = "$" inline val TOPLEVEL_SUFFIX = "$package" + inline val TOPLEVEL_INLINE_SUFFIX = "$inline$accessors" inline val NAME_JOIN = "$" inline val DEFAULT_GETTER = "$default$" inline val LOCALDUMMY_PREFIX = " inlineAccessors.addAccessorDefs(cls, body) case _ => body + def inlineAccessorsModule(topLevelClass: Symbol)(using Context): Symbol = + assert(topLevelClass.asClass.owner.is(Package), topLevelClass) + ctx.property(InlineAccessorsModuleKey).get.getOrElse(topLevelClass, NoSymbol) + + def requiredInlineAccessorsModule(topLevelClass: Symbol)(using Context): Symbol = + assert(topLevelClass.asClass.owner.is(Package), topLevelClass) + ctx.property(InlineAccessorsModuleKey) match + case Some(inlineAccessorsModule) => + inlineAccessorsModule.getOrElseUpdate( + topLevelClass, + newAccessorModule(str.TOPLEVEL_INLINE_SUFFIX)) + case None => NoSymbol + class InlineAccessors extends AccessProxies { /** If an inline accessor name wraps a unique inline name, this is taken as indication @@ -99,20 +121,32 @@ object PrepareInlineable { * advantage that we can re-use the receiver as is. But it is only * possible if the receiver is essentially this or an outer this, which is indicated * by the test that we can find a host for the accessor. + * + * @param inlineSym symbol of the inline method + * @param compat use inline accessor format of 3.0-3.2 */ - class MakeInlineableDirect(inlineSym: Symbol) extends MakeInlineableMap(inlineSym) { + class MakeInlineableDirect(inlineSym: Symbol, compat: Boolean) extends MakeInlineableMap(inlineSym) { def preTransform(tree: Tree)(using Context): Tree = tree match { case tree: RefTree if needsAccessor(tree.symbol) => if tree.symbol.isConstructor then report.error("Implementation restriction: cannot use private constructors in inline methods", tree.srcPos) tree // TODO: create a proper accessor for the private constructor - else + else if compat then + // Generate the accessor for backwards compatibility with 3.0-3.3 val nearestHost = AccessProxies.hostForAccessorOf(tree.symbol) val host = if nearestHost.is(Package) then ctx.owner.topLevelClass else nearestHost useAccessor(tree, host) + else + // Generate the accessor for 3.4+ + val nearestHost = AccessProxies.hostForAccessorOf(tree.symbol) + if nearestHost.is(Package) || (tree.symbol.owner.isStaticOwner && !nearestHost.isStaticOwner) then + useAccessor(tree, requiredInlineAccessorsModule(ctx.owner.topLevelClass)) + else + useAccessor(tree, nearestHost) case _ => tree } + override def ifNoHost(reference: RefTree)(using Context): Tree = reference } @@ -228,8 +262,12 @@ object PrepareInlineable { // so no accessors are needed for them. tree else + // Generate inline accessors for 3.0-3.3 + new MakeInlineablePassing(inlineSym).transform( + new MakeInlineableDirect(inlineSym, compat = true).transform(tree)) + // Generate and use inline accessors for 3.4+ new MakeInlineablePassing(inlineSym).transform( - new MakeInlineableDirect(inlineSym).transform(tree)) + new MakeInlineableDirect(inlineSym, compat = false).transform(tree)) } } diff --git a/compiler/src/dotty/tools/dotc/quoted/Interpreter.scala b/compiler/src/dotty/tools/dotc/quoted/Interpreter.scala index 38cecb7953b8..601107d98d11 100644 --- a/compiler/src/dotty/tools/dotc/quoted/Interpreter.scala +++ b/compiler/src/dotty/tools/dotc/quoted/Interpreter.scala @@ -349,9 +349,10 @@ object Interpreter: if !ctx.compilationUnit.isSuspendable then None else targetException match case _: NoClassDefFoundError | _: ClassNotFoundException => - val className = targetException.getMessage - if className eq null then None + val msg = targetException.getMessage + if msg eq null then None else + val className = msg.stripSuffix(str.TOPLEVEL_INLINE_SUFFIX + str.MODULE_SUFFIX) val sym = staticRef(className.toTypeName).symbol if (sym.isDefinedInCurrentRun) Some(sym) else None case _ => None diff --git a/compiler/src/dotty/tools/dotc/typer/TopLevelExtensionModules.scala b/compiler/src/dotty/tools/dotc/typer/TopLevelExtensionModules.scala new file mode 100644 index 000000000000..1de6cc52dd1c --- /dev/null +++ b/compiler/src/dotty/tools/dotc/typer/TopLevelExtensionModules.scala @@ -0,0 +1,76 @@ +package dotty.tools.dotc.typer + +import dotty.tools.dotc.ast.*, untpd.* +import dotty.tools.dotc.core.Contexts.* +import dotty.tools.dotc.core.Flags.* +import dotty.tools.dotc.core.Names.* +import dotty.tools.dotc.core.Scopes.* +import dotty.tools.dotc.core.StdNames.* +import dotty.tools.dotc.core.Symbols.* + +/** Creation of top-level extension modules */ +object TopLevelExtensionModules: + import tpd.* + + /** Creates the symbols for a new extension module associated with the current + * top-level class. These definitions are invisible in the source code. + * + * ```scala + * class C + * // symbols generated by `newAccessorModule("$extension")` + * lazy val C$extension = new C$extension$ + * class C$extension$ + * ``` + * + * @param suffix suffix that will be appended to the name of the top-level class + * @return The class symbol of the new module + */ + def newAccessorModule(suffix: String)(using Context): ClassSymbol = + val inlineAccessorObjectName: TermName = + assert(suffix.startsWith("$"), "suffix should start with $") + assert(suffix.size > 1, "suffix should start with $ followed by a name") + val fileName = ctx.source.file.name + val sourceName = ctx.owner.topLevelClass.name + (sourceName ++ suffix).toTermName + + val mod = newNormalizedModuleSymbol( + ctx.owner.topLevelClass.owner, + inlineAccessorObjectName, + ModuleValCreationFlags & Invisible, + ModuleClassCreationFlags & Invisible, + List(defn.ObjectClass.typeRef), + newScope, + NoSymbol, + coord = ctx.owner.topLevelClass.span + ) + val cls = mod.moduleClass.asClass + cls.enter(newConstructor(cls, Synthetic, Nil, Nil)) + cls + + /** Generate a list with the ValDef and TypeDef trees of an extension module (created with `newAccessorModule`). + * + * ```scala + * // given the moduleClassSym for `C$extension$` and `body` this generates the trees + * lazy val C$extension = new C$extension$ + * class C$extension$: + * + * ``` + * @param moduleClassSym class symbol of the extension module + * @param body list of definitions in the extension module + */ + def topLevelModuleDefTree(moduleClassSym: ClassSymbol, body: List[Tree])(using Context): List[Tree] = + assert(moduleClassSym.owner.is(Package)) + val untpdCtr = untpd.DefDef(nme.CONSTRUCTOR, Nil, tpd.TypeTree(defn.UnitClass.typeRef), tpd.EmptyTree) + val ctr = ctx.typeAssigner.assignType(untpdCtr, moduleClassSym.primaryConstructor) + + val parents = List(TypeTree(defn.ObjectClass.typeRef)) + val clsDef = + tpd.ClassDefWithParents(moduleClassSym.asClass, ctr, parents, body) + .withSpan(moduleClassSym.span) + + val newCls = + Apply(New(ref(moduleClassSym)).select(moduleClassSym.primaryConstructor), Nil) + val modVal = + ValDef(moduleClassSym.companionModule.asTerm, newCls).withSpan(clsDef.span) + .withSpan(moduleClassSym.companionModule.span) + List(modVal, clsDef) diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index a87d6dd7e703..77f67cbfb603 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -34,6 +34,8 @@ import util.Spans._ import util.common._ import util.{Property, SimpleIdentityMap, SrcPos} import Applications.{tupleComponentTypes, wrapDefs, defaultArgument} +import TopLevelExtensionModules.topLevelModuleDefTree + import collection.mutable import annotation.tailrec @@ -2713,6 +2715,16 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer pkg.moduleClass.info.decls.lookup(topLevelClassName).ensureCompleted() var stats1 = typedStats(tree.stats, pkg.moduleClass)._1 if (!ctx.isAfterTyper) + val inlineAccessorClasses = stats1.view.collect { + case tdef @ TypeDef(name, rhs) if tdef.symbol.isClass && PrepareInlineable.inlineAccessorsModule(tdef.symbol).exists => + val inlineAccessorsModuleClass = PrepareInlineable.inlineAccessorsModule(tdef.symbol) + topLevelModuleDefTree( + inlineAccessorsModuleClass.asClass, + addAccessorDefs(inlineAccessorsModuleClass, Nil) + ) + }.flatten + stats1 = stats1 ++ inlineAccessorClasses + stats1 = stats1 ++ typedBlockStats(MainProxies.proxies(stats1))._1 cpy.PackageDef(tree)(pid1, stats1).withType(pkg.termRef) } diff --git a/compiler/test/dotty/tools/backend/jvm/DottyBytecodeTests.scala b/compiler/test/dotty/tools/backend/jvm/DottyBytecodeTests.scala index ac4ba3ee0e75..a5156a88ac93 100644 --- a/compiler/test/dotty/tools/backend/jvm/DottyBytecodeTests.scala +++ b/compiler/test/dotty/tools/backend/jvm/DottyBytecodeTests.scala @@ -1682,6 +1682,83 @@ class DottyBytecodeTests extends DottyBytecodeTest { assertSameCode(instructions, expected) } } + + @Test + def i13215(): Unit = { + val code = + """package foo: + | trait Bar: + | inline def baz = Baz + | private[foo] object Baz + """.stripMargin + checkBCode(code) { dir => + val privateAccessors = Opcodes.ACC_PRIVATE | Opcodes.ACC_PROTECTED + + // For 3.0-3.3 compat + val barClass = loadClassNode(dir.subdirectoryNamed("foo").lookupName("Bar.class", directory = false).input, skipDebugInfo = false) + val accessorOld = getMethod(barClass, "foo$Bar$$inline$Baz") + assert(accessorOld.signature == "()Lfoo/Baz$;", accessorOld.signature) + assert((accessorOld.access & privateAccessors) == 0) + + // For 3.4+ + val accessorsClass = loadClassNode(dir.subdirectoryNamed("foo").lookupName("Bar$inline$accessors.class", directory = false).input, skipDebugInfo = false) + val accessorNew = getMethod(accessorsClass, "inline$Baz") + assert(accessorNew.signature == "()Lfoo/Baz$;", accessorNew.signature) + assert((accessorNew.access & privateAccessors) == 0) + } + } + + @Test + def i15413(): Unit = { + val code = + """import scala.quoted.* + |class Macro: + | inline def foo = Macro.fooImpl + |object Macro: + | private def fooImpl = {} + """.stripMargin + checkBCode(code) { dir => + val privateAccessors = Opcodes.ACC_PRIVATE | Opcodes.ACC_PROTECTED + + // For 3.0-3.3 compat + val macroClass = loadClassNode(dir.lookupName("Macro.class", directory = false).input, skipDebugInfo = false) + val accessorOld = getMethod(macroClass, "Macro$$inline$fooImpl") + assert(accessorOld.signature == "()V") + assert((accessorOld.access & privateAccessors) == 0) + + // For 3.4+ + val accessorsClass = loadClassNode(dir.lookupName("Macro$inline$accessors.class", directory = false).input, skipDebugInfo = false) + val accessorNew = getMethod(accessorsClass, "inline$fooImpl") + assert(accessorNew.signature == "()V") + assert((accessorNew.access & privateAccessors) == 0) + } + } + + @Test + def i15413b(): Unit = { + val code = + """package foo + |class C: + | inline def baz = D.bazImpl + |object D: + | private[foo] def bazImpl = {} + """.stripMargin + checkBCode(code) { dir => + val privateAccessors = Opcodes.ACC_PRIVATE | Opcodes.ACC_PROTECTED + + // For 3.0-3.3 compat + val barClass = loadClassNode(dir.subdirectoryNamed("foo").lookupName("C.class", directory = false).input, skipDebugInfo = false) + val accessorOld = getMethod(barClass, "inline$bazImpl$i1") + assert(accessorOld.desc == "(Lfoo/D$;)V", accessorOld.desc) + assert((accessorOld.access & privateAccessors) == 0) + + // For 3.4+ + val accessorsClass = loadClassNode(dir.subdirectoryNamed("foo").lookupName("C$inline$accessors.class", directory = false).input, skipDebugInfo = false) + val accessorNew = getMethod(accessorsClass, "inline$bazImpl") + assert(accessorNew.signature == "()V", accessorNew.signature) + assert((accessorNew.access & privateAccessors) == 0) + } + } } object invocationReceiversTestCode { diff --git a/tests/pos-macros/i15413/Macro_1.scala b/tests/pos-macros/i15413/Macro_1.scala new file mode 100644 index 000000000000..6e7354ad87b5 --- /dev/null +++ b/tests/pos-macros/i15413/Macro_1.scala @@ -0,0 +1,7 @@ +import scala.quoted.* + +class Macro: + inline def foo = ${ Macro.fooImpl } + +object Macro: + private def fooImpl(using Quotes) = '{} diff --git a/tests/pos-macros/i15413/Test_2.scala b/tests/pos-macros/i15413/Test_2.scala new file mode 100644 index 000000000000..a8310a8970fd --- /dev/null +++ b/tests/pos-macros/i15413/Test_2.scala @@ -0,0 +1,2 @@ +def test = + new Macro().foo diff --git a/tests/pos-macros/i15413b/Macro_1.scala b/tests/pos-macros/i15413b/Macro_1.scala new file mode 100644 index 000000000000..4cbcf0dc9208 --- /dev/null +++ b/tests/pos-macros/i15413b/Macro_1.scala @@ -0,0 +1,5 @@ +import scala.quoted.* + +inline def foo = ${ fooImpl } + +private def fooImpl(using Quotes) = '{} diff --git a/tests/pos-macros/i15413b/Test_2.scala b/tests/pos-macros/i15413b/Test_2.scala new file mode 100644 index 000000000000..54c769c9618f --- /dev/null +++ b/tests/pos-macros/i15413b/Test_2.scala @@ -0,0 +1 @@ +def test = foo diff --git a/tests/pos-macros/i15413c/Macro.scala b/tests/pos-macros/i15413c/Macro.scala new file mode 100644 index 000000000000..6e7354ad87b5 --- /dev/null +++ b/tests/pos-macros/i15413c/Macro.scala @@ -0,0 +1,7 @@ +import scala.quoted.* + +class Macro: + inline def foo = ${ Macro.fooImpl } + +object Macro: + private def fooImpl(using Quotes) = '{} diff --git a/tests/pos-macros/i15413c/Test.scala b/tests/pos-macros/i15413c/Test.scala new file mode 100644 index 000000000000..a8310a8970fd --- /dev/null +++ b/tests/pos-macros/i15413c/Test.scala @@ -0,0 +1,2 @@ +def test = + new Macro().foo diff --git a/tests/pos-macros/i15413d/Macro.scala b/tests/pos-macros/i15413d/Macro.scala new file mode 100644 index 000000000000..4cbcf0dc9208 --- /dev/null +++ b/tests/pos-macros/i15413d/Macro.scala @@ -0,0 +1,5 @@ +import scala.quoted.* + +inline def foo = ${ fooImpl } + +private def fooImpl(using Quotes) = '{} diff --git a/tests/pos-macros/i15413d/Test.scala b/tests/pos-macros/i15413d/Test.scala new file mode 100644 index 000000000000..54c769c9618f --- /dev/null +++ b/tests/pos-macros/i15413d/Test.scala @@ -0,0 +1 @@ +def test = foo diff --git a/tests/run-macros/i13215-compat-3.1/A_1_c3.1.0.scala b/tests/run-macros/i13215-compat-3.1/A_1_c3.1.0.scala new file mode 100644 index 000000000000..81a9891609ae --- /dev/null +++ b/tests/run-macros/i13215-compat-3.1/A_1_c3.1.0.scala @@ -0,0 +1,6 @@ +package foo + +trait Bar: + inline def baz = Baz + +private[foo] object Baz diff --git a/tests/run-macros/i13215-compat-3.1/A_3.scala b/tests/run-macros/i13215-compat-3.1/A_3.scala new file mode 100644 index 000000000000..81a9891609ae --- /dev/null +++ b/tests/run-macros/i13215-compat-3.1/A_3.scala @@ -0,0 +1,6 @@ +package foo + +trait Bar: + inline def baz = Baz + +private[foo] object Baz diff --git a/tests/run-macros/i13215-compat-3.1/B_2.scala b/tests/run-macros/i13215-compat-3.1/B_2.scala new file mode 100644 index 000000000000..3ef02baf0ef1 --- /dev/null +++ b/tests/run-macros/i13215-compat-3.1/B_2.scala @@ -0,0 +1,22 @@ +// We first compile A using 3.1 to generate the old accessors +// Then we compile this file to link against the old accessors +// Finally we recompile A using the current compiler to generate a version +// of A that contains the new accessors (and the old for backwards compat) + +@main def Test = + val bar: foo.Bar = new foo.Bar{} + bar.baz // test that old accessor links in 3.4+ + + // Check that both accessors exist in the bytecode + val barMethods = + java.lang.Class.forName("foo.Bar").getMethods() + .filter(_.getName().contains("inline")) + .filter(x => (x.getModifiers() & java.lang.reflect.Modifier.STATIC) == 0) + .map(_.getName()) + val barObjectMethods = + java.lang.Class.forName("foo.Bar$inline$accessors").getMethods() + .filter(_.getName().contains("inline")) + .map(_.getName()) + + println("3.0-3.3 inline accessor: " + barMethods.toList) + println("3.4+ inline accessor: " + barObjectMethods.toList) diff --git a/tests/run-macros/i15413-compat-3.1.check b/tests/run-macros/i15413-compat-3.1.check new file mode 100644 index 000000000000..bffe4e35fd7f --- /dev/null +++ b/tests/run-macros/i15413-compat-3.1.check @@ -0,0 +1,2 @@ +3.0-3.3 inline accessor: List(Macro$$inline$fooImpl) +3.4+ inline accessor: List(inline$fooImpl) diff --git a/tests/run-macros/i15413-compat-3.1/Macro_1_c3.1.0.scala b/tests/run-macros/i15413-compat-3.1/Macro_1_c3.1.0.scala new file mode 100644 index 000000000000..011086bbd2cd --- /dev/null +++ b/tests/run-macros/i15413-compat-3.1/Macro_1_c3.1.0.scala @@ -0,0 +1,7 @@ +import scala.quoted.* + +class Macro: + inline def foo = /*${*/ Macro.fooImpl /*}*/ + +object Macro: + private def fooImpl/*(using Quotes)*/ = {} diff --git a/tests/run-macros/i15413-compat-3.1/Macro_3.scala b/tests/run-macros/i15413-compat-3.1/Macro_3.scala new file mode 100644 index 000000000000..011086bbd2cd --- /dev/null +++ b/tests/run-macros/i15413-compat-3.1/Macro_3.scala @@ -0,0 +1,7 @@ +import scala.quoted.* + +class Macro: + inline def foo = /*${*/ Macro.fooImpl /*}*/ + +object Macro: + private def fooImpl/*(using Quotes)*/ = {} diff --git a/tests/run-macros/i15413-compat-3.1/Test_2.scala b/tests/run-macros/i15413-compat-3.1/Test_2.scala new file mode 100644 index 000000000000..ee8a92035741 --- /dev/null +++ b/tests/run-macros/i15413-compat-3.1/Test_2.scala @@ -0,0 +1,19 @@ +// We first compile Macro using 3.1 to generate the old accessors +// Then we compile this file to link against the old accessors +// Finally we recompile Macro using the current compiler to generate a version +// of Macro that contains the new accessors (and the old for backwards compat) + +@main def Test = + // Check that both accessors exist in the bytecode + val barMethods = + java.lang.Class.forName("Macro").getMethods() + .filter(_.getName().contains("inline")) + .filter(x => (x.getModifiers() & java.lang.reflect.Modifier.STATIC) == 0) + .map(_.getName()) + val barObjectMethods = + java.lang.Class.forName("Macro$inline$accessors").getMethods() + .filter(_.getName().contains("inline")) + .map(_.getName()) + + println("3.0-3.3 inline accessor: " + barMethods.toList) + println("3.4+ inline accessor: " + barObjectMethods.toList) diff --git a/tests/run-macros/i15413b-compat-3.1.check b/tests/run-macros/i15413b-compat-3.1.check new file mode 100644 index 000000000000..37d598a7f699 --- /dev/null +++ b/tests/run-macros/i15413b-compat-3.1.check @@ -0,0 +1,2 @@ +3.0-3.3 inline accessor: List(inline$bazImpl$i1) +3.4+ inline accessor: List(inline$bazImpl) diff --git a/tests/run-macros/i15413b-compat-3.1/Macro_1_c3.1.0.scala b/tests/run-macros/i15413b-compat-3.1/Macro_1_c3.1.0.scala new file mode 100644 index 000000000000..444abeb7a060 --- /dev/null +++ b/tests/run-macros/i15413b-compat-3.1/Macro_1_c3.1.0.scala @@ -0,0 +1,6 @@ +package foo +class C: + inline def baz = D.bazImpl + +object D: + private[foo] def bazImpl = {} diff --git a/tests/run-macros/i15413b-compat-3.1/Macro_3.scala b/tests/run-macros/i15413b-compat-3.1/Macro_3.scala new file mode 100644 index 000000000000..444abeb7a060 --- /dev/null +++ b/tests/run-macros/i15413b-compat-3.1/Macro_3.scala @@ -0,0 +1,6 @@ +package foo +class C: + inline def baz = D.bazImpl + +object D: + private[foo] def bazImpl = {} diff --git a/tests/run-macros/i15413b-compat-3.1/Test_2.scala b/tests/run-macros/i15413b-compat-3.1/Test_2.scala new file mode 100644 index 000000000000..f3d15d7a8714 --- /dev/null +++ b/tests/run-macros/i15413b-compat-3.1/Test_2.scala @@ -0,0 +1,22 @@ +// We first compile Macro using 3.1 to generate the old accessors +// Then we compile this file to link against the old accessors +// Finally we recompile Macro using the current compiler to generate a version +// of Macro that contains the new accessors (and the old for backwards compat) + +@main def Test = + val bar = new foo.C + bar.baz + + // Check that both accessors exist in the bytecode + val barMethods = + java.lang.Class.forName("foo.C").getMethods() + .filter(_.getName().contains("inline")) + .filter(x => (x.getModifiers() & java.lang.reflect.Modifier.STATIC) == 0) + .map(_.getName()) + val barObjectMethods = + java.lang.Class.forName("foo.C$inline$accessors").getMethods() + .filter(_.getName().contains("inline")) + .map(_.getName()) + + println("3.0-3.3 inline accessor: " + barMethods.toList) + println("3.4+ inline accessor: " + barObjectMethods.toList) diff --git a/tests/run-macros/inline-macro-inner-object.check b/tests/run-macros/inline-macro-inner-object.check index 480feef5f62f..11b38c4b1403 100644 --- a/tests/run-macros/inline-macro-inner-object.check +++ b/tests/run-macros/inline-macro-inner-object.check @@ -1,3 +1,5 @@ A.f A.B.f A.B.C.f +A.B.D.f +A.B.E.f diff --git a/tests/run-macros/inline-macro-inner-object/Macro_1.scala b/tests/run-macros/inline-macro-inner-object/Macro_1.scala index ef9c9fb537f0..f25f4daa19ff 100644 --- a/tests/run-macros/inline-macro-inner-object/Macro_1.scala +++ b/tests/run-macros/inline-macro-inner-object/Macro_1.scala @@ -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")} + } + } } } diff --git a/tests/run-macros/inline-macro-inner-object/Test_2.scala b/tests/run-macros/inline-macro-inner-object/Test_2.scala index 8209c8f045cc..b277999f5b86 100644 --- a/tests/run-macros/inline-macro-inner-object/Test_2.scala +++ b/tests/run-macros/inline-macro-inner-object/Test_2.scala @@ -5,5 +5,7 @@ object Test { A.f A.B.f A.B.C.f + A.B.D.f + A.B.E.f } } diff --git a/tests/run/i13215-compat-3.1.check b/tests/run/i13215-compat-3.1.check new file mode 100644 index 000000000000..8fddc5be62a6 --- /dev/null +++ b/tests/run/i13215-compat-3.1.check @@ -0,0 +1,2 @@ +3.0-3.3 inline accessor: List(foo$Bar$$inline$Baz) +3.4+ inline accessor: List(inline$Baz) diff --git a/tests/run/i13215.scala b/tests/run/i13215.scala new file mode 100644 index 000000000000..85652b053a4b --- /dev/null +++ b/tests/run/i13215.scala @@ -0,0 +1,10 @@ +package foo { + trait Bar: + inline def baz = Baz + + private[foo] object Baz +} + +@main def Test: Unit = + val bar = new foo.Bar {} + bar.baz diff --git a/tests/run/i13252b/Foo_1.scala b/tests/run/i13252/Foo_1.scala similarity index 100% rename from tests/run/i13252b/Foo_1.scala rename to tests/run/i13252/Foo_1.scala diff --git a/tests/run/i13252b/Test_2.scala b/tests/run/i13252/Test_2.scala similarity index 100% rename from tests/run/i13252b/Test_2.scala rename to tests/run/i13252/Test_2.scala