From 72df9cd0c72a5231dc74ba5338a359be4e37a086 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Tue, 7 Feb 2023 10:24:39 +0100 Subject: [PATCH 1/2] Refactor use of hostForAccessorOf Only inline accessors can add accessors to static members defined outside their top-level class. These get the package owning the top-level class from `hostForAccessorOf`. --- .../src/dotty/tools/dotc/inlines/PrepareInlineable.scala | 8 +++++--- .../src/dotty/tools/dotc/transform/AccessProxies.scala | 7 ++----- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/inlines/PrepareInlineable.scala b/compiler/src/dotty/tools/dotc/inlines/PrepareInlineable.scala index 85293d4a82d7..005e0f5d4571 100644 --- a/compiler/src/dotty/tools/dotc/inlines/PrepareInlineable.scala +++ b/compiler/src/dotty/tools/dotc/inlines/PrepareInlineable.scala @@ -103,11 +103,13 @@ object PrepareInlineable { class MakeInlineableDirect(inlineSym: Symbol) 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 + val nearestHost = AccessProxies.hostForAccessorOf(tree.symbol) + val host = if nearestHost.is(Package) then ctx.owner.topLevelClass else nearestHost + useAccessor(tree, host) case _ => tree } diff --git a/compiler/src/dotty/tools/dotc/transform/AccessProxies.scala b/compiler/src/dotty/tools/dotc/transform/AccessProxies.scala index 14362260d032..dcdbfec87b33 100644 --- a/compiler/src/dotty/tools/dotc/transform/AccessProxies.scala +++ b/compiler/src/dotty/tools/dotc/transform/AccessProxies.scala @@ -130,12 +130,9 @@ 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 accessorInfo = accessed.info.ensureMethodic.asSeenFrom(accessorClass.thisType, accessed.owner) @@ -152,7 +149,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 } From eefe6acd5129047c0bc58f9659e263cba01727e4 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Tue, 31 Jan 2023 17:48:57 +0100 Subject: [PATCH 2/2] Generate static inline accessors If a class needs inline accessors that would be added top-level or if the accessor is to a static member, we place it in the top-level class as a java static method. 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 --- .../dotc/inlines/PrepareInlineable.scala | 36 ++++- .../dotty/tools/dotc/quoted/Interpreter.scala | 12 +- .../tools/dotc/transform/AccessProxies.scala | 22 ++-- .../dotty/tools/dotc/transform/MixinOps.scala | 1 + .../dotc/transform/ProtectedAccessors.scala | 4 +- .../src/dotty/tools/dotc/typer/Typer.scala | 1 + .../backend/jvm/DottyBytecodeTests.scala | 123 ++++++++++++++++++ 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 + tests/pos-macros/i15413e/Macro_1.scala | 18 +++ tests/pos-macros/i15413e/Test_2.scala | 6 + .../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 | 25 ++++ 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 | 23 ++++ 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 | 27 ++++ .../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/i13215b.scala | 10 ++ tests/run/i13215c.scala | 8 ++ tests/run/{i13252b => i13252}/Foo_1.scala | 0 tests/run/{i13252b => i13252}/Test_2.scala | 0 37 files changed, 397 insertions(+), 21 deletions(-) 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/pos-macros/i15413e/Macro_1.scala create mode 100644 tests/pos-macros/i15413e/Test_2.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 create mode 100644 tests/run/i13215b.scala create mode 100644 tests/run/i13215c.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/inlines/PrepareInlineable.scala b/compiler/src/dotty/tools/dotc/inlines/PrepareInlineable.scala index 005e0f5d4571..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,20 +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 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, ctx.owner.topLevelClass, isStatic = true) + else + useAccessor(tree, nearestHost) case _ => tree } + override def ifNoHost(reference: RefTree)(using Context): Tree = reference } @@ -228,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 dcdbfec87b33..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,16 +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, accessorClass: Symbol)(using Context): Tree = { + def useAccessor(reference: RefTree, accessorClass: Symbol, isStatic: Boolean = false)(using Context): Tree = { val accessed = reference.symbol.asTerm if (accessorClass.exists) { - 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) 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