Skip to content

Commit

Permalink
Improve
Browse files Browse the repository at this point in the history
  • Loading branch information
som-snytt committed Jan 11, 2024
1 parent 46d6a58 commit badd133
Show file tree
Hide file tree
Showing 4 changed files with 70 additions and 34 deletions.
24 changes: 17 additions & 7 deletions compiler/src/dotty/tools/dotc/typer/RefChecks.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down
4 changes: 3 additions & 1 deletion scaladoc-testcases/src/tests/implicitConversions.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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 = ???
Expand Down
46 changes: 26 additions & 20 deletions tests/neg/i16743.check
Original file line number Diff line number Diff line change
@@ -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`
30 changes: 24 additions & 6 deletions tests/neg/i16743.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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:
Expand All @@ -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")
}

0 comments on commit badd133

Please sign in to comment.