From 27409276c9fa6b1b00ca9d33106c4dc565a5729f Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Thu, 8 Feb 2024 10:39:23 +0100 Subject: [PATCH] Inline transparent implicit parameters when typing Unapply trees We needed to delay the inlining of the transparent inline when typing the unapply function application. We used the NoInline mode, but this also stopped the inlining of the arguments of the unapply. To fix this we target more precisely the inlining of the unapply method and not the implicit arguments. To do this we detect the dummy argument that is used type the unapply as an application, before it is transformed into a pattern. Fixes #19623 [Cherry-picked d244a30b67ce6db9e929f3e7e9c0ef4051ac0e5c] --- .../dotty/tools/dotc/inlines/Inlines.scala | 12 ++++++ .../dotty/tools/dotc/typer/Applications.scala | 2 +- tests/pos/i19623.scala | 40 +++++++++++++++++++ 3 files changed, 53 insertions(+), 1 deletion(-) create mode 100644 tests/pos/i19623.scala diff --git a/compiler/src/dotty/tools/dotc/inlines/Inlines.scala b/compiler/src/dotty/tools/dotc/inlines/Inlines.scala index 1da036ccd5cf..cfb697797936 100644 --- a/compiler/src/dotty/tools/dotc/inlines/Inlines.scala +++ b/compiler/src/dotty/tools/dotc/inlines/Inlines.scala @@ -5,6 +5,7 @@ package inlines import ast.*, core.* import Flags.*, Symbols.*, Types.*, Decorators.*, Constants.*, Contexts.* import StdNames.{tpnme, nme} +import NameOps.* import typer.* import NameKinds.BodyRetainerName import SymDenotations.SymDenotation @@ -54,6 +55,16 @@ object Inlines: def needsInlining(tree: Tree)(using Context): Boolean = tree match { case Block(_, expr) => needsInlining(expr) case _ => + def isUnapplyExpressionWithDummy: Boolean = + // The first step of typing an `unapply` consists in typing the call + // with a dummy argument (see Applications.typedUnApply). We delay the + // inlining of this call. + def rec(tree: Tree): Boolean = tree match + case Apply(_, ProtoTypes.dummyTreeOfType(_) :: Nil) => true + case Apply(fn, _) => rec(fn) + case _ => false + tree.symbol.name.isUnapplyName && rec(tree) + isInlineable(tree.symbol) && !tree.tpe.widenTermRefExpr.isInstanceOf[MethodOrPoly] && StagingLevel.level == 0 @@ -64,6 +75,7 @@ object Inlines: && !ctx.typer.hasInliningErrors && !ctx.base.stopInlining && !ctx.mode.is(Mode.NoInline) + && !isUnapplyExpressionWithDummy } private def needsTransparentInlining(tree: Tree)(using Context): Boolean = diff --git a/compiler/src/dotty/tools/dotc/typer/Applications.scala b/compiler/src/dotty/tools/dotc/typer/Applications.scala index 1ab44c39d6da..73d27d3ee1e7 100644 --- a/compiler/src/dotty/tools/dotc/typer/Applications.scala +++ b/compiler/src/dotty/tools/dotc/typer/Applications.scala @@ -1476,7 +1476,7 @@ trait Applications extends Compatibility { val dummyArg = dummyTreeOfType(ownType) val (newUnapplyFn, unapplyApp) = - val unapplyAppCall = withMode(Mode.NoInline): + val unapplyAppCall = typedExpr(untpd.TypedSplice(Apply(unapplyFn, dummyArg :: Nil))) inlinedUnapplyFnAndApp(dummyArg, unapplyAppCall) diff --git a/tests/pos/i19623.scala b/tests/pos/i19623.scala new file mode 100644 index 000000000000..8ab8cde159a9 --- /dev/null +++ b/tests/pos/i19623.scala @@ -0,0 +1,40 @@ +import scala.compiletime.* +import scala.language.dynamics + +abstract class % extends Selectable + +trait Select { type Out <: % } +trait Selector extends Dynamic { + def selectDynamic[S <: Singleton & String](label: S): Any = ??? + + def unapply[R: RecordLike](record: R)(using + t: Select, + r: RecordLike[t.Out] + ): r.ElemTypes = ??? +} + +trait RecordLike[R] { + type ElemTypes <: Tuple +} + + +@main def Test = { + val r: %{ val name: String; } = ??? + + // originally derived in macro, use dummy instance instead + transparent inline given outputRecordLike[R <: %]: RecordLike[R] = null.asInstanceOf[ + RecordLike[R] { + type ElemTypes = String *: EmptyTuple + } + ] + + type FieldSelector = Select { type Out = % { val name: String } } + given fieldSelector: FieldSelector = ??? + val selector: Selector = ??? + + val works = selector.unapply(r) + val works2 = selector.unapply(r)(using summon, fieldSelector, summon) + r match { + case selector(value) => value // compilation error + } +}