Skip to content

Commit f2360f6

Browse files
authored
Merge pull request #8498 from dotty-staging/fix-#8464
Fix #8464: Refine recursion limits for printing
2 parents 30f8c6f + 35578c6 commit f2360f6

File tree

12 files changed

+120
-59
lines changed

12 files changed

+120
-59
lines changed

compiler/src/dotty/tools/backend/jvm/DottyBackendInterface.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -981,7 +981,7 @@ class DottyBackendInterface(outputDirectory: AbstractFile, val superCallsMap: Ma
981981
}
982982
}
983983

984-
def summaryString: String = tp.showSummary
984+
def summaryString: String = tp.showSummary()
985985

986986
def params: List[Symbol] =
987987
Nil // backend uses this to emit annotations on parameter lists of forwarders

compiler/src/dotty/tools/dotc/core/Contexts.scala

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -636,7 +636,7 @@ object Contexts {
636636
owner = NoSymbol
637637
tree = untpd.EmptyTree
638638
typeAssigner = TypeAssigner
639-
moreProperties = Map.empty
639+
moreProperties = Map(MessageLimiter -> DefaultMessageLimiter())
640640
source = NoSource
641641
store = initialStore
642642
.updated(settingsStateLoc, settingsGroup.defaultState)
@@ -772,10 +772,6 @@ object Contexts {
772772

773773
private[core] var denotTransformers: Array[DenotTransformer] = _
774774

775-
// Printers state
776-
/** Number of recursive invocations of a show method on current stack */
777-
private[dotc] var toTextRecursions: Int = 0
778-
779775
// Reporters state
780776
private[dotc] var indent: Int = 0
781777

compiler/src/dotty/tools/dotc/core/Mode.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,8 +58,8 @@ object Mode {
5858
/** Assume -language:strictEquality */
5959
val StrictEquality: Mode = newMode(9, "StrictEquality")
6060

61-
/** We are currently printing something: avoid to produce more logs about
62-
* the printing
61+
/** We are currently printing something: avoid producing more logs about
62+
* the printing.
6363
*/
6464
val Printing: Mode = newMode(10, "Printing")
6565

compiler/src/dotty/tools/dotc/printing/Formatting.scala

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,9 @@ object Formatting {
3131
case NonFatal(ex)
3232
if !ctx.mode.is(Mode.PrintShowExceptions) &&
3333
!ctx.settings.YshowPrintErrors.value =>
34-
val msg = ex match { case te: TypeError => te.toMessage case _ => ex.getMessage }
34+
val msg = ex match
35+
case te: TypeError => te.toMessage
36+
case _ => ex.getMessage
3537
s"[cannot display due to $msg, raw string = ${arg.toString}]"
3638
}
3739
case _ => arg.toString
@@ -69,10 +71,9 @@ object Formatting {
6971
* like concatenation, stripMargin etc on the values returned by em"...", and in the current error
7072
* message composition methods, this is crucial.
7173
*/
72-
class ErrorMessageFormatter(sc: StringContext) extends StringFormatter(sc) {
74+
class ErrorMessageFormatter(sc: StringContext) extends StringFormatter(sc):
7375
override protected def showArg(arg: Any)(implicit ctx: Context): String =
74-
wrapNonSensical(arg, super.showArg(arg))
75-
}
76+
wrapNonSensical(arg, super.showArg(arg)(using errorMessageCtx))
7677

7778
class SyntaxFormatter(sc: StringContext) extends StringFormatter(sc) {
7879
override protected def showArg(arg: Any)(implicit ctx: Context): String =
@@ -254,12 +255,19 @@ object Formatting {
254255
if (explainLines.isEmpty) "" else i"where: $explainLines%\n %\n"
255256
}
256257

258+
private def errorMessageCtx(using ctx: Context): Context =
259+
ctx.property(MessageLimiter) match
260+
case Some(_: ErrorMessageLimiter) => ctx
261+
case _ => ctx.fresh.setProperty(MessageLimiter, ErrorMessageLimiter())
262+
257263
/** Context with correct printer set for explanations */
258-
private def explainCtx(seen: Seen)(implicit ctx: Context): Context = ctx.printer match {
259-
case dp: ExplainingPrinter =>
260-
ctx // re-use outer printer and defer explanation to it
261-
case _ => ctx.fresh.setPrinterFn(ctx => new ExplainingPrinter(seen)(ctx))
262-
}
264+
private def explainCtx(seen: Seen)(implicit ctx: Context): Context =
265+
val ectx = errorMessageCtx
266+
ectx.printer match
267+
case dp: ExplainingPrinter =>
268+
ectx // re-use outer printer and defer explanation to it
269+
case _ =>
270+
ectx.fresh.setPrinterFn(ctx => new ExplainingPrinter(seen)(ctx))
263271

264272
/** Entrypoint for explanation string interpolator:
265273
*
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
package dotty.tools
2+
package dotc
3+
package printing
4+
5+
import core._
6+
import Contexts.Context
7+
import util.Property
8+
import Texts.Text
9+
10+
abstract class MessageLimiter:
11+
12+
protected def recurseLimit = 100
13+
protected var recurseCount: Int = 0
14+
15+
def register(str: String): Unit = ()
16+
17+
protected def recursionLimitExceeded()(using Context): Unit = ()
18+
19+
final def controlled(op: => Text)(using Context): Text =
20+
if recurseCount < recurseLimit then
21+
try
22+
recurseCount += 1
23+
op
24+
finally
25+
recurseCount -= 1
26+
else
27+
recursionLimitExceeded()
28+
"..."
29+
30+
object MessageLimiter extends Property.Key[MessageLimiter]
31+
32+
class DefaultMessageLimiter extends MessageLimiter:
33+
override def recursionLimitExceeded()(using ctx: Context): Unit =
34+
if ctx.debug then
35+
ctx.warning("Exceeded recursion depth attempting to print.")
36+
Thread.dumpStack()
37+
38+
class SummarizeMessageLimiter(depth: Int) extends MessageLimiter:
39+
override val recurseLimit = recurseCount + depth
40+
override def recursionLimitExceeded()(using ctx: Context): Unit = ()
41+
42+
class ErrorMessageLimiter extends MessageLimiter:
43+
private val initialRecurseLimit = 50
44+
private val sizeLimit = 10000
45+
46+
private var textLength: Int = 0
47+
48+
override def register(str: String): Unit =
49+
textLength += str.length
50+
51+
override def recurseLimit =
52+
val freeFraction: Double = ((sizeLimit - textLength) max 0).toDouble / sizeLimit
53+
(initialRecurseLimit * freeFraction).toInt
54+
55+

compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala

Lines changed: 10 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ import typer.ImportInfo
1111
import Variances.varianceSign
1212
import util.SourcePosition
1313
import java.lang.Integer.toOctalString
14-
import config.Config.summarizeDepth
1514
import scala.util.control.NonFatal
1615
import scala.annotation.switch
1716

@@ -23,24 +22,15 @@ class PlainPrinter(_ctx: Context) extends Printer {
2322

2423
protected def maxToTextRecursions: Int = 100
2524

26-
protected final def controlled(op: => Text): Text =
27-
if (ctx.base.toTextRecursions < maxToTextRecursions && ctx.base.toTextRecursions < maxSummarized)
28-
try {
29-
ctx.base.toTextRecursions += 1
30-
op
31-
}
32-
finally
33-
ctx.base.toTextRecursions -= 1
34-
else {
35-
if (ctx.base.toTextRecursions >= maxToTextRecursions)
36-
recursionLimitExceeded()
37-
"..."
38-
}
25+
protected final def limiter: MessageLimiter = ctx.property(MessageLimiter).get
3926

40-
protected def recursionLimitExceeded(): Unit = {
41-
ctx.warning("Exceeded recursion depth attempting to print.")
42-
if (ctx.debug) Thread.dumpStack()
43-
}
27+
protected def controlled(op: => Text): Text = limiter.controlled(op)
28+
29+
def Str(str: String, lineRange: LineRange = EmptyLineRange): Str =
30+
limiter.register(str)
31+
Texts.Str(str, lineRange)
32+
33+
given stringToText as Conversion[String, Text] = Str(_)
4434

4535
/** If true, tweak output so it is the same before and after pickling */
4636
protected def homogenizedView: Boolean = ctx.settings.YtestPickler.value
@@ -112,7 +102,8 @@ class PlainPrinter(_ctx: Context) extends Printer {
112102
/** Pretty-print comma-separated type arguments for a constructor to be inserted among parentheses or brackets
113103
* (hence with `GlobalPrec` precedence).
114104
*/
115-
protected def argsText(args: List[Type]): Text = atPrec(GlobalPrec) { Text(args.map(arg => argText(arg) ), ", ") }
105+
protected def argsText(args: List[Type]): Text =
106+
atPrec(GlobalPrec) { Text(args.map(arg => argText(arg) ), ", ") }
116107

117108
/** The longest sequence of refinement types, starting at given type
118109
* and following parents.
@@ -573,17 +564,6 @@ class PlainPrinter(_ctx: Context) extends Printer {
573564
case _ => "{...}"
574565
s"import $exprStr.$selectorStr"
575566

576-
private var maxSummarized = Int.MaxValue
577-
578-
def summarized[T](depth: Int)(op: => T): T = {
579-
val saved = maxSummarized
580-
maxSummarized = ctx.base.toTextRecursions + depth
581-
try op
582-
finally maxSummarized = saved
583-
}
584-
585-
def summarized[T](op: => T): T = summarized(summarizeDepth)(op)
586-
587567
def plain: PlainPrinter = this
588568

589569
protected def keywordStr(text: String): String = coloredStr(text, SyntaxHighlighting.KeywordColor)

compiler/src/dotty/tools/dotc/printing/Printer.scala

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -167,11 +167,6 @@ abstract class Printer {
167167
def toTextGlobal(elems: Traversable[Showable], sep: String): Text =
168168
atPrec(GlobalPrec) { toText(elems, sep) }
169169

170-
/** Perform string or text-producing operation `op` so that only a
171-
* summarized text with given recursion depth is shown
172-
*/
173-
def summarized[T](depth: Int)(op: => T): T
174-
175170
/** A plain printer without any embellishments */
176171
def plain: Printer
177172
}

compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -67,8 +67,6 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) {
6767
case _ => false
6868
}
6969

70-
override protected def recursionLimitExceeded(): Unit = {}
71-
7270
protected def PrintableFlags(isType: Boolean): FlagSet = {
7371
val fs =
7472
if (isType) TypeSourceModifierFlags | Module | Local // DOTTY problem: cannot merge these two statements
@@ -467,7 +465,7 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) {
467465
!homogenizedView && ctx.settings.XprintInline.value) ~
468466
blockText(bindings :+ body)
469467
case tpt: untpd.DerivedTypeTree =>
470-
"<derived typetree watching " ~ summarized(toText(tpt.watched)) ~ ">"
468+
"<derived typetree watching " ~ tpt.watched.showSummary() ~ ">"
471469
case TypeTree() =>
472470
typeText(toText(tree.typeOpt))
473471
case SingletonTypeTree(ref) =>

compiler/src/dotty/tools/dotc/printing/Showable.scala

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,6 @@ trait Showable extends Any {
2626
* Recursion depth is limited to some smallish value. Default is
2727
* Config.summarizeDepth.
2828
*/
29-
def showSummary(depth: Int)(implicit ctx: Context): String =
30-
ctx.printer.summarized(depth)(show)
31-
32-
def showSummary(implicit ctx: Context): String = showSummary(summarizeDepth)
29+
def showSummary(depth: Int = summarizeDepth)(using ctx: Context): String =
30+
show(using ctx.fresh.setProperty(MessageLimiter, SummarizeMessageLimiter(depth)))
3331
}

compiler/test/dotty/tools/dotc/CompilationTests.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,7 @@ class CompilationTests extends ParallelTesting {
120120
compileFilesInDir("tests/neg-custom-args/fatal-warnings", defaultOptions.and("-Xfatal-warnings")),
121121
compileFilesInDir("tests/neg-custom-args/erased", defaultOptions.and("-Yerased-terms")),
122122
compileFilesInDir("tests/neg-custom-args/allow-double-bindings", allowDoubleBindings),
123+
compileFilesInDir("tests/neg-custom-args/allow-deep-subtypes", allowDeepSubtypes),
123124
compileFilesInDir("tests/neg-custom-args/explicit-nulls", defaultOptions.and("-Yexplicit-nulls")),
124125
compileDir("tests/neg-custom-args/impl-conv", defaultOptions.and("-Xfatal-warnings", "-feature")),
125126
compileFile("tests/neg-custom-args/implicit-conversions.scala", defaultOptions.and("-Xfatal-warnings", "-feature")),
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
type Id = String
2+
3+
enum Kind {
4+
case Type
5+
}
6+
7+
enum Term[T <: Term[T, K], K] {
8+
case Wrap(t: T)
9+
case Fun(id: Id, tag: K, ret: Term[T, K])
10+
}
11+
12+
enum Type {
13+
case Var(id: Id)
14+
}
15+
16+
val tExp: Term[Type, Kind] =
17+
Term.Fun("x", Kind.Type, Term.Wrap(Type.Var("x"))) // error
18+
19+
def main(args: Array[String]): Unit = { }

tests/neg/i8464.scala

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
enum Term[T <: Term[T]] {
2+
case Wrap(t: T)
3+
case Fun(id: Id, tag: K, ret: Term[T]) // error // error
4+
}
5+
6+
enum Type {
7+
case Var(id: Id) // error
8+
}
9+
10+
val tExp: Term[Type, Kind] = // error
11+
Term.Fun("x", Term.Wrap(Type.Var("x"))) // error

0 commit comments

Comments
 (0)