Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Test case for new typeclass derivation scheme #6531

Merged
merged 35 commits into from Jun 4, 2019
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
2b1ec55
New test case
odersky May 17, 2019
844c5d7
Fix widenAbstractTypes
odersky May 18, 2019
d5c5c11
Special treatment for Singletons
odersky May 18, 2019
6fda1f3
Fix printer bug
odersky May 19, 2019
c30af3c
Add desugar Printer
odersky May 20, 2019
3617cba
Add deriving.Mirror infrastructure
odersky May 20, 2019
42807da
Add isGenericProduct test
odersky May 20, 2019
5829928
Synthesis for mirror infrastructure
odersky May 20, 2019
c6c0f71
Rename SyntheticMethods -> SyntheticMembers
odersky May 20, 2019
5c924ca
Mirror infrastructure for generic sum types
odersky May 20, 2019
159c883
Refine isGenericSum condition
odersky May 21, 2019
88cec9f
Always generate companion objects for sealed classes
odersky May 21, 2019
4cccf41
Revert "Always generate companion objects for sealed classes"
odersky May 21, 2019
c031b30
Make enum cases implement Mirror.Singleton
odersky May 21, 2019
9120105
Add Mirror.Singleton to enum cases after typer
odersky May 21, 2019
ed79907
Refactor special case handling in implicit arguments
odersky May 21, 2019
f898fc1
Add new utility method: withAttachment
odersky May 22, 2019
934c4ad
Use attachment to mark singleton cases
odersky May 22, 2019
be1a063
Synthesize implicits for product and sum mirrors
odersky May 22, 2019
275a0a9
Generate sum mirrors for sealed traits that do not have a companion
odersky May 22, 2019
e50a032
Use Label field for sum and product mirrors
odersky May 22, 2019
eda4232
Synthesize mirrors also for Scala 2 defined classes and objects
odersky May 22, 2019
f24e247
Polishings
odersky May 22, 2019
7583adc
Avoid name clashes by prefixing all type members with Mirrored
odersky May 22, 2019
4784e2f
Update typeclass-scaling data using old Generic scheme
odersky May 23, 2019
a87b42b
Measure typeclass scaling using new scheme
odersky May 23, 2019
56c0f96
Drop old deriving infrastructure
odersky May 23, 2019
01d3ff7
Make scheme work also for nested classes and companion objects
odersky May 23, 2019
6d3c2f9
Don't constrain MirroredElemTypes to be a subtype of Tuple
odersky May 24, 2019
c02861f
Introduce MirroredTypeConstructor
odersky May 24, 2019
930ca64
Avoid widening derived instances too far
milessabin Jun 2, 2019
91df766
Generalized type class derivation for higher kinded type classes
milessabin Jun 2, 2019
ddb0c4c
Renamed elemTypes to elemsType
milessabin Jun 4, 2019
bf67516
MirroredMonoType -> MirroredType in comment
milessabin Jun 4, 2019
2afa89c
Removed redundant case and inlined RHSs
milessabin Jun 4, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/core/StdNames.scala
Expand Up @@ -343,7 +343,7 @@ object StdNames {
val MirroredElemLabels: N = "MirroredElemLabels"
val MirroredLabel: N = "MirroredLabel"
val MirroredMonoType: N = "MirroredMonoType"
val MirroredTypeConstructor: N = "MirroredTypeConstructor"
val MirroredType: N = "MirroredType"
val Modifiers: N = "Modifiers"
val NestedAnnotArg: N = "NestedAnnotArg"
val NoFlags: N = "NoFlags"
Expand Down
90 changes: 50 additions & 40 deletions compiler/src/dotty/tools/dotc/typer/Deriving.scala
Expand Up @@ -89,47 +89,57 @@ trait Deriving { this: Typer =>
val typeClass = derivedType.classSymbol
val nparams = typeClass.typeParams.length

// A matrix of all parameter combinations of current class parameters
// and derived typeclass parameters.
// Rows: parameters of current class
// Columns: parameters of typeclass

// Running example: typeclass: class TC[X, Y, Z], deriving class: class A[T, U]
// clsParamss =
// T_X T_Y T_Z
// U_X U_Y U_Z
val clsParamss: List[List[TypeSymbol]] = cls.typeParams.map { tparam =>
if (nparams == 0) Nil
else if (nparams == 1) tparam :: Nil
else typeClass.typeParams.map(tcparam =>
tparam.copy(name = s"${tparam.name}_$$_${tcparam.name}".toTypeName)
.asInstanceOf[TypeSymbol])
}
val firstKindedParamss = clsParamss.filter {
case param :: _ => !param.info.isLambdaSub
case nil => false
}
lazy val clsTpe = cls.typeRef.EtaExpand(cls.typeParams)
if (nparams == 1 && clsTpe.hasSameKindAs(typeClass.typeParams.head.info)) {
// A "natural" type class instance ... the kind of the data type
// matches the kind of the unique type class type parameter

val resultType = derivedType.appliedTo(clsTpe)
val instanceInfo = ExprType(resultType)
addDerivedInstance(originalType.typeSymbol.name, instanceInfo, derived.sourcePos)
} else {
// A matrix of all parameter combinations of current class parameters
// and derived typeclass parameters.
// Rows: parameters of current class
// Columns: parameters of typeclass

// Running example: typeclass: class TC[X, Y, Z], deriving class: class A[T, U]
// clsParamss =
// T_X T_Y T_Z
// U_X U_Y U_Z
val clsParamss: List[List[TypeSymbol]] = cls.typeParams.map { tparam =>
if (nparams == 0) Nil
else if (nparams == 1) tparam :: Nil
else typeClass.typeParams.map(tcparam =>
tparam.copy(name = s"${tparam.name}_$$_${tcparam.name}".toTypeName)
.asInstanceOf[TypeSymbol])
}
val firstKindedParamss = clsParamss.filter {
case param :: _ => !param.info.isLambdaSub
case nil => false
}

// The types of the required evidence parameters. In the running example:
// TC[T_X, T_Y, T_Z], TC[U_X, U_Y, U_Z]
val evidenceParamInfos =
for (row <- firstKindedParamss)
yield derivedType.appliedTo(row.map(_.typeRef))

// The class instances in the result type. Running example:
// A[T_X, U_X], A[T_Y, U_Y], A[T_Z, U_Z]
val resultInstances =
for (n <- List.range(0, nparams))
yield cls.typeRef.appliedTo(clsParamss.map(row => row(n).typeRef))

// TC[A[T_X, U_X], A[T_Y, U_Y], A[T_Z, U_Z]]
val resultType = derivedType.appliedTo(resultInstances)

val clsParams: List[TypeSymbol] = clsParamss.flatten
val instanceInfo =
if (clsParams.isEmpty) ExprType(resultType)
else PolyType.fromParams(clsParams, ImplicitMethodType(evidenceParamInfos, resultType))
addDerivedInstance(originalType.typeSymbol.name, instanceInfo, derived.sourcePos)
// The types of the required evidence parameters. In the running example:
// TC[T_X, T_Y, T_Z], TC[U_X, U_Y, U_Z]
val evidenceParamInfos =
for (row <- firstKindedParamss)
yield derivedType.appliedTo(row.map(_.typeRef))

// The class instances in the result type. Running example:
// A[T_X, U_X], A[T_Y, U_Y], A[T_Z, U_Z]
val resultInstances =
for (n <- List.range(0, nparams))
yield cls.typeRef.appliedTo(clsParamss.map(row => row(n).typeRef))

// TC[A[T_X, U_X], A[T_Y, U_Y], A[T_Z, U_Z]]
val resultType = derivedType.appliedTo(resultInstances)

val clsParams: List[TypeSymbol] = clsParamss.flatten
val instanceInfo =
if (clsParams.isEmpty) ExprType(resultType)
else PolyType.fromParams(clsParams, ImplicitMethodType(evidenceParamInfos, resultType))
addDerivedInstance(originalType.typeSymbol.name, instanceInfo, derived.sourcePos)
}
}

/** Create symbols for derived instances and infrastructure,
Expand Down
206 changes: 126 additions & 80 deletions compiler/src/dotty/tools/dotc/typer/Implicits.scala
Expand Up @@ -867,16 +867,10 @@ trait Implicits { self: Typer =>
* MirroredTypeConstrictor = <tycon>
* MirroredLabel = <label> }
*/
private def mirrorCore(parent: Type, monoType: Type, label: Name)(implicit ctx: Context) = {
val mirroredType = monoType match {
case monoType @ AppliedType(tycon, targs) if targs.forall(_.isInstanceOf[TypeBounds]) =>
EtaExpansion(tycon)
case _ =>
monoType
}
private def mirrorCore(parent: Type, monoType: Type, mirroredType: Type, label: Name)(implicit ctx: Context) = {
parent
.refinedWith(tpnme.MirroredMonoType, TypeAlias(monoType))
.refinedWith(tpnme.MirroredTypeConstructor, TypeAlias(mirroredType))
.refinedWith(tpnme.MirroredType, TypeAlias(mirroredType))
.refinedWith(tpnme.MirroredLabel, TypeAlias(ConstantType(Constant(label.toString))))
}

Expand All @@ -892,106 +886,158 @@ trait Implicits { self: Typer =>
*/
milessabin marked this conversation as resolved.
Show resolved Hide resolved
lazy val synthesizedProductMirror: SpecialHandler =
(formal: Type, span: Span) => implicit (ctx: Context) => {
def mirrorFor(monoType: Type): Tree = monoType match {
case AndType(tp1, tp2) =>
mirrorFor(tp1).orElse(mirrorFor(tp2))
case _ =>
if (monoType.termSymbol.is(CaseVal)) {
val module = monoType.termSymbol
val modulePath = pathFor(monoType).withSpan(span)
if (module.info.classSymbol.is(Scala2x)) {
val mirrorType = mirrorCore(defn.Mirror_SingletonProxyType, monoType, module.name)
val mirrorRef = New(defn.Mirror_SingletonProxyType, modulePath :: Nil)
mirrorRef.cast(mirrorType)
def mirrorFor(mirroredType0: Type): Tree = {
val mirroredType = mirroredType0.stripTypeVar
mirroredType match {
case AndType(tp1, tp2) =>
mirrorFor(tp1).orElse(mirrorFor(tp2))
case _ =>
if (mirroredType.termSymbol.is(CaseVal)) {
val module = mirroredType.termSymbol
val modulePath = pathFor(mirroredType).withSpan(span)
if (module.info.classSymbol.is(Scala2x)) {
val mirrorType = mirrorCore(defn.Mirror_SingletonProxyType, mirroredType, mirroredType, module.name)
val mirrorRef = New(defn.Mirror_SingletonProxyType, modulePath :: Nil)
mirrorRef.cast(mirrorType)
}
else {
val mirrorType = mirrorCore(defn.Mirror_SingletonType, mirroredType, mirroredType, module.name)
modulePath.cast(mirrorType)
}
}
else {
val mirrorType = mirrorCore(defn.Mirror_SingletonType, monoType, module.name)
modulePath.cast(mirrorType)
else if (mirroredType.classSymbol.isGenericProduct) {
val cls = mirroredType.classSymbol
val accessors = cls.caseAccessors.filterNot(_.is(PrivateLocal))
val elemLabels = accessors.map(acc => ConstantType(Constant(acc.name.toString)))
val (monoType, elemTypes) = mirroredType match {
case mirroredType: HKTypeLambda =>
val elems =
milessabin marked this conversation as resolved.
Show resolved Hide resolved
mirroredType.derivedLambdaType(
resType = TypeOps.nestedPairs(accessors.map(mirroredType.memberInfo(_).widenExpr))
)
val AppliedType(tycon, _) = mirroredType.resultType
val monoType = AppliedType(tycon, mirroredType.paramInfos)
(monoType, elems)
case _ =>
val elems = TypeOps.nestedPairs(accessors.map(mirroredType.memberInfo(_).widenExpr))
(mirroredType, elems)
}
val mirrorType =
mirrorCore(defn.Mirror_ProductType, monoType, mirroredType, cls.name)
.refinedWith(tpnme.MirroredElemTypes, TypeAlias(elemTypes))
.refinedWith(tpnme.MirroredElemLabels, TypeAlias(TypeOps.nestedPairs(elemLabels)))
val mirrorRef =
if (cls.is(Scala2x)) anonymousMirror(monoType, ExtendsProductMirror, span)
else companionPath(mirroredType, span)
mirrorRef.cast(mirrorType)
}
}
else if (monoType.classSymbol.isGenericProduct) {
val cls = monoType.classSymbol
val accessors = cls.caseAccessors.filterNot(_.is(PrivateLocal))
val elemTypes = accessors.map(monoType.memberInfo(_).widenExpr)
val elemLabels = accessors.map(acc => ConstantType(Constant(acc.name.toString)))
val mirrorType =
mirrorCore(defn.Mirror_ProductType, monoType, cls.name)
.refinedWith(tpnme.MirroredElemTypes, TypeAlias(TypeOps.nestedPairs(elemTypes)))
.refinedWith(tpnme.MirroredElemLabels, TypeAlias(TypeOps.nestedPairs(elemLabels)))
val mirrorRef =
if (cls.is(Scala2x)) anonymousMirror(monoType, ExtendsProductMirror, span)
else companionPath(monoType, span)
mirrorRef.cast(mirrorType)
}
else EmptyTree
else EmptyTree
}
}
formal.member(tpnme.MirroredMonoType).info match {
case monoAlias @ TypeAlias(monoType) => mirrorFor(monoType)
case _ => EmptyTree

formal.member(tpnme.MirroredType).info match {
milessabin marked this conversation as resolved.
Show resolved Hide resolved
case TypeAlias(mirroredType) => mirrorFor(mirroredType)
case TypeBounds(mirroredType, _) => mirrorFor(mirroredType)
case other => EmptyTree
}
}

/** An implied instance for a type of the form `Mirror.Sum { type MirroredMonoType = T }`
* where `T` is a generic sum type.
milessabin marked this conversation as resolved.
Show resolved Hide resolved
*/
lazy val synthesizedSumMirror: SpecialHandler =
(formal: Type, span: Span) => implicit (ctx: Context) =>
formal.member(tpnme.MirroredMonoType).info match {
case TypeAlias(monoType) if monoType.classSymbol.isGenericSum =>
val cls = monoType.classSymbol
val elemTypes = cls.children.map {
(formal: Type, span: Span) => implicit (ctx: Context) => {
def mirrorFor(mirroredType0: Type): Tree = {
val mirroredType = mirroredType0.stripTypeVar
if (mirroredType.classSymbol.isGenericSum) {
val cls = mirroredType.classSymbol
val elemLabels = cls.children.map(c => ConstantType(Constant(c.name.toString)))

def solve(sym: Symbol): Type = sym match {
case caseClass: ClassSymbol =>
assert(caseClass.is(Case))
if (caseClass.is(Module))
caseClass.sourceModule.termRef
else caseClass.primaryConstructor.info match {
case info: PolyType =>
// Compute the the full child type by solving the subtype constraint
// `C[X1, ..., Xn] <: P`, where
//
// - P is the current `monoType`
// - C is the child class, with type parameters X1, ..., Xn
//
// Contravariant type parameters are minimized, all other type parameters are maximized.
def instantiate(implicit ctx: Context) = {
val poly = constrained(info, untpd.EmptyTree)._1
val resType = poly.finalResultType
resType <:< monoType
val tparams = poly.paramRefs
val variances = caseClass.typeParams.map(_.paramVariance)
val instanceTypes = (tparams, variances).zipped.map((tparam, variance) =>
ctx.typeComparer.instanceType(tparam, fromBelow = variance < 0))
resType.substParams(poly, instanceTypes)
}
instantiate(ctx.fresh.setExploreTyperState().setOwner(caseClass))
case _ =>
caseClass.typeRef
else {
caseClass.primaryConstructor.info match {
case info: PolyType =>
// Compute the the full child type by solving the subtype constraint
// `C[X1, ..., Xn] <: P`, where
//
// - P is the current `mirroredType`
// - C is the child class, with type parameters X1, ..., Xn
//
// Contravariant type parameters are minimized, all other type parameters are maximized.
def instantiate(implicit ctx: Context) = {
val poly = constrained(info, untpd.EmptyTree)._1
val resType = poly.finalResultType
val target = mirroredType match {
case tp: HKTypeLambda => tp.resultType
case tp => tp
}
resType <:< target
val tparams = poly.paramRefs
val variances = caseClass.typeParams.map(_.paramVariance)
val instanceTypes = (tparams, variances).zipped.map((tparam, variance) =>
ctx.typeComparer.instanceType(tparam, fromBelow = variance < 0))
resType.substParams(poly, instanceTypes)
}
instantiate(ctx.fresh.setExploreTyperState().setOwner(caseClass))
case _ =>
caseClass.typeRef
}
}
case child => child.termRef
}

val (monoType, elemTypes) = mirroredType match {
case mirroredType: HKTypeLambda =>
val elems = mirroredType.derivedLambdaType(
resType = TypeOps.nestedPairs(cls.children.map(solve))
)
val AppliedType(tycon, _) = mirroredType.resultType
val monoType = AppliedType(tycon, mirroredType.paramInfos)
(monoType, elems)
case _ =>
val elems = TypeOps.nestedPairs(cls.children.map(solve))
(mirroredType, elems)
}

val mirrorType =
mirrorCore(defn.Mirror_SumType, monoType, cls.name)
.refinedWith(tpnme.MirroredElemTypes, TypeAlias(TypeOps.nestedPairs(elemTypes)))
mirrorCore(defn.Mirror_SumType, monoType, mirroredType, cls.name)
.refinedWith(tpnme.MirroredElemTypes, TypeAlias(elemTypes))
.refinedWith(tpnme.MirroredElemLabels, TypeAlias(TypeOps.nestedPairs(elemLabels)))
val mirrorRef =
if (cls.linkedClass.exists && !cls.is(Scala2x)) companionPath(monoType, span)
if (cls.linkedClass.exists && !cls.is(Scala2x)) companionPath(mirroredType, span)
else anonymousMirror(monoType, ExtendsSumMirror, span)
mirrorRef.cast(mirrorType)
case _ =>
EmptyTree
} else EmptyTree
}

formal.member(tpnme.MirroredType).info match {
case TypeAlias(mirroredType) => mirrorFor(mirroredType)
milessabin marked this conversation as resolved.
Show resolved Hide resolved
case TypeBounds(mirroredType, _) => mirrorFor(mirroredType)
case _ => EmptyTree
}
}

/** An implied instance for a type of the form `Mirror { type MirroredMonoType = T }`
milessabin marked this conversation as resolved.
Show resolved Hide resolved
* where `T` is a generic sum or product or singleton type.
*/
lazy val synthesizedMirror: SpecialHandler =
(formal: Type, span: Span) => implicit (ctx: Context) =>
formal.member(tpnme.MirroredMonoType).info match {
case monoAlias @ TypeAlias(monoType) =>
if (monoType.termSymbol.is(CaseVal) || monoType.classSymbol.isGenericProduct)
synthesizedProductMirror(formal, span)(ctx)
else
synthesizedSumMirror(formal, span)(ctx)
(formal: Type, span: Span) => implicit (ctx: Context) => {
def mirrorFor(mirroredType: Type): Tree =
if (mirroredType.termSymbol.is(CaseVal) || mirroredType.classSymbol.isGenericProduct)
synthesizedProductMirror(formal, span)(ctx)
else
synthesizedSumMirror(formal, span)(ctx)

formal.member(tpnme.MirroredType).info match {
case TypeAlias(mirroredType) => mirrorFor(mirroredType)
milessabin marked this conversation as resolved.
Show resolved Hide resolved
case TypeBounds(mirroredType, _) => mirrorFor(mirroredType)
case _ => EmptyTree
}
}

private var mySpecialHandlers: SpecialHandlers = null

Expand Down