Skip to content

Commit 8144f98

Browse files
authored
Merge pull request #6531 from dotty-staging/tc-derive
Test case for new typeclass derivation scheme
2 parents 2ae2dea + 2afa89c commit 8144f98

37 files changed

+1528
-762
lines changed

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

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import reporting.diagnostic.messages._
1515
import reporting.trace
1616
import annotation.constructorOnly
1717
import printing.Formatting.hl
18+
import config.Printers
1819

1920
import scala.annotation.internal.sharable
2021

@@ -51,7 +52,7 @@ object desugar {
5152
private type VarInfo = (NameTree, Tree)
5253

5354
/** Is `name` the name of a method that can be invalidated as a compiler-generated
54-
* case class method that clashes with a user-defined method?
55+
* case class method if it clashes with a user-defined method?
5556
*/
5657
def isRetractableCaseClassMethodName(name: Name)(implicit ctx: Context): Boolean = name match {
5758
case nme.apply | nme.unapply | nme.unapplySeq | nme.copy => true
@@ -765,7 +766,7 @@ object desugar {
765766
}
766767

767768
flatTree(cdef1 :: companions ::: implicitWrappers)
768-
}
769+
}.reporting(res => i"desugared: $res", Printers.desugar)
769770

770771
/** Expand
771772
*
@@ -848,8 +849,7 @@ object desugar {
848849
fwd
849850
}
850851
val moduleName = tdef.name.toTermName
851-
val localRef = Select(Ident(moduleName), tdef.name)
852-
localRef.pushAttachment(SuppressAccessCheck, ())
852+
val localRef = Select(Ident(moduleName), tdef.name).withAttachment(SuppressAccessCheck, ())
853853
val aliasType = cpy.TypeDef(tdef)(rhs = completeForwarder(localRef)).withSpan(tdef.span.startPos)
854854
val localType = tdef.withMods(Modifiers(Synthetic | Opaque).withPrivateWithin(tdef.name))
855855

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

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import Symbols._, StdNames._, Trees._
88
import Decorators._
99
import util.{Property, SourceFile}
1010
import typer.ErrorReporting._
11+
import transform.SyntheticMembers.ExtendsSingletonMirror
1112

1213
import scala.annotation.internal.sharable
1314

@@ -115,11 +116,16 @@ object DesugarEnums {
115116
val toStringDef =
116117
DefDef(nme.toString_, Nil, Nil, TypeTree(), Ident(nme.name))
117118
.withFlags(Override)
118-
def creator = New(Template(emptyConstructor, enumClassRef :: Nil, Nil, EmptyValDef,
119-
List(enumTagDef, toStringDef) ++ registerCall))
119+
val creator = New(Template(
120+
constr = emptyConstructor,
121+
parents = enumClassRef :: Nil,
122+
derived = Nil,
123+
self = EmptyValDef,
124+
body = List(enumTagDef, toStringDef) ++ registerCall
125+
).withAttachment(ExtendsSingletonMirror, ()))
120126
DefDef(nme.DOLLAR_NEW, Nil,
121127
List(List(param(nme.tag, defn.IntType), param(nme.name, defn.StringType))),
122-
TypeTree(), creator)
128+
TypeTree(), creator).withFlags(Private | Synthetic)
123129
}
124130

125131
/** The return type of an enum case apply method and any widening methods in which
@@ -259,6 +265,7 @@ object DesugarEnums {
259265
.withFlags(Override)
260266
val (tagMeth, scaffolding) = enumTagMeth(CaseKind.Object)
261267
val impl1 = cpy.Template(impl)(body = List(tagMeth, toStringMeth) ++ registerCall)
268+
.withAttachment(ExtendsSingletonMirror, ())
262269
val vdef = ValDef(name, TypeTree(), New(impl1)).withMods(mods | Final)
263270
flatTree(scaffolding ::: vdef :: Nil).withSpan(span)
264271
}

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

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -400,6 +400,26 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo {
400400
case ConstantType(value) => Literal(value)
401401
}
402402

403+
/** A path that corresponds to the given type `tp`. Error if `tp` is not a refinement
404+
* of an addressable singleton type.
405+
*/
406+
def pathFor(tp: Type)(implicit ctx: Context): Tree = {
407+
def recur(tp: Type): Tree = tp match {
408+
case tp: NamedType =>
409+
tp.info match {
410+
case TypeAlias(alias) => recur(alias)
411+
case _: TypeBounds => EmptyTree
412+
case _ => singleton(tp)
413+
}
414+
case tp: TypeProxy => recur(tp.superType)
415+
case _ => EmptyTree
416+
}
417+
recur(tp).orElse {
418+
ctx.error(em"$tp is not an addressable singleton type")
419+
TypeTree(tp)
420+
}
421+
}
422+
403423
/** A tree representing a `newXYZArray` operation of the right
404424
* kind for the given element type in `elemTpe`. No type arguments or
405425
* `length` arguments are given.

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

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -263,10 +263,7 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo {
263263
}
264264

265265
/** Install the derived type tree as a dependency on `sym` */
266-
def watching(sym: Symbol): this.type = {
267-
pushAttachment(OriginalSymbol, sym)
268-
this
269-
}
266+
def watching(sym: Symbol): this.type = withAttachment(OriginalSymbol, sym)
270267

