Skip to content

Commit 0b8dc35

Browse files
mbovelWojciechMazur
authored andcommitted
Fix implicit search failure reporting
[Cherry-picked fc06435]
1 parent e530a98 commit 0b8dc35

14 files changed

+154
-79
lines changed

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

Lines changed: 57 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -3626,7 +3626,6 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
36263626

36273627
private def adapt1(tree: Tree, pt: Type, locked: TypeVars)(using Context): Tree = {
36283628
assert(pt.exists && !pt.isInstanceOf[ExprType] || ctx.reporter.errorsReported, i"tree: $tree, pt: $pt")
3629-
def methodStr = err.refStr(methPart(tree).tpe)
36303629

36313630
def readapt(tree: Tree)(using Context) = adapt(tree, pt, locked)
36323631
def readaptSimplified(tree: Tree)(using Context) = readapt(simplify(tree, pt, locked))
@@ -3803,49 +3802,38 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
38033802
arg :: inferArgsAfter(arg)
38043803
end implicitArgs
38053804

3806-
val args = implicitArgs(wtp.paramInfos, 0, pt)
3807-
3808-
def propagatedFailure(args: List[Tree]): Type = args match {
3809-
case arg :: args1 =>
3810-
arg.tpe match {
3811-
case ambi: AmbiguousImplicits =>
3812-
propagatedFailure(args1) match {
3813-
case NoType | (_: AmbiguousImplicits) => ambi
3814-
case failed => failed
3815-
}
3816-
case failed: SearchFailureType => failed
3817-
case _ => propagatedFailure(args1)
3818-
}
3819-
case Nil => NoType
3820-
}
3821-
3822-
val propFail = propagatedFailure(args)
3823-
3824-
def issueErrors(): Tree = {
3825-
def paramSymWithMethodTree(paramName: TermName) =
3826-
if tree.symbol.exists then
3827-
tree.symbol.paramSymss.flatten
3828-
.map(sym => sym.name -> sym)
3829-
.toMap
3830-
.get(paramName)
3831-
.map((_, tree))
3832-
else
3833-
None
3834-
3835-
wtp.paramNames.lazyZip(wtp.paramInfos).lazyZip(args).foreach { (paramName, formal, arg) =>
3836-
arg.tpe match {
3805+
/** Reports errors for arguments of `appTree` that have a
3806+
* `SearchFailureType`, recursively traversing arguments that are
3807+
* themselves applications. `mt` must be the type of `appTree.fun`.
3808+
*/
3809+
def reportErrors(appTree: Apply, mt: MethodType): Unit =
3810+
val Apply(fun, args) = appTree
3811+
for (paramName, formal, arg) <- mt.paramNames.lazyZip(mt.paramInfos).lazyZip(args) do
3812+
arg.tpe match
38373813
case failure: SearchFailureType =>
3838-
report.error(
3839-
missingArgMsg(arg, formal, implicitParamString(paramName, methodStr, tree), paramSymWithMethodTree(paramName)),
3840-
tree.srcPos.endPos
3841-
)
3842-
case _ =>
3843-
}
3844-
}
3845-
untpd.Apply(tree, args).withType(propFail)
3846-
}
3814+
arg match
3815+
case childAppTree: Apply =>
3816+
childAppTree.fun.tpe.widen match
3817+
case childMt: MethodType => reportErrors(childAppTree, childMt)
3818+
case _ => ()
3819+
case _ => ()
3820+
3821+
val methodStr = err.refStr(methPart(fun).tpe)
3822+
val paramStr = implicitParamString(paramName, methodStr, fun)
3823+
val paramSymWithMethodCallTree =
3824+
fun.symbol.paramSymss.flatten
3825+
.find(_.name == paramName)
3826+
.map((_, appTree))
3827+
val message = missingArgMsg(arg, formal, paramStr, paramSymWithMethodCallTree)
3828+
// Note: if the same error type appears on several trees, we
3829+
// might report it several times, but this is not a problem
3830+
// because only the first one will be displayed. We traverse in
3831+
// post-order, so that the most detailed message gets displayed.
3832+
report.error(message, fun.srcPos.endPos)
3833+
case _ => ()
38473834

3848-
if (propFail.exists) {
3835+
val args = implicitArgs(wtp.paramInfos, 0, pt)
3836+
if (args.tpes.exists(_.isInstanceOf[SearchFailureType])) {
38493837
// If there are several arguments, some arguments might already
38503838
// have influenced the context, binding variables, but later ones
38513839
// might fail. In that case the constraint and instantiated variables
@@ -3854,15 +3842,36 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
38543842

38553843
// If method has default params, fall back to regular application
38563844
// where all inferred implicits are passed as named args.
3857-
if hasDefaultParams && !propFail.isInstanceOf[AmbiguousImplicits] then
3858-
val namedArgs = wtp.paramNames.lazyZip(args).flatMap { (pname, arg) =>
3859-
if (arg.tpe.isError) Nil else untpd.NamedArg(pname, untpd.TypedSplice(arg)) :: Nil
3860-
}
3845+
if hasDefaultParams then
3846+
// Only keep the arguments that don't have an error type, or that
3847+
// have an `AmbiguousImplicits` error type. The later ensures that a
3848+
// default argument can't override an ambiguous implicit. See tests
3849+
// `given-ambiguous-default*` and `19414*`.
3850+
val namedArgs =
3851+
wtp.paramNames.lazyZip(args)
3852+
.filter((_, arg) => !arg.tpe.isError || arg.tpe.isInstanceOf[AmbiguousImplicits])
3853+
.map((pname, arg) => untpd.NamedArg(pname, untpd.TypedSplice(arg)))
3854+
38613855
val app = cpy.Apply(tree)(untpd.TypedSplice(tree), namedArgs)
38623856
if (wtp.isContextualMethod) app.setApplyKind(ApplyKind.Using)
38633857
typr.println(i"try with default implicit args $app")
3864-
typed(app, pt, locked)
3865-
else issueErrors()
3858+
val retyped = typed(app, pt, locked)
3859+
3860+
// If the retyped tree still has an error type and is an `Apply`
3861+
// node, we can report the errors for each argument nicely.
3862+
// Otherwise, we don't report anything here.
3863+
retyped match
3864+
case retyped: Apply if retyped.tpe.isError => reportErrors(retyped, wtp)
3865+
case _ => ()
3866+
3867+
retyped
3868+
else
3869+
val firstNonAmbiguous = args.tpes.find(tp => tp.isError && !tp.isInstanceOf[AmbiguousImplicits])
3870+
def firstError = args.tpes.find(_.isError)
3871+
val errorType = firstNonAmbiguous.orElse(firstError).getOrElse(NoType)
3872+
val res = untpd.Apply(tree, args).withType(errorType)
3873+
reportErrors(res, wtp)
3874+
res
38663875
}
38673876
else tree match {
38683877
case tree: Block =>

tests/neg/19414-desugared.check

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
-- [E172] Type Error: tests/neg/19414-desugared.scala:22:34 ------------------------------------------------------------
2+
22 | 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 writer of given instance given_BodySerializer_B

tests/neg/19414-desugared.scala

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
trait JsValue
2+
trait JsObject extends JsValue
3+
4+
trait Writer[T]
5+
trait BodySerializer[-B]
6+
7+
class Printer
8+
9+
given Writer[JsValue] = ???
10+
given Writer[JsObject] = ???
11+
12+
// This is not an exact desugaring of the original code: currently the compiler
13+
// actually changes the modifier of the parameter list from `using` to
14+
// `implicit` when desugaring the context-bound `B: Writer` to `implicit writer:
15+
// Writer[B]`, but we can't write it in user code as this is not valid syntax.
16+
given [B](using
17+
writer: Writer[B],
18+
printer: Printer = new Printer
19+
): BodySerializer[B] = ???
20+
21+
def f: Unit =
22+
summon[BodySerializer[JsObject]] // error: Ambiguous given instances

tests/neg/19414.check

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
-- [E172] Type Error: tests/neg/19414.scala:15:34 ----------------------------------------------------------------------
2+
15 | 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 a context parameter of given instance given_BodySerializer_B

tests/neg/19414.scala

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
trait JsValue
2+
trait JsObject extends JsValue
3+
4+
trait Writer[T]
5+
trait BodySerializer[-B]
6+
7+
class Printer
8+
9+
given Writer[JsValue] = ???
10+
given Writer[JsObject] = ???
11+
12+
given [B: Writer](using printer: Printer = new Printer): BodySerializer[B] = ???
13+
14+
def f: Unit =
15+
summon[BodySerializer[JsObject]] // error: Ambiguous given instances
Lines changed: 4 additions & 0 deletions
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 a of given instance given_B
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
/** This test checks that provided ambiguous given instances take precedence
2+
* over default given arguments. In the following code, the compiler must
3+
* report an "Ambiguous implicits" error for the parameter `a`, and must not
4+
* use its default value.
5+
*
6+
* See also:
7+
* - tests/neg/19414.scala
8+
* - tests/neg/19414-desugared.scala
9+
* - tests/neg/given-ambiguous-default-2.scala
10+
*/
11+
12+
class A
13+
class B
14+
given a1: A = ???
15+
given a2: A = ???
16+
given (using a: A = A()): B = ???
17+
18+
def f: Unit = summon[B] // error: Ambiguous given instances
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
-- [E172] Type Error: tests/neg/given-ambiguous-default-2.scala:18:23 --------------------------------------------------
2+
18 |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 a of given instance given_C
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
/** This test checks that provided given instances take precedence over default
2+
* given arguments, even when there are multiple default arguments. Before the
3+
* fix for issue #19414, this code would compile without errors.
4+
*
5+
* See also:
6+
* - tests/neg/given-ambiguous-default-1.scala
7+
* - tests/neg/19414.scala
8+
* - tests/neg/19414-desugared.scala
9+
*/
10+
11+
class A
12+
class B
13+
class C
14+
given a1: A = ???
15+
given a2: A = ???
16+
given (using a: A = A(), b: B = B()): C = ???
17+
18+
def f: Unit = summon[C] // error: Ambiguous given instances

tests/neg/i8827a.check

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,7 @@
11
-- [E172] Type Error: tests/neg/i8827a.scala:16:26 ---------------------------------------------------------------------
22
16 | summon[Order[List[Foo]]] // error
33
| ^
4-
| No given instance of type pkg.Order[List[pkg.Foo]] was found for parameter x of method summon in object Predef.
5-
| I found:
6-
|
7-
| pkg.Order.orderList[pkg.Foo](/* missing */summon[pkg.Order[pkg.Foo]])
8-
|
9-
| But no implicit values were found that match type pkg.Order[pkg.Foo].
4+
| No given instance of type pkg.Order[pkg.Foo] was found for parameter orderA of method orderList in object Order
105
|
116
| The following import might fix the problem:
127
|

tests/neg/i8827b.check

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,7 @@
11
-- [E172] Type Error: tests/neg/i8827b.scala:16:28 ---------------------------------------------------------------------
22
16 | summon[Order[Option[Foo]]] // error
33
| ^
4-
|No given instance of type pkg.Order[Option[pkg.Foo]] was found for parameter x of method summon in object Predef.
5-
|I found:
6-
|
7-
| pkg.Order.given_Order_Option[pkg.Foo](/* missing */summon[pkg.Order[pkg.Foo]])
8-
|
9-
|But no implicit values were found that match type pkg.Order[pkg.Foo].
4+
|No given instance of type pkg.Order[pkg.Foo] was found for parameter orderA of given instance given_Order_Option in object Order
105
|
116
|The following import might fix the problem:
127
|

tests/neg/i9568.check

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,10 @@
44
| No given instance of type => Monad[F] was found for parameter ev of method blaMonad in object Test.
55
| I found:
66
|
7-
| Test.blaMonad[F², S](Test.blaMonad[F³, S²])
7+
| Test.blaMonad[F², S]
88
|
9-
| But method blaMonad in object Test does not match type => Monad[F²]
9+
| But method blaMonad in object Test does not match type => Monad[F]
1010
|
1111
| where: F is a type variable with constraint <: [_] =>> Any
1212
| F² is a type variable with constraint <: [_] =>> Any
13-
| F³ is a type variable with constraint <: [_] =>> Any
14-
| S is a type variable
15-
| S² is a type variable
1613
| .

tests/neg/implicitSearch.check

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,7 @@
11
-- [E172] Type Error: tests/neg/implicitSearch.scala:13:12 -------------------------------------------------------------
22
13 | sort(xs) // error (with a partially constructed implicit argument shown)
33
| ^
4-
| No given instance of type Test.Ord[List[List[T]]] was found for parameter o of method sort in object Test.
5-
| I found:
6-
|
7-
| Test.listOrd[List[T]](Test.listOrd[T](/* missing */summon[Test.Ord[T]]))
8-
|
9-
| But no implicit values were found that match type Test.Ord[T].
4+
| No given instance of type Test.Ord[T] was found for parameter o of method listOrd in object Test
105
-- [E172] Type Error: tests/neg/implicitSearch.scala:15:38 -------------------------------------------------------------
116
15 | listOrd(listOrd(implicitly[Ord[T]] /*not found*/)) // error
127
| ^

tests/neg/missing-implicit3.check

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,9 @@
11
-- [E172] Type Error: tests/neg/missing-implicit3.scala:13:36 ----------------------------------------------------------
22
13 |val sortedFoos = sort(List(new Foo)) // error
33
| ^
4-
| No given instance of type ord.Ord[ord.Foo] was found for a context parameter of method sort in package ord.
5-
| I found:
4+
|No given instance of type ord.Foo => Comparable[? >: ord.Foo] was found for parameter x$1 of given instance ordered in object Ord
65
|
7-
| ord.Ord.ordered[ord.Foo](/* missing */summon[ord.Foo => Comparable[? >: ord.Foo]])
6+
|The following import might make progress towards fixing the problem:
87
|
9-
| But no implicit values were found that match type ord.Foo => Comparable[? >: ord.Foo].
10-
|
11-
| The following import might make progress towards fixing the problem:
12-
|
13-
| import scala.math.Ordered.orderingToOrdered
8+
| import scala.math.Ordered.orderingToOrdered
149
|

0 commit comments

Comments
 (0)