Skip to content

Commit a071cd0

Browse files
committed
Improvements to typeclass derivation
1. add a Generic instance only if the implementation of a derived typeclass mentions the type.
1 parent b44adf7 commit a071cd0

File tree

2 files changed

+27
-6
lines changed

2 files changed

+27
-6
lines changed

compiler/src/dotty/tools/dotc/ast/TreeInfo.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -666,7 +666,7 @@ trait TypedTreeInfo extends TreeInfo[Type] { self: Trees.Instance[Type] =>
666666
* Pre: `sym` must have a position.
667667
*/
668668
def defPath(sym: Symbol, root: Tree)(implicit ctx: Context): List[Tree] = trace.onDebug(s"defpath($sym with position ${sym.span}, ${root.show})") {
669-
require(sym.span.exists)
669+
require(sym.span.exists, sym)
670670
object accum extends TreeAccumulator[List[Tree]] {
671671
def apply(x: List[Tree], tree: Tree)(implicit ctx: Context): List[Tree] = {
672672
if (tree.span.contains(sym.span))

compiler/src/dotty/tools/dotc/typer/Deriving.scala

Lines changed: 26 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@ trait Deriving { this: Typer =>
3232
/** A buffer for synthesized symbols */
3333
private var synthetics = new mutable.ListBuffer[Symbol]
3434

35+
private var derivesGeneric = false
36+
3537
/** the children of `cls` ordered by textual occurrence */
3638
lazy val children: List[Symbol] = cls.children
3739

@@ -170,7 +172,7 @@ trait Deriving { this: Typer =>
170172
val derivedType = checkClassType(underlyingType, derived.sourcePos, traitReq = false, stablePrefixReq = true)
171173
val nparams = derivedType.classSymbol.typeParams.length
172174
if (derivedType.isRef(defn.GenericClass))
173-
() // do nothing, a Generic instance will be created anyway by `addGeneric`
175+
derivesGeneric = true
174176
else if (nparams == 1) {
175177
val typeClass = derivedType.classSymbol
176178
val firstKindedParams = cls.typeParams.filterNot(_.info.isLambdaSub)
@@ -210,14 +212,33 @@ trait Deriving { this: Typer =>
210212
addDerivedInstance(defn.GenericType.name, genericCompleter, codePos, reportErrors = false)
211213
}
212214

215+
/** If any of the instances has a companion with a `derived` member
216+
* that refers to `scala.reflect.Generic`, add an implied instance
217+
* of `Generic`. Note: this is just an optimization to avoid possible
218+
* code duplication. Generic instances are created on the fly if they
219+
* are missing from the companion.
220+
*/
221+
private def maybeAddGeneric(): Unit = {
222+
val genericCls = defn.GenericClass
223+
def refersToGeneric(sym: Symbol): Boolean = {
224+
val companion = sym.info.finalResultType.classSymbol.companionModule
225+
val derivd = companion.info.member(nme.derived)
226+
derivd.hasAltWith(sd => sd.info.existsPart(p => p.typeSymbol == genericCls))
227+
}
228+
if (derivesGeneric || synthetics.exists(refersToGeneric)) {
229+
derive.println(i"add generic infrastructure for $cls")
230+
addGeneric()
231+
addGenericClass()
232+
}
233+
}
234+
213235
/** Create symbols for derived instances and infrastructure,
214-
* append them to `synthetics` buffer,
215-
* and enter them into class scope.
236+
* append them to `synthetics` buffer, and enter them into class scope.
237+
* Also, add generic instances if needed.
216238
*/
217239
def enterDerived(derived: List[untpd.Tree]) = {
218240
derived.foreach(processDerivedInstance(_))
219-
addGeneric()
220-
addGenericClass()
241+
maybeAddGeneric()
221242
}
222243

223244
private def tupleElems(tp: Type): List[Type] = tp match {

0 commit comments

Comments
 (0)