From b2e62b6a273b8afddd28780490985378f8bedade Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Tue, 6 Apr 2021 11:42:04 +0200 Subject: [PATCH 1/2] Better error message for errors arising from implicit completions Fixes #11994 --- .../src/dotty/tools/dotc/typer/Typer.scala | 42 +++++++++---------- tests/neg/i11994.check | 14 +++++++ tests/neg/i11994.scala | 2 + 3 files changed, 37 insertions(+), 21 deletions(-) create mode 100644 tests/neg/i11994.check create mode 100644 tests/neg/i11994.scala diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index 49b91a37c43f..8cb7ce3ee61d 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -3065,30 +3065,30 @@ class Typer extends Namer for err <- nestedCtx.reporter.allErrors.take(1) do rememberSearchFailure(qual, SearchFailure(app.withType(FailedExtension(app, selectionProto, err.msg)))) + + // try an implicit conversion or given extension + if ctx.mode.is(Mode.ImplicitsEnabled) && !tree.name.isConstructorName && qual.tpe.isValueType then + trace(i"try insert impl on qualifier $tree $pt") { + val selProto = selectionProto + inferView(qual, selProto) match + case SearchSuccess(found, _, _, isExtension) => + if isExtension then return found + else + checkImplicitConversionUseOK(found) + return typedSelect(tree, pt, found) + case failure: SearchFailure => + if failure.isAmbiguous then + return ( + if canDefineFurther(qual.tpe.widen) then + tryExtensionOrConversion(tree, pt, mbrProto, qual, locked, compat, privateOK) + else + err.typeMismatch(qual, selProto, failure.reason) // TODO: report NotAMember instead, but need to be aware of failure + ) + rememberSearchFailure(qual, failure) + } catch case ex: TypeError => rememberSearchFailure(qual, SearchFailure(qual.withType(NestedFailure(ex.toMessage, selectionProto)))) - - // try an implicit conversion or given extension - if ctx.mode.is(Mode.ImplicitsEnabled) && !tree.name.isConstructorName && qual.tpe.isValueType then - trace(i"try insert impl on qualifier $tree $pt") { - val selProto = selectionProto - inferView(qual, selProto) match - case SearchSuccess(found, _, _, isExtension) => - if isExtension then return found - else - checkImplicitConversionUseOK(found) - return typedSelect(tree, pt, found) - case failure: SearchFailure => - if failure.isAmbiguous then - return ( - if canDefineFurther(qual.tpe.widen) then - tryExtensionOrConversion(tree, pt, mbrProto, qual, locked, compat, privateOK) - else - err.typeMismatch(qual, selProto, failure.reason) // TODO: report NotAMember instead, but need to be aware of failure - ) - rememberSearchFailure(qual, failure) - } EmptyTree end tryExtensionOrConversion diff --git a/tests/neg/i11994.check b/tests/neg/i11994.check new file mode 100644 index 000000000000..24d18257ed9f --- /dev/null +++ b/tests/neg/i11994.check @@ -0,0 +1,14 @@ +-- [E008] Not Found Error: tests/neg/i11994.scala:1:28 ----------------------------------------------------------------- +1 |implicit def foo[T <: Tuple.meow]: Unit = ??? // error + | ^^^^^^^^^^ + | type meow is not a member of object Tuple. + | Extension methods were tried, but the search failed with: + | + | Cyclic reference involving method foo +-- [E008] Not Found Error: tests/neg/i11994.scala:2:18 ----------------------------------------------------------------- +2 |given [T <: Tuple.meow]: Unit = ??? // error + | ^^^^^^^^^^ + | type meow is not a member of object Tuple. + | Extension methods were tried, but the search failed with: + | + | Cyclic reference involving method given_Unit diff --git a/tests/neg/i11994.scala b/tests/neg/i11994.scala new file mode 100644 index 000000000000..373de300dd42 --- /dev/null +++ b/tests/neg/i11994.scala @@ -0,0 +1,2 @@ +implicit def foo[T <: Tuple.meow]: Unit = ??? // error +given [T <: Tuple.meow]: Unit = ??? // error From 9c1647bd393f65e5e5a0f6aa2dd5d24175843ce7 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Tue, 6 Apr 2021 13:22:45 +0200 Subject: [PATCH 2/2] Refine previous fix We cannot pull the try that catches the type error over both parts of `tryExtensionOrConversion` since a failed extension should still cause a conversion to be tried. We need to try both parts separately instead. --- compiler/src/dotty/tools/dotc/typer/Typer.scala | 15 ++++++++++----- tests/pos/i11994.scala | 6 ++++++ 2 files changed, 16 insertions(+), 5 deletions(-) create mode 100644 tests/pos/i11994.scala diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index 8cb7ce3ee61d..04cba60052ea 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -3055,6 +3055,10 @@ class Typer extends Namer case _ => EmptyTree + def nestedFailure(ex: TypeError) = + rememberSearchFailure(qual, + SearchFailure(qual.withType(NestedFailure(ex.toMessage, selectionProto)))) + // try an extension method in scope try val nestedCtx = ctx.fresh.setNewTyperState() @@ -3065,9 +3069,11 @@ class Typer extends Namer for err <- nestedCtx.reporter.allErrors.take(1) do rememberSearchFailure(qual, SearchFailure(app.withType(FailedExtension(app, selectionProto, err.msg)))) + catch case ex: TypeError => nestedFailure(ex) - // try an implicit conversion or given extension - if ctx.mode.is(Mode.ImplicitsEnabled) && !tree.name.isConstructorName && qual.tpe.isValueType then + // try an implicit conversion or given extension + if ctx.mode.is(Mode.ImplicitsEnabled) && !tree.name.isConstructorName && qual.tpe.isValueType then + try trace(i"try insert impl on qualifier $tree $pt") { val selProto = selectionProto inferView(qual, selProto) match @@ -3086,9 +3092,8 @@ class Typer extends Namer ) rememberSearchFailure(qual, failure) } - catch case ex: TypeError => - rememberSearchFailure(qual, - SearchFailure(qual.withType(NestedFailure(ex.toMessage, selectionProto)))) + catch case ex: TypeError => nestedFailure(ex) + EmptyTree end tryExtensionOrConversion diff --git a/tests/pos/i11994.scala b/tests/pos/i11994.scala new file mode 100644 index 000000000000..3ae8d311051f --- /dev/null +++ b/tests/pos/i11994.scala @@ -0,0 +1,6 @@ +class WeirdNumber(v: Double) extends java.lang.Number { + override def doubleValue = v + override def intValue = v.intValue + override def longValue = v.longValue + override def floatValue = v.floatValue +}