Skip to content

Commit 797bbb7

Browse files
authored
Fix #20335: Try extensions for arguments with type mismatch error (#23212)
Fixes #20335 The problem seems to be that the compiler does not try to look for extensions and implicit conversions in case of an error, which is generally correct, for performance reasons. But if the argument type does not match the one the function expects, it is correct to try to find an extension with the appropriate type.
2 parents 1eee888 + 6fcab69 commit 797bbb7

File tree

3 files changed

+31
-9
lines changed

3 files changed

+31
-9
lines changed

compiler/src/dotty/tools/dotc/reporting/messages.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ trait ShowMatchTrace(tps: Type*)(using Context) extends Message:
6262
override def msgPostscript(using Context): String =
6363
super.msgPostscript ++ matchReductionAddendum(tps*)
6464

65-
abstract class TypeMismatchMsg(found: Type, expected: Type)(errorId: ErrorMessageID)(using Context)
65+
abstract class TypeMismatchMsg(found: Type, val expected: Type)(errorId: ErrorMessageID)(using Context)
6666
extends Message(errorId), ShowMatchTrace(found, expected):
6767
def kind = MessageKind.TypeMismatch
6868
def explain(using Context) = err.whyNoMatchStr(found, expected)

compiler/src/dotty/tools/dotc/typer/ProtoTypes.scala

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -427,21 +427,28 @@ object ProtoTypes {
427427
* - t2 is a ascription (t22: T) and t1 is at the outside of t22
428428
* - t2 is a closure (...) => t22 and t1 is at the outside of t22
429429
*/
430-
def hasInnerErrors(t: Tree)(using Context): Boolean = t match
431-
case Typed(expr, tpe) => hasInnerErrors(expr)
432-
case closureDef(mdef) => hasInnerErrors(mdef.rhs)
430+
def hasInnerErrors(t: Tree, argType: Type)(using Context): Boolean = t match
431+
case Typed(expr, tpe) => hasInnerErrors(expr, argType)
432+
case closureDef(mdef) => hasInnerErrors(mdef.rhs, argType)
433433
case _ =>
434434
t.existsSubTree { t1 =>
435435
if t1.typeOpt.isError
436436
&& t.span.toSynthetic != t1.span.toSynthetic
437437
&& t.typeOpt != t1.typeOpt then
438438
typr.println(i"error subtree $t1 of $t with ${t1.typeOpt}, spans = ${t1.span}, ${t.span}")
439-
true
439+
t1.typeOpt match
440+
case errorType: ErrorType if errorType.msg.isInstanceOf[TypeMismatchMsg] =>
441+
// if error is caused by an argument type mismatch,
442+
// then return false to try to find an extension.
443+
// see i20335.scala for test case.
444+
val typeMismtachMsg = errorType.msg.asInstanceOf[TypeMismatchMsg]
445+
argType != typeMismtachMsg.expected
446+
case _ => true
440447
else
441448
false
442449
}
443450

444-
private def cacheTypedArg(arg: untpd.Tree, typerFn: untpd.Tree => Tree, force: Boolean)(using Context): Tree = {
451+
private def cacheTypedArg(arg: untpd.Tree, typerFn: untpd.Tree => Tree, force: Boolean, argType: Type)(using Context): Tree = {
445452
var targ = state.typedArg(arg)
446453
if (targ == null)
447454
untpd.functionWithUnknownParamType(arg) match {
@@ -459,7 +466,7 @@ object ProtoTypes {
459466
targ = typerFn(arg)
460467
// TODO: investigate why flow typing is not working on `targ`
461468
if ctx.reporter.hasUnreportedErrors then
462-
if hasInnerErrors(targ.nn) then
469+
if hasInnerErrors(targ.nn, argType) then
463470
state.errorArgs += arg
464471
else
465472
state.typedArg = state.typedArg.updated(arg, targ.nn)
@@ -487,7 +494,7 @@ object ProtoTypes {
487494
val protoTyperState = ctx.typerState
488495
val oldConstraint = protoTyperState.constraint
489496
val args1 = args.mapWithIndexConserve((arg, idx) =>
490-
cacheTypedArg(arg, arg => typer.typed(norm(arg, idx)), force = false))
497+
cacheTypedArg(arg, arg => typer.typed(norm(arg, idx)), force = false, NoType))
491498
val newConstraint = protoTyperState.constraint
492499

493500
if !args1.exists(arg => isUndefined(arg.tpe)) then state.typedArgs = args1
@@ -534,7 +541,8 @@ object ProtoTypes {
534541
val locked = ctx.typerState.ownedVars
535542
val targ = cacheTypedArg(arg,
536543
typer.typedUnadapted(_, wideFormal, locked)(using argCtx),
537-
force = true)
544+
force = true,
545+
wideFormal)
538546
val targ1 = typer.adapt(targ, wideFormal, locked)
539547
if wideFormal eq formal then targ1
540548
else checkNoWildcardCaptureForCBN(targ1)

tests/pos/i20335.scala

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import java.time.OffsetDateTime
2+
import scala.concurrent.duration.*
3+
4+
val dateTime: OffsetDateTime = OffsetDateTime.now()
5+
6+
implicit class DateTimeOps(val dateTime: OffsetDateTime) {
7+
def plus(amount: FiniteDuration): OffsetDateTime =
8+
dateTime
9+
}
10+
11+
def test = {
12+
dateTime.plus(Duration.Zero)
13+
dateTime.plus(if (true) Duration.Zero else Duration.Zero)
14+
}

0 commit comments

Comments
 (0)