Skip to content

Commit 2d16d9f

Browse files
authored
Merge pull request #5540 from dotty-staging/add-derive-2
Add typeclass derivation
2 parents 4ba7da3 + 5e4fa24 commit 2d16d9f

File tree

116 files changed

+3352
-433
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

116 files changed

+3352
-433
lines changed

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

Lines changed: 30 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import Symbols._, StdNames._, Trees._
88
import Decorators._, transform.SymUtils._
99
import NameKinds.{UniqueName, EvidenceParamName, DefaultGetterName}
1010
import typer.FrontEnd
11-
import util.{Property, SourceFile}
11+
import util.{Property, SourceFile, SourcePosition}
1212
import collection.mutable.ListBuffer
1313
import reporting.diagnostic.messages._
1414
import reporting.trace
@@ -23,7 +23,13 @@ object desugar {
2323
/** If a Select node carries this attachment, suppress the check
2424
* that its type refers to an acessible symbol.
2525
*/
26-
val SuppressAccessCheck = new Property.Key[Unit]
26+
val SuppressAccessCheck: Property.Key[Unit] = new Property.Key
27+
28+
/** An attachment for companion modules of classes that have a `derives` clause.
29+
* The position value indicates the start position of the template of the
30+
* deriving class.
31+
*/
32+
val DerivingCompanion: Property.Key[SourcePosition] = new Property.Key
2733

2834
/** Info of a variable in a pattern: The named tree and its type */
2935
private type VarInfo = (NameTree, Tree)
@@ -297,7 +303,8 @@ object desugar {
297303
/** The expansion of a class definition. See inline comments for what is involved */
298304
def classDef(cdef: TypeDef)(implicit ctx: Context): Tree = {
299305
val className = checkNotReservedName(cdef).asTypeName
300-
val impl @ Template(constr0, parents, self, _) = cdef.rhs
306+
val impl @ Template(_, _, self, _) = cdef.rhs
307+
val parents = impl.parents
301308
val mods = cdef.mods
302309
val companionMods = mods
303310
.withFlags((mods.flags & (AccessFlags | Final)).toCommonFlags)
@@ -312,7 +319,7 @@ object desugar {
312319
meth
313320
}
314321

315-
val constr1 = decompose(defDef(constr0, isPrimaryConstructor = true))
322+
val constr1 = decompose(defDef(impl.constr, isPrimaryConstructor = true))
316323

317324
// The original type and value parameters in the constructor already have the flags
318325
// needed to be type members (i.e. param, and possibly also private and local unless
@@ -557,14 +564,23 @@ object desugar {
557564
}
558565
def eqInstances = if (isEnum) eqInstance :: Nil else Nil
559566

567+
// derived type classes of non-module classes go to their companions
568+
val (clsDerived, companionDerived) =
569+
if (mods.is(Module)) (impl.derived, Nil) else (Nil, impl.derived)
570+
560571
// The thicket which is the desugared version of the companion object
561-
// synthetic object C extends parentTpt { defs }
562-
def companionDefs(parentTpt: Tree, defs: List[Tree]) =
563-
moduleDef(
572+
// synthetic object C extends parentTpt derives class-derived { defs }
573+
def companionDefs(parentTpt: Tree, defs: List[Tree]) = {
574+
val mdefs = moduleDef(
564575
ModuleDef(
565-
className.toTermName, Template(emptyConstructor, parentTpt :: Nil, EmptyValDef, defs))
576+
className.toTermName, Template(emptyConstructor, parentTpt :: Nil, companionDerived, EmptyValDef, defs))
566577
.withMods(companionMods | Synthetic))
567-
.withSpan(cdef.span).toList
578+
.withSpan(cdef.span).toList
579+
if (companionDerived.nonEmpty)
580+
for (modClsDef @ TypeDef(_, _) <- mdefs)
581+
modClsDef.putAttachment(DerivingCompanion, impl.sourcePos.startPos)
582+
mdefs
583+
}
568584

569585
val companionMembers = defaultGetters ::: eqInstances ::: enumCases
570586

@@ -613,10 +629,10 @@ object desugar {
613629
}
614630
companionDefs(companionParent, applyMeths ::: unapplyMeth :: companionMembers)
615631
}
616-
else if (companionMembers.nonEmpty)
632+
else if (companionMembers.nonEmpty || companionDerived.nonEmpty)
617633
companionDefs(anyRef, companionMembers)
618634
else if (isValueClass) {
619-
constr0.vparamss match {
635+
impl.constr.vparamss match {
620636
case (_ :: Nil) :: _ => companionDefs(anyRef, Nil)
621637
case _ => Nil // error will be emitted in typer
622638
}
@@ -675,7 +691,7 @@ object desugar {
675691
}
676692
cpy.TypeDef(cdef: TypeDef)(
677693
name = className,
678-
rhs = cpy.Template(impl)(constr, parents1, self1,
694+
rhs = cpy.Template(impl)(constr, parents1, clsDerived, self1,
679695
tparamAccessors ::: vparamAccessors ::: normalizedBody ::: caseClassMeths)): TypeDef
680696
}
681697

@@ -772,7 +788,7 @@ object desugar {
772788
val localType = tdef.withMods(Modifiers(Synthetic | Opaque).withPrivateWithin(tdef.name))
773789

774790
val companions = moduleDef(ModuleDef(
775-
moduleName, Template(emptyConstructor, Nil, EmptyValDef, localType :: Nil))
791+
moduleName, Template(emptyConstructor, Nil, Nil, EmptyValDef, localType :: Nil))
776792
.withFlags(Synthetic | Opaque))
777793
Thicket(aliasType :: companions.toList)
778794
}
@@ -1335,7 +1351,7 @@ object desugar {
13351351
val (classParents, self) =
13361352
if (parentCores.length == 1 && (parent.tpe eq parentCores.head)) (untpdParent :: Nil, EmptyValDef)
13371353
else (parentCores map TypeTree, ValDef(nme.WILDCARD, untpdParent, EmptyTree))
1338-
val impl = Template(emptyConstructor, classParents, self, refinements)
1354+
val impl = Template(emptyConstructor, classParents, Nil, self, refinements)
13391355
TypeDef(tpnme.REFINE_CLASS, impl).withFlags(Trait)
13401356
}
13411357

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ object DesugarEnums {
115115
val toStringDef =
116116
DefDef(nme.toString_, Nil, Nil, TypeTree(), Ident(nme.name))
117117
.withFlags(Override)
118-
def creator = New(Template(emptyConstructor, enumClassRef :: Nil, EmptyValDef,
118+
def creator = New(Template(emptyConstructor, enumClassRef :: Nil, Nil, EmptyValDef,
119119
List(enumTagDef, toStringDef) ++ registerCall))
120120
DefDef(nme.DOLLAR_NEW, Nil,
121121
List(List(param(nme.tag, defn.IntType), param(nme.name, defn.StringType))),
@@ -216,7 +216,7 @@ object DesugarEnums {
216216
if (!enumClass.exists) EmptyTree
217217
else if (enumClass.typeParams.nonEmpty) {
218218
val parent = interpolatedEnumParent(span)
219-
val impl = Template(emptyConstructor, parent :: Nil, EmptyValDef, Nil)
219+
val impl = Template(emptyConstructor, parent :: Nil, Nil, EmptyValDef, Nil)
220220
expandEnumModule(name, impl, mods, span)
221221
}
222222
else {

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

Lines changed: 18 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -506,7 +506,6 @@ object Trees {
506506
/** selector match { cases } */
507507
case class Match[-T >: Untyped] private[ast] (selector: Tree[T], cases: List[CaseDef[T]])(implicit @constructorOnly src: SourceFile)
508508
extends TermTree[T] {
509-
assert(cases.nonEmpty)
510509
type ThisTree[-T >: Untyped] = Match[T]
511510
def isInline = false
512511
}
@@ -739,16 +738,24 @@ object Trees {
739738
def isClassDef: Boolean = rhs.isInstanceOf[Template[_]]
740739
}
741740

742-
/** extends parents { self => body } */
743-
case class Template[-T >: Untyped] private[ast] (constr: DefDef[T], parents: List[Tree[T]], self: ValDef[T], private var preBody: LazyTreeList)(implicit @constructorOnly src: SourceFile)
741+
/** extends parents { self => body }
742+
* @param parentsOrDerived A list of parents followed by a list of derived classes,
743+
* if this is of class untpd.DerivingTemplate.
744+
* Typed templates only have parents.
745+
*/
746+
case class Template[-T >: Untyped] private[ast] (constr: DefDef[T], parentsOrDerived: List[Tree[T]], self: ValDef[T], private var preBody: LazyTreeList)(implicit @constructorOnly src: SourceFile)
744747
extends DefTree[T] with WithLazyField[List[Tree[T]]] {
745748
type ThisTree[-T >: Untyped] = Template[T]
746749
def unforcedBody: LazyTreeList = unforced
747750
def unforced: LazyTreeList = preBody
748751
protected def force(x: AnyRef): Unit = preBody = x
749752
def body(implicit ctx: Context): List[Tree[T]] = forceIfLazy
753+
754+
def parents: List[Tree[T]] = parentsOrDerived // overridden by DerivingTemplate
755+
def derived: List[untpd.Tree] = Nil // overridden by DerivingTemplate
750756
}
751757

758+
752759
/** import expr.selectors
753760
* where a selector is either an untyped `Ident`, `name` or
754761
* an untyped thicket consisting of `name` and `rename`.
@@ -1143,9 +1150,9 @@ object Trees {
11431150
case tree: TypeDef if (name == tree.name) && (rhs eq tree.rhs) => tree
11441151
case _ => finalize(tree, untpd.TypeDef(name, rhs)(tree.source))
11451152
}
1146-
def Template(tree: Tree)(constr: DefDef, parents: List[Tree], self: ValDef, body: LazyTreeList)(implicit ctx: Context): Template = tree match {
1147-
case tree: Template if (constr eq tree.constr) && (parents eq tree.parents) && (self eq tree.self) && (body eq tree.unforcedBody) => tree
1148-
case _ => finalize(tree, untpd.Template(constr, parents, self, body)(tree.source))
1153+
def Template(tree: Tree)(constr: DefDef, parents: List[Tree], derived: List[untpd.Tree], self: ValDef, body: LazyTreeList)(implicit ctx: Context): Template = tree match {
1154+
case tree: Template if (constr eq tree.constr) && (parents eq tree.parents) && (derived eq tree.derived) && (self eq tree.self) && (body eq tree.unforcedBody) => tree
1155+
case tree => finalize(tree, untpd.Template(constr, parents, derived, self, body)(tree.source))
11491156
}
11501157
def Import(tree: Tree)(expr: Tree, selectors: List[untpd.Tree])(implicit ctx: Context): Import = tree match {
11511158
case tree: Import if (expr eq tree.expr) && (selectors eq tree.selectors) => tree
@@ -1182,8 +1189,8 @@ object Trees {
11821189
DefDef(tree: Tree)(name, tparams, vparamss, tpt, rhs)
11831190
def TypeDef(tree: TypeDef)(name: TypeName = tree.name, rhs: Tree = tree.rhs)(implicit ctx: Context): TypeDef =
11841191
TypeDef(tree: Tree)(name, rhs)
1185-
def Template(tree: Template)(constr: DefDef = tree.constr, parents: List[Tree] = tree.parents, self: ValDef = tree.self, body: LazyTreeList = tree.unforcedBody)(implicit ctx: Context): Template =
1186-
Template(tree: Tree)(constr, parents, self, body)
1192+
def Template(tree: Template)(constr: DefDef = tree.constr, parents: List[Tree] = tree.parents, derived: List[untpd.Tree] = tree.derived, self: ValDef = tree.self, body: LazyTreeList = tree.unforcedBody)(implicit ctx: Context): Template =
1193+
Template(tree: Tree)(constr, parents, derived, self, body)
11871194
}
11881195

11891196
/** Hook to indicate that a transform of some subtree should be skipped */
@@ -1292,8 +1299,8 @@ object Trees {
12921299
case tree @ TypeDef(name, rhs) =>
12931300
implicit val ctx = localCtx
12941301
cpy.TypeDef(tree)(name, transform(rhs))
1295-
case tree @ Template(constr, parents, self, _) =>
1296-
cpy.Template(tree)(transformSub(constr), transform(parents), transformSub(self), transformStats(tree.body))
1302+
case tree @ Template(constr, parents, self, _) if tree.derived.isEmpty =>
1303+
cpy.Template(tree)(transformSub(constr), transform(tree.parents), Nil, transformSub(self), transformStats(tree.body))
12971304
case Import(expr, selectors) =>
12981305
cpy.Import(tree)(transform(expr), selectors)
12991306
case PackageDef(pid, stats) =>
@@ -1416,7 +1423,7 @@ object Trees {
14161423
case TypeDef(name, rhs) =>
14171424
implicit val ctx = localCtx
14181425
this(x, rhs)
1419-
case tree @ Template(constr, parents, self, _) =>
1426+
case tree @ Template(constr, parents, self, _) if tree.derived.isEmpty =>
14201427
this(this(this(this(x, constr), parents), self), tree.body)
14211428
case Import(expr, selectors) =>
14221429
this(x, expr)

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -293,7 +293,7 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo {
293293
val findLocalDummy = new FindLocalDummyAccumulator(cls)
294294
val localDummy = ((NoSymbol: Symbol) /: body)(findLocalDummy.apply)
295295
.orElse(ctx.newLocalDummy(cls))
296-
val impl = untpd.Template(constr, parents, selfType, newTypeParams ++ body)
296+
val impl = untpd.Template(constr, parents, Nil, selfType, newTypeParams ++ body)
297297
.withType(localDummy.termRef)
298298
ta.assignType(untpd.TypeDef(cls.name, impl), cls)
299299
}

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

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,19 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo {
3939
def withName(name: Name)(implicit ctx: Context): ModuleDef = cpy.ModuleDef(this)(name.toTermName, impl)
4040
}
4141

42-
case class ParsedTry(expr: Tree, handler: Tree, finalizer: Tree)(implicit @constructorOnly src: SourceFile) extends Tree with TermTree
42+
/** An untyped template with a derives clause. Derived parents are added to the end
43+
* of the `parents` list. `derivedCount` keeps track of how many there are.
44+
* This representation was chosen because it balances two concerns:
45+
* - maximize overlap between DerivingTemplate and Template for code streamlining
46+
* - keep invariant that elements of untyped trees align with source positions
47+
*/
48+
class DerivingTemplate(constr: DefDef, parentsOrDerived: List[Tree], self: ValDef, preBody: LazyTreeList, derivedCount: Int)(implicit @constructorOnly src: SourceFile)
49+
extends Template(constr, parentsOrDerived, self, preBody) {
50+
override val parents = parentsOrDerived.dropRight(derivedCount)
51+
override val derived = parentsOrDerived.takeRight(derivedCount)
52+
}
53+
54+
case class ParsedTry(expr: Tree, handler: Tree, finalizer: Tree)(implicit @constructorOnly src: SourceFile) extends TermTree
4355

4456
case class SymbolLit(str: String)(implicit @constructorOnly src: SourceFile) extends TermTree
4557

@@ -303,7 +315,9 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo {
303315
def ValDef(name: TermName, tpt: Tree, rhs: LazyTree)(implicit src: SourceFile): ValDef = new ValDef(name, tpt, rhs)
304316
def DefDef(name: TermName, tparams: List[TypeDef], vparamss: List[List[ValDef]], tpt: Tree, rhs: LazyTree)(implicit src: SourceFile): DefDef = new DefDef(name, tparams, vparamss, tpt, rhs)
305317
def TypeDef(name: TypeName, rhs: Tree)(implicit src: SourceFile): TypeDef = new TypeDef(name, rhs)
306-
def Template(constr: DefDef, parents: List[Tree], self: ValDef, body: LazyTreeList)(implicit src: SourceFile): Template = new Template(constr, parents, self, body)
318+
def Template(constr: DefDef, parents: List[Tree], derived: List[Tree], self: ValDef, body: LazyTreeList)(implicit src: SourceFile): Template =
319+
if (derived.isEmpty) new Template(constr, parents, self, body)
320+
else new DerivingTemplate(constr, parents ++ derived, self, body, derived.length)
307321
def Import(expr: Tree, selectors: List[Tree])(implicit src: SourceFile): Import = new Import(expr, selectors)
308322
def PackageDef(pid: RefTree, stats: List[Tree])(implicit src: SourceFile): PackageDef = new PackageDef(pid, stats)
309323
def Annotated(arg: Tree, annot: Tree)(implicit src: SourceFile): Annotated = new Annotated(arg, annot)
@@ -431,8 +445,7 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo {
431445
case _ => finalize(tree, untpd.ModuleDef(name, impl)(tree.source))
432446
}
433447
def ParsedTry(tree: Tree)(expr: Tree, handler: Tree, finalizer: Tree)(implicit ctx: Context): TermTree = tree match {
434-
case tree: ParsedTry
435-
if (expr eq tree.expr) && (handler eq tree.handler) && (finalizer eq tree.finalizer) => tree
448+
case tree: ParsedTry if (expr eq tree.expr) && (handler eq tree.handler) && (finalizer eq tree.finalizer) => tree
436449
case _ => finalize(tree, untpd.ParsedTry(expr, handler, finalizer)(tree.source))
437450
}
438451
def SymbolLit(tree: Tree)(str: String)(implicit ctx: Context): TermTree = tree match {
@@ -513,6 +526,8 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo {
513526
override def transformMoreCases(tree: Tree)(implicit ctx: Context): Tree = tree match {
514527
case ModuleDef(name, impl) =>
515528
cpy.ModuleDef(tree)(name, transformSub(impl))
529+
case tree: DerivingTemplate =>
530+
cpy.Template(tree)(transformSub(tree.constr), transform(tree.parents), transform(tree.derived), transformSub(tree.self), transformStats(tree.body))
516531
case ParsedTry(expr, handler, finalizer) =>
517532
cpy.ParsedTry(tree)(transform(expr), transform(handler), transform(finalizer))
518533
case SymbolLit(str) =>
@@ -560,6 +575,8 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo {
560575
override def foldMoreCases(x: X, tree: Tree)(implicit ctx: Context): X = tree match {
561576
case ModuleDef(name, impl) =>
562577
this(x, impl)
578+
case tree: DerivingTemplate =>
579+
this(this(this(this(this(x, tree.constr), tree.parents), tree.derived), tree.self), tree.body)
563580
case ParsedTry(expr, handler, finalizer) =>
564581
this(this(this(x, expr), handler), finalizer)
565582
case SymbolLit(str) =>

compiler/src/dotty/tools/dotc/config/Printers.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ object Printers {
1818
val config: Printer = noPrinter
1919
val cyclicErrors: Printer = noPrinter
2020
val debug = noPrinter
21+
val derive: Printer = noPrinter
2122
val dottydoc: Printer = noPrinter
2223
val exhaustivity: Printer = noPrinter
2324
val gadts: Printer = noPrinter

0 commit comments

Comments
 (0)