From cc2ecb7a5225567e8c11d490ee10a35aa3aaaf13 Mon Sep 17 00:00:00 2001 From: odersky Date: Sat, 23 Apr 2022 19:37:06 +0200 Subject: [PATCH] Refine condition when to report errors in an inserted apply Fixes #15000 --- .../src/dotty/tools/dotc/typer/Typer.scala | 22 +++++++++++++---- tests/neg/i15000.check | 24 +++++++++++++++++++ tests/neg/i15000.scala | 21 ++++++++++++++++ 3 files changed, 62 insertions(+), 5 deletions(-) create mode 100644 tests/neg/i15000.check create mode 100644 tests/neg/i15000.scala diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index 0f0e0a2a5744..cd83918832a3 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -3184,12 +3184,26 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer case _ => false } + var assumeApplyExists = false + // if true, issue any errors about the apply instead of `fallBack`, + // since they are more likely to be informative. + def tryApply(using Context) = { val pt1 = pt.withContext(ctx) val sel = typedSelect(untpd.Select(untpd.TypedSplice(tree), nme.apply), pt1) .withAttachment(InsertedApply, ()) - if (sel.tpe.isError) sel - else try adapt(simplify(sel, pt1, locked), pt1, locked) finally sel.removeAttachment(InsertedApply) + if sel.tpe.isError then + // assume the apply exists if qualifier has a hidden search failure of type + // FailedExtension or NestedFailure + sel match + case Select(qual, _) => + assumeApplyExists = qual.getAttachment(Typer.HiddenSearchFailure).exists( + _.exists(_.reason.isInstanceOf[FailedExtension | NestedFailure])) + case _ => + sel + else + assumeApplyExists = true + try adapt(simplify(sel, pt1, locked), pt1, locked) finally sel.removeAttachment(InsertedApply) } def tryImplicit(fallBack: => Tree) = @@ -3209,11 +3223,9 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer if (isApplyProto(pt) || isMethod(tree) || isSyntheticApply(tree)) tryImplicit(fallBack) else tryEither(tryApply) { (app, appState) => tryImplicit { - if (tree.tpe.member(nme.apply).exists) { - // issue the error about the apply, since it is likely more informative than the fallback + if assumeApplyExists then appState.commit() app - } else fallBack } } diff --git a/tests/neg/i15000.check b/tests/neg/i15000.check new file mode 100644 index 000000000000..c63866993103 --- /dev/null +++ b/tests/neg/i15000.check @@ -0,0 +1,24 @@ +-- [E006] Not Found Error: tests/neg/i15000.scala:19:11 ---------------------------------------------------------------- +19 | str(x, barY) // error + | ^^^^ + | Not found: barY + | + | longer explanation available when compiling with `-explain` +-- [E006] Not Found Error: tests/neg/i15000.scala:20:9 ----------------------------------------------------------------- +20 | c(x, barY) // error // error + | ^^^^ + | Not found: barY + | + | longer explanation available when compiling with `-explain` +-- [E008] Not Found Error: tests/neg/i15000.scala:20:4 ----------------------------------------------------------------- +20 | c(x, barY) // error // error + | ^ + |value apply is not a member of object ExtensionMethodReproduction.c. + |An extension method was tried, but could not be fully constructed: + | + | apply(ExtensionMethodReproduction.c) failed with + | + | Ambiguous overload. The overloaded alternatives of method apply in object ExtensionMethodReproduction with types + | (c: ExtensionMethodReproduction.C)(x: Int, y: Int): String + | (c: ExtensionMethodReproduction.C)(x: Int, y: String): String + | both match arguments (ExtensionMethodReproduction.c.type) diff --git a/tests/neg/i15000.scala b/tests/neg/i15000.scala new file mode 100644 index 000000000000..bd3dabc9ec39 --- /dev/null +++ b/tests/neg/i15000.scala @@ -0,0 +1,21 @@ +object ExtensionMethodReproduction { + import scala.language.implicitConversions + class StringExtensions { + def apply(x: Int, y: String): String = "foo" + def apply(x: Int, y: Int): String = "foo" + } + implicit def stringExtensions(m: String): StringExtensions = new StringExtensions() + + class C + object c extends C + + extension (c: C) + def apply(x: Int, y: String): String = "foo" + def apply(x: Int, y: Int): String = "foo" + + val str = "POST" + val x: Int = 1 + val fooY: String = "foo" + str(x, barY) // error + c(x, barY) // error // error + } \ No newline at end of file