From f4e4bb00d2835d3cf7b97384e61a9301c854730a Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Tue, 31 Jan 2023 17:48:57 +0100 Subject: [PATCH] Generate static inline accessors module If a class `C` needs inline accessors that would be added top-level or if the accessor is to a static member, we place it in a new invisible module `C$inline$accessors`. If the accessor location in the new scheme is not the same as the previous location, we also generate the old accessor for backward binary compatibility but do not use it. Fixes #13215 Fixes #15413 --- .../src/dotty/tools/dotc/core/StdNames.scala | 1 + .../dotc/inlines/PrepareInlineable.scala | 46 ++++++++++- .../dotty/tools/dotc/quoted/Interpreter.scala | 5 +- .../dotc/typer/TopLevelExtensionModules.scala | 76 ++++++++++++++++++ .../src/dotty/tools/dotc/typer/Typer.scala | 12 +++ .../backend/jvm/DottyBytecodeTests.scala | 77 +++++++++++++++++++ tests/pos-macros/i15413/Macro_1.scala | 7 ++ tests/pos-macros/i15413/Test_2.scala | 2 + tests/pos-macros/i15413b/Macro_1.scala | 5 ++ tests/pos-macros/i15413b/Test_2.scala | 1 + tests/pos-macros/i15413c/Macro.scala | 7 ++ tests/pos-macros/i15413c/Test.scala | 2 + tests/pos-macros/i15413d/Macro.scala | 5 ++ tests/pos-macros/i15413d/Test.scala | 1 + .../i13215-compat-3.1/A_1_c3.1.0.scala | 6 ++ tests/run-macros/i13215-compat-3.1/A_3.scala | 6 ++ tests/run-macros/i13215-compat-3.1/B_2.scala | 22 ++++++ tests/run-macros/i15413-compat-3.1.check | 2 + .../i15413-compat-3.1/Macro_1_c3.1.0.scala | 7 ++ .../i15413-compat-3.1/Macro_3.scala | 7 ++ .../run-macros/i15413-compat-3.1/Test_2.scala | 19 +++++ tests/run-macros/i15413b-compat-3.1.check | 2 + .../i15413b-compat-3.1/Macro_1_c3.1.0.scala | 6 ++ .../i15413b-compat-3.1/Macro_3.scala | 6 ++ .../i15413b-compat-3.1/Test_2.scala | 22 ++++++ .../inline-macro-inner-object.check | 2 + .../inline-macro-inner-object/Macro_1.scala | 14 ++++ .../inline-macro-inner-object/Test_2.scala | 2 + tests/run/i13215-compat-3.1.check | 2 + tests/run/i13215.scala | 10 +++ tests/run/{i13252b => i13252}/Foo_1.scala | 0 tests/run/{i13252b => i13252}/Test_2.scala | 0 32 files changed, 376 insertions(+), 6 deletions(-) create mode 100644 compiler/src/dotty/tools/dotc/typer/TopLevelExtensionModules.scala create mode 100644 tests/pos-macros/i15413/Macro_1.scala create mode 100644 tests/pos-macros/i15413/Test_2.scala create mode 100644 tests/pos-macros/i15413b/Macro_1.scala create mode 100644 tests/pos-macros/i15413b/Test_2.scala create mode 100644 tests/pos-macros/i15413c/Macro.scala create mode 100644 tests/pos-macros/i15413c/Test.scala create mode 100644 tests/pos-macros/i15413d/Macro.scala create mode 100644 tests/pos-macros/i15413d/Test.scala create mode 100644 tests/run-macros/i13215-compat-3.1/A_1_c3.1.0.scala create mode 100644 tests/run-macros/i13215-compat-3.1/A_3.scala create mode 100644 tests/run-macros/i13215-compat-3.1/B_2.scala create mode 100644 tests/run-macros/i15413-compat-3.1.check create mode 100644 tests/run-macros/i15413-compat-3.1/Macro_1_c3.1.0.scala create mode 100644 tests/run-macros/i15413-compat-3.1/Macro_3.scala create mode 100644 tests/run-macros/i15413-compat-3.1/Test_2.scala create mode 100644 tests/run-macros/i15413b-compat-3.1.check create mode 100644 tests/run-macros/i15413b-compat-3.1/Macro_1_c3.1.0.scala create mode 100644 tests/run-macros/i15413b-compat-3.1/Macro_3.scala create mode 100644 tests/run-macros/i15413b-compat-3.1/Test_2.scala create mode 100644 tests/run/i13215-compat-3.1.check create mode 100644 tests/run/i13215.scala rename tests/run/{i13252b => i13252}/Foo_1.scala (100%) rename tests/run/{i13252b => i13252}/Test_2.scala (100%) 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.3 */ - 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