From d244a30b67ce6db9e929f3e7e9c0ef4051ac0e5c 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 --- .../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 230092898051..a97917e28771 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 8a1db87a4f92..fd1421d81b61 100644 --- a/compiler/src/dotty/tools/dotc/typer/Applications.scala +++ b/compiler/src/dotty/tools/dotc/typer/Applications.scala @@ -1492,7 +1492,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 + } +}