271268
/** A hook to ensure that all necessary symbols are completed so that
272269
* OriginalSymbol attachments are propagated to this tree

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ object Printers {
1919
val cyclicErrors: Printer = noPrinter
2020
val debug = noPrinter // no type annotation here to force inlining
2121
val derive: Printer = noPrinter
22+
val desugar: Printer = noPrinter
2223
val dottydoc: Printer = noPrinter
2324
val exhaustivity: Printer = noPrinter
2425
val gadts: Printer = noPrinter

compiler/src/dotty/tools/dotc/core/Definitions.scala

Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -692,16 +692,23 @@ class Definitions {
692692
lazy val ModuleSerializationProxyConstructor: TermSymbol =
693693
ModuleSerializationProxyClass.requiredMethod(nme.CONSTRUCTOR, List(ClassType(TypeBounds.empty)))
694694

695-
lazy val GenericType: TypeRef = ctx.requiredClassRef("scala.reflect.Generic")
696-
def GenericClass(implicit ctx: Context): ClassSymbol = GenericType.symbol.asClass
697-
lazy val ShapeType: TypeRef = ctx.requiredClassRef("scala.compiletime.Shape")
698-
def ShapeClass(implicit ctx: Context): ClassSymbol = ShapeType.symbol.asClass
699-
lazy val ShapeCaseType: TypeRef = ctx.requiredClassRef("scala.compiletime.Shape.Case")
700-
def ShapeCaseClass(implicit ctx: Context): ClassSymbol = ShapeCaseType.symbol.asClass
701-
lazy val ShapeCasesType: TypeRef = ctx.requiredClassRef("scala.compiletime.Shape.Cases")
702-
def ShapeCasesClass(implicit ctx: Context): ClassSymbol = ShapeCasesType.symbol.asClass
703-
lazy val MirrorType: TypeRef = ctx.requiredClassRef("scala.reflect.Mirror")
704-
lazy val GenericClassType: TypeRef = ctx.requiredClassRef("scala.reflect.GenericClass")
695+
lazy val MirrorType: TypeRef = ctx.requiredClassRef("scala.deriving.Mirror")
696+
def MirrorClass(implicit ctx: Context): ClassSymbol = MirrorType.symbol.asClass
697+
698+
lazy val Mirror_ProductType: TypeRef = ctx.requiredClassRef("scala.deriving.Mirror.Product")
699+
def Mirror_ProductClass(implicit ctx: Context): ClassSymbol = Mirror_ProductType.symbol.asClass
700+
701+
lazy val Mirror_Product_fromProductR: TermRef = Mirror_ProductClass.requiredMethodRef(nme.fromProduct)
702+
def Mirror_Product_fromProduct(implicit ctx: Context): Symbol = Mirror_Product_fromProductR.symbol
703+
704+
lazy val Mirror_SumType: TypeRef = ctx.requiredClassRef("scala.deriving.Mirror.Sum")
705+
def Mirror_SumClass(implicit ctx: Context): ClassSymbol = Mirror_SumType.symbol.asClass
706+
707+
lazy val Mirror_SingletonType: TypeRef = ctx.requiredClassRef("scala.deriving.Mirror.Singleton")
708+
def Mirror_SingletonClass(implicit ctx: Context): ClassSymbol = Mirror_SingletonType.symbol.asClass
709+
710+
lazy val Mirror_SingletonProxyType: TypeRef = ctx.requiredClassRef("scala.deriving.Mirror.SingletonProxy")
711+
def Mirror_SingletonProxyClass(implicit ctx: Context): ClassSymbol = Mirror_SingletonProxyType.symbol.asClass
705712

706713
lazy val LanguageModuleRef: TermSymbol = ctx.requiredModule("scala.language")
707714
def LanguageModuleClass(implicit ctx: Context): ClassSymbol = LanguageModuleRef.moduleClass.asClass

compiler/src/dotty/tools/dotc/core/Flags.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -628,6 +628,7 @@ object Flags {
628628

629629
/** An enum case */
630630
final val EnumCase: FlagConjunction = allOf(Enum, Case)
631+
final val EnumCaseVal: FlagConjunction = allOf(Enum, CaseVal)
631632

