Skip to content

Commit e9a95bb

Browse files
committed
Detect and flag deep findMember recursions
Deep findMember recursions can happen as a recult of illegal cyclic structures. I don't think there's a complete way to avoid these structures before the cyclic search can happen. So we now detect it by imposing a recursion limit (configurable, by default 100).
1 parent f2b432e commit e9a95bb

File tree

5 files changed

+11
-19
lines changed

5 files changed

+11
-19
lines changed

compiler/src/dotty/tools/dotc/config/Config.scala

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -184,11 +184,6 @@ object Config {
184184
*/
185185
final val LogPendingFindMemberThreshold = 9
186186

187-
/** Maximal number of outstanding recursive calls to findMember before backing out
188-
* when findMemberLimit is set.
189-
*/
190-
final val PendingFindMemberLimit = LogPendingFindMemberThreshold * 4
191-
192187
/** When in IDE, turn StaleSymbol errors into warnings instead of crashing */
193188
final val ignoreStaleInIDE = true
194189
}

compiler/src/dotty/tools/dotc/config/ScalaSettings.scala

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ class ScalaSettings extends Settings.SettingGroup {
6363
val Xhelp = BooleanSetting("-X", "Print a synopsis of advanced options.")
6464
val XnoForwarders = BooleanSetting("-Xno-forwarders", "Do not generate static forwarders in mirror classes.")
6565
val XminImplicitSearchDepth = IntSetting("-Xmin-implicit-search-depth", "Set number of levels of implicit searches undertaken before checking for divergence.", 5)
66-
val xmaxInlines = IntSetting("-Xmax-inlines", "Maximal number of successive inlines", 32)
66+
val XmaxInlines = IntSetting("-Xmax-inlines", "Maximal number of successive inlines", 32)
6767
val XmaxClassfileName = IntSetting("-Xmax-classfile-name", "Maximum filename length for generated classes", 255, 72 to 255)
6868
val Xmigration = VersionSetting("-Xmigration", "Warn about constructs whose behavior may have changed since version.")
6969
val Xprint = PhasesSetting("-Xprint", "Print out program after")
@@ -74,6 +74,7 @@ class ScalaSettings extends Settings.SettingGroup {
7474
val XmainClass = StringSetting("-Xmain-class", "path", "Class for manifest's Main-Class entry (only useful with -d <jar>)", "")
7575
val XnoValueClasses = BooleanSetting("-Xno-value-classes", "Do not use value classes. Helps debugging.")
7676
val XreplLineWidth = IntSetting("-Xrepl-line-width", "Maximal number of columns per line for REPL output", 390)
77+
val XmaxMemberRecursions = IntSetting("-Xmax-member-recursions", "Maximal number of recursive calls to find-member", 100)
7778
val XfatalWarnings = BooleanSetting("-Xfatal-warnings", "Fail the compilation if there are any warnings.")
7879
val XverifySignatures = BooleanSetting("-Xverify-signatures", "Verify generic signatures in generated bytecode.")
7980

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

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -326,10 +326,4 @@ trait TypeOps { this: Context => // TODO: Make standalone object.
326326

327327
object TypeOps {
328328
@sharable var track = false // !!!DEBUG
329-
330-
/** When a property with this key is set in a context, it limits the number
331-
* of recursive member searches. If the limit is reached, findMember returns
332-
* NoDenotation.
333-
*/
334-
val findMemberLimit = new Property.Key[Unit]
335329
}

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

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -648,17 +648,15 @@ object Types {
648648

649649
val recCount = ctx.findMemberCount
650650
if (recCount >= Config.LogPendingFindMemberThreshold) {
651-
if (ctx.property(TypeOps.findMemberLimit).isDefined &&
652-
ctx.findMemberCount > Config.PendingFindMemberLimit)
653-
return NoDenotation
651+
if (ctx.findMemberCount > ctx.settings.XmaxMemberRecursions.value)
652+
throw new CyclicFindMember(pre, name)
654653
ctx.pendingMemberSearches = name :: ctx.pendingMemberSearches
655654
}
656655
ctx.findMemberCount = recCount + 1
657-
//assert(ctx.findMemberCount < 20)
658656
try go(this)
659657
catch {
660658
case ex: Throwable =>
661-
core.println(i"findMember exception for $this member $name, pre = $pre")
659+
core.println(s"findMember exception for $this member $name, pre = $pre, recCount = $recCount")
662660
throw ex // DEBUG
663661
}
664662
finally {
@@ -4562,6 +4560,10 @@ object Types {
45624560
if (ctx.debug) printStackTrace()
45634561
}
45644562

4563+
class CyclicFindMember(pre: Type, name: Name)(implicit ctx: Context) extends TypeError(
4564+
i"""member search with prefix $pre too deep.
4565+
|searches, from inner to outer: .${ctx.pendingMemberSearches}% .%""")
4566+
45654567
private def otherReason(pre: Type)(implicit ctx: Context): String = pre match {
45664568
case pre: ThisType if pre.cls.givenSelfType.exists =>
45674569
i"\nor the self type of $pre might not contain all transitive dependencies"

compiler/src/dotty/tools/dotc/typer/Inliner.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -257,13 +257,13 @@ object Inliner {
257257
* and body that replace it.
258258
*/
259259
def inlineCall(tree: Tree, pt: Type)(implicit ctx: Context): Tree =
260-
if (enclosingInlineds.length < ctx.settings.xmaxInlines.value) {
260+
if (enclosingInlineds.length < ctx.settings.XmaxInlines.value) {
261261
val body = bodyToInline(tree.symbol) // can typecheck the tree and thereby produce errors
262262
if (ctx.reporter.hasErrors) tree else new Inliner(tree, body).inlined(pt)
263263
}
264264
else errorTree(
265265
tree,
266-
i"""|Maximal number of successive inlines (${ctx.settings.xmaxInlines.value}) exceeded,
266+
i"""|Maximal number of successive inlines (${ctx.settings.XmaxInlines.value}) exceeded,
267267
|Maybe this is caused by a recursive inline method?
268268
|You can use -Xmax:inlines to change the limit."""
269269
)

0 commit comments

Comments
 (0)