diff --git a/compiler/src/dotty/tools/dotc/inlines/PrepareInlineable.scala b/compiler/src/dotty/tools/dotc/inlines/PrepareInlineable.scala index 85293d4a82d7..1e4bafad604e 100644 --- a/compiler/src/dotty/tools/dotc/inlines/PrepareInlineable.scala +++ b/compiler/src/dotty/tools/dotc/inlines/PrepareInlineable.scala @@ -2,6 +2,8 @@ package dotty.tools package dotc package inlines +import scala.collection.mutable + import dotty.tools.dotc.ast.{Trees, tpd, untpd} import Trees._ import core._ @@ -22,6 +24,9 @@ import transform.SymUtils.* import config.Printers.inlining import util.Property import dotty.tools.dotc.transform.TreeMapWithStages._ +import dotty.tools.dotc.ast.desugar.packageObjectName +import dotty.tools.dotc.core.StdNames.str +import dotty.tools.dotc.util.{Spans, SrcPos} object PrepareInlineable { import tpd._ @@ -53,9 +58,12 @@ 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 = - val accName = InlineAccessorName(name) - if site.isExtensibleClass then accName.expandedName(site) else accName + def accessorNameOf(accessed: Symbol, site: Symbol, isStatic: Boolean)(using Context): TermName = + val baseName = + if isStatic then ("static$" + accessed.fullName.toString.replace('.', '$')).toTermName + else accessed.name.asTermName + val accName = InlineAccessorName(baseName) + if site.isExtensibleClass && !isStatic then accName.expandedName(site) else accName /** A definition needs an accessor if it is private, protected, or qualified private * and it is not part of the tree that gets inlined. The latter test is implemented @@ -99,18 +107,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) { + 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 useAccessor(tree) + 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, ctx.owner.topLevelClass, isStatic = true) + else + useAccessor(tree, nearestHost) case _ => tree } + override def ifNoHost(reference: RefTree)(using Context): Tree = reference } @@ -226,8 +248,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 851e3c422460..657a6dee1715 100644 --- a/compiler/src/dotty/tools/dotc/quoted/Interpreter.scala +++ b/compiler/src/dotty/tools/dotc/quoted/Interpreter.scala @@ -162,13 +162,17 @@ class Interpreter(pos: SrcPos, classLoader0: ClassLoader)(using Context): private def interpretVarargs(args: List[Object]): Object = args.toSeq - private def interpretedStaticMethodCall(moduleClass: Symbol, fn: Symbol, args: List[Object]): Object = { - val inst = - try loadModule(moduleClass) + private def interpretedStaticMethodCall(owner: Symbol, fn: Symbol, args: List[Object]): Object = { + val (inst, clazz) = + try + if owner.is(Module) then + val inst = loadModule(owner) + (inst, inst.getClass) + else + (null, loadClass(owner.binaryClassName)) catch case MissingClassDefinedInCurrentRun(sym) => suspendOnMissing(sym, pos) - val clazz = inst.getClass val name = fn.name.asTermName val method = getMethod(clazz, name, paramsSig(fn)) stopIfRuntimeException(method.invoke(inst, args: _*), method) diff --git a/compiler/src/dotty/tools/dotc/transform/AccessProxies.scala b/compiler/src/dotty/tools/dotc/transform/AccessProxies.scala index 14362260d032..0cd0189a5c40 100644 --- a/compiler/src/dotty/tools/dotc/transform/AccessProxies.scala +++ b/compiler/src/dotty/tools/dotc/transform/AccessProxies.scala @@ -66,8 +66,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 `accessed` in given `site` */ + def accessorNameOf(accessed: Symbol, site: Symbol, isStatic: Boolean)(using Context): TermName def needsAccessor(sym: Symbol)(using Context): Boolean def ifNoHost(reference: RefTree)(using Context): Tree = { @@ -76,9 +76,10 @@ abstract class AccessProxies { } /** A fresh accessor symbol */ - private def newAccessorSymbol(owner: Symbol, name: TermName, info: Type, accessed: Symbol)(using Context): TermSymbol = { + private def newAccessorSymbol(owner: Symbol, name: TermName, info: Type, accessed: Symbol, isStatic: Boolean)(using Context): TermSymbol = { val sym = newSymbol(owner, name, Synthetic | Method, info, coord = accessed.span).entered if accessed.is(Private) then sym.setFlag(Final) + if isStatic then sym.setFlag(JavaStaticTerm) else if sym.allOverriddenSymbols.exists(!_.is(Deferred)) then sym.setFlag(Override) if accessed.hasAnnotation(defn.ExperimentalAnnot) then sym.addAnnotation(defn.ExperimentalAnnot) @@ -86,10 +87,10 @@ abstract class AccessProxies { } /** An accessor symbol, create a fresh one unless one exists already */ - protected def accessorSymbol(owner: Symbol, accessorName: TermName, accessorInfo: Type, accessed: Symbol)(using Context): Symbol = { + protected def accessorSymbol(owner: Symbol, accessorName: TermName, accessorInfo: Type, accessed: Symbol, isStatic: Boolean = false)(using Context): Symbol = { def refersToAccessed(sym: Symbol) = accessedBy.get(sym).contains(accessed) owner.info.decl(accessorName).suchThat(refersToAccessed).symbol.orElse { - val acc = newAccessorSymbol(owner, accessorName, accessorInfo, accessed) + val acc = newAccessorSymbol(owner, accessorName, accessorInfo, accessed, isStatic) accessedBy(acc) = accessed acc } @@ -127,19 +128,17 @@ abstract class AccessProxies { /** Create an accessor unless one exists already, and replace the original * access with a reference to the accessor. * - * @param reference The original reference to the non-public symbol - * @param onLHS The reference is on the left-hand side of an assignment + * @param reference The original reference to the non-public symbol + * @param accessorClass Class where the accessor will be generated + * @param isStatic If this accessor will become a java static method */ - def useAccessor(reference: RefTree)(using Context): Tree = { + def useAccessor(reference: RefTree, accessorClass: Symbol, isStatic: Boolean = false)(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, isStatic) val accessorInfo = accessed.info.ensureMethodic.asSeenFrom(accessorClass.thisType, accessed.owner) - val accessor = accessorSymbol(accessorClass, accessorName, accessorInfo, accessed) + val accessor = accessorSymbol(accessorClass, accessorName, accessorInfo, accessed, isStatic) rewire(reference, accessor) } else ifNoHost(reference) @@ -152,7 +151,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 } diff --git a/compiler/src/dotty/tools/dotc/transform/MixinOps.scala b/compiler/src/dotty/tools/dotc/transform/MixinOps.scala index fa1c09806893..a9581dede44e 100644 --- a/compiler/src/dotty/tools/dotc/transform/MixinOps.scala +++ b/compiler/src/dotty/tools/dotc/transform/MixinOps.scala @@ -68,6 +68,7 @@ class MixinOps(cls: ClassSymbol, thisPhase: DenotTransformer)(using Context) { (meth.name == nme.readResolve || meth.name == nme.writeReplace) && meth.info.paramNamess.flatten.isEmpty !meth.isConstructor && + !meth.is(JavaStatic) && meth.is(Method, butNot = PrivateOrAccessorOrDeferred) && (ctx.settings.mixinForwarderChoices.isTruthy || meth.owner.is(Scala2x) || needsDisambiguation || hasNonInterfaceDefinition || generateJUnitForwarder || generateSerializationForwarder) && diff --git a/compiler/src/dotty/tools/dotc/transform/ProtectedAccessors.scala b/compiler/src/dotty/tools/dotc/transform/ProtectedAccessors.scala index 6d8f7bdb32cb..827401b3b6b8 100644 --- a/compiler/src/dotty/tools/dotc/transform/ProtectedAccessors.scala +++ b/compiler/src/dotty/tools/dotc/transform/ProtectedAccessors.scala @@ -64,7 +64,9 @@ 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(accessed: Symbol, site: Symbol, isStatic: Boolean)(using Context): TermName = + assert(!isStatic) + ProtectedAccessorName(accessed.name.asTermName) def needsAccessor(sym: Symbol)(using Context) = ProtectedAccessors.needsAccessor(sym) override def ifNoHost(reference: RefTree)(using Context): Tree = { diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index 592d35d9d23e..f815be99e50d 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -35,6 +35,7 @@ import util.common._ import util.{Property, SimpleIdentityMap, SrcPos} import Applications.{tupleComponentTypes, wrapDefs, defaultArgument} + import collection.mutable import annotation.tailrec import Implicits._ diff --git a/compiler/test/dotty/tools/backend/jvm/DottyBytecodeTests.scala b/compiler/test/dotty/tools/backend/jvm/DottyBytecodeTests.scala index ac4ba3ee0e75..b35b7cc2c66d 100644 --- a/compiler/test/dotty/tools/backend/jvm/DottyBytecodeTests.scala +++ b/compiler/test/dotty/tools/backend/jvm/DottyBytecodeTests.scala @@ -1682,6 +1682,129 @@ 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 + val barClass = loadClassNode(dir.subdirectoryNamed("foo").lookupName("Bar.class", directory = false).input, skipDebugInfo = false) + + // For 3.0-3.3 compat + val accessorOld = getMethod(barClass, "foo$Bar$$inline$Baz") + assert(accessorOld.signature == "()Lfoo/Baz$;", accessorOld.signature) + assert((accessorOld.access & privateAccessors) == 0) + + // For 3.4+ + val accessorNew = getMethod(barClass, "inline$static$foo$Baz") // FIXME "foo$$inline$static$Baz"? + assert(accessorNew.signature == "()Lfoo/Baz$;", accessorNew.signature) + assert((accessorNew.access & privateAccessors) == 0) + } + } + + @Test + def i13215b(): Unit = { + val code = + """package foo: + | final class Bar: + | inline def baz = Baz + | private[foo] object Baz + """.stripMargin + checkBCode(code) { dir => + val privateAccessors = Opcodes.ACC_PRIVATE | Opcodes.ACC_PROTECTED + val barClass = loadClassNode(dir.subdirectoryNamed("foo").lookupName("Bar.class", directory = false).input, skipDebugInfo = false) + + // For 3.0-3.3 compat + val accessorOld = getMethod(barClass, "inline$Baz") + assert(accessorOld.signature == "()Lfoo/Baz$;", accessorOld.signature) + assert((accessorOld.access & privateAccessors) == 0) + + // For 3.4+ + val accessorNew = getMethod(barClass, "inline$static$foo$Baz") // FIXME "foo$$inline$static$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 + val macroClass = loadClassNode(dir.lookupName("Macro.class", directory = false).input, skipDebugInfo = false) + + // For 3.0-3.3 compat + val accessorOld = getMethod(macroClass, "Macro$$inline$fooImpl") + assert(accessorOld.signature == "()V") + assert((accessorOld.access & privateAccessors) == 0) + + // For 3.4+ + val accessorNew = getMethod(macroClass, "inline$static$Macro$$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 + val barClass = loadClassNode(dir.subdirectoryNamed("foo").lookupName("C.class", directory = false).input, skipDebugInfo = false) + + // For 3.0-3.3 compat + val accessorOld = getMethod(barClass, "inline$bazImpl$i1") + assert(accessorOld.desc == "(Lfoo/D$;)V", accessorOld.desc) + assert((accessorOld.access & privateAccessors) == 0) + + // For 3.4+ + val accessorNew = getMethod(barClass, "inline$static$foo$D$$bazImpl") // FIXME "foo$D$$inline$static$bazImpl"? + assert(accessorNew.signature == "()V", accessorNew.signature) + assert((accessorNew.access & privateAccessors) == 0) + } + } + + @Test + def i15413c(): Unit = { + val code = + """package foo + |final 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 + val barClass = loadClassNode(dir.subdirectoryNamed("foo").lookupName("C.class", directory = false).input, skipDebugInfo = false) + + // For 3.0-3.3 compat + val accessorOld = getMethod(barClass, "inline$bazImpl$i1") + assert(accessorOld.desc == "(Lfoo/D$;)V", accessorOld.desc) + assert((accessorOld.access & privateAccessors) == 0) + + // For 3.4+ + val accessorNew = getMethod(barClass, "inline$static$foo$D$$bazImpl") // FIXME "foo$D$$inline$static$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/pos-macros/i15413e/Macro_1.scala b/tests/pos-macros/i15413e/Macro_1.scala new file mode 100644 index 000000000000..357556961945 --- /dev/null +++ b/tests/pos-macros/i15413e/Macro_1.scala @@ -0,0 +1,18 @@ +package myMacro + +import scala.quoted.* + +class Macro: + inline def foo = ${ Foo.impl } + inline def bar = ${ Bar.impl } + inline def baz = ${ pack.Foo.impl } + +object Foo: + private[myMacro] def impl(using Quotes) = '{} + +object Bar: + private[myMacro] def impl(using Quotes) = '{1} + +package pack: + object Foo: + private[myMacro] def impl(using Quotes) = '{"abc"} diff --git a/tests/pos-macros/i15413e/Test_2.scala b/tests/pos-macros/i15413e/Test_2.scala new file mode 100644 index 000000000000..2d5ec53aa587 --- /dev/null +++ b/tests/pos-macros/i15413e/Test_2.scala @@ -0,0 +1,6 @@ +import myMacro.Macro + +def test(m: Macro) = + m.foo + m.bar + m.baz 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..cda5d4ae87a4 --- /dev/null +++ b/tests/run-macros/i13215-compat-3.1/B_2.scala @@ -0,0 +1,25 @@ +// 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 inlineAccessors = + java.lang.Class.forName("foo.Bar").getMethods() + .filter(_.getName().contains("inline")) + .filter(x => (x.getModifiers() & java.lang.reflect.Modifier.STATIC) == 0) + .map(_.getName()) + .toList + val staticInlineAccessors = + java.lang.Class.forName("foo.Bar").getMethods() + .filter(_.getName().contains("inline")) + .filter(x => (x.getModifiers() & java.lang.reflect.Modifier.STATIC) != 0) + .map(_.getName()) + .toList + + println("3.0-3.3 inline accessor: " + inlineAccessors) + println("3.4+ inline accessor: " + staticInlineAccessors) 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..92cec0d4a892 --- /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$static$Macro$$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..532bdd76941c --- /dev/null +++ b/tests/run-macros/i15413-compat-3.1/Test_2.scala @@ -0,0 +1,23 @@ +// 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 inlineAccessors = + java.lang.Class.forName("Macro").getMethods() + .filter(_.getName().contains("inline")) + .filter(x => (x.getModifiers() & java.lang.reflect.Modifier.STATIC) == 0) + .map(_.getName()) + .toList + + val staticInlineAccessors = + java.lang.Class.forName("Macro").getMethods() + .filter(_.getName().contains("inline")) + .filter(x => (x.getModifiers() & java.lang.reflect.Modifier.STATIC) != 0) + .map(_.getName()) + .toList + + println("3.0-3.3 inline accessor: " + inlineAccessors) + println("3.4+ inline accessor: " + staticInlineAccessors) 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..cec5e7b90355 --- /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$static$foo$D$$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..d4a9a924cfa3 --- /dev/null +++ b/tests/run-macros/i15413b-compat-3.1/Test_2.scala @@ -0,0 +1,27 @@ +// 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 inlineAccessors = + java.lang.Class.forName("foo.C").getMethods() + .filter(_.getName().contains("inline")) + .filter(x => (x.getModifiers() & java.lang.reflect.Modifier.STATIC) == 0) + .map(_.getName()) + .toList + + val staticInlineAccessors = + java.lang.Class.forName("foo.C").getMethods() + .filter(_.getName().contains("inline")) + .filter(x => (x.getModifiers() & java.lang.reflect.Modifier.STATIC) != 0) + .map(_.getName()) + .toList + + + println("3.0-3.3 inline accessor: " + inlineAccessors) + println("3.4+ inline accessor: " + staticInlineAccessors) 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/i13215b.scala b/tests/run/i13215b.scala new file mode 100644 index 000000000000..a2b578de5822 --- /dev/null +++ b/tests/run/i13215b.scala @@ -0,0 +1,10 @@ +package foo { + final class 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/i13215c.scala b/tests/run/i13215c.scala new file mode 100644 index 000000000000..59f1e0695687 --- /dev/null +++ b/tests/run/i13215c.scala @@ -0,0 +1,8 @@ +final class Bar: + inline def baz = Baz + +private object Baz + +@main def Test: Unit = + val bar = new 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