From badd133939cdececb7590a5197a30211a9c77a50 Mon Sep 17 00:00:00 2001 From: Som Snytt Date: Thu, 11 Jan 2024 12:20:11 -0800 Subject: [PATCH] Improve --- .../dotty/tools/dotc/typer/RefChecks.scala | 24 +++++++--- .../src/tests/implicitConversions.scala | 4 +- tests/neg/i16743.check | 46 +++++++++++-------- tests/neg/i16743.scala | 30 +++++++++--- 4 files changed, 70 insertions(+), 34 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/typer/RefChecks.scala b/compiler/src/dotty/tools/dotc/typer/RefChecks.scala index 3f13b6e85f98..7295bc263b8b 100644 --- a/compiler/src/dotty/tools/dotc/typer/RefChecks.scala +++ b/compiler/src/dotty/tools/dotc/typer/RefChecks.scala @@ -1052,20 +1052,30 @@ object RefChecks { * An extension method is hidden if it does not offer a parameter that is not subsumed * by the corresponding parameter of the member (or of all alternatives of an overload). * - * If the member has no parameters and the extension method has only implicit parameters, - * then warn that the extension is shadowed unless called with explicit arguments. + * If the member has implicit parameters, then the application must supply explicit `using` + * arguments to select an extension method. The reason is that if the arguments are not + * supplied implicitly, typechecking fails and stops. But with explicit arguments, extensions + * may be considered, and of course the extension must also accept implicit parameters. + * + * If the extension method is nullary, it is always hidden by a member of the same name. */ def checkExtensionMethods(sym: Symbol)(using Context): Unit = if sym.is(Extension) then - extension (tp: Type) def firstExplicitParamTypes = Applications.stripImplicit(tp.stripPoly, wildcardOnly = true).firstParamTypes + extension (tp: Type) + def firstExplicitParamTypes = Applications.stripImplicit(tp.stripPoly, wildcardOnly = true).firstParamTypes + def hasImplicitParams = tp.stripPoly match { case mt: MethodType => mt.isImplicitMethod case _ => false } val target = sym.info.firstParamTypes.head // required for extension method if !target.typeSymbol.denot.isAliasType && !target.typeSymbol.denot.isOpaqueAlias then - val paramTps = sym.denot.info.resultType.firstExplicitParamTypes + val methTp = sym.denot.info.resultType + val paramTps = methTp.firstExplicitParamTypes val hidden = target.nonPrivateMember(sym.name) .filterWithPredicate: - _.info.firstExplicitParamTypes - .lazyZip(paramTps) - .forall((m, x) => x frozen_<:< m) + member => paramTps.isEmpty || member.info.hasImplicitParams && !methTp.hasImplicitParams || { + val memberTps = member.info.firstExplicitParamTypes + !memberTps.isEmpty + && memberTps.lengthCompare(paramTps) == 0 + && memberTps.lazyZip(paramTps).forall((m, x) => x frozen_<:< m) + } .exists if hidden then report.warning(ExtensionNullifiedByMember(sym, target.typeSymbol), sym.srcPos) diff --git a/scaladoc-testcases/src/tests/implicitConversions.scala b/scaladoc-testcases/src/tests/implicitConversions.scala index f12b8ad11801..c3051e653663 100644 --- a/scaladoc-testcases/src/tests/implicitConversions.scala +++ b/scaladoc-testcases/src/tests/implicitConversions.scala @@ -6,7 +6,9 @@ given Conversion[A, B] with { def apply(a: A): B = ??? } -extension (a: A) def extended_bar(): String = ??? +extension (a: A) + @annotation.nowarn + def extended_bar(): String = ??? class A { implicit def conversion(c: C): D = ??? diff --git a/tests/neg/i16743.check b/tests/neg/i16743.check index a46d2045249f..f7e87f6b78aa 100644 --- a/tests/neg/i16743.check +++ b/tests/neg/i16743.check @@ -1,60 +1,66 @@ --- [E193] Potential Issue Error: tests/neg/i16743.scala:26:6 ----------------------------------------------------------- -26 | def t = 27 // error +-- [E193] Potential Issue Error: tests/neg/i16743.scala:29:6 ----------------------------------------------------------- +29 | def t = 27 // error | ^ | Suspicious extension t is already a member of T | | longer explanation available when compiling with `-explain` --- [E193] Potential Issue Error: tests/neg/i16743.scala:28:6 ----------------------------------------------------------- -28 | def g(x: String)(i: Int): String = x*i // error +-- [E193] Potential Issue Error: tests/neg/i16743.scala:31:6 ----------------------------------------------------------- +31 | def g(x: String)(i: Int): String = x*i // error | ^ | Suspicious extension g is already a member of T | | longer explanation available when compiling with `-explain` --- [E193] Potential Issue Error: tests/neg/i16743.scala:29:6 ----------------------------------------------------------- -29 | def h(x: String): String = x // error +-- [E193] Potential Issue Error: tests/neg/i16743.scala:32:6 ----------------------------------------------------------- +32 | def h(x: String): String = x // error | ^ | Suspicious extension h is already a member of T | | longer explanation available when compiling with `-explain` --- [E193] Potential Issue Error: tests/neg/i16743.scala:31:6 ----------------------------------------------------------- -31 | def j(x: Any, y: Int): String = (x.toString)*y // error +-- [E193] Potential Issue Error: tests/neg/i16743.scala:34:6 ----------------------------------------------------------- +34 | def j(x: Any, y: Int): String = (x.toString)*y // error | ^ | Suspicious extension j is already a member of T | | longer explanation available when compiling with `-explain` --- [E193] Potential Issue Error: tests/neg/i16743.scala:32:6 ----------------------------------------------------------- -32 | def k(x: String): String = x // error +-- [E193] Potential Issue Error: tests/neg/i16743.scala:35:6 ----------------------------------------------------------- +35 | def k(x: String): String = x // error | ^ | Suspicious extension k is already a member of T | | longer explanation available when compiling with `-explain` --- [E193] Potential Issue Error: tests/neg/i16743.scala:33:6 ----------------------------------------------------------- -33 | def l(using String): String = summon[String] // error: can't be called implicitly +-- [E193] Potential Issue Error: tests/neg/i16743.scala:36:6 ----------------------------------------------------------- +36 | def l(using String): String = summon[String] // error: can't be called implicitly | ^ | Suspicious extension l is already a member of T | | longer explanation available when compiling with `-explain` --- [E193] Potential Issue Error: tests/neg/i16743.scala:34:6 ----------------------------------------------------------- -34 | def m(using String): String = "m" + summon[String] // error +-- [E193] Potential Issue Error: tests/neg/i16743.scala:37:6 ----------------------------------------------------------- +37 | def m(using String): String = "m" + summon[String] // error | ^ | Suspicious extension m is already a member of T | | longer explanation available when compiling with `-explain` --- [E193] Potential Issue Error: tests/neg/i16743.scala:35:6 ----------------------------------------------------------- -35 | def n(using String): String = "n" + summon[String] // error +-- [E193] Potential Issue Error: tests/neg/i16743.scala:38:6 ----------------------------------------------------------- +38 | def n(using String): String = "n" + summon[String] // error | ^ | Suspicious extension n is already a member of T | | longer explanation available when compiling with `-explain` --- [E193] Potential Issue Error: tests/neg/i16743.scala:36:6 ----------------------------------------------------------- -36 | def o: String = "42" // error +-- [E193] Potential Issue Error: tests/neg/i16743.scala:39:6 ----------------------------------------------------------- +39 | def o: String = "42" // error | ^ | Suspicious extension o is already a member of T | | longer explanation available when compiling with `-explain` --- [E193] Potential Issue Error: tests/neg/i16743.scala:37:6 ----------------------------------------------------------- -37 | def u: Int = 27 // error +-- [E193] Potential Issue Error: tests/neg/i16743.scala:40:6 ----------------------------------------------------------- +40 | def u: Int = 27 // error | ^ | Suspicious extension u is already a member of T | | longer explanation available when compiling with `-explain` +-- [E193] Potential Issue Error: tests/neg/i16743.scala:43:6 ----------------------------------------------------------- +43 | def at: Int = 42 // error + | ^ + | Suspicious extension at is already a member of T + | + | longer explanation available when compiling with `-explain` diff --git a/tests/neg/i16743.scala b/tests/neg/i16743.scala index 0b9eed435501..9ec9abe25b4e 100644 --- a/tests/neg/i16743.scala +++ b/tests/neg/i16743.scala @@ -21,6 +21,9 @@ trait T: def u(n: Int): Int = u + n def v(n: Int): Int = u + n def v(s: String): String = s + u + def end: Int = 42 + def at(n: Int) = n + def w(n: Int): Int = 42 + n extension (_t: T) def t = 27 // error @@ -36,6 +39,9 @@ extension (_t: T) def o: String = "42" // error def u: Int = 27 // error def v(d: Double) = 3.14 + def end(n: Int): Int = 42 + n + def at: Int = 42 // error + def w(using String)(n: String): Int = (summon[String] + n).toInt // deferred extension is defined in subclass trait Foo: @@ -62,9 +68,21 @@ class Result: println(x.i("hi", 5)) // OK! println(x.j("hi", 5)) // member! println(x.k) - println(x.l(using "x")) - println(x.l) - println(x.m(using "x")) - println(x.m(2)) - println(x.n) // OK just checks - println(x.n(using "x")) // also just checks + //println(x.k("hi")) // no, implicit is either omitted (supplied implicitly) or explicitly (using foo) + println(x.l) // usual, invokes member + println(x.l(using "x")) // explicit, member doesn't check, try extension + println(x.m(using "x")) // same idea as previous, except member takes no implicits or any params + println(x.m(2)) // member checks by adapting result + println(x.n) // Result + println(x.n.apply) // apply Result with given + println(x.n(using "x")) // apply Result explicitly, not extension + println(x.end(2)) + println(x.at(2)) + println { + val p = x.at + p(2) + } + println { + given String = "42" + x.w("27") + }