diff --git a/compiler/src/dotty/tools/dotc/reporting/messages.scala b/compiler/src/dotty/tools/dotc/reporting/messages.scala index e8029d790d0a..f41d34b8c17c 100644 --- a/compiler/src/dotty/tools/dotc/reporting/messages.scala +++ b/compiler/src/dotty/tools/dotc/reporting/messages.scala @@ -2554,7 +2554,8 @@ class MissingImplicitArgument( pt: Type, where: String, paramSymWithMethodCallTree: Option[(Symbol, tpd.Tree)] = None, - ignoredInstanceNormalImport: => Option[SearchSuccess] + ignoredInstanceNormalImport: => Option[SearchSuccess], + ignoredConvertibleImplicits: => Iterable[TermRef] )(using Context) extends TypeMsg(MissingImplicitArgumentID), ShowMatchTrace(pt): arg.tpe match @@ -2743,8 +2744,18 @@ class MissingImplicitArgument( // show all available additional info def hiddenImplicitNote(s: SearchSuccess) = i"\n\nNote: ${s.ref.symbol.showLocated} was not considered because it was not imported with `import given`." + def showImplicitAndConversions(imp: TermRef, convs: Iterable[TermRef]) = + i"\n- ${imp.symbol.showDcl}${convs.map(c => "\n - " + c.symbol.showDcl).mkString}" + def noChainConversionsNote(ignoredConvertibleImplicits: Iterable[TermRef]): Option[String] = + Option.when(ignoredConvertibleImplicits.nonEmpty)( + i"\n\nNote: implicit conversions are not automatically applied to arguments of using clauses. " + + i"You will have to pass the argument explicitly.\n" + + i"The following implicits in scope can be implicitly converted to ${pt.show}:" + + ignoredConvertibleImplicits.map { imp => s"\n- ${imp.symbol.showDcl}"}.mkString + ) super.msgPostscript ++ ignoredInstanceNormalImport.map(hiddenImplicitNote) + .orElse(noChainConversionsNote(ignoredConvertibleImplicits)) .getOrElse(ctx.typer.importSuggestionAddendum(pt)) def explain(using Context) = "" diff --git a/compiler/src/dotty/tools/dotc/typer/Implicits.scala b/compiler/src/dotty/tools/dotc/typer/Implicits.scala index 3e0e7dd5879d..08ec108a6d9a 100644 --- a/compiler/src/dotty/tools/dotc/typer/Implicits.scala +++ b/compiler/src/dotty/tools/dotc/typer/Implicits.scala @@ -923,7 +923,34 @@ trait Implicits: // example where searching for a nested type causes an infinite loop. None - MissingImplicitArgument(arg, pt, where, paramSymWithMethodCallTree, ignoredInstanceNormalImport) + def allImplicits(currImplicits: ContextualImplicits): List[ImplicitRef] = + if currImplicits.outerImplicits == null then currImplicits.refs + else currImplicits.refs ::: allImplicits(currImplicits.outerImplicits) + + /** Whether the given type is for an implicit def that's a Scala 2 implicit conversion */ + def isImplicitDefConversion(typ: Type): Boolean = typ match { + case PolyType(_, resType) => isImplicitDefConversion(resType) + case mt: MethodType => !mt.isImplicitMethod && !mt.isContextualMethod + case _ => false + } + + def ignoredConvertibleImplicits = arg.tpe match + case fail: SearchFailureType => + if (fail.expectedType eq pt) || isFullyDefined(fail.expectedType, ForceDegree.none) then + // Get every implicit in scope and try to convert each + allImplicits(ctx.implicits) + .view + .map(_.underlyingRef) + .distinctBy(_.denot) + .filter { imp => + !isImplicitDefConversion(imp.underlying) + && imp.symbol != defn.Predef_conforms + && viewExists(imp, fail.expectedType) + } + else + Nil + + MissingImplicitArgument(arg, pt, where, paramSymWithMethodCallTree, ignoredInstanceNormalImport, ignoredConvertibleImplicits) } /** A string indicating the formal parameter corresponding to a missing argument */ diff --git a/tests/neg/i16453.check b/tests/neg/i16453.check new file mode 100644 index 000000000000..e01ddf5cab7a --- /dev/null +++ b/tests/neg/i16453.check @@ -0,0 +1,45 @@ +-- [E172] Type Error: tests/neg/i16453.scala:21:19 --------------------------------------------------------------------- +21 | summon[List[Int]] // error + | ^ + | No given instance of type List[Int] was found for parameter x of method summon in object Predef +-- [E172] Type Error: tests/neg/i16453.scala:23:21 --------------------------------------------------------------------- +23 | summon[Option[Int]] // error + | ^ + |No given instance of type Option[Int] was found for parameter x of method summon in object Predef + | + |Note: implicit conversions are not automatically applied to arguments of using clauses. You will have to pass the argument explicitly. + |The following implicits in scope can be implicitly converted to Option[Int]: + |- final lazy given val baz3: Char + |- final lazy given val bar3: Int +-- [E172] Type Error: tests/neg/i16453.scala:24:26 --------------------------------------------------------------------- +24 | implicitly[Option[Char]] // error + | ^ + |No given instance of type Option[Char] was found for parameter e of method implicitly in object Predef + | + |Note: implicit conversions are not automatically applied to arguments of using clauses. You will have to pass the argument explicitly. + |The following implicits in scope can be implicitly converted to Option[Char]: + |- final lazy given val baz3: Char +-- [E172] Type Error: tests/neg/i16453.scala:25:20 --------------------------------------------------------------------- +25 | implicitly[String] // error + | ^ + |No given instance of type String was found for parameter e of method implicitly in object Predef + | + |Note: implicit conversions are not automatically applied to arguments of using clauses. You will have to pass the argument explicitly. + |The following implicits in scope can be implicitly converted to String: + |- final lazy given val baz3: Char +-- [E172] Type Error: tests/neg/i16453.scala:35:16 --------------------------------------------------------------------- +35 | summon[String] // error + | ^ + |No given instance of type String was found for parameter x of method summon in object Predef + | + |Note: implicit conversions are not automatically applied to arguments of using clauses. You will have to pass the argument explicitly. + |The following implicits in scope can be implicitly converted to String: + |- implicit val baz2: Char +-- [E172] Type Error: tests/neg/i16453.scala:36:25 --------------------------------------------------------------------- +36 | implicitly[Option[Int]] // error + | ^ + |No given instance of type Option[Int] was found for parameter e of method implicitly in object Predef + | + |Note: implicit conversions are not automatically applied to arguments of using clauses. You will have to pass the argument explicitly. + |The following implicits in scope can be implicitly converted to Option[Int]: + |- implicit val bar2: Int diff --git a/tests/neg/i16453.scala b/tests/neg/i16453.scala new file mode 100644 index 000000000000..00495c39e21a --- /dev/null +++ b/tests/neg/i16453.scala @@ -0,0 +1,37 @@ +import scala.language.implicitConversions + +trait Foo { type T } + +// This one is irrelevant, shouldn't be included in error message +given irrelevant: Long = ??? + +/** Use Scala 3 givens/conversions */ +def testScala3() = { + given c1[T]: Conversion[T, Option[T]] = ??? + given c2[F <: Foo](using f: F): Conversion[f.T, Option[f.T]] = ??? + given Conversion[Char, String] = ??? + given Conversion[Char, Option[Int]] = ??? + + given foo: Foo with + type T = Int + given bar3: Int = 0 + given baz3: Char = 'a' + + // This should get the usual error + summon[List[Int]] // error + + summon[Option[Int]] // error + implicitly[Option[Char]] // error + implicitly[String] // error +} + +/** Use Scala 2 implicits */ +def testScala2() = { + implicit def toOpt[T](t: T): Option[T] = ??? + implicit def char2Str(c: Char): String = ??? + implicit val bar2: Int = 1 + implicit val baz2: Char = 'b' + + summon[String] // error + implicitly[Option[Int]] // error +}