diff --git a/compiler/src/dotty/tools/dotc/typer/Deriving.scala b/compiler/src/dotty/tools/dotc/typer/Deriving.scala index 03281d9cbca1..6c9222f12eeb 100644 --- a/compiler/src/dotty/tools/dotc/typer/Deriving.scala +++ b/compiler/src/dotty/tools/dotc/typer/Deriving.scala @@ -53,8 +53,14 @@ trait Deriving { this: Typer => // If we set the Synthetic flag here widenDelegate will widen too far and the // derived instance will have too low a priority to be selected over a freshly // derived instance at the summoning site. + + val (lazyOrMethod, rhsType) = info match { + case ExprType(rhsType) => (Final | Lazy | StableRealizable, rhsType) + case _ => (Method, info) + } + synthetics += - ctx.newSymbol(ctx.owner, instanceName, Delegate | Method, info, coord = pos.span) + ctx.newSymbol(ctx.owner, instanceName, Delegate | lazyOrMethod, rhsType, coord = pos.span) .entered } } @@ -154,7 +160,7 @@ trait Deriving { this: Typer => import tpd._ /** The type class instance definition with symbol `sym` */ - def typeclassInstance(sym: Symbol)(implicit ctx: Context): List[Type] => (List[List[tpd.Tree]] => tpd.Tree) = + def typeclassDefInstance(sym: Symbol)(implicit ctx: Context): List[Type] => (List[List[tpd.Tree]] => tpd.Tree) = (tparamRefs: List[Type]) => (paramRefss: List[List[tpd.Tree]]) => { val tparams = tparamRefs.map(_.typeSymbol.asType) val params = if (paramRefss.isEmpty) Nil else paramRefss.head.map(_.symbol.asTerm) @@ -165,20 +171,36 @@ trait Deriving { this: Typer => case info: MethodType => info.instantiate(params.map(_.termRef)) case info => info.widenExpr } - def companionRef(tp: Type): TermRef = tp match { - case tp @ TypeRef(prefix, _) if tp.symbol.isClass => - prefix.select(tp.symbol.companionModule).asInstanceOf[TermRef] - case tp: TypeProxy => - companionRef(tp.underlying) - } val resultType = instantiated(sym.info) val module = untpd.ref(companionRef(resultType)).withSpan(sym.span) val rhs = untpd.Select(module, nme.derived) typed(rhs, resultType) } - def syntheticDef(sym: Symbol): Tree = - tpd.polyDefDef(sym.asTerm, typeclassInstance(sym)(ctx.fresh.setOwner(sym).setNewScope)) + def typeclassValInstance(sym: Symbol)(implicit ctx: Context): Tree = { + val rhsType = sym.info + val module = untpd.ref(companionRef(rhsType)).withSpan(sym.span) + val rhs = untpd.Select(module, nme.derived) + val typedRhs = typed(rhs, rhsType) + tpd.ValDef(sym.asTerm, typedRhs) + } + + def companionRef(tp: Type): TermRef = tp match { + case tp @ TypeRef(prefix, _) if tp.symbol.isClass => + prefix.select(tp.symbol.companionModule).asInstanceOf[TermRef] + case tp: TypeProxy => + companionRef(tp.underlying) + } + + def syntheticDef(sym: Symbol): Tree = { + val localCtx = ctx.fresh.setOwner(sym).setNewScope + sym.info match { + case _: MethodicType => + tpd.polyDefDef(sym.asTerm, typeclassDefInstance(sym)(localCtx)) + case _ => + typeclassValInstance(sym)(localCtx) + } + } synthetics.map(syntheticDef).toList } diff --git a/tests/run/lazy-derives.scala b/tests/run/lazy-derives.scala new file mode 100644 index 000000000000..23c7402a8cd2 --- /dev/null +++ b/tests/run/lazy-derives.scala @@ -0,0 +1,46 @@ +import scala.deriving._ + +object Test extends App { + case class Mono(i: Int) derives Foo, Bar, Inline + case class Poly[T](t: T) derives Foo, Bar, Baz, Inline + + trait Quux[T] + object Quux { + given [T] as Quux[T] = new Quux[T] {} + } + + trait Foo[T] + object Foo { + given as Foo[Int] {} + def derived[T] given (m: Mirror.Of[T]): Foo[T] = new Foo[T] {} + } + + trait Bar[T] + object Bar { + given as Bar[Int] {} + def derived[T] given (m: Mirror.Of[T], o: Quux[T]): Bar[T] = new Bar[T] {} + } + + trait Baz[F[_]] + object Baz { + def derived[F[_]] given (m: Mirror { type MirroredType = F }): Baz[F] = new Baz[F] {} + } + + trait Inline[T] + object Inline { + given as Inline[Int] {} + inline def derived[T] given (m: Mirror.Of[T]): Inline[T] = new Inline[T] {} + } + + // Unfortunately these tests doesn't distinguish a lazy val + // from a cached def + assert(the[Foo[Mono]] eq the[Foo[Mono]]) + assert(the[Bar[Mono]] eq the[Bar[Mono]]) + assert(the[Baz[Poly]] eq the[Baz[Poly]]) + assert(the[Inline[Mono]] eq the[Inline[Mono]]) + + // These have term arguments so should be distinct + assert(the[Foo[Poly[Int]]] ne the[Foo[Poly[Int]]]) + assert(the[Bar[Poly[Int]]] ne the[Bar[Poly[Int]]]) + assert(the[Inline[Poly[Int]]] ne the[Inline[Poly[Int]]]) +}