632633
/** A term parameter or parameter accessor */
633634
final val TermParamOrAccessor: FlagSet = Param | ParamAccessor

compiler/src/dotty/tools/dotc/core/StdNames.scala

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -339,6 +339,11 @@ object StdNames {
339339
val LiteralAnnotArg: N = "LiteralAnnotArg"
340340
val longHash: N = "longHash"
341341
val MatchCase: N = "MatchCase"
342+
val MirroredElemTypes: N = "MirroredElemTypes"
343+
val MirroredElemLabels: N = "MirroredElemLabels"
344+
val MirroredLabel: N = "MirroredLabel"
345+
val MirroredMonoType: N = "MirroredMonoType"
346+
val MirroredType: N = "MirroredType"
342347
val Modifiers: N = "Modifiers"
343348
val NestedAnnotArg: N = "NestedAnnotArg"
344349
val NoFlags: N = "NoFlags"
@@ -432,6 +437,7 @@ object StdNames {
432437
val flagsFromBits : N = "flagsFromBits"
433438
val flatMap: N = "flatMap"
434439
val foreach: N = "foreach"
440+
val fromProduct: N = "fromProduct"
435441
val genericArrayOps: N = "genericArrayOps"
436442
val genericClass: N = "genericClass"
437443
val get: N = "get"

compiler/src/dotty/tools/dotc/core/TypeComparer.scala

Lines changed: 20 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2313,19 +2313,30 @@ class TrackingTypeComparer(initctx: Context) extends TypeComparer(initctx) {
23132313
cas
23142314
}
23152315
def widenAbstractTypes(tp: Type): Type = new TypeMap {
2316+
var seen = Set[TypeParamRef]()
23162317
def apply(tp: Type) = tp match {
23172318
case tp: TypeRef =>
2318-
if (tp.symbol.isAbstractOrParamType | tp.symbol.isOpaqueAlias)
2319-
WildcardType
2320-
else tp.info match {
2321-
case TypeAlias(alias) =>
2322-
val alias1 = widenAbstractTypes(alias)
2323-
if (alias1 ne alias) alias1 else tp
2324-
case _ => mapOver(tp)
2319+
tp.info match {
2320+
case info: MatchAlias =>
2321+
mapOver(tp)
2322+
// TODO: We should follow the alias in this case, but doing so
2323+
// risks infinite recursion
2324+
case TypeBounds(lo, hi) =>
2325+
if (hi frozen_<:< lo) {
2326+
val alias = apply(lo)
2327+
if (alias ne lo) alias else mapOver(tp)
2328+
}
2329+
else WildcardType
2330+
case _ =>
2331+
mapOver(tp)
23252332
}
2326-
2333+
case tp: TypeLambda =>
2334+
val saved = seen
2335+
seen ++= tp.paramRefs
2336+
try mapOver(tp)
2337+
finally seen = saved
23272338
case tp: TypeVar if !tp.isInstantiated => WildcardType
2328-
case _: TypeParamRef => WildcardType
2339+
case tp: TypeParamRef if !seen.contains(tp) => WildcardType
23292340
case _ => mapOver(tp)
23302341
}
23312342
}.apply(tp)

compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -547,7 +547,7 @@ class PlainPrinter(_ctx: Context) extends Printer {
547547
val saved = maxSummarized
548548
maxSummarized = ctx.base.toTextRecursions + depth
549549
try op
550-
finally maxSummarized = depth
550+
finally maxSummarized = saved
551551
}
552552

553553
def summarized[T](op: => T): T = summarized(summarizeDepth)(op)

compiler/src/dotty/tools/dotc/transform/PostTyper.scala

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ object PostTyper {
2626
* field (corresponding = super class field is initialized with subclass field)
2727
* (@see ForwardParamAccessors)
2828
*
29-
* (3) Add synthetic methods (@see SyntheticMethods)
29+
* (3) Add synthetic members (@see SyntheticMembers)
3030
*
3131
* (4) Check that `New` nodes can be instantiated, and that annotations are valid
3232
*
@@ -64,7 +64,7 @@ class PostTyper extends MacroTransform with IdentityDenotTransformer { thisPhase
6464
case _ =>
6565
}
6666

67-
override def changesMembers: Boolean = true // the phase adds super accessors and synthetic methods
67+
override def changesMembers: Boolean = true // the phase adds super accessors and synthetic members
6868

6969
override def transformPhase(implicit ctx: Context): Phase = thisPhase.next
7070

@@ -73,7 +73,7 @@ class PostTyper extends MacroTransform with IdentityDenotTransformer { thisPhase
7373

7474
val superAcc: SuperAccessors = new SuperAccessors(thisPhase)
7575
val paramFwd: ParamForwarding = new ParamForwarding(thisPhase)
76-
val synthMth: SyntheticMethods = new SyntheticMethods(thisPhase)
76+
val synthMbr: SyntheticMembers = new SyntheticMembers(thisPhase)
7777

7878
private def newPart(tree: Tree): Option[New] = methPart(tree) match {
7979
case Select(nu: New, _) => Some(nu)
@@ -230,7 +230,7 @@ class PostTyper extends MacroTransform with IdentityDenotTransformer { thisPhase
230230
case tree: Template =>
231231
withNoCheckNews(tree.parents.flatMap(newPart)) {
232232
val templ1 = paramFwd.forwardParamAccessors(tree)
233-
synthMth.addSyntheticMethods(
233+
synthMbr.addSyntheticMembers(
234234
superAcc.wrapTemplate(templ1)(
235235
super.transform(_).asInstanceOf[Template]))
236236
}

compiler/src/dotty/tools/dotc/transform/SymUtils.scala

Lines changed: 53 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ import StdNames._
1212
import NameKinds._
1313
import Flags._
1414
import Annotations._
15+
import ValueClasses.isDerivedValueClass
16+
import Decorators._
1517

1618
import language.implicitConversions
1719
import scala.annotation.tailrec
@@ -59,10 +61,56 @@ class SymUtils(val self: Symbol) extends AnyVal {
5961

6062
def isSuperAccessor(implicit ctx: Context): Boolean = self.name.is(SuperAccessorName)
6163

62-
/** A type or term parameter or a term parameter accessor */
64+
/** Is this a type or term parameter or a term parameter accessor? */
6365
def isParamOrAccessor(implicit ctx: Context): Boolean =
6466
self.is(Param) || self.is(ParamAccessor)
6567

68+
/** Is this a case class for which a product mirror is generated?
69+
* Excluded are value classes, abstract classes and case classes with more than one
70+
* parameter section.
71+
*/
72+
def whyNotGenericProduct(implicit ctx: Context): String =
73+
if (!self.is(CaseClass)) "it is not a case class"
74+
else if (self.is(Abstract)) "it is an abstract class"
75+
else if (self.primaryConstructor.info.paramInfoss.length != 1) "it takes more than one parameter list"
76+
else if (isDerivedValueClass(self)) "it is a value class"
77+
else ""
78+
79+
def isGenericProduct(implicit ctx: Context): Boolean = whyNotGenericProduct.isEmpty
80+
81+
/** Is this a sealed class or trait for which a sum mirror is generated?
82+
* It must satisfy the following conditions:
83+
* - it has at least one child class or object
84+
* - none of its children are anonymous classes
85+
* - all of its children are addressable through a path from its companion object
86+
* - all of its children are generic products or singletons
87+
*/
88+
def whyNotGenericSum(implicit ctx: Context): String =
89+
if (!self.is(Sealed))
90+
s"it is not a sealed ${if (self.is(Trait)) "trait" else "class"}"
91+
else {
92+
val children = self.children
93+
val companion = self.linkedClass
94+
def problem(child: Symbol) = {
95+
96+
def isAccessible(sym: Symbol): Boolean =
97+
companion.isContainedIn(sym) || sym.is(Module) && isAccessible(sym.owner)
98+
99+
if (child == self) "it has anonymous or inaccessible subclasses"
100+
else if (!isAccessible(child.owner)) i"its child $child is not accessible"
101+
else if (!child.isClass) ""
102+
else {
103+
val s = child.whyNotGenericProduct
104+
if (s.isEmpty) s
105+
else i"its child $child is not a generic product because $s"
106+
}
107+
}
108+
if (children.isEmpty) "it does not have subclasses"
109+
else children.map(problem).find(!_.isEmpty).getOrElse("")
110+
}
111+
112+
def isGenericSum(implicit ctx: Context): Boolean = whyNotGenericSum.isEmpty
113+
66114
/** If this is a constructor, its owner: otherwise this. */
67115
final def skipConstructor(implicit ctx: Context): Symbol =
68116
if (self.isConstructor) self.owner else self
@@ -151,6 +199,10 @@ class SymUtils(val self: Symbol) extends AnyVal {
151199
else owner.isLocal
152200
}
153201

202+
/** The typeRef with wildcard arguments for each type parameter */
203+
def rawTypeRef(implicit ctx: Context) =
204+
self.typeRef.appliedTo(self.typeParams.map(_ => TypeBounds.empty))
205+
154206
/** Is symbol a quote operation? */
155207
def isQuote(implicit ctx: Context): Boolean =
156208
self == defn.InternalQuoted_exprQuote || self == defn.InternalQuoted_typeQuote

0 commit comments

Comments
 (0)