Skip to content

Commit

Permalink
Merge pull request #6879 from milessabin/topic/mirror-implicit-scope
Browse files Browse the repository at this point in the history
Support implicit scope augmentation for Mirror
  • Loading branch information
milessabin authored Jul 25, 2019
2 parents 9195bbb + b2e3e0f commit 8095c23
Show file tree
Hide file tree
Showing 3 changed files with 131 additions and 75 deletions.
164 changes: 89 additions & 75 deletions compiler/src/dotty/tools/dotc/typer/Implicits.scala
Original file line number Diff line number Diff line change
Expand Up @@ -884,15 +884,15 @@ trait Implicits { self: Typer =>
*
* <parent> {
* MirroredMonoType = <monoType>
* MirroredTypeConstrictor = <tycon>
* MirroredType = <mirroredType>
* MirroredLabel = <label> }
* }
*/
private def mirrorCore(parentClass: ClassSymbol, monoType: Type, mirroredType: Type, label: Name)(implicit ctx: Context) = {
parentClass.typeRef
private def mirrorCore(parentClass: ClassSymbol, monoType: Type, mirroredType: Type, label: Name, formal: Type)(implicit ctx: Context) =
formal & parentClass.typeRef
.refinedWith(tpnme.MirroredMonoType, TypeAlias(monoType))
.refinedWith(tpnme.MirroredType, TypeAlias(mirroredType))
.refinedWith(tpnme.MirroredLabel, TypeAlias(ConstantType(Constant(label.toString))))
}

/** A path referencing the companion of class type `clsType` */
private def companionPath(clsType: Type, span: Span)(implicit ctx: Context) = {
Expand All @@ -901,6 +901,16 @@ trait Implicits { self: Typer =>
ref.withSpan(span)
}

private def checkFormal(formal: Type)(implicit ctx: Context): Boolean = {
@tailrec
def loop(tp: Type): Boolean = tp match {
case RefinedType(parent, _, _: TypeBounds) => loop(parent)
case RefinedType(_, _, _) => false
case _ => true
}
loop(formal)
}

/** An implied instance for a type of the form `Mirror.Product { type MirroredType = T }`
* where `T` is a generic product type or a case object or an enum case.
*/
Expand All @@ -916,12 +926,12 @@ trait Implicits { self: Typer =>
val module = mirroredType.termSymbol
val modulePath = pathFor(mirroredType).withSpan(span)
if (module.info.classSymbol.is(Scala2x)) {
val mirrorType = mirrorCore(defn.Mirror_SingletonProxyClass, mirroredType, mirroredType, module.name)
val mirrorType = mirrorCore(defn.Mirror_SingletonProxyClass, mirroredType, mirroredType, module.name, formal)
val mirrorRef = New(defn.Mirror_SingletonProxyClass.typeRef, modulePath :: Nil)
mirrorRef.cast(mirrorType)
}
else {
val mirrorType = mirrorCore(defn.Mirror_SingletonClass, mirroredType, mirroredType, module.name)
val mirrorType = mirrorCore(defn.Mirror_SingletonClass, mirroredType, mirroredType, module.name, formal)
modulePath.cast(mirrorType)
}
}
Expand All @@ -943,7 +953,7 @@ trait Implicits { self: Typer =>
(mirroredType, elems)
}
val mirrorType =
mirrorCore(defn.Mirror_ProductClass, monoType, mirroredType, cls.name)
mirrorCore(defn.Mirror_ProductClass, monoType, mirroredType, cls.name, formal)
.refinedWith(tpnme.MirroredElemTypes, TypeAlias(elemsType))
.refinedWith(tpnme.MirroredElemLabels, TypeAlias(TypeOps.nestedPairs(elemLabels)))
val mirrorRef =
Expand All @@ -955,85 +965,89 @@ trait Implicits { self: Typer =>
}
}

formal.member(tpnme.MirroredType).info match {
case TypeBounds(mirroredType, _) => mirrorFor(mirroredType)
case other => EmptyTree
}
if (!checkFormal(formal)) EmptyTree
else
formal.member(tpnme.MirroredType).info match {
case TypeBounds(mirroredType, _) => mirrorFor(mirroredType)
case other => EmptyTree
}
}

