-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Hint about forbidden combination of implicit values and conversions #16735
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 15 commits
ea13cad
fc9acd7
393dab4
1d7f8dc
208071a
3f42e53
6bded27
76c826d
adc1995
82d69a7
859d30b
b5755de
9579aa5
ebb616c
98bf4b2
dda91bb
2584cf6
95c9dfb
809f9fc
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -923,7 +923,51 @@ 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) | ||
|
||
/** 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) | ||
case mt: MethodType => !mt.isImplicitMethod && !mt.isContextualMethod | ||
case _ => false | ||
} | ||
|
||
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 => | ||
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) | ||
ysthakur marked this conversation as resolved.
Show resolved
Hide resolved
|
||
.filter { imp => | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Actually, what was the problem with using There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I was thinking that in case the implicit value was something like There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. So I tried the following to check if there are applicable conversions: ctx.implicits.eligible(
ViewProto(
wildApprox(imp.underlying.widen.finalResultType),
fail.expectedType
)
).nonEmpty This works fine when the conversion doesn't have any type parameters ( There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I tried to dig into that and it seems that we were trying to reinvent the wheel. There's already the method There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nice find! |
||
if isScala2Conv(imp.underlying) || imp.symbol == defn.Predef_conforms then | ||
ysthakur marked this conversation as resolved.
Show resolved
Hide resolved
|
||
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 | ||
ysthakur marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
else | ||
Nil | ||
|
||
MissingImplicitArgument(arg, pt, where, paramSymWithMethodCallTree, ignoredInstanceNormalImport, ignoredConvertibleImplicits) | ||
} | ||
|
||
/** A string indicating the formal parameter corresponding to a missing argument */ | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 | ||
} |
Uh oh!
There was an error while loading. Please reload this page.