diff --git a/compiler/src/dotty/tools/dotc/core/StdNames.scala b/compiler/src/dotty/tools/dotc/core/StdNames.scala index a2e78add1338..f1c121f77592 100644 --- a/compiler/src/dotty/tools/dotc/core/StdNames.scala +++ b/compiler/src/dotty/tools/dotc/core/StdNames.scala @@ -812,6 +812,9 @@ object StdNames { case _ => termName("x$" + i) } + def isProductAccessorName(name: Name): Boolean = + name.startsWith("_") && name.toString.drop(1).toIntOption.isDefined + def productAccessorName(j: Int): TermName = (j: @switch) match { case 1 => nme._1 case 2 => nme._2 diff --git a/compiler/src/dotty/tools/dotc/transform/InlinePatterns.scala b/compiler/src/dotty/tools/dotc/transform/InlinePatterns.scala index 18333ae506fd..0d6f4e2e5b27 100644 --- a/compiler/src/dotty/tools/dotc/transform/InlinePatterns.scala +++ b/compiler/src/dotty/tools/dotc/transform/InlinePatterns.scala @@ -3,8 +3,10 @@ package dotc package transform import core.* +import inlines.Inlines import MegaPhase.* -import Symbols.*, Contexts.*, Types.*, Decorators.* +import Symbols.*, Contexts.*, Types.*, Decorators.*, StdNames.* +import Flags.* import NameOps.* import Names.* @@ -36,7 +38,14 @@ class InlinePatterns extends MiniPhase: // by the pattern matcher but are still not visible in that group of phases. override def runsAfterGroupsOf: Set[String] = Set(PatternMatcher.name) - override def transformApply(app: Apply)(using Context): Tree = + override def transformSelect(sel: Select)(using Context): Tree = + if PatternMatcher.isPatmatGenerated(sel) && sel.symbol.is(Inline) then + val tree = Inlines.inlineCall(sel) + report.log(i"inline $sel -> $tree") + tree + else sel + + override def transformApply(app: Apply)(using Context): Tree = { if app.symbol.name.isUnapplyName && !app.tpe.isInstanceOf[MethodicType] then app match case App(Select(fn, name), argss) => @@ -46,6 +55,7 @@ class InlinePatterns extends MiniPhase: case _ => app else app + } private object App: def unapply(app: Tree): (Tree, List[List[Tree]]) = diff --git a/compiler/src/dotty/tools/dotc/transform/Inlining.scala b/compiler/src/dotty/tools/dotc/transform/Inlining.scala index 907fe948ac30..4f4bb89c37ed 100644 --- a/compiler/src/dotty/tools/dotc/transform/Inlining.scala +++ b/compiler/src/dotty/tools/dotc/transform/Inlining.scala @@ -5,6 +5,7 @@ import core.* import Flags.* import Contexts.* import Symbols.* +import StdNames.* import dotty.tools.dotc.ast.tpd import dotty.tools.dotc.ast.Trees.* @@ -44,8 +45,12 @@ class Inlining extends MacroTransform, IdentityDenotTransformer { tree match { case PackageDef(pid, _) if tree.symbol.owner == defn.RootClass => new TreeTraverser { + def traverse(tree: Tree)(using Context): Unit = tree match + case tree: Select if PatternMatcher.isPatmatGenerated(tree) => + // Purposefully skip any nodes introduced by the pattern matcher. We + // will inline them at a later stage. case tree: RefTree if !Inlines.inInlineMethod && StagingLevel.level == 0 => assert(!tree.symbol.isInlineMethod, tree.show) case _ => diff --git a/compiler/src/dotty/tools/dotc/transform/PatternMatcher.scala b/compiler/src/dotty/tools/dotc/transform/PatternMatcher.scala index bed29a122399..8101d0d33564 100644 --- a/compiler/src/dotty/tools/dotc/transform/PatternMatcher.scala +++ b/compiler/src/dotty/tools/dotc/transform/PatternMatcher.scala @@ -78,6 +78,12 @@ object PatternMatcher { def isPatmatGenerated(sym: Symbol)(using Context): Boolean = sym.is(Synthetic) && sym.name.is(PatMatStdBinderName) + def isPatmatGenerated(select: Select)(using Context): Boolean = select match { + case Select(sel, nme.isEmpty | nme.get) if isPatmatGenerated(sel.symbol) => true + case Select(sel, name) if isPatmatGenerated(sel.symbol) && nme.isProductAccessorName(name) => true + case _ => false + } + /** The pattern matching translator. * Its general structure is a pipeline: * diff --git a/compiler/src/dotty/tools/dotc/util/Signatures.scala b/compiler/src/dotty/tools/dotc/util/Signatures.scala index 9131f4f761a2..9a424c475be3 100644 --- a/compiler/src/dotty/tools/dotc/util/Signatures.scala +++ b/compiler/src/dotty/tools/dotc/util/Signatures.scala @@ -453,8 +453,7 @@ object Signatures { * Filter returning only members starting with underscore followed with number */ private object underscoreMembersFilter extends NameFilter { - def apply(pre: Type, name: Name)(using Context): Boolean = - name.startsWith("_") && name.toString.drop(1).toIntOption.isDefined + def apply(pre: Type, name: Name)(using Context): Boolean = nme.isProductAccessorName(name) def isStable = true } diff --git a/tests/pos/byname-inline.scala b/tests/pos/byname-inline.scala new file mode 100644 index 000000000000..5cf9ef7b68d6 --- /dev/null +++ b/tests/pos/byname-inline.scala @@ -0,0 +1,39 @@ +final class ByName1(val x: Int) extends AnyVal: + inline def isEmpty: Boolean = x == 1 + inline def get: String = x.toString + +object ByName1: + inline def unapply(x: Int): ByName1 = new ByName1(x) + +def useByName1PatMatch: String = + val x = 1 + x match + case ByName1(s) => s + +final class ByName2(val x: Int) extends AnyVal: + inline def isEmpty: false = false + inline def get: Int = x + +object ByName2: + inline def unapply(x: Int): ByName2 = new ByName2(x) + +def useByName2PatMatch: Int = + val x = 1 + x match + case ByName2(s) => s + +final class Accessor(val x: Int) extends AnyVal: + inline def _1: Int = x + 1 + inline def _2: String = x.toString + +final class ByName3(val x: Int) extends AnyVal: + inline def isEmpty: Boolean = x == 1 + inline def get: Accessor = new Accessor(x) + +object ByName3: + inline def unapply(x: Int): ByName3 = new ByName3(x) + +def useByName3PatMatch: (Int, String) = + val x = 1 + x match + case ByName3(i, s) => (i, s)