Skip to content

Commit 3c7ce63

Browse files
committed
Tweak implicit resolution
I have noted that the "more implicit parameters first" rule when applied unconditionally can lead to implicit search explosion. The test case is i3430.scala: println(Nil.min) // error: ambiguous We need to find an `Ordering[T]` for uninstantiated `T`. Before implicit arguments were taken into account we'd try two types (in this case Long and BigInteger) and get an ambiguity. Then we'd check whether any of the other candidates would dominate both of these types, which was not the case. So, we are done with an ambiguity. But when implicit arguments were taken into account there are many types that still are better then these. E.g. [T1: Ordering, T2: Ordering]: Ordering[(T1, T2)] and so on far all other supported arities of Ordering for tuples! So one of these has to be tried. That leads to searches for other uninstantiated orderings and so on. Running i3430.scala by hand, I got a compiler hang. I am not sure why the test suite succeeded nevertheless; there must be something surprising going on in the tests. My fix to get round these issue is that now implicit parameters are only considered if the result types of two alternatives are unifiable. Hopefully, that's not too constraining.
1 parent 5f5b5b0 commit 3c7ce63

File tree

6 files changed

+18
-9
lines changed

6 files changed

+18
-9
lines changed

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -851,6 +851,9 @@ object Types {
851851
ctx.typeComparer.isSameType(this, that)
852852
}
853853

854+
final def frozen_=:=(that: Type)(implicit ctx: Context): Boolean =
855+
ctx.typeComparer.isSameTypeWhenFrozen(this, that)
856+
854857
/** Is this type a primitive value type which can be widened to the primitive value type `that`? */
855858
def isValueSubType(that: Type)(implicit ctx: Context): Boolean = widen match {
856859
case self: TypeRef if self.symbol.isPrimitiveValueClass =>

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

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -67,8 +67,7 @@ class PlainPrinter(_ctx: Context) extends Printer {
6767
else tp
6868

6969
private def sameBound(lo: Type, hi: Type): Boolean =
70-
try ctx.typeComparer.isSameTypeWhenFrozen(lo, hi)
71-
catch { case NonFatal(ex) => false }
70+
try lo frozen_=:= hi catch { case NonFatal(ex) => false }
7271

7372
private def homogenizeArg(tp: Type) = tp match {
7473
case TypeBounds(lo, hi) if homogenizedView && sameBound(lo, hi) => homogenize(hi)

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

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1370,11 +1370,14 @@ trait Applications extends Compatibility { self: Typer with Dynamic =>
13701370
val strippedType2 = stripImplicit(fullType2, +1)
13711371

13721372
val result = compareWithTypes(strippedType1, strippedType2)
1373-
if (result != 0) result
1374-
else if (implicitBalance != 0) -implicitBalance.signum
1373+
if (result != 0 || !ctx.typerState.test(implicit ctx => strippedType1 =:= strippedType2))
1374+
result
1375+
else if (implicitBalance != 0)
1376+
-implicitBalance.signum
13751377
else if ((strippedType1 `ne` fullType1) || (strippedType2 `ne` fullType2))
13761378
compareWithTypes(fullType1, fullType2)
1377-
else 0
1379+
else
1380+
0
13781381
}}
13791382

13801383
def narrowMostSpecific(alts: List[TermRef])(implicit ctx: Context): List[TermRef] = track("narrowMostSpecific") {
@@ -1637,7 +1640,7 @@ trait Applications extends Compatibility { self: Typer with Dynamic =>
16371640
// ps(i) = List(p_i_1, ..., p_i_n) -- i.e. a column
16381641
// If all p_i_k's are the same, assume the type as formal parameter
16391642
// type of the i'th parameter of the closure.
1640-
if (isUniform(ps)(ctx.typeComparer.isSameTypeWhenFrozen(_, _))) ps.head
1643+
if (isUniform(ps)(_ frozen_=:= _)) ps.head
16411644
else WildcardType)
16421645
def isPartial = // we should generate a partial function for the arg
16431646
fn.get.isInstanceOf[untpd.Match] &&

docs/docs/reference/changed-features/implicit-resolution.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -114,8 +114,10 @@ affect implicits on the language level.
114114
An alternative A is _more specific_ than an alternative B if
115115

116116
- the relative weight of A over B is greater than the relative weight of B over A, or
117-
- the relative weights are the same and A takes more inferable parameters than B, or
118-
- the relative weights and the number of inferable parameters are the same and
117+
- the relative weights are the same, and the returned types of A and B are
118+
unifiable, and A takes more inferable parameters than B, or
119+
- the relative weights and the number of inferable parameters are the same, and
120+
the returned types of A and B are unifiable, and
119121
A is more specific than B if all inferable parameters in either alternative are
120122
replaced by regular parameters.
121123

tests/neg/i3430.check

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
<44..44> in i3430.scala
2+
ambiguous implicit arguments: both object Long in object Ordering and object BigDecimal in object Ordering match type Ordering[B] of parameter cmp of method min in trait TraversableOnce

tests/neg/i3430.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
object Test extends App {
22

3-
println(Nil.min) // error: no implicit found
3+
println(Nil.min) // error: ambiguous
44

55
}

0 commit comments

Comments
 (0)