diff --git a/compiler/src/dotty/tools/dotc/core/Mode.scala b/compiler/src/dotty/tools/dotc/core/Mode.scala index 71b49394ae14..1622f47a0b47 100644 --- a/compiler/src/dotty/tools/dotc/core/Mode.scala +++ b/compiler/src/dotty/tools/dotc/core/Mode.scala @@ -122,6 +122,9 @@ object Mode { val PatternOrTypeBits: Mode = Pattern | Type + /** Skip inlining of unapply methods. */ + val NoInlineUnapply: Mode = newMode(18, "NoInlineUnapply") + /** We are elaborating the fully qualified name of a package clause. * In this case, identifiers should never be imported. */ diff --git a/compiler/src/dotty/tools/dotc/inlines/Inlines.scala b/compiler/src/dotty/tools/dotc/inlines/Inlines.scala index 230092898051..1e9d6fd6c986 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 @@ -64,6 +65,7 @@ object Inlines: && !ctx.typer.hasInliningErrors && !ctx.base.stopInlining && !ctx.mode.is(Mode.NoInline) + && !(ctx.mode.is(Mode.NoInlineUnapply) && tree.symbol.name.isUnapplyName) } 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 452c6d197310..a9e316aa14e0 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 = withMode(Mode.NoInlineUnapply): 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 + } +}