Skip to content

Commit 34ee767

Browse files
committed
Impose implicit search limit
Impose a configurable limit on the total number of nodes constructed during an implicit search. Fixes #13838
1 parent 483bf2c commit 34ee767

File tree

5 files changed

+101
-6
lines changed

5 files changed

+101
-6
lines changed

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -218,6 +218,7 @@ private sealed trait XSettings:
218218
val Xtarget: Setting[String] = ChoiceSetting("-Xtarget", "target", "Emit bytecode for the specified version of the Java platform. This might produce bytecode that will break at runtime. When on JDK 9+, consider -release as a safer alternative.", ScalaSettings.supportedTargetVersions, "", aliases = List("--Xtarget"))
219219
val XcheckMacros: Setting[Boolean] = BooleanSetting("-Xcheck-macros", "Check some invariants of macro generated code while expanding macros", aliases = List("--Xcheck-macros"))
220220
val XmainClass: Setting[String] = StringSetting("-Xmain-class", "path", "Class for manifest's Main-Class entry (only useful with -d <jar>)", "")
221+
val XimplicitSearchLimit: Setting[Int] = IntSetting("-Ximplicit-search-limit", "Maximal number of expressions to be generated in an implicit search", 100000)
221222

222223
val XmixinForceForwarders = ChoiceSetting(
223224
name = "-Xmixin-force-forwarders",

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

Lines changed: 41 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -489,6 +489,11 @@ object Implicits:
489489
@sharable val NoMatchingImplicitsFailure: SearchFailure =
490490
SearchFailure(NoMatchingImplicits, NoSpan)(using NoContext)
491491

492+
@sharable object ImplicitSearchTooLarge extends NoMatchingImplicits(NoType, EmptyTree, OrderingConstraint.empty)
493+
494+
@sharable val ImplicitSearchTooLargeFailure: SearchFailure =
495+
SearchFailure(ImplicitSearchTooLarge, NoSpan)(using NoContext)
496+
492497
/** An ambiguous implicits failure */
493498
class AmbiguousImplicits(val alt1: SearchSuccess, val alt2: SearchSuccess, val expectedType: Type, val argument: Tree) extends SearchFailureType {
494499
def explanation(using Context): String =
@@ -1129,18 +1134,44 @@ trait Implicits:
11291134

11301135
val isNotGiven: Boolean = wildProto.classSymbol == defn.NotGivenClass
11311136

1137+
private def searchTooLarge(): Boolean = ctx.searchHistory match
1138+
case root: SearchRoot =>
1139+
root.nestedSearches = 1
1140+
false
1141+
case h =>
1142+
val limit = ctx.settings.XimplicitSearchLimit.value
1143+
val nestedSearches = h.root.nestedSearches
1144+
val result = nestedSearches > limit
1145+
if result then
1146+
var c = ctx
1147+
while c.outer.typer eq ctx.typer do c = c.outer
1148+
report.echo(
1149+
em"""Implicit search problem too large.
1150+
|an implicit search was terminated with failure after trying $limit expressions.
1151+
|
1152+
|You can change the behavior by setting the `-Ximplicit-search-limit` value.
1153+
|Smaller values cause the search to fail faster.
1154+
|Larger values might make a very large search problem succeed.
1155+
|""",
1156+
ctx.source.atSpan(span))(using c)
1157+
else
1158+
h.root.nestedSearches = nestedSearches + 1
1159+
result
1160+
11321161
/** Try to type-check implicit reference, after checking that this is not
11331162
* a diverging search
11341163
*/
11351164
def tryImplicit(cand: Candidate, contextual: Boolean): SearchResult =
11361165
if checkDivergence(cand) then
11371166
SearchFailure(new DivergingImplicit(cand.ref, wideProto, argument), span)
1138-
else {
1167+
else if searchTooLarge() then
1168+
ImplicitSearchTooLargeFailure
1169+
else
11391170
val history = ctx.searchHistory.nest(cand, pt)
11401171
val typingCtx =
11411172
nestedContext().setNewTyperState().setFreshGADTBounds.setSearchHistory(history)
11421173
val result = typedImplicit(cand, pt, argument, span)(using typingCtx)
1143-
result match {
1174+
result match
11441175
case res: SearchSuccess =>
11451176
ctx.searchHistory.defineBynameImplicit(wideProto, res)
11461177
case _ =>
@@ -1152,8 +1183,6 @@ trait Implicits:
11521183
// tests/neg/implicitSearch.check
11531184
typingCtx.typerState.gc()
11541185
result
1155-
}
1156-
}
11571186

11581187
/** Search a list of eligible implicit references */
11591188
private def searchImplicit(eligible: List[Candidate], contextual: Boolean): SearchResult =
@@ -1242,7 +1271,9 @@ trait Implicits:
12421271

12431272
negateIfNot(tryImplicit(cand, contextual)) match {
12441273
case fail: SearchFailure =>
1245-
if (fail.isAmbiguous)
1274+
if fail eq ImplicitSearchTooLargeFailure then
1275+
fail
1276+
else if (fail.isAmbiguous)
12461277
if migrateTo3 then
12471278
val result = rank(remaining, found, NoMatchingImplicitsFailure :: rfailures)
12481279
if (result.isSuccess)
@@ -1610,13 +1641,17 @@ case class OpenSearch(cand: Candidate, pt: Type, outer: SearchHistory)(using Con
16101641
end OpenSearch
16111642

16121643
/**
1613-
* The the state corresponding to the outermost context of an implicit searcch.
1644+
* The state corresponding to the outermost context of an implicit searcch.
16141645
*/
16151646
final class SearchRoot extends SearchHistory:
16161647
val root = this
16171648
val byname = false
16181649
def openSearchPairs = Nil
16191650

1651+
/** How many expressions were constructed so far in the current toplevel implicit search?
1652+
*/
1653+
var nestedSearches: Int = 0
1654+
16201655
/** The dictionary of recursive implicit types and corresponding terms for this search. */
16211656
var myImplicitDictionary: mutable.Map[Type, (TermRef, tpd.Tree)] = null
16221657
private def implicitDictionary =

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,7 @@ class CompilationTests {
183183
compileFile("tests/neg-custom-args/feature-shadowing.scala", defaultOptions.and("-Xfatal-warnings", "-feature")),
184184
compileDir("tests/neg-custom-args/hidden-type-errors", defaultOptions.and("-explain")),
185185
compileFile("tests/neg-custom-args/i13026.scala", defaultOptions.and("-print-lines")),
186+
compileFile("tests/neg-custom-args/i13838.scala", defaultOptions.and("-Ximplicit-search-limit", "1000")),
186187
).checkExpectedErrors()
187188
}
188189

tests/neg-custom-args/i13838.check

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
-- [E007] Type Mismatch Error: tests/neg-custom-args/i13838.scala:8:48 -------------------------------------------------
2+
8 | def liftF[F[_], A](fa: F[A]): F[Foo[A]] = map(fa)(???) // error
3+
| ^^
4+
| Found: (fa : F[A])
5+
| Required: F²[Foo[A²]]
6+
|
7+
| where: A is a type in method liftF
8+
| A² is a type variable
9+
| F is a type in method liftF with bounds <: [_] =>> Any
10+
| F² is a type variable with constraint <: [_] =>> Any
11+
|
12+
|
13+
| The following import might make progress towards fixing the problem:
14+
|
15+
| import collection.Searching.search
16+
|
17+
18+
longer explanation available when compiling with `-explain`

tests/neg-custom-args/i13838.scala

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
implicit def catsSyntaxEq[A: Eq](a: A): Foo[A] = ???
2+
3+
class Foo[A]
4+
object Foo:
5+
given [A: Eq]: Eq[Foo[A]] = ???
6+
7+
object FooT:
8+
def liftF[F[_], A](fa: F[A]): F[Foo[A]] = map(fa)(???) // error
9+
10+
def map[F[_], A](ffa: F[Foo[A]])(f: A): Nothing = ???
11+
12+
given OrderFFooA[F[_], A](using Ord: Order[F[Foo[A]]]): Order[F[Foo[A]]] = ???
13+
14+
trait Eq[A]
15+
trait Order[A] extends Eq[A]
16+
17+
object Eq {
18+
given catsKernelOrderForTuple1[A0](using A0: Order[A0]): Order[Tuple1[A0]] = ???
19+
given catsKernelOrderForTuple2[A0, A1](using A0: Order[A0], A1: Order[A1]): Order[(A0, A1)] = ???
20+
given catsKernelOrderForTuple3[A0, A1, A2](using A0: Order[A0], A1: Order[A1], A2: Order[A2]): Order[(A0, A1, A2)] = ???
21+
given catsKernelOrderForTuple4[A0, A1, A2, A3](using A0: Order[A0], A1: Order[A1], A2: Order[A2], A3: Order[A3]): Order[(A0, A1, A2, A3)] = ???
22+
given catsKernelOrderForTuple5[A0, A1, A2, A3, A4](using A0: Order[A0], A1: Order[A1], A2: Order[A2], A3: Order[A3], A4: Order[A4]): Order[(A0, A1, A2, A3, A4)] = ???
23+
given catsKernelOrderForTuple6[A0, A1, A2, A3, A4, A5](using A0: Order[A0], A1: Order[A1], A2: Order[A2], A3: Order[A3], A4: Order[A4], A5: Order[A5]): Order[(A0, A1, A2, A3, A4, A5)] = ???
24+
given catsKernelOrderForTuple7[A0, A1, A2, A3, A4, A5, A6](using A0: Order[A0], A1: Order[A1], A2: Order[A2], A3: Order[A3], A4: Order[A4], A5: Order[A5], A6: Order[A6]): Order[(A0, A1, A2, A3, A4, A5, A6)] = ???
25+
given catsKernelOrderForTuple8[A0, A1, A2, A3, A4, A5, A6, A7](using A0: Order[A0], A1: Order[A1], A2: Order[A2], A3: Order[A3], A4: Order[A4], A5: Order[A5], A6: Order[A6], A7: Order[A7]): Order[(A0, A1, A2, A3, A4, A5, A6, A7)] = ???
26+
given catsKernelOrderForTuple9[A0, A1, A2, A3, A4, A5, A6, A7, A8](using A0: Order[A0], A1: Order[A1], A2: Order[A2], A3: Order[A3], A4: Order[A4], A5: Order[A5], A6: Order[A6], A7: Order[A7], A8: Order[A8]): Order[(A0, A1, A2, A3, A4, A5, A6, A7, A8)] = ???
27+
given catsKernelOrderForTuple10[A0, A1, A2, A3, A4, A5, A6, A7, A8, A9](using A0: Order[A0], A1: Order[A1], A2: Order[A2], A3: Order[A3], A4: Order[A4], A5: Order[A5], A6: Order[A6], A7: Order[A7], A8: Order[A8], A9: Order[A9]): Order[(A0, A1, A2, A3, A4, A5, A6, A7, A8, A9)] = ???
28+
given catsKernelOrderForTuple11[A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10](using A0: Order[A0], A1: Order[A1], A2: Order[A2], A3: Order[A3], A4: Order[A4], A5: Order[A5], A6: Order[A6], A7: Order[A7], A8: Order[A8], A9: Order[A9], A10: Order[A10]): Order[(A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10)] = ???
29+
given catsKernelOrderForTuple12[A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11](using A0: Order[A0], A1: Order[A1], A2: Order[A2], A3: Order[A3], A4: Order[A4], A5: Order[A5], A6: Order[A6], A7: Order[A7], A8: Order[A8], A9: Order[A9], A10: Order[A10], A11: Order[A11]): Order[(A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11)] = ???
30+
given catsKernelOrderForTuple13[A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12](using A0: Order[A0], A1: Order[A1], A2: Order[A2], A3: Order[A3], A4: Order[A4], A5: Order[A5], A6: Order[A6], A7: Order[A7], A8: Order[A8], A9: Order[A9], A10: Order[A10], A11: Order[A11], A12: Order[A12]): Order[(A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12)] = ???
31+
given catsKernelOrderForTuple14[A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13](using A0: Order[A0], A1: Order[A1], A2: Order[A2], A3: Order[A3], A4: Order[A4], A5: Order[A5], A6: Order[A6], A7: Order[A7], A8: Order[A8], A9: Order[A9], A10: Order[A10], A11: Order[A11], A12: Order[A12], A13: Order[A13]): Order[(A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13)] = ???
32+
given catsKernelOrderForTuple15[A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14](using A0: Order[A0], A1: Order[A1], A2: Order[A2], A3: Order[A3], A4: Order[A4], A5: Order[A5], A6: Order[A6], A7: Order[A7], A8: Order[A8], A9: Order[A9], A10: Order[A10], A11: Order[A11], A12: Order[A12], A13: Order[A13], A14: Order[A14]): Order[(A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14)] = ???
33+
given catsKernelOrderForTuple16[A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15](using A0: Order[A0], A1: Order[A1], A2: Order[A2], A3: Order[A3], A4: Order[A4], A5: Order[A5], A6: Order[A6], A7: Order[A7], A8: Order[A8], A9: Order[A9], A10: Order[A10], A11: Order[A11], A12: Order[A12], A13: Order[A13], A14: Order[A14], A15: Order[A15]): Order[(A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15)] = ???
34+
given catsKernelOrderForTuple17[A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16](using A0: Order[A0], A1: Order[A1], A2: Order[A2], A3: Order[A3], A4: Order[A4], A5: Order[A5], A6: Order[A6], A7: Order[A7], A8: Order[A8], A9: Order[A9], A10: Order[A10], A11: Order[A11], A12: Order[A12], A13: Order[A13], A14: Order[A14], A15: Order[A15], A16: Order[A16]): Order[(A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16)] = ???
35+
given catsKernelOrderForTuple18[A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17](using A0: Order[A0], A1: Order[A1], A2: Order[A2], A3: Order[A3], A4: Order[A4], A5: Order[A5], A6: Order[A6], A7: Order[A7], A8: Order[A8], A9: Order[A9], A10: Order[A10], A11: Order[A11], A12: Order[A12], A13: Order[A13], A14: Order[A14], A15: Order[A15], A16: Order[A16], A17: Order[A17]): Order[(A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17)] = ???
36+
given catsKernelOrderForTuple19[A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18](using A0: Order[A0], A1: Order[A1], A2: Order[A2], A3: Order[A3], A4: Order[A4], A5: Order[A5], A6: Order[A6], A7: Order[A7], A8: Order[A8], A9: Order[A9], A10: Order[A10], A11: Order[A11], A12: Order[A12], A13: Order[A13], A14: Order[A14], A15: Order[A15], A16: Order[A16], A17: Order[A17], A18: Order[A18]): Order[(A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18)] = ???
37+
given catsKernelOrderForTuple20[A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19](using A0: Order[A0], A1: Order[A1], A2: Order[A2], A3: Order[A3], A4: Order[A4], A5: Order[A5], A6: Order[A6], A7: Order[A7], A8: Order[A8], A9: Order[A9], A10: Order[A10], A11: Order[A11], A12: Order[A12], A13: Order[A13], A14: Order[A14], A15: Order[A15], A16: Order[A16], A17: Order[A17], A18: Order[A18], A19: Order[A19]): Order[(A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19)] = ???
38+
given catsKernelOrderForTuple21[A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19, A20](using A0: Order[A0], A1: Order[A1], A2: Order[A2], A3: Order[A3], A4: Order[A4], A5: Order[A5], A6: Order[A6], A7: Order[A7], A8: Order[A8], A9: Order[A9], A10: Order[A10], A11: Order[A11], A12: Order[A12], A13: Order[A13], A14: Order[A14], A15: Order[A15], A16: Order[A16], A17: Order[A17], A18: Order[A18], A19: Order[A19], A20: Order[A20]): Order[(A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19, A20)] = ???
39+
given catsKernelOrderForTuple22[A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19, A20, A21](using A0: Order[A0], A1: Order[A1], A2: Order[A2], A3: Order[A3], A4: Order[A4], A5: Order[A5], A6: Order[A6], A7: Order[A7], A8: Order[A8], A9: Order[A9], A10: Order[A10], A11: Order[A11], A12: Order[A12], A13: Order[A13], A14: Order[A14], A15: Order[A15], A16: Order[A16], A17: Order[A17], A18: Order[A18], A19: Order[A19], A20: Order[A20], A21: Order[A21]): Order[(A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19, A20, A21)] = ???
40+
}

0 commit comments

Comments
 (0)