Skip to content

Commit 0b08074

Browse files
authored
Hint about forbidden combination of implicit values and conversions (#16735)
For fixing #16453. When an implicit argument of some type `T` is missing, this looks for all implicit conversions in scope resulting in type `T` and includes a note that you can't combine implicit values and conversions.
2 parents 42f2764 + 809f9fc commit 0b08074

File tree

4 files changed

+122
-2
lines changed

4 files changed

+122
-2
lines changed

Diff for: compiler/src/dotty/tools/dotc/reporting/messages.scala

+12-1
Original file line numberDiff line numberDiff line change
@@ -2554,7 +2554,8 @@ class MissingImplicitArgument(
25542554
pt: Type,
25552555
where: String,
25562556
paramSymWithMethodCallTree: Option[(Symbol, tpd.Tree)] = None,
2557-
ignoredInstanceNormalImport: => Option[SearchSuccess]
2557+
ignoredInstanceNormalImport: => Option[SearchSuccess],
2558+
ignoredConvertibleImplicits: => Iterable[TermRef]
25582559
)(using Context) extends TypeMsg(MissingImplicitArgumentID), ShowMatchTrace(pt):
25592560

25602561
arg.tpe match
@@ -2743,8 +2744,18 @@ class MissingImplicitArgument(
27432744
// show all available additional info
27442745
def hiddenImplicitNote(s: SearchSuccess) =
27452746
i"\n\nNote: ${s.ref.symbol.showLocated} was not considered because it was not imported with `import given`."
2747+
def showImplicitAndConversions(imp: TermRef, convs: Iterable[TermRef]) =
2748+
i"\n- ${imp.symbol.showDcl}${convs.map(c => "\n - " + c.symbol.showDcl).mkString}"
2749+
def noChainConversionsNote(ignoredConvertibleImplicits: Iterable[TermRef]): Option[String] =
2750+
Option.when(ignoredConvertibleImplicits.nonEmpty)(
2751+
i"\n\nNote: implicit conversions are not automatically applied to arguments of using clauses. " +
2752+
i"You will have to pass the argument explicitly.\n" +
2753+
i"The following implicits in scope can be implicitly converted to ${pt.show}:" +
2754+
ignoredConvertibleImplicits.map { imp => s"\n- ${imp.symbol.showDcl}"}.mkString
2755+
)
27462756
super.msgPostscript
27472757
++ ignoredInstanceNormalImport.map(hiddenImplicitNote)
2758+
.orElse(noChainConversionsNote(ignoredConvertibleImplicits))
27482759
.getOrElse(ctx.typer.importSuggestionAddendum(pt))
27492760

27502761
def explain(using Context) = ""

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

+28-1
Original file line numberDiff line numberDiff line change
@@ -923,7 +923,34 @@ trait Implicits:
923923
// example where searching for a nested type causes an infinite loop.
924924
None
925925

926-
MissingImplicitArgument(arg, pt, where, paramSymWithMethodCallTree, ignoredInstanceNormalImport)
926+
def allImplicits(currImplicits: ContextualImplicits): List[ImplicitRef] =
927+
if currImplicits.outerImplicits == null then currImplicits.refs
928+
else currImplicits.refs ::: allImplicits(currImplicits.outerImplicits)
929+
930+
/** Whether the given type is for an implicit def that's a Scala 2 implicit conversion */
931+
def isImplicitDefConversion(typ: Type): Boolean = typ match {
932+
case PolyType(_, resType) => isImplicitDefConversion(resType)
933+
case mt: MethodType => !mt.isImplicitMethod && !mt.isContextualMethod
934+
case _ => false
935+
}
936+
937+
def ignoredConvertibleImplicits = arg.tpe match
938+
case fail: SearchFailureType =>
939+
if (fail.expectedType eq pt) || isFullyDefined(fail.expectedType, ForceDegree.none) then
940+
// Get every implicit in scope and try to convert each
941+
allImplicits(ctx.implicits)
942+
.view
943+
.map(_.underlyingRef)
944+
.distinctBy(_.denot)
945+
.filter { imp =>
946+
!isImplicitDefConversion(imp.underlying)
947+
&& imp.symbol != defn.Predef_conforms
948+
&& viewExists(imp, fail.expectedType)
949+
}
950+
else
951+
Nil
952+
953+
MissingImplicitArgument(arg, pt, where, paramSymWithMethodCallTree, ignoredInstanceNormalImport, ignoredConvertibleImplicits)
927954
}
928955

929956
/** A string indicating the formal parameter corresponding to a missing argument */

Diff for: tests/neg/i16453.check

+45
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
-- [E172] Type Error: tests/neg/i16453.scala:21:19 ---------------------------------------------------------------------
2+
21 | summon[List[Int]] // error
3+
| ^
4+
| No given instance of type List[Int] was found for parameter x of method summon in object Predef
5+
-- [E172] Type Error: tests/neg/i16453.scala:23:21 ---------------------------------------------------------------------
6+
23 | summon[Option[Int]] // error
7+
| ^
8+
|No given instance of type Option[Int] was found for parameter x of method summon in object Predef
9+
|
10+
|Note: implicit conversions are not automatically applied to arguments of using clauses. You will have to pass the argument explicitly.
11+
|The following implicits in scope can be implicitly converted to Option[Int]:
12+
|- final lazy given val baz3: Char
13+
|- final lazy given val bar3: Int
14+
-- [E172] Type Error: tests/neg/i16453.scala:24:26 ---------------------------------------------------------------------
15+
24 | implicitly[Option[Char]] // error
16+
| ^
17+
|No given instance of type Option[Char] was found for parameter e of method implicitly in object Predef
18+
|
19+
|Note: implicit conversions are not automatically applied to arguments of using clauses. You will have to pass the argument explicitly.
20+
|The following implicits in scope can be implicitly converted to Option[Char]:
21+
|- final lazy given val baz3: Char
22+
-- [E172] Type Error: tests/neg/i16453.scala:25:20 ---------------------------------------------------------------------
23+
25 | implicitly[String] // error
24+
| ^
25+
|No given instance of type String was found for parameter e of method implicitly in object Predef
26+
|
27+
|Note: implicit conversions are not automatically applied to arguments of using clauses. You will have to pass the argument explicitly.
28+
|The following implicits in scope can be implicitly converted to String:
29+
|- final lazy given val baz3: Char
30+
-- [E172] Type Error: tests/neg/i16453.scala:35:16 ---------------------------------------------------------------------
31+
35 | summon[String] // error
32+
| ^
33+
|No given instance of type String was found for parameter x of method summon in object Predef
34+
|
35+
|Note: implicit conversions are not automatically applied to arguments of using clauses. You will have to pass the argument explicitly.
36+
|The following implicits in scope can be implicitly converted to String:
37+
|- implicit val baz2: Char
38+
-- [E172] Type Error: tests/neg/i16453.scala:36:25 ---------------------------------------------------------------------
39+
36 | implicitly[Option[Int]] // error
40+
| ^
41+
|No given instance of type Option[Int] was found for parameter e of method implicitly in object Predef
42+
|
43+
|Note: implicit conversions are not automatically applied to arguments of using clauses. You will have to pass the argument explicitly.
44+
|The following implicits in scope can be implicitly converted to Option[Int]:
45+
|- implicit val bar2: Int

Diff for: tests/neg/i16453.scala

+37
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import scala.language.implicitConversions
2+
3+
trait Foo { type T }
4+
5+
// This one is irrelevant, shouldn't be included in error message
6+
given irrelevant: Long = ???
7+
8+
/** Use Scala 3 givens/conversions */
9+
def testScala3() = {
10+
given c1[T]: Conversion[T, Option[T]] = ???
11+
given c2[F <: Foo](using f: F): Conversion[f.T, Option[f.T]] = ???
12+
given Conversion[Char, String] = ???
13+
given Conversion[Char, Option[Int]] = ???
14+
15+
given foo: Foo with
16+
type T = Int
17+
given bar3: Int = 0
18+
given baz3: Char = 'a'
19+
20+
// This should get the usual error
21+
summon[List[Int]] // error
22+
23+
summon[Option[Int]] // error
24+
implicitly[Option[Char]] // error
25+
implicitly[String] // error
26+
}
27+
28+
/** Use Scala 2 implicits */
29+
def testScala2() = {
30+
implicit def toOpt[T](t: T): Option[T] = ???
31+
implicit def char2Str(c: Char): String = ???
32+
implicit val bar2: Int = 1
33+
implicit val baz2: Char = 'b'
34+
35+
summon[String] // error
36+
implicitly[Option[Int]] // error
37+
}

0 commit comments

Comments
 (0)