Skip to content

Commit 4c1d58f

Browse files
committed
Don't try default args when there are ambiguous implicits
Before this commit, the logic in `class Typer` / `def adapt1` / `def addImplicitArgs` was to try default arguments if *any* argument had an implicit search error that was not an ambiguous implicits error. This commit changes the logic to try default arguments only if *all* arguments' implicit search errors are not ambiguous implicits error. The rational is that if there is any ambiguous implicits error, it's not worth trying default arguments because this cannot solve the ambiguity. Instead, we report the first ambiguous implicits error directly.
1 parent cba1cfc commit 4c1d58f

9 files changed

+124
-19
lines changed

Diff for: compiler/src/dotty/tools/dotc/typer/Typer.scala

+9-19
Original file line numberDiff line numberDiff line change
@@ -3849,22 +3849,9 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
38493849
end implicitArgs
38503850

38513851
val args = implicitArgs(wtp.paramInfos, 0, pt)
3852-
3853-
def propagatedFailure(args: List[Tree]): Type = args match {
3854-
case arg :: args1 =>
3855-
arg.tpe match {
3856-
case ambi: AmbiguousImplicits =>
3857-
propagatedFailure(args1) match {
3858-
case NoType | (_: AmbiguousImplicits) => ambi
3859-
case failed => failed
3860-
}
3861-
case failed: SearchFailureType => failed
3862-
case _ => propagatedFailure(args1)
3863-
}
3864-
case Nil => NoType
3865-
}
3866-
3867-
val propFail = propagatedFailure(args)
3852+
val firstAmbiguous = args.tpes.find(_.isInstanceOf[AmbiguousImplicits])
3853+
def firstError = args.tpes.find(_.isError)
3854+
val propFail = firstAmbiguous.orElse(firstError).getOrElse(NoType)
38683855

