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

Backport "Add formatters for Option, Map, Period, and Atoms" to LTS #20915

Merged
merged 2 commits into from
Jul 2, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 @@ -194,7 +194,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 @@ -219,7 +219,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