From f93f00f76177daea6e1979a3694f0582ab5ef114 Mon Sep 17 00:00:00 2001 From: odersky Date: Wed, 8 May 2024 19:40:13 +0200 Subject: [PATCH] Bring back ambiguity filter when report implicit not found This reverts one part of #20261. When we fail with both an ambiguity on one implicit argument and another error on another we prefer the other error. I added a comment why this is needed. Fixes #20354 --- .../src/dotty/tools/dotc/typer/Typer.scala | 9 ++++- tests/run/i20354.scala | 40 +++++++++++++++++++ 2 files changed, 48 insertions(+), 1 deletion(-) create mode 100644 tests/run/i20354.scala diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index 2a69c948baae..9bf8071ba2f4 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -4113,7 +4113,14 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer * `SearchFailureType`. */ def issueErrors(fun: Tree, args: List[Tree]): Tree = - def firstFailure = args.tpes.find(_.isInstanceOf[SearchFailureType]).getOrElse(NoType) + // Prefer other errors over ambiguities. If nested in outer searches a missing + // implicit can be healed by simply dropping this alternative and tryng something + // else. But an ambiguity is sticky and propagates outwards. If we have both + // a missing implicit on one argument and an ambiguity on another the whole + // branch should be classified as a missing implicit. + val firstNonAmbiguous = args.tpes.find(tp => tp.isError && !tp.isInstanceOf[AmbiguousImplicits]) + def firstError = args.tpes.find(_.isInstanceOf[SearchFailureType]).getOrElse(NoType) + def firstFailure = firstNonAmbiguous.getOrElse(firstError) val errorType = firstFailure match case tp: AmbiguousImplicits => diff --git a/tests/run/i20354.scala b/tests/run/i20354.scala new file mode 100644 index 000000000000..2cb4c76d0a75 --- /dev/null +++ b/tests/run/i20354.scala @@ -0,0 +1,40 @@ +trait CmdLineParser { outer => + + trait Opt[+T] { + val default: T + val names: Set[String] + val help: String + } + + trait IntOpt extends Opt[Int] { + val parser = outer // <=== comment out this line, we get "true true" + } +} + +object FirstParser extends CmdLineParser { + object OptMinSuccess extends IntOpt { + val default = 100 + val names = Set("bla") + val help = "bla" + } + + val opts = List(OptMinSuccess) +} + +object SecondParser extends CmdLineParser { + object OptMinSuccess extends IntOpt { + val default = 50 + val names = Set("bla") + val help = "help" + } +} +@main def Test = + + val a = SecondParser.OptMinSuccess.isInstanceOf[FirstParser.IntOpt] + + println(a) + + (SecondParser.OptMinSuccess: SecondParser.IntOpt) match { + case _: FirstParser.IntOpt => println("true") + case _ => println("false") + }