/** An implied instance for a type of the form `Mirror.Sum { type MirroredType = T }`
* where `T` is a generic sum type.
*/
lazy val synthesizedSumMirror: SpecialHandler =
(formal, span) => implicit ctx => {
formal.member(tpnme.MirroredType).info match {
case TypeBounds(mirroredType0, _) =>
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 `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
if (!checkFormal(formal)) EmptyTree
else
formal.member(tpnme.MirroredType).info match {
case TypeBounds(mirroredType0, _) =>
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 `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)
}
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
instantiate(ctx.fresh.setExploreTyperState().setOwner(caseClass))
case _ =>
caseClass.typeRef
}
}
}
case child => child.termRef
}
case child => child.termRef
}

val (monoType, elemsType) = 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 (monoType, elemsType) = 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_SumClass, monoType, mirroredType, cls.name)
.refinedWith(tpnme.MirroredElemTypes, TypeAlias(elemsType))
.refinedWith(tpnme.MirroredElemLabels, TypeAlias(TypeOps.nestedPairs(elemLabels)))
val mirrorRef =
if (cls.linkedClass.exists && !cls.is(Scala2x)) companionPath(mirroredType, span)
else anonymousMirror(monoType, ExtendsSumMirror, span)
mirrorRef.cast(mirrorType)
} else EmptyTree
case _ => EmptyTree
}
val mirrorType =
mirrorCore(defn.Mirror_SumClass, monoType, mirroredType, cls.name, formal)
.refinedWith(tpnme.MirroredElemTypes, TypeAlias(elemsType))
.refinedWith(tpnme.MirroredElemLabels, TypeAlias(TypeOps.nestedPairs(elemLabels)))
val mirrorRef =
if (cls.linkedClass.exists && !cls.is(Scala2x)) companionPath(mirroredType, span)
else anonymousMirror(monoType, ExtendsSumMirror, span)
mirrorRef.cast(mirrorType)
} else EmptyTree
case _ => EmptyTree
}
}

/** An implied instance for a type of the form `Mirror { type MirroredType = T }`
Expand Down
15 changes: 15 additions & 0 deletions tests/neg/mirror-implicit-scope.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import scala.deriving._

object Test {
class SomeClass
case class ISB(i: Int, s: String, b: Boolean)
case class BI(b: Boolean, i: Int)

val v0 = the[Mirror.ProductOf[ISB]] // OK
val v1 = the[SomeClass & Mirror.ProductOf[ISB]] // error
val v2 = the[Mirror.ProductOf[ISB] & Mirror.ProductOf[BI]] // error
val v3 = the[Mirror.Product { type MirroredType = ISB ; def foo: Int }] // error
val v4 = the[Mirror.Product { type MirroredType = ISB ; def foo(i: Int): Int }] // error
val v5 = the[Mirror.Product { type MirroredType = ISB ; def foo[T](t: T): T }] // error // error
val v6 = the[Mirror.Product { type MirroredType = ISB ; val foo: Int }] // error
}
27 changes: 27 additions & 0 deletions tests/pos/mirror-implicit-scope.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import scala.deriving._

object Test {
object K0 {
type Generic[T] = Mirror { type Scope = K0.type ; type MirroredType = T ; type MirroredElemTypes }
given Ops {
inline def (gen: Generic[T]) toRepr[T <: Product](t: T): gen.MirroredElemTypes = Tuple.fromProduct(t).asInstanceOf
}
}

object K1 {
type Generic[F[_]] = Mirror { type Scope = K1.type ; type MirroredType = F ; type MirroredElemTypes[_] }
given Ops {
inline def (gen: Generic[F]) toRepr[F[_] <: Product, T](t: F[T]): gen.MirroredElemTypes[T] = Tuple.fromProduct(t).asInstanceOf
}
}

case class ISB(i: Int, s: String, b: Boolean)
val v0 = the[K0.Generic[ISB]]
val v1 = v0.toRepr(ISB(23, "foo", true))
val v2: (Int, String, Boolean) = v1

case class Box[T](t: T)
val v3 = the[K1.Generic[Box]]
val v4 = v3.toRepr(Box(23))
val v5: Tuple1[Int] = v4
}

0 comments on commit 8095c23

Please sign in to comment.