Skip to content

Commit

Permalink
Mirrors can now be materialized with additional type member refinements.
Browse files Browse the repository at this point in the history
Any additional type members will contribute to the implicit scope of any
searches involving the type of the summoned Mirror.  Typically such
types will be the singleton types of objects containing implicit
extension methods for the Mirror, or other related implicit definitions.

Pass through all extra refinements
  • Loading branch information
milessabin committed Jul 18, 2019
1 parent a35d724 commit a2ed239
Show file tree
Hide file tree
Showing 2 changed files with 59 additions and 8 deletions.
40 changes: 32 additions & 8 deletions compiler/src/dotty/tools/dotc/typer/Implicits.scala
Original file line number Diff line number Diff line change
Expand Up @@ -884,14 +884,38 @@ trait Implicits { self: Typer =>
*
* <parent> {
* MirroredMonoType = <monoType>
* MirroredTypeConstrictor = <tycon>
* MirroredLabel = <label> }
* MirroredType = <mirroredType>
* MirroredLabel = <label>
* Scope = <scope> // if scope ne NoType
* }
*/
private def mirrorCore(parent: Type, monoType: Type, mirroredType: Type, label: Name)(implicit ctx: Context) = {
parent
private def mirrorCore(parent: Type, monoType: Type, mirroredType: Type, label: Name, extras: List[(Name, Type)])(implicit ctx: Context) = {
val m = parent
.refinedWith(tpnme.MirroredMonoType, TypeAlias(monoType))
.refinedWith(tpnme.MirroredType, TypeAlias(mirroredType))
.refinedWith(tpnme.MirroredLabel, TypeAlias(ConstantType(Constant(label.toString))))

extras.foldLeft(m) { case (m, (tpnme, tpe)) => m.refinedWith(tpnme, tpe) }
}

private def extraRefinements(formal: Type)(implicit ctx: Context): List[(Name, Type)] = {
def isMirrorMember(nme: Name): Boolean =
nme == tpnme.MirroredType ||
nme == tpnme.MirroredMonoType ||
nme == tpnme.MirroredLabel ||
nme == tpnme.MirroredElemTypes ||
nme == tpnme.MirroredElemLabels

@tailrec
def loop(tp: Type, acc: List[(Name, Type)]): List[(Name, Type)] = tp match {
case RefinedType(parent, nme, rhs) if !isMirrorMember(nme) =>
loop(parent, (nme, rhs) :: acc)
case RefinedType(parent, nme, rhs) =>
loop(parent, acc)
case other =>
acc
}
loop(formal, Nil)
}

/** A path referencing the companion of class type `clsType` */
Expand All @@ -916,12 +940,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_SingletonProxyType, mirroredType, mirroredType, module.name)
val mirrorType = mirrorCore(defn.Mirror_SingletonProxyType, mirroredType, mirroredType, module.name, extraRefinements(formal))
val mirrorRef = New(defn.Mirror_SingletonProxyType, modulePath :: Nil)
mirrorRef.cast(mirrorType)
}
else {
val mirrorType = mirrorCore(defn.Mirror_SingletonType, mirroredType, mirroredType, module.name)
val mirrorType = mirrorCore(defn.Mirror_SingletonType, mirroredType, mirroredType, module.name, extraRefinements(formal))
modulePath.cast(mirrorType)
}
}
Expand All @@ -943,7 +967,7 @@ trait Implicits { self: Typer =>
(mirroredType, elems)
}
val mirrorType =
mirrorCore(defn.Mirror_ProductType, monoType, mirroredType, cls.name)
mirrorCore(defn.Mirror_ProductType, monoType, mirroredType, cls.name, extraRefinements(formal))
.refinedWith(tpnme.MirroredElemTypes, TypeAlias(elemsType))
.refinedWith(tpnme.MirroredElemLabels, TypeAlias(TypeOps.nestedPairs(elemLabels)))
val mirrorRef =
Expand Down Expand Up @@ -1024,7 +1048,7 @@ trait Implicits { self: Typer =>
}

val mirrorType =
mirrorCore(defn.Mirror_SumType, monoType, mirroredType, cls.name)
mirrorCore(defn.Mirror_SumType, monoType, mirroredType, cls.name, extraRefinements(formal))
.refinedWith(tpnme.MirroredElemTypes, TypeAlias(elemsType))
.refinedWith(tpnme.MirroredElemLabels, TypeAlias(TypeOps.nestedPairs(elemLabels)))
val mirrorRef =
Expand Down
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 a2ed239

Please sign in to comment.