Skip to content

Commit

Permalink
Add formatters for Option, Map, Period, and Atoms (#19658)
Browse files Browse the repository at this point in the history
  • Loading branch information
dwijnand authored Feb 13, 2024
2 parents c529a48 + b2b175c commit f92349b
Show file tree
Hide file tree
Showing 3 changed files with 77 additions and 23 deletions.
39 changes: 36 additions & 3 deletions compiler/src/dotty/tools/dotc/core/Periods.scala
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
package dotty.tools.dotc.core
package dotty.tools
package dotc
package core

import Contexts.*
import printing.*
import Texts.*
import Phases.unfusedPhases

object Periods {
Expand Down Expand Up @@ -35,7 +39,7 @@ object Periods {
*
* // Dmitry: sign == 0 isn't actually always true, in some cases phaseId == -1 is used for shifts, that easily creates code < 0
*/
class Period(val code: Int) extends AnyVal {
class Period(val code: Int) extends AnyVal with Showable {

/** The run identifier of this period. */
def runId: RunId = code >>> (PhaseWidth * 2)
Expand Down Expand Up @@ -97,7 +101,25 @@ object Periods {
this.firstPhaseId min that.firstPhaseId,
this.lastPhaseId max that.lastPhaseId)

override def toString: String = s"Period($firstPhaseId..$lastPhaseId, run = $runId)"
def toText(p: Printer): Text =
inContext(p.printerContext):
this match
case Nowhere => "Nowhere"
case InitialPeriod => "InitialPeriod"
case InvalidPeriod => "InvalidPeriod"
case Period(NoRunId, 0, PhaseMask) => s"Period(NoRunId.all)"
case Period(runId, 0, PhaseMask) => s"Period($runId.all)"
case Period(runId, p1, pn) if p1 == pn => s"Period($runId.$p1(${ctx.base.phases(p1)}))"
case Period(runId, p1, pn) => s"Period($runId.$p1(${ctx.base.phases(p1)})-$pn(${ctx.base.phases(pn)}))"

override def toString: String = this match
case Nowhere => "Nowhere"
case InitialPeriod => "InitialPeriod"
case InvalidPeriod => "InvalidPeriod"
case Period(NoRunId, 0, PhaseMask) => s"Period(NoRunId.all)"
case Period(runId, 0, PhaseMask) => s"Period($runId.all)"
case Period(runId, p1, pn) if p1 == pn => s"Period($runId.$p1)"
case Period(runId, p1, pn) => s"Period($runId.$p1-$pn)"

def ==(that: Period): Boolean = this.code == that.code
def !=(that: Period): Boolean = this.code != that.code
Expand All @@ -116,6 +138,17 @@ object Periods {
/** The interval consisting of all periods of given run id */
def allInRun(rid: RunId): Period =
apply(rid, 0, PhaseMask)

def unapply(p: Period): Extractor = new Extractor(p.code)

final class Extractor(private val code: Int) extends AnyVal {
private def p = new Period(code)
def isEmpty: false = false
def get: this.type = this
def _1 = p.runId
def _2 = p.firstPhaseId
def _3 = p.lastPhaseId
}
}

inline val NowhereCode = 0
Expand Down
53 changes: 37 additions & 16 deletions compiler/src/dotty/tools/dotc/printing/Formatting.scala
Original file line number Diff line number Diff line change
Expand Up @@ -24,17 +24,27 @@ object Formatting {
object Shown:
given [A: Show]: Conversion[A, Shown] = Show[A].show(_)

extension (s: Shown)
def runCtxShow(using Context): Shown = s match
case cs: CtxShow => cs.run
case _ => s

def toStr(x: Shown)(using Context): String = x match
case seq: Seq[?] => seq.map(toStr).mkString("[", ", ", "]")
case res => res.tryToShow

import Shown.runCtxShow

sealed abstract class Show[-T]:
/** Show a value T by returning a "shown" result. */
def show(x: T): Shown

trait CtxShow:
def run(using Context): Shown

extension (s: Shown)
def ctxShow(using Context): Shown = s match
case cs: CtxShow => cs.run
case _ => s
private inline def CtxShow(inline x: Context ?=> Shown) = new CtxShow { def run(using Context) = x(using ctx) }
private def toStr[A: Show](x: A)(using Context): String = Shown.toStr(toShown(x))
private def toShown[A: Show](x: A)(using Context): Shown = Show[A].show(x).runCtxShow

/** The base implementation, passing the argument to StringFormatter which will try to `.show` it. */
object ShowAny extends Show[Any]:
Expand All @@ -54,16 +64,26 @@ object Formatting {
object Show extends ShowImplicits1:
inline def apply[A](using inline z: Show[A]): Show[A] = z

given [X: Show]: Show[Option[X]] with
def show(x: Option[X]) =
CtxShow(x.map(toStr))
end given

given [X: Show]: Show[Seq[X]] with
def show(x: Seq[X]) = new CtxShow:
def run(using Context) = x.map(show1)
def show(x: Seq[X]) = CtxShow(x.map(toStr))

given [K: Show, V: Show]: Show[Map[K, V]] with
def show(x: Map[K, V]) =
CtxShow(x.map((k, v) => s"${toStr(k)} => ${toStr(v)}"))
end given

given [H: Show, T <: Tuple: Show]: Show[H *: T] with
def show(x: H *: T) = new CtxShow:
def run(using Context) = show1(x.head) *: Show[T].show(x.tail).ctxShow.asInstanceOf[Tuple]
def show(x: H *: T) =
CtxShow(toStr(x.head) *: toShown(x.tail).asInstanceOf[Tuple])
end given

given [X: Show]: Show[X | Null] with
def show(x: X | Null) = if x == null then "null" else Show[X].show(x.nn)
def show(x: X | Null) = if x == null then "null" else CtxShow(toStr(x.nn))

given Show[FlagSet] with
def show(x: FlagSet) = x.flagsString
Expand All @@ -79,7 +99,13 @@ object Formatting {
case ast.TreeInfo.Impure => "PurityLevel.Impure"
case ast.TreeInfo.PurePath => "PurityLevel.PurePath"
case ast.TreeInfo.IdempotentPath => "PurityLevel.IdempotentPath"
case _ => s"PurityLevel(${x.x})"
case _ => s"PurityLevel(${x.x.toBinaryString})"

given Show[Atoms] with
def show(x: Atoms) = x match
case Atoms.Unknown => "Unknown"
case Atoms.Range(lo, hi) => CtxShow(s"Range(${toStr(lo.toList)}, ${toStr(hi.toList)})")
end given

given Show[Showable] = ShowAny
given Show[Shown] = ShowAny
Expand All @@ -101,11 +127,6 @@ object Formatting {
given Show[util.Spans.Span] = ShowAny
given Show[tasty.TreeUnpickler#OwnerTree] = ShowAny
given Show[typer.ForceDegree.Value] = ShowAny

private def show1[A: Show](x: A)(using Context) = show2(Show[A].show(x).ctxShow)
private def show2(x: Shown)(using Context): String = x match
case seq: Seq[?] => seq.map(show2).mkString("[", ", ", "]")
case res => res.tryToShow
end Show
end ShownDef
export ShownDef.{ Show, Shown }
Expand All @@ -122,7 +143,7 @@ object Formatting {
class StringFormatter(protected val sc: StringContext) {
protected def showArg(arg: Any)(using Context): String = arg.tryToShow

private def treatArg(arg: Shown, suffix: String)(using Context): (String, String) = arg.ctxShow match {
private def treatArg(arg: Shown, suffix: String)(using Context): (String, String) = arg.runCtxShow match {
case arg: Seq[?] if suffix.indexOf('%') == 0 && suffix.indexOf('%', 1) != -1 =>
val end = suffix.indexOf('%', 1)
val sep = StringContext.processEscapes(suffix.substring(1, end))
Expand Down
8 changes: 4 additions & 4 deletions compiler/src/dotty/tools/dotc/transform/patmat/Space.scala
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ object SpaceEngine {
def decompose(typ: Typ)(using Context): List[Typ] = typ.decompose

/** Simplify space such that a space equal to `Empty` becomes `Empty` */
def computeSimplify(space: Space)(using Context): Space = trace(s"simplify($space)")(space match {
def computeSimplify(space: Space)(using Context): Space = trace(i"simplify($space)")(space match {
case Prod(tp, fun, spaces) =>
val sps = spaces.mapconserve(simplify)
if sps.contains(Empty) then Empty
Expand Down Expand Up @@ -166,7 +166,7 @@ object SpaceEngine {
}

/** Is `a` a subspace of `b`? Equivalent to `simplify(simplify(a) - simplify(b)) == Empty`, but faster */
def computeIsSubspace(a: Space, b: Space)(using Context): Boolean = trace(s"isSubspace($a, $b)") {
def computeIsSubspace(a: Space, b: Space)(using Context): Boolean = trace(i"isSubspace($a, $b)") {
val a2 = simplify(a)
val b2 = simplify(b)
if (a ne a2) || (b ne b2) then isSubspace(a2, b2)
Expand Down Expand Up @@ -195,7 +195,7 @@ object SpaceEngine {
}

/** Intersection of two spaces */
def intersect(a: Space, b: Space)(using Context): Space = trace(s"$a & $b") {
def intersect(a: Space, b: Space)(using Context): Space = trace(i"intersect($a & $b)") {
(a, b) match {
case (Empty, _) | (_, Empty) => Empty
case (_, Or(ss)) => Or(ss.map(intersect(a, _)).filter(_ ne Empty))
Expand All @@ -220,7 +220,7 @@ object SpaceEngine {
}

/** The space of a not covered by b */
def minus(a: Space, b: Space)(using Context): Space = trace(s"$a - $b") {
def minus(a: Space, b: Space)(using Context): Space = trace(i"minus($a - $b)") {
(a, b) match {
case (Empty, _) => Empty
case (_, Empty) => a
Expand Down

0 comments on commit f92349b

Please sign in to comment.