From ea13cad629f85cb3156753dc784e4af0438d0167 Mon Sep 17 00:00:00 2001 From: ysthakur <45539777+ysthakur@users.noreply.github.com> Date: Fri, 20 Jan 2023 15:53:55 -0500 Subject: [PATCH 01/18] Search for Conversions if implicit search fails --- compiler/src/dotty/tools/dotc/reporting/messages.scala | 8 +++++++- compiler/src/dotty/tools/dotc/typer/Implicits.scala | 9 ++++++++- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/reporting/messages.scala b/compiler/src/dotty/tools/dotc/reporting/messages.scala index e8029d790d0a..692e72d504ad 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], + ignoredConversions: => Set[SearchSuccess] )(using Context) extends TypeMsg(MissingImplicitArgumentID), ShowMatchTrace(pt): arg.tpe match @@ -2743,8 +2744,13 @@ 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 noChainConversionsNote(s: Set[SearchSuccess]): Option[String] = + Option.when(s.isEmpty)( + i"\n\nNote: Chaining implicit conversions is no longer allowed in Scala. The following conversions were ignored: ${s.map(_.ref.symbol.showLocated).mkString("\n")}" + ) super.msgPostscript ++ ignoredInstanceNormalImport.map(hiddenImplicitNote) + .orElse(noChainConversionsNote(ignoredConversions)) .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..f373e0563154 100644 --- a/compiler/src/dotty/tools/dotc/typer/Implicits.scala +++ b/compiler/src/dotty/tools/dotc/typer/Implicits.scala @@ -923,7 +923,14 @@ trait Implicits: // example where searching for a nested type causes an infinite loop. None - MissingImplicitArgument(arg, pt, where, paramSymWithMethodCallTree, ignoredInstanceNormalImport) + def ignoredConversions = arg.tpe match + case fail: SearchFailureType => + if (fail.expectedType eq pt) || isFullyDefined(fail.expectedType, ForceDegree.none) then + ImplicitSearch(fail.expectedType, dummyTreeOfType(WildcardType), arg.span).allImplicits + else + Set.empty + + MissingImplicitArgument(arg, pt, where, paramSymWithMethodCallTree, ignoredInstanceNormalImport, ignoredConversions) } /** A string indicating the formal parameter corresponding to a missing argument */ From fc9acd7fbd37c1e84bc178dab86f1cd0779f2caf Mon Sep 17 00:00:00 2001 From: ysthakur <45539777+ysthakur@users.noreply.github.com> Date: Fri, 20 Jan 2023 18:47:16 -0500 Subject: [PATCH 02/18] Use ctx.implicits.eligible directly --- .../src/dotty/tools/dotc/reporting/messages.scala | 8 ++++---- compiler/src/dotty/tools/dotc/typer/Implicits.scala | 5 +++-- compiler/test/dotty/tools/dotc/CompilationTests.scala | 1 + tests/neg/i16453.check | 7 +++++++ tests/neg/i16453.scala | 11 +++++++++++ 5 files changed, 26 insertions(+), 6 deletions(-) create mode 100644 tests/neg/i16453.check create mode 100644 tests/neg/i16453.scala diff --git a/compiler/src/dotty/tools/dotc/reporting/messages.scala b/compiler/src/dotty/tools/dotc/reporting/messages.scala index 692e72d504ad..cca5dae293e2 100644 --- a/compiler/src/dotty/tools/dotc/reporting/messages.scala +++ b/compiler/src/dotty/tools/dotc/reporting/messages.scala @@ -2555,7 +2555,7 @@ class MissingImplicitArgument( where: String, paramSymWithMethodCallTree: Option[(Symbol, tpd.Tree)] = None, ignoredInstanceNormalImport: => Option[SearchSuccess], - ignoredConversions: => Set[SearchSuccess] + ignoredConversions: => Iterable[TermRef] )(using Context) extends TypeMsg(MissingImplicitArgumentID), ShowMatchTrace(pt): arg.tpe match @@ -2744,9 +2744,9 @@ 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 noChainConversionsNote(s: Set[SearchSuccess]): Option[String] = - Option.when(s.isEmpty)( - i"\n\nNote: Chaining implicit conversions is no longer allowed in Scala. The following conversions were ignored: ${s.map(_.ref.symbol.showLocated).mkString("\n")}" + def noChainConversionsNote(s: Iterable[TermRef]): Option[String] = + Option.when(s.nonEmpty)( + i"\n\nNote: Chaining implicit conversions is no longer allowed in Scala. The following conversions were ignored:${s.map(g => "\n - " + g.symbol.showLocated).mkString}" ) super.msgPostscript ++ ignoredInstanceNormalImport.map(hiddenImplicitNote) diff --git a/compiler/src/dotty/tools/dotc/typer/Implicits.scala b/compiler/src/dotty/tools/dotc/typer/Implicits.scala index f373e0563154..f723a18eb241 100644 --- a/compiler/src/dotty/tools/dotc/typer/Implicits.scala +++ b/compiler/src/dotty/tools/dotc/typer/Implicits.scala @@ -926,9 +926,10 @@ trait Implicits: def ignoredConversions = arg.tpe match case fail: SearchFailureType => if (fail.expectedType eq pt) || isFullyDefined(fail.expectedType, ForceDegree.none) then - ImplicitSearch(fail.expectedType, dummyTreeOfType(WildcardType), arg.span).allImplicits + ctx.implicits.eligible(ViewProto(WildcardType, wildApprox(fail.expectedType))) + .collect { case c if c.isConversion => c.ref } else - Set.empty + Nil MissingImplicitArgument(arg, pt, where, paramSymWithMethodCallTree, ignoredInstanceNormalImport, ignoredConversions) } diff --git a/compiler/test/dotty/tools/dotc/CompilationTests.scala b/compiler/test/dotty/tools/dotc/CompilationTests.scala index b8b38cce92e4..0346662b5434 100644 --- a/compiler/test/dotty/tools/dotc/CompilationTests.scala +++ b/compiler/test/dotty/tools/dotc/CompilationTests.scala @@ -188,6 +188,7 @@ class CompilationTests { compileFile("tests/neg-custom-args/i13026.scala", defaultOptions.and("-print-lines")), compileFile("tests/neg-custom-args/i13838.scala", defaultOptions.and("-Ximplicit-search-limit", "1000")), compileFile("tests/neg-custom-args/jdk-9-app.scala", defaultOptions.and("-release:8")), + compileFile("tests/neg/i16453.scala", defaultOptions), ).checkExpectedErrors() } diff --git a/tests/neg/i16453.check b/tests/neg/i16453.check new file mode 100644 index 000000000000..76fe7330e3b9 --- /dev/null +++ b/tests/neg/i16453.check @@ -0,0 +1,7 @@ +-- [E172] Type Error: tests/neg/i16453.scala:10:43 --------------------------------------------------------------------- +10 | val fails = summon[String => Option[Int]] // error + | ^ + | No given instance of type String => Option[Int] was found for parameter x of method summon in object Predef + | + | Note: Chaining implicit conversions is no longer allowed in Scala. The following conversions were ignored: + | - given instance given_Conversion_Function_Function diff --git a/tests/neg/i16453.scala b/tests/neg/i16453.scala new file mode 100644 index 000000000000..b60c174e4077 --- /dev/null +++ b/tests/neg/i16453.scala @@ -0,0 +1,11 @@ +import scala.language.implicitConversions + +given [T]: Conversion[String => T, String => Option[T]] = ??? +// This one is irrelevant, shouldn't be included in error message +given irrelevant[T]: Conversion[String => T, String => Byte] = ??? + +def test() = { + given foo: (String => Int) = _ => 42 + + val fails = summon[String => Option[Int]] // error +} From 393dab4f93c64766149abfa822c8a8947d3eb766 Mon Sep 17 00:00:00 2001 From: ysthakur <45539777+ysthakur@users.noreply.github.com> Date: Fri, 20 Jan 2023 19:23:12 -0500 Subject: [PATCH 03/18] Use symbol.showDcl, add sentence on explicitly passing arg --- compiler/src/dotty/tools/dotc/reporting/messages.scala | 4 +++- tests/neg/i16453.check | 7 ++++--- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/reporting/messages.scala b/compiler/src/dotty/tools/dotc/reporting/messages.scala index cca5dae293e2..be5b4c16d170 100644 --- a/compiler/src/dotty/tools/dotc/reporting/messages.scala +++ b/compiler/src/dotty/tools/dotc/reporting/messages.scala @@ -2746,7 +2746,9 @@ class MissingImplicitArgument( i"\n\nNote: ${s.ref.symbol.showLocated} was not considered because it was not imported with `import given`." def noChainConversionsNote(s: Iterable[TermRef]): Option[String] = Option.when(s.nonEmpty)( - i"\n\nNote: Chaining implicit conversions is no longer allowed in Scala. The following conversions were ignored:${s.map(g => "\n - " + g.symbol.showLocated).mkString}" + i"\n\nNote: Chaining implicit conversions is no longer allowed in Scala. " + + i"The following conversions were ignored:${s.map(g => s"\n - ${g.symbol.showDcl}").mkString}" + + i"\nIf there is an implicit `Conversion[A, ${pt.show}]` and an implicit `A` in scope, you need to explicitly pass the argument, e.g. `(using summon[A])`" ) super.msgPostscript ++ ignoredInstanceNormalImport.map(hiddenImplicitNote) diff --git a/tests/neg/i16453.check b/tests/neg/i16453.check index 76fe7330e3b9..871df3b6d7d2 100644 --- a/tests/neg/i16453.check +++ b/tests/neg/i16453.check @@ -1,7 +1,8 @@ -- [E172] Type Error: tests/neg/i16453.scala:10:43 --------------------------------------------------------------------- 10 | val fails = summon[String => Option[Int]] // error | ^ - | No given instance of type String => Option[Int] was found for parameter x of method summon in object Predef + |No given instance of type String => Option[Int] was found for parameter x of method summon in object Predef | - | Note: Chaining implicit conversions is no longer allowed in Scala. The following conversions were ignored: - | - given instance given_Conversion_Function_Function + |Note: Chaining implicit conversions is no longer allowed in Scala. The following conversions were ignored: + | - final given def given_Conversion_Function_Function[T]: Conversion[String => T, String => Option[T]] + |If there is an implicit `Conversion[A, String => Option[Int]]` and an implicit `A` in scope, you need to explicitly pass the argument, e.g. `(using summon[A])` From 1d7f8dc06706d2d53a1f16088c1fda79d558ee53 Mon Sep 17 00:00:00 2001 From: ysthakur <45539777+ysthakur@users.noreply.github.com> Date: Tue, 24 Jan 2023 04:15:20 -0500 Subject: [PATCH 04/18] Remove test from negAll MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Michał Pałka --- compiler/test/dotty/tools/dotc/CompilationTests.scala | 1 - 1 file changed, 1 deletion(-) diff --git a/compiler/test/dotty/tools/dotc/CompilationTests.scala b/compiler/test/dotty/tools/dotc/CompilationTests.scala index 0346662b5434..b8b38cce92e4 100644 --- a/compiler/test/dotty/tools/dotc/CompilationTests.scala +++ b/compiler/test/dotty/tools/dotc/CompilationTests.scala @@ -188,7 +188,6 @@ class CompilationTests { compileFile("tests/neg-custom-args/i13026.scala", defaultOptions.and("-print-lines")), compileFile("tests/neg-custom-args/i13838.scala", defaultOptions.and("-Ximplicit-search-limit", "1000")), compileFile("tests/neg-custom-args/jdk-9-app.scala", defaultOptions.and("-release:8")), - compileFile("tests/neg/i16453.scala", defaultOptions), ).checkExpectedErrors() } From 208071a5b333541d2e2db6cefc91c41ef1c55138 Mon Sep 17 00:00:00 2001 From: ysthakur <45539777+ysthakur@users.noreply.github.com> Date: Tue, 24 Jan 2023 22:12:23 -0500 Subject: [PATCH 05/18] Test implicitly() too; change err msg --- .../dotty/tools/dotc/reporting/messages.scala | 6 ++--- tests/neg/i16453.check | 24 +++++++++++++------ tests/neg/i16453.scala | 13 ++++++---- 3 files changed, 29 insertions(+), 14 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/reporting/messages.scala b/compiler/src/dotty/tools/dotc/reporting/messages.scala index be5b4c16d170..743a50a33700 100644 --- a/compiler/src/dotty/tools/dotc/reporting/messages.scala +++ b/compiler/src/dotty/tools/dotc/reporting/messages.scala @@ -2746,9 +2746,9 @@ class MissingImplicitArgument( i"\n\nNote: ${s.ref.symbol.showLocated} was not considered because it was not imported with `import given`." def noChainConversionsNote(s: Iterable[TermRef]): Option[String] = Option.when(s.nonEmpty)( - i"\n\nNote: Chaining implicit conversions is no longer allowed in Scala. " + - i"The following conversions were ignored:${s.map(g => s"\n - ${g.symbol.showDcl}").mkString}" + - i"\nIf there is an implicit `Conversion[A, ${pt.show}]` and an implicit `A` in scope, you need to explicitly pass the argument, e.g. `(using summon[A])`" + i"\n\nNote: Chaining implicit conversions are not allowed in Scala. " + + i"The following conversions are in scope:${s.map(g => s"\n - ${g.symbol.showDcl}").mkString}" + + i"\nIf there is an implicit `Conversion[A, ${pt.show}]` and an implicit `A` in scope, you can explicitly pass the argument, e.g. `(using summon[A])`" ) super.msgPostscript ++ ignoredInstanceNormalImport.map(hiddenImplicitNote) diff --git a/tests/neg/i16453.check b/tests/neg/i16453.check index 871df3b6d7d2..d112266a44c6 100644 --- a/tests/neg/i16453.check +++ b/tests/neg/i16453.check @@ -1,8 +1,18 @@ --- [E172] Type Error: tests/neg/i16453.scala:10:43 --------------------------------------------------------------------- -10 | val fails = summon[String => Option[Int]] // error - | ^ - |No given instance of type String => Option[Int] was found for parameter x of method summon in object Predef +-- [E172] Type Error: tests/neg/i16453.scala:14:21 --------------------------------------------------------------------- +14 | summon[Option[Int]] // error + | ^ + |No given instance of type Option[Int] was found for parameter x of method summon in object Predef | - |Note: Chaining implicit conversions is no longer allowed in Scala. The following conversions were ignored: - | - final given def given_Conversion_Function_Function[T]: Conversion[String => T, String => Option[T]] - |If there is an implicit `Conversion[A, String => Option[Int]]` and an implicit `A` in scope, you need to explicitly pass the argument, e.g. `(using summon[A])` + |Note: Chaining implicit conversions are not allowed in Scala. The following conversions are in scope: + | - final given def given_Conversion_T_Option[T]: Conversion[T, Option[T]] + | - implicit def toOption[T](t: T): Option[T] + |If there is an implicit `Conversion[A, Option[Int]]` and an implicit `A` in scope, you can explicitly pass the argument, e.g. `(using summon[A])` +-- [E172] Type Error: tests/neg/i16453.scala:15:25 --------------------------------------------------------------------- +15 | implicitly[Option[Int]] // error + | ^ + |No given instance of type Option[Int] was found for parameter e of method implicitly in object Predef + | + |Note: Chaining implicit conversions are not allowed in Scala. The following conversions are in scope: + | - final given def given_Conversion_T_Option[T]: Conversion[T, Option[T]] + | - implicit def toOption[T](t: T): Option[T] + |If there is an implicit `Conversion[A, Option[Int]]` and an implicit `A` in scope, you can explicitly pass the argument, e.g. `(using summon[A])` diff --git a/tests/neg/i16453.scala b/tests/neg/i16453.scala index b60c174e4077..a143237711d9 100644 --- a/tests/neg/i16453.scala +++ b/tests/neg/i16453.scala @@ -1,11 +1,16 @@ import scala.language.implicitConversions -given [T]: Conversion[String => T, String => Option[T]] = ??? +// Scala 3 style conversion +given [T]: Conversion[T, Option[T]] = ??? +// Scala 2 style conversion +implicit def toOption[T](t: T): Option[T] = Option(t) + // This one is irrelevant, shouldn't be included in error message -given irrelevant[T]: Conversion[String => T, String => Byte] = ??? +given irrelevant: Conversion[Int, Option[Long]] = ??? def test() = { - given foo: (String => Int) = _ => 42 + given foo: Int = 0 - val fails = summon[String => Option[Int]] // error + summon[Option[Int]] // error + implicitly[Option[Int]] // error } From 6bded27149b83c508d795bd5772bad43a8b334e6 Mon Sep 17 00:00:00 2001 From: ysthakur <45539777+ysthakur@users.noreply.github.com> Date: Wed, 25 Jan 2023 11:01:38 -0500 Subject: [PATCH 06/18] Use clearer name MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Michał Pałka --- compiler/src/dotty/tools/dotc/reporting/messages.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/src/dotty/tools/dotc/reporting/messages.scala b/compiler/src/dotty/tools/dotc/reporting/messages.scala index 743a50a33700..8b52544922ec 100644 --- a/compiler/src/dotty/tools/dotc/reporting/messages.scala +++ b/compiler/src/dotty/tools/dotc/reporting/messages.scala @@ -2744,7 +2744,7 @@ 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 noChainConversionsNote(s: Iterable[TermRef]): Option[String] = + def noChainConversionsNote(ignoredConversions: Iterable[TermRef]): Option[String] = Option.when(s.nonEmpty)( i"\n\nNote: Chaining implicit conversions are not allowed in Scala. " + i"The following conversions are in scope:${s.map(g => s"\n - ${g.symbol.showDcl}").mkString}" + From 76c826d2ec81f1475521bf09569e0dffbbfdd85e Mon Sep 17 00:00:00 2001 From: ysthakur <45539777+ysthakur@users.noreply.github.com> Date: Wed, 25 Jan 2023 12:04:38 -0500 Subject: [PATCH 07/18] Update message to be more generic --- compiler/src/dotty/tools/dotc/reporting/messages.scala | 8 ++++---- tests/neg/i16453.check | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/reporting/messages.scala b/compiler/src/dotty/tools/dotc/reporting/messages.scala index 8b52544922ec..9816ac25023b 100644 --- a/compiler/src/dotty/tools/dotc/reporting/messages.scala +++ b/compiler/src/dotty/tools/dotc/reporting/messages.scala @@ -2745,10 +2745,10 @@ class MissingImplicitArgument( def hiddenImplicitNote(s: SearchSuccess) = i"\n\nNote: ${s.ref.symbol.showLocated} was not considered because it was not imported with `import given`." def noChainConversionsNote(ignoredConversions: Iterable[TermRef]): Option[String] = - Option.when(s.nonEmpty)( - i"\n\nNote: Chaining implicit conversions are not allowed in Scala. " + - i"The following conversions are in scope:${s.map(g => s"\n - ${g.symbol.showDcl}").mkString}" + - i"\nIf there is an implicit `Conversion[A, ${pt.show}]` and an implicit `A` in scope, you can explicitly pass the argument, e.g. `(using summon[A])`" + Option.when(ignoredConversions.nonEmpty)( + i"\n\nNote: implicit conversions are not automatically applied to implicit arguments. " + + i"You will have to pass the argument explicitly.\n" + + i"The following conversions in scope result in ${pt.show}: ${ignoredConversions.map(g => s"\n - ${g.symbol.showDcl}").mkString}" ) super.msgPostscript ++ ignoredInstanceNormalImport.map(hiddenImplicitNote) diff --git a/tests/neg/i16453.check b/tests/neg/i16453.check index d112266a44c6..e9fb0443311e 100644 --- a/tests/neg/i16453.check +++ b/tests/neg/i16453.check @@ -3,16 +3,16 @@ | ^ |No given instance of type Option[Int] was found for parameter x of method summon in object Predef | - |Note: Chaining implicit conversions are not allowed in Scala. The following conversions are in scope: + |Note: implicit conversions are not automatically applied to implicit arguments. You will have to pass the argument explicitly. + |The following conversions in scope result in Option[Int]: | - final given def given_Conversion_T_Option[T]: Conversion[T, Option[T]] | - implicit def toOption[T](t: T): Option[T] - |If there is an implicit `Conversion[A, Option[Int]]` and an implicit `A` in scope, you can explicitly pass the argument, e.g. `(using summon[A])` -- [E172] Type Error: tests/neg/i16453.scala:15:25 --------------------------------------------------------------------- 15 | implicitly[Option[Int]] // error | ^ |No given instance of type Option[Int] was found for parameter e of method implicitly in object Predef | - |Note: Chaining implicit conversions are not allowed in Scala. The following conversions are in scope: + |Note: implicit conversions are not automatically applied to implicit arguments. You will have to pass the argument explicitly. + |The following conversions in scope result in Option[Int]: | - final given def given_Conversion_T_Option[T]: Conversion[T, Option[T]] | - implicit def toOption[T](t: T): Option[T] - |If there is an implicit `Conversion[A, Option[Int]]` and an implicit `A` in scope, you can explicitly pass the argument, e.g. `(using summon[A])` From adc1995e1313a35eabf4b9fbf56210ee13982316 Mon Sep 17 00:00:00 2001 From: ysthakur <45539777+ysthakur@users.noreply.github.com> Date: Wed, 25 Jan 2023 12:07:19 -0500 Subject: [PATCH 08/18] Edit message according to suggestion --- compiler/src/dotty/tools/dotc/reporting/messages.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/src/dotty/tools/dotc/reporting/messages.scala b/compiler/src/dotty/tools/dotc/reporting/messages.scala index 9816ac25023b..def04818255a 100644 --- a/compiler/src/dotty/tools/dotc/reporting/messages.scala +++ b/compiler/src/dotty/tools/dotc/reporting/messages.scala @@ -2746,7 +2746,7 @@ class MissingImplicitArgument( i"\n\nNote: ${s.ref.symbol.showLocated} was not considered because it was not imported with `import given`." def noChainConversionsNote(ignoredConversions: Iterable[TermRef]): Option[String] = Option.when(ignoredConversions.nonEmpty)( - i"\n\nNote: implicit conversions are not automatically applied to implicit arguments. " + + 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 conversions in scope result in ${pt.show}: ${ignoredConversions.map(g => s"\n - ${g.symbol.showDcl}").mkString}" ) From 82d69a761d9912705f45613c0aaeff9ca90c54d2 Mon Sep 17 00:00:00 2001 From: ysthakur <45539777+ysthakur@users.noreply.github.com> Date: Wed, 25 Jan 2023 15:10:21 -0500 Subject: [PATCH 09/18] Update check --- tests/neg/i16453.check | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/neg/i16453.check b/tests/neg/i16453.check index e9fb0443311e..46fea0f7c8c0 100644 --- a/tests/neg/i16453.check +++ b/tests/neg/i16453.check @@ -3,7 +3,7 @@ | ^ |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 implicit arguments. You will have to pass the argument explicitly. + |Note: implicit conversions are not automatically applied to arguments of using clauses. You will have to pass the argument explicitly. |The following conversions in scope result in Option[Int]: | - final given def given_Conversion_T_Option[T]: Conversion[T, Option[T]] | - implicit def toOption[T](t: T): Option[T] @@ -12,7 +12,7 @@ | ^ |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 implicit arguments. You will have to pass the argument explicitly. + |Note: implicit conversions are not automatically applied to arguments of using clauses. You will have to pass the argument explicitly. |The following conversions in scope result in Option[Int]: | - final given def given_Conversion_T_Option[T]: Conversion[T, Option[T]] | - implicit def toOption[T](t: T): Option[T] From 859d30b0cd8a2de2510cab0d315ca731174b3fe6 Mon Sep 17 00:00:00 2001 From: ysthakur <45539777+ysthakur@users.noreply.github.com> Date: Thu, 26 Jan 2023 23:41:46 -0500 Subject: [PATCH 10/18] Find all implicits first --- .../src/dotty/tools/dotc/reporting/messages.scala | 12 +++++++++--- .../src/dotty/tools/dotc/typer/Implicits.scala | 14 ++++++++++++-- tests/neg/i16453.scala | 9 +++++++-- 3 files changed, 28 insertions(+), 7 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/reporting/messages.scala b/compiler/src/dotty/tools/dotc/reporting/messages.scala index def04818255a..83209fb63c13 100644 --- a/compiler/src/dotty/tools/dotc/reporting/messages.scala +++ b/compiler/src/dotty/tools/dotc/reporting/messages.scala @@ -2555,7 +2555,7 @@ class MissingImplicitArgument( where: String, paramSymWithMethodCallTree: Option[(Symbol, tpd.Tree)] = None, ignoredInstanceNormalImport: => Option[SearchSuccess], - ignoredConversions: => Iterable[TermRef] + ignoredConversions: => Iterable[(TermRef, Iterable[TermRef])] )(using Context) extends TypeMsg(MissingImplicitArgumentID), ShowMatchTrace(pt): arg.tpe match @@ -2744,12 +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 noChainConversionsNote(ignoredConversions: Iterable[TermRef]): Option[String] = + def showImplicitAndConversions(imp: TermRef, convs: Iterable[TermRef]) = + i"\n- ${imp.symbol.showDcl}${convs.map(c => "\n - " + c.symbol.showDcl).mkString}" + def noChainConversionsNote(ignoredConversions: Iterable[(TermRef, Iterable[TermRef])]): Option[String] = { + val convsFormatted = ignoredConversions.map{ (imp, convs) => + i"\n- ${imp.symbol.showDcl}${convs.map(c => "\n - " + c.symbol.showDcl).mkString}" + }.mkString Option.when(ignoredConversions.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 conversions in scope result in ${pt.show}: ${ignoredConversions.map(g => s"\n - ${g.symbol.showDcl}").mkString}" + i"The following conversions in scope result in ${pt.show}: $convsFormatted" ) + } super.msgPostscript ++ ignoredInstanceNormalImport.map(hiddenImplicitNote) .orElse(noChainConversionsNote(ignoredConversions)) diff --git a/compiler/src/dotty/tools/dotc/typer/Implicits.scala b/compiler/src/dotty/tools/dotc/typer/Implicits.scala index f723a18eb241..928ac2ec999b 100644 --- a/compiler/src/dotty/tools/dotc/typer/Implicits.scala +++ b/compiler/src/dotty/tools/dotc/typer/Implicits.scala @@ -923,11 +923,21 @@ trait Implicits: // example where searching for a nested type causes an infinite loop. None + def allImplicits(currImplicits: ContextualImplicits): List[ImplicitRef] = + if currImplicits.outerImplicits == null then currImplicits.refs + else currImplicits.refs ::: allImplicits(currImplicits.outerImplicits) + def ignoredConversions = arg.tpe match case fail: SearchFailureType => + // Get every implicit in scope and find Conversions for each if (fail.expectedType eq pt) || isFullyDefined(fail.expectedType, ForceDegree.none) then - ctx.implicits.eligible(ViewProto(WildcardType, wildApprox(fail.expectedType))) - .collect { case c if c.isConversion => c.ref } + // todo filter out implicit conversions + allImplicits(ctx.implicits).map { imp => + // todo imp.underlyingRef.underlying does not work for implicit functions or givens + // with type or implicit parameters + val convs = ctx.implicits.eligible(ViewProto(imp.underlyingRef.underlying, wildApprox(fail.expectedType))) + (imp.underlyingRef, convs.map(_.ref)) + }.filter(_._2.nonEmpty) else Nil diff --git a/tests/neg/i16453.scala b/tests/neg/i16453.scala index a143237711d9..0288c05938b7 100644 --- a/tests/neg/i16453.scala +++ b/tests/neg/i16453.scala @@ -1,7 +1,10 @@ import scala.language.implicitConversions +trait Foo { type T } + // Scala 3 style conversion -given [T]: Conversion[T, Option[T]] = ??? +// given [T]: Conversion[T, Option[T]] = ??? +given [F <: Foo](using f: F): Conversion[f.T, Option[f.T]] = ??? // Scala 2 style conversion implicit def toOption[T](t: T): Option[T] = Option(t) @@ -9,7 +12,9 @@ implicit def toOption[T](t: T): Option[T] = Option(t) given irrelevant: Conversion[Int, Option[Long]] = ??? def test() = { - given foo: Int = 0 + given foo: Foo with + type T = Int + given bar: Int = 0 summon[Option[Int]] // error implicitly[Option[Int]] // error From b5755deba79f04db5d846fcc24004e15973f04b0 Mon Sep 17 00:00:00 2001 From: ysthakur <45539777+ysthakur@users.noreply.github.com> Date: Fri, 27 Jan 2023 00:28:41 -0500 Subject: [PATCH 11/18] Use finalResultType --- .../src/dotty/tools/dotc/reporting/messages.scala | 2 +- compiler/src/dotty/tools/dotc/typer/Implicits.scala | 13 +++++++++++-- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/reporting/messages.scala b/compiler/src/dotty/tools/dotc/reporting/messages.scala index 83209fb63c13..69965ab4a10a 100644 --- a/compiler/src/dotty/tools/dotc/reporting/messages.scala +++ b/compiler/src/dotty/tools/dotc/reporting/messages.scala @@ -2748,7 +2748,7 @@ class MissingImplicitArgument( i"\n- ${imp.symbol.showDcl}${convs.map(c => "\n - " + c.symbol.showDcl).mkString}" def noChainConversionsNote(ignoredConversions: Iterable[(TermRef, Iterable[TermRef])]): Option[String] = { val convsFormatted = ignoredConversions.map{ (imp, convs) => - i"\n- ${imp.symbol.showDcl}${convs.map(c => "\n - " + c.symbol.showDcl).mkString}" + s"\n- ${imp.symbol.showDcl}${convs.map(c => "\n - " + c.symbol.showDcl).mkString}" }.mkString Option.when(ignoredConversions.nonEmpty)( i"\n\nNote: implicit conversions are not automatically applied to arguments of using clauses. " + diff --git a/compiler/src/dotty/tools/dotc/typer/Implicits.scala b/compiler/src/dotty/tools/dotc/typer/Implicits.scala index 928ac2ec999b..c7d9a7c9ef89 100644 --- a/compiler/src/dotty/tools/dotc/typer/Implicits.scala +++ b/compiler/src/dotty/tools/dotc/typer/Implicits.scala @@ -935,8 +935,17 @@ trait Implicits: allImplicits(ctx.implicits).map { imp => // todo imp.underlyingRef.underlying does not work for implicit functions or givens // with type or implicit parameters - val convs = ctx.implicits.eligible(ViewProto(imp.underlyingRef.underlying, wildApprox(fail.expectedType))) - (imp.underlyingRef, convs.map(_.ref)) + val impRef = imp.underlyingRef + val impResultType = wildApprox(impRef.underlying.finalResultType) + val convs = ctx.implicits.eligible(ViewProto(impResultType, fail.expectedType)) + .filter { conv => + if !conv.isConversion then false + else + // Actually feed the summoned implicit into the Conversion to + // check if it works + true + } + (impRef, convs.map(_.ref)) }.filter(_._2.nonEmpty) else Nil From 9579aa56bd04704d63ee727be79634ff71313c4f Mon Sep 17 00:00:00 2001 From: ysthakur <45539777+ysthakur@users.noreply.github.com> Date: Fri, 27 Jan 2023 23:38:55 -0500 Subject: [PATCH 12/18] Filter out Scala 2-style implicit conversions --- .../dotty/tools/dotc/typer/Implicits.scala | 37 +++++++++++-------- 1 file changed, 22 insertions(+), 15 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/typer/Implicits.scala b/compiler/src/dotty/tools/dotc/typer/Implicits.scala index c7d9a7c9ef89..32ab6518bce9 100644 --- a/compiler/src/dotty/tools/dotc/typer/Implicits.scala +++ b/compiler/src/dotty/tools/dotc/typer/Implicits.scala @@ -927,26 +927,33 @@ trait Implicits: if currImplicits.outerImplicits == null then currImplicits.refs else currImplicits.refs ::: allImplicits(currImplicits.outerImplicits) + /** Ensure an implicit is not a Scala 2-style implicit conversion, based on its type */ + def notImplicitConv(typ: Type): Boolean = typ match { + case PolyType(_, resType) => notImplicitConv(resType) + case mt: MethodType => mt.isImplicitMethod || mt.isContextualMethod + case _ => true + } + def ignoredConversions = arg.tpe match case fail: SearchFailureType => // Get every implicit in scope and find Conversions for each if (fail.expectedType eq pt) || isFullyDefined(fail.expectedType, ForceDegree.none) then // todo filter out implicit conversions - allImplicits(ctx.implicits).map { imp => - // todo imp.underlyingRef.underlying does not work for implicit functions or givens - // with type or implicit parameters - val impRef = imp.underlyingRef - val impResultType = wildApprox(impRef.underlying.finalResultType) - val convs = ctx.implicits.eligible(ViewProto(impResultType, fail.expectedType)) - .filter { conv => - if !conv.isConversion then false - else - // Actually feed the summoned implicit into the Conversion to - // check if it works - true - } - (impRef, convs.map(_.ref)) - }.filter(_._2.nonEmpty) + allImplicits(ctx.implicits) + .filter(imp => notImplicitConv(imp.underlyingRef.underlying)) + .map { imp => + val impRef = imp.underlyingRef + val impResultType = wildApprox(impRef.underlying.finalResultType) + val convs = ctx.implicits.eligible(ViewProto(impResultType, fail.expectedType)) + .filter { conv => + if !conv.isConversion then false + else + // TODO Actually feed the summoned implicit into the Conversion to + // check if it works + true + } + (impRef, convs.map(_.ref)) + }.filter(_._2.nonEmpty) else Nil From ebb616c0e15b75c752badb855e9bb65176fd3125 Mon Sep 17 00:00:00 2001 From: ysthakur <45539777+ysthakur@users.noreply.github.com> Date: Sat, 28 Jan 2023 21:17:55 -0500 Subject: [PATCH 13/18] Use typed directly instead of finding conversions --- .../dotty/tools/dotc/reporting/messages.scala | 19 ++++------ .../dotty/tools/dotc/typer/Implicits.scala | 38 +++++++++++-------- tests/neg/i16453.check | 20 ++++------ tests/neg/i16453.scala | 2 +- 4 files changed, 39 insertions(+), 40 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/reporting/messages.scala b/compiler/src/dotty/tools/dotc/reporting/messages.scala index 69965ab4a10a..785f502a0f9c 100644 --- a/compiler/src/dotty/tools/dotc/reporting/messages.scala +++ b/compiler/src/dotty/tools/dotc/reporting/messages.scala @@ -2555,7 +2555,7 @@ class MissingImplicitArgument( where: String, paramSymWithMethodCallTree: Option[(Symbol, tpd.Tree)] = None, ignoredInstanceNormalImport: => Option[SearchSuccess], - ignoredConversions: => Iterable[(TermRef, Iterable[TermRef])] + ignoredConvertibleImplicits: => Iterable[TermRef] )(using Context) extends TypeMsg(MissingImplicitArgumentID), ShowMatchTrace(pt): arg.tpe match @@ -2746,19 +2746,16 @@ class MissingImplicitArgument( 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(ignoredConversions: Iterable[(TermRef, Iterable[TermRef])]): Option[String] = { - val convsFormatted = ignoredConversions.map{ (imp, convs) => - s"\n- ${imp.symbol.showDcl}${convs.map(c => "\n - " + c.symbol.showDcl).mkString}" - }.mkString - Option.when(ignoredConversions.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 conversions in scope result in ${pt.show}: $convsFormatted" + 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 converted to ${pt.show}:" + + ignoredConvertibleImplicits.map { imp => s"\n- ${imp.symbol.showDcl}"}.mkString ) - } super.msgPostscript ++ ignoredInstanceNormalImport.map(hiddenImplicitNote) - .orElse(noChainConversionsNote(ignoredConversions)) + .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 32ab6518bce9..c016d469f5ac 100644 --- a/compiler/src/dotty/tools/dotc/typer/Implicits.scala +++ b/compiler/src/dotty/tools/dotc/typer/Implicits.scala @@ -934,26 +934,34 @@ trait Implicits: case _ => true } + def isScala2Conv(typ: Type): Boolean = typ match { + case PolyType(_, resType) => isScala2Conv(resType) + case mt: MethodType => + (!mt.resultType.isImplicitMethod && !mt.resultType.isContextualMethod) + || isScala2Conv(mt.resultType) + case _ => false + } + def ignoredConversions = arg.tpe match case fail: SearchFailureType => // Get every implicit in scope and find Conversions for each if (fail.expectedType eq pt) || isFullyDefined(fail.expectedType, ForceDegree.none) then - // todo filter out implicit conversions allImplicits(ctx.implicits) - .filter(imp => notImplicitConv(imp.underlyingRef.underlying)) - .map { imp => - val impRef = imp.underlyingRef - val impResultType = wildApprox(impRef.underlying.finalResultType) - val convs = ctx.implicits.eligible(ViewProto(impResultType, fail.expectedType)) - .filter { conv => - if !conv.isConversion then false - else - // TODO Actually feed the summoned implicit into the Conversion to - // check if it works - true - } - (impRef, convs.map(_.ref)) - }.filter(_._2.nonEmpty) + .map(_.underlyingRef) + .filter { imp => + if notImplicitConv(imp.underlying) then false + else + val locked = ctx.typerState.ownedVars + val tryCtx = ctx.fresh + val tried = Contexts.withMode(Mode.Printing) { + typed( + tpd.ref(imp).withSpan(arg.span), + fail.expectedType, + locked + ) + }(using tryCtx) + !tryCtx.reporter.hasErrors && fail.expectedType =:= tried.tpe + } else Nil diff --git a/tests/neg/i16453.check b/tests/neg/i16453.check index 46fea0f7c8c0..5ccd9b491a1e 100644 --- a/tests/neg/i16453.check +++ b/tests/neg/i16453.check @@ -1,18 +1,12 @@ --- [E172] Type Error: tests/neg/i16453.scala:14:21 --------------------------------------------------------------------- -14 | summon[Option[Int]] // error +-- [E172] Type Error: tests/neg/i16453.scala:19:21 --------------------------------------------------------------------- +19 | 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 conversions in scope result in Option[Int]: - | - final given def given_Conversion_T_Option[T]: Conversion[T, Option[T]] - | - implicit def toOption[T](t: T): Option[T] --- [E172] Type Error: tests/neg/i16453.scala:15:25 --------------------------------------------------------------------- -15 | implicitly[Option[Int]] // error + |The following conversions in scope can be converted to Option[Int]: + |- given bar: Int +-- [E172] Type Error: tests/neg/i16453.scala:20:25 --------------------------------------------------------------------- +20 | 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 conversions in scope result in Option[Int]: - | - final given def given_Conversion_T_Option[T]: Conversion[T, Option[T]] - | - implicit def toOption[T](t: T): Option[T] + | No given instance of type Option[Int] was found for parameter e of method implicitly in object Predef diff --git a/tests/neg/i16453.scala b/tests/neg/i16453.scala index 0288c05938b7..517a22cc1270 100644 --- a/tests/neg/i16453.scala +++ b/tests/neg/i16453.scala @@ -3,7 +3,7 @@ import scala.language.implicitConversions trait Foo { type T } // Scala 3 style conversion -// given [T]: Conversion[T, Option[T]] = ??? +given [T]: Conversion[T, Option[T]] = ??? given [F <: Foo](using f: F): Conversion[f.T, Option[f.T]] = ??? // Scala 2 style conversion implicit def toOption[T](t: T): Option[T] = Option(t) From 98bf4b2b5f7dd634e3fa54b676b1171dd1969a1a Mon Sep 17 00:00:00 2001 From: ysthakur <45539777+ysthakur@users.noreply.github.com> Date: Sun, 29 Jan 2023 06:39:45 +0000 Subject: [PATCH 14/18] Fix inversion of if, check for errors properly --- .../dotty/tools/dotc/reporting/messages.scala | 2 +- .../dotty/tools/dotc/typer/Implicits.scala | 38 ++++++++------- tests/neg/i16453.check | 47 ++++++++++++++++--- tests/neg/i16453.scala | 34 ++++++++++---- 4 files changed, 86 insertions(+), 35 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/reporting/messages.scala b/compiler/src/dotty/tools/dotc/reporting/messages.scala index 785f502a0f9c..f41d34b8c17c 100644 --- a/compiler/src/dotty/tools/dotc/reporting/messages.scala +++ b/compiler/src/dotty/tools/dotc/reporting/messages.scala @@ -2750,7 +2750,7 @@ class MissingImplicitArgument( 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 converted to ${pt.show}:" + + i"The following implicits in scope can be implicitly converted to ${pt.show}:" + ignoredConvertibleImplicits.map { imp => s"\n- ${imp.symbol.showDcl}"}.mkString ) super.msgPostscript diff --git a/compiler/src/dotty/tools/dotc/typer/Implicits.scala b/compiler/src/dotty/tools/dotc/typer/Implicits.scala index c016d469f5ac..1553053e9d2e 100644 --- a/compiler/src/dotty/tools/dotc/typer/Implicits.scala +++ b/compiler/src/dotty/tools/dotc/typer/Implicits.scala @@ -928,44 +928,46 @@ trait Implicits: else currImplicits.refs ::: allImplicits(currImplicits.outerImplicits) /** Ensure an implicit is not a Scala 2-style implicit conversion, based on its type */ - def notImplicitConv(typ: Type): Boolean = typ match { - case PolyType(_, resType) => notImplicitConv(resType) - case mt: MethodType => mt.isImplicitMethod || mt.isContextualMethod - case _ => true - } - def isScala2Conv(typ: Type): Boolean = typ match { case PolyType(_, resType) => isScala2Conv(resType) - case mt: MethodType => - (!mt.resultType.isImplicitMethod && !mt.resultType.isContextualMethod) - || isScala2Conv(mt.resultType) + case mt: MethodType => !mt.isImplicitMethod && !mt.isContextualMethod case _ => false } - def ignoredConversions = arg.tpe match + def hasErrors(tree: Tree): Boolean = + if tree.tpe.isInstanceOf[ErrorType] then true + else + tree match { + case Apply(_, List(arg)) => arg.tpe.isInstanceOf[ErrorType] + case _ => false + } + + def ignoredConvertibleImplicits = arg.tpe match case fail: SearchFailureType => - // Get every implicit in scope and find Conversions for each if (fail.expectedType eq pt) || isFullyDefined(fail.expectedType, ForceDegree.none) then + // Get every implicit in scope and try to convert each allImplicits(ctx.implicits) + .distinctBy(_.underlyingRef.denot) + .view .map(_.underlyingRef) .filter { imp => - if notImplicitConv(imp.underlying) then false + if isScala2Conv(imp.underlying) || imp.symbol == defn.Predef_conforms then + false else - val locked = ctx.typerState.ownedVars - val tryCtx = ctx.fresh + // Using Mode.Printing will stop it from printing errors val tried = Contexts.withMode(Mode.Printing) { typed( tpd.ref(imp).withSpan(arg.span), fail.expectedType, - locked + ctx.typerState.ownedVars ) - }(using tryCtx) - !tryCtx.reporter.hasErrors && fail.expectedType =:= tried.tpe + } + !hasErrors(tried) && fail.expectedType =:= tried.tpe } else Nil - MissingImplicitArgument(arg, pt, where, paramSymWithMethodCallTree, ignoredInstanceNormalImport, ignoredConversions) + 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 index 5ccd9b491a1e..e01ddf5cab7a 100644 --- a/tests/neg/i16453.check +++ b/tests/neg/i16453.check @@ -1,12 +1,45 @@ --- [E172] Type Error: tests/neg/i16453.scala:19:21 --------------------------------------------------------------------- -19 | summon[Option[Int]] // error +-- [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 conversions in scope can be converted to Option[Int]: - |- given bar: Int --- [E172] Type Error: tests/neg/i16453.scala:20:25 --------------------------------------------------------------------- -20 | implicitly[Option[Int]] // error + |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 + |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 index 517a22cc1270..00495c39e21a 100644 --- a/tests/neg/i16453.scala +++ b/tests/neg/i16453.scala @@ -2,20 +2,36 @@ import scala.language.implicitConversions trait Foo { type T } -// Scala 3 style conversion -given [T]: Conversion[T, Option[T]] = ??? -given [F <: Foo](using f: F): Conversion[f.T, Option[f.T]] = ??? -// Scala 2 style conversion -implicit def toOption[T](t: T): Option[T] = Option(t) - // This one is irrelevant, shouldn't be included in error message -given irrelevant: Conversion[Int, Option[Long]] = ??? +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]] = ??? -def test() = { given foo: Foo with type T = Int - given bar: Int = 0 + 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 } From dda91bba3387155bd40677c238722fe72ec597eb Mon Sep 17 00:00:00 2001 From: ysthakur <45539777+ysthakur@users.noreply.github.com> Date: Mon, 30 Jan 2023 21:11:46 -0500 Subject: [PATCH 15/18] Make code more readable --- .../dotty/tools/dotc/typer/Implicits.scala | 42 ++++++++++--------- 1 file changed, 22 insertions(+), 20 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/typer/Implicits.scala b/compiler/src/dotty/tools/dotc/typer/Implicits.scala index 1553053e9d2e..3ebe60e327ea 100644 --- a/compiler/src/dotty/tools/dotc/typer/Implicits.scala +++ b/compiler/src/dotty/tools/dotc/typer/Implicits.scala @@ -927,42 +927,44 @@ trait Implicits: if currImplicits.outerImplicits == null then currImplicits.refs else currImplicits.refs ::: allImplicits(currImplicits.outerImplicits) - /** Ensure an implicit is not a Scala 2-style implicit conversion, based on its type */ - def isScala2Conv(typ: Type): Boolean = typ match { - case PolyType(_, resType) => isScala2Conv(resType) + /** 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 hasErrors(tree: Tree): Boolean = - if tree.tpe.isInstanceOf[ErrorType] then true - else - tree match { + /** Whether a found implicit be converted to the desired type */ + def canBeConverted(ref: TermRef, expected: Type): Boolean = { + // Using Mode.Printing will stop it from printing errors + val tried = Contexts.withMode(Mode.Printing) { + typed( + tpd.ref(ref).withSpan(arg.span), + expected, + ctx.typerState.ownedVars + ) + } + val hasErrors = + if tried.tpe.isInstanceOf[ErrorType] then true + else tried match { case Apply(_, List(arg)) => arg.tpe.isInstanceOf[ErrorType] case _ => false } + !hasErrors && expected =:= tried.tpe + } 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) - .distinctBy(_.underlyingRef.denot) .view .map(_.underlyingRef) + .distinctBy(_.denot) .filter { imp => - if isScala2Conv(imp.underlying) || imp.symbol == defn.Predef_conforms then - false - else - // Using Mode.Printing will stop it from printing errors - val tried = Contexts.withMode(Mode.Printing) { - typed( - tpd.ref(imp).withSpan(arg.span), - fail.expectedType, - ctx.typerState.ownedVars - ) - } - !hasErrors(tried) && fail.expectedType =:= tried.tpe + !isImplicitDefConversion(imp.underlying) + && imp.symbol != defn.Predef_conforms + && canBeConverted(imp, fail.expectedType) } else Nil From 2584cf6aee10970a51c00f371980ff87e3aa204b Mon Sep 17 00:00:00 2001 From: ysthakur <45539777+ysthakur@users.noreply.github.com> Date: Mon, 30 Jan 2023 21:12:53 -0500 Subject: [PATCH 16/18] Remove unnecessary check for Predef.conforms --- compiler/src/dotty/tools/dotc/typer/Implicits.scala | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/typer/Implicits.scala b/compiler/src/dotty/tools/dotc/typer/Implicits.scala index 3ebe60e327ea..5d97adea514e 100644 --- a/compiler/src/dotty/tools/dotc/typer/Implicits.scala +++ b/compiler/src/dotty/tools/dotc/typer/Implicits.scala @@ -962,9 +962,7 @@ trait Implicits: .map(_.underlyingRef) .distinctBy(_.denot) .filter { imp => - !isImplicitDefConversion(imp.underlying) - && imp.symbol != defn.Predef_conforms - && canBeConverted(imp, fail.expectedType) + !isImplicitDefConversion(imp.underlying) && canBeConverted(imp, fail.expectedType) } else Nil From 95c9dfba8f8efbf578a1baad1740179cdfb14354 Mon Sep 17 00:00:00 2001 From: ysthakur <45539777+ysthakur@users.noreply.github.com> Date: Mon, 30 Jan 2023 22:48:48 -0500 Subject: [PATCH 17/18] Put check for Predef.conforms back --- compiler/src/dotty/tools/dotc/typer/Implicits.scala | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/compiler/src/dotty/tools/dotc/typer/Implicits.scala b/compiler/src/dotty/tools/dotc/typer/Implicits.scala index 5d97adea514e..3ebe60e327ea 100644 --- a/compiler/src/dotty/tools/dotc/typer/Implicits.scala +++ b/compiler/src/dotty/tools/dotc/typer/Implicits.scala @@ -962,7 +962,9 @@ trait Implicits: .map(_.underlyingRef) .distinctBy(_.denot) .filter { imp => - !isImplicitDefConversion(imp.underlying) && canBeConverted(imp, fail.expectedType) + !isImplicitDefConversion(imp.underlying) + && imp.symbol != defn.Predef_conforms + && canBeConverted(imp, fail.expectedType) } else Nil From 809f9fc91aade75b91c759aebe7d49335888f994 Mon Sep 17 00:00:00 2001 From: ysthakur <45539777+ysthakur@users.noreply.github.com> Date: Tue, 31 Jan 2023 12:25:48 -0500 Subject: [PATCH 18/18] Use viewExists --- .../dotty/tools/dotc/typer/Implicits.scala | 21 +------------------ 1 file changed, 1 insertion(+), 20 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/typer/Implicits.scala b/compiler/src/dotty/tools/dotc/typer/Implicits.scala index 3ebe60e327ea..08ec108a6d9a 100644 --- a/compiler/src/dotty/tools/dotc/typer/Implicits.scala +++ b/compiler/src/dotty/tools/dotc/typer/Implicits.scala @@ -934,25 +934,6 @@ trait Implicits: case _ => false } - /** Whether a found implicit be converted to the desired type */ - def canBeConverted(ref: TermRef, expected: Type): Boolean = { - // Using Mode.Printing will stop it from printing errors - val tried = Contexts.withMode(Mode.Printing) { - typed( - tpd.ref(ref).withSpan(arg.span), - expected, - ctx.typerState.ownedVars - ) - } - val hasErrors = - if tried.tpe.isInstanceOf[ErrorType] then true - else tried match { - case Apply(_, List(arg)) => arg.tpe.isInstanceOf[ErrorType] - case _ => false - } - !hasErrors && expected =:= tried.tpe - } - def ignoredConvertibleImplicits = arg.tpe match case fail: SearchFailureType => if (fail.expectedType eq pt) || isFullyDefined(fail.expectedType, ForceDegree.none) then @@ -964,7 +945,7 @@ trait Implicits: .filter { imp => !isImplicitDefConversion(imp.underlying) && imp.symbol != defn.Predef_conforms - && canBeConverted(imp, fail.expectedType) + && viewExists(imp, fail.expectedType) } else Nil