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 + } +}