38693856
def issueErrors(): Tree = {
38703857
def paramSymWithMethodTree(paramName: TermName) =
@@ -3897,9 +3884,12 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
38973884
// need to be reset.
38983885
ctx.typerState.resetTo(saved)
38993886

3900-
// If method has default params, fall back to regular application
3901-
// where all inferred implicits are passed as named args.
3902-
if hasDefaultParams && !propFail.isInstanceOf[AmbiguousImplicits] then
3887+
// If method has default params and there are no "Ambiguous implicits"
3888+
// error, fall back to regular application where all inferred
3889+
// implicits are passed as named args.
3890+
// If there are "Ambiguous implicits" errors, these take precedence
3891+
// over the default params (see issue #19414 and related tests).
3892+
if hasDefaultParams && firstAmbiguous.isEmpty then
39033893
val namedArgs = wtp.paramNames.lazyZip(args).flatMap { (pname, arg) =>
39043894
if (arg.tpe.isError) Nil else untpd.NamedArg(pname, untpd.TypedSplice(arg)) :: Nil
39053895
}

Diff for: tests/neg/19414-desugared.check

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
-- [E172] Type Error: tests/neg/19414-desugared.scala:34:34 ------------------------------------------------------------
2+
34 | summon[BodySerializer[JsObject]] // error: Ambiguous given instances
3+
| ^
4+
|Ambiguous given instances: both given instance given_Writer_JsValue and given instance given_Writer_JsObject match type Writer[B] of parameter x of method summon in object Predef

Diff for: tests/neg/19414-desugared.scala

+34
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
/** This test checks that provided given instances take precedence over default
2+
* given arguments, even when there are multiple default arguments.
3+
*
4+
* Before the fix for issue #19414, this code would fail with a "No given
5+
* instance of type BodySerializer[JsObject]".
6+
*
7+
* See also:
8+
* - tests/neg/19414.scala
9+
* - tests/neg/given-ambiguous-default-1.scala
10+
* - tests/neg/given-ambiguous-default-2.scala
11+
*/
12+
13+
trait JsValue
14+
trait JsObject extends JsValue
15+
16+
trait Writer[T]
17+
trait BodySerializer[-B]
18+
19+
class Printer
20+
21+
given Writer[JsValue] = ???
22+
given Writer[JsObject] = ???
23+
24+
// This is not an exact desugaring of the original code: currently the compiler
25+
// actually changes the modifier of the parameter list from `using` to
26+
// `implicit` when desugaring the context-bound `B: Writer` to `implicit writer:
27+
// Writer[B]`, but we can't write in user code as this is not valid syntax.
28+
given [B](using
29+
writer: Writer[B],
30+
printer: Printer = new Printer
31+
): BodySerializer[B] = ???
32+
33+
def f: Unit =
34+
summon[BodySerializer[JsObject]] // error: Ambiguous given instances

Diff for: tests/neg/19414.check

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
-- [E172] Type Error: tests/neg/19414.scala:27:34 ----------------------------------------------------------------------
2+
27 | summon[BodySerializer[JsObject]] // error: Ambiguous given instances
3+
| ^
4+
|Ambiguous given instances: both given instance given_Writer_JsValue and given instance given_Writer_JsObject match type Writer[B] of parameter x of method summon in object Predef

Diff for: tests/neg/19414.scala

+27
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
/** This test checks that provided given instances take precedence over default
2+
* given arguments, even when there are multiple default arguments.
3+
*
4+
* Before the fix for issue #19414, this code would fail with a "No given
5+
* instance of type BodySerializer[JsObject]".
6+
*
7+
* See also:
8+
* - tests/neg/19414-desugared.scala
9+
* - tests/neg/given-ambiguous-default-1.scala
10+
* - tests/neg/given-ambiguous-default-2.scala
11+
*/
12+
13+
trait JsValue
14+
trait JsObject extends JsValue
15+
16+
trait Writer[T]
17+
trait BodySerializer[-B]
18+
19+
class Printer
20+
21+
given Writer[JsValue] = ???
22+
given Writer[JsObject] = ???
23+
24+
given [B: Writer](using printer: Printer = new Printer): BodySerializer[B] = ???
25+
26+
def f: Unit =
27+
summon[BodySerializer[JsObject]] // error: Ambiguous given instances

Diff for: tests/neg/given-ambiguous-default-1.check

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
-- [E172] Type Error: tests/neg/given-ambiguous-default-1.scala:18:23 --------------------------------------------------
2+
18 |def f: Unit = summon[B] // error: Ambiguous given instances
3+
| ^
4+
|Ambiguous given instances: both given instance a1 and given instance a2 match type A of parameter x of method summon in object Predef

Diff for: tests/neg/given-ambiguous-default-1.scala

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
/** This test checks that provided given instances take precedence over default
2+
* given arguments.
3+
*
4+
* In the following code, the compiler must report an "Ambiguous implicits"
5+
* error for the parameter `a`, and must not use its default value.
6+
*
7+
* See also:
8+
* - tests/neg/19414.scala
9+
* - tests/neg/19414-desugared.scala
10+
* - tests/neg/given-ambiguous-default-2.scala
11+
*/
12+
13+
class A
14+
class B
15+
given a1: A = ???
16+
given a2: A = ???
17+
given (using a: A = A()): B = ???
18+
19+
def f: Unit = summon[B] // error: Ambiguous given instances

Diff for: tests/neg/given-ambiguous-default-2.check

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
-- [E172] Type Error: tests/neg/given-ambiguous-default-2.scala:19:23 --------------------------------------------------
2+
19 |def f: Unit = summon[C] // error: Ambiguous given instances
3+
| ^
4+
|Ambiguous given instances: both given instance a1 and given instance a2 match type A of parameter x of method summon in object Predef

Diff for: tests/neg/given-ambiguous-default-2.scala

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
/** This test checks that provided given instances take precedence over default
2+
* given arguments, even when there are multiple default arguments.
3+
*
4+
* Before the fix for issue #19414, this code would compile without errors.
5+
*
6+
* See also:
7+
* - tests/neg/given-ambiguous-default-1.scala
8+
* - tests/neg/19414.scala
9+
* - tests/neg/19414-desugared.scala
10+
*/
11+
12+
class A
13+
class B
14+
class C
15+
given a1: A = ???
16+
given a2: A = ???
17+
given (using a: A = A(), b: B = B()): C = ???
18+
19+
def f: Unit = summon[C] // error: Ambiguous given instances

0 commit comments

Comments
 (0)