Skip to content

Commit 9376c7a

Browse files
Backport "Suppress "extension method will never be selected" for overrides" to LTS (#21060)
Backports #20164 to the LTS branch. PR submitted by the release tooling. [skip ci]
2 parents 52d6c1b + cb761cb commit 9376c7a

File tree

7 files changed

+48
-30
lines changed

7 files changed

+48
-30
lines changed

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

+28-25
Original file line numberDiff line numberDiff line change
@@ -1026,6 +1026,8 @@ object RefChecks {
10261026
* An extension method is hidden if it does not offer a parameter that is not subsumed
10271027
* by the corresponding parameter of the member with the same name (or of all alternatives of an overload).
10281028
*
1029+
* This check is suppressed if this method is an override.
1030+
*
10291031
* For example, it is not possible to define a type-safe extension `contains` for `Set`,
10301032
* since for any parameter type, the existing `contains` method will compile and would be used.
10311033
*
@@ -1043,31 +1045,32 @@ object RefChecks {
10431045
* If the extension method is nullary, it is always hidden by a member of the same name.
10441046
* (Either the member is nullary, or the reference is taken as the eta-expansion of the member.)
10451047
*/
1046-
def checkExtensionMethods(sym: Symbol)(using Context): Unit = if sym.is(Extension) then
1047-
extension (tp: Type)
1048-
def strippedResultType = Applications.stripImplicit(tp.stripPoly, wildcardOnly = true).resultType
1049-
def firstExplicitParamTypes = Applications.stripImplicit(tp.stripPoly, wildcardOnly = true).firstParamTypes
1050-
def hasImplicitParams = tp.stripPoly match { case mt: MethodType => mt.isImplicitMethod case _ => false }
1051-
val target = sym.info.firstExplicitParamTypes.head // required for extension method, the putative receiver
1052-
val methTp = sym.info.strippedResultType // skip leading implicits and the "receiver" parameter
1053-
def hidden =
1054-
target.nonPrivateMember(sym.name)
1055-
.filterWithPredicate:
1056-
member =>
1057-
val memberIsImplicit = member.info.hasImplicitParams
1058-
val paramTps =
1059-
if memberIsImplicit then methTp.stripPoly.firstParamTypes
1060-
else methTp.firstExplicitParamTypes
1061-
1062-
paramTps.isEmpty || memberIsImplicit && !methTp.hasImplicitParams || {
1063-
val memberParamTps = member.info.stripPoly.firstParamTypes
1064-
!memberParamTps.isEmpty
1065-
&& memberParamTps.lengthCompare(paramTps) == 0
1066-
&& memberParamTps.lazyZip(paramTps).forall((m, x) => x frozen_<:< m)
1067-
}
1068-
.exists
1069-
if !target.typeSymbol.denot.isAliasType && !target.typeSymbol.denot.isOpaqueAlias && hidden
1070-
then report.warning(ExtensionNullifiedByMember(sym, target.typeSymbol), sym.srcPos)
1048+
def checkExtensionMethods(sym: Symbol)(using Context): Unit =
1049+
if sym.is(Extension) && !sym.nextOverriddenSymbol.exists then
1050+
extension (tp: Type)
1051+
def strippedResultType = Applications.stripImplicit(tp.stripPoly, wildcardOnly = true).resultType
1052+
def firstExplicitParamTypes = Applications.stripImplicit(tp.stripPoly, wildcardOnly = true).firstParamTypes
1053+
def hasImplicitParams = tp.stripPoly match { case mt: MethodType => mt.isImplicitMethod case _ => false }
1054+
val target = sym.info.firstExplicitParamTypes.head // required for extension method, the putative receiver
1055+
val methTp = sym.info.strippedResultType // skip leading implicits and the "receiver" parameter
1056+
def hidden =
1057+
target.nonPrivateMember(sym.name)
1058+
.filterWithPredicate:
1059+
member =>
1060+
val memberIsImplicit = member.info.hasImplicitParams
1061+
val paramTps =
1062+
if memberIsImplicit then methTp.stripPoly.firstParamTypes
1063+
else methTp.firstExplicitParamTypes
1064+
1065+
paramTps.isEmpty || memberIsImplicit && !methTp.hasImplicitParams || {
1066+
val memberParamTps = member.info.stripPoly.firstParamTypes
1067+
!memberParamTps.isEmpty
1068+
&& memberParamTps.lengthCompare(paramTps) == 0
1069+
&& memberParamTps.lazyZip(paramTps).forall((m, x) => x frozen_<:< m)
1070+
}
1071+
.exists
1072+
if !target.typeSymbol.denot.isAliasType && !target.typeSymbol.denot.isOpaqueAlias && hidden
1073+
then report.warning(ExtensionNullifiedByMember(sym, target.typeSymbol), sym.srcPos)
10711074
end checkExtensionMethods
10721075

10731076
/** Verify that references in the user-defined `@implicitNotFound` message are valid.

compiler/test/dotc/pos-test-pickling.blacklist

+1
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ i13433.scala
2828
i16649-irrefutable.scala
2929
strict-pattern-bindings-3.0-migration.scala
3030
i17255
31+
i17735.scala
3132

3233
# Tree is huge and blows stack for printing Text
3334
i7034.scala

tests/pos/ext-override.scala

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
//> using options -Xfatal-warnings
2+
3+
trait Foo[T]:
4+
extension (x: T)
5+
def hi: String
6+
7+
class Bla:
8+
def hi: String = "hi"
9+
object Bla:
10+
given Foo[Bla] with
11+
extension (x: Bla)
12+
def hi: String = x.hi

tests/pos-special/fatal-warnings/i17735.scala renamed to tests/pos/i17735.scala

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// scalac: -Wvalue-discard
1+
//> using options -Xfatal-warnings -Wvalue-discard
22

33
import scala.collection.mutable
44
import scala.annotation.nowarn
@@ -21,4 +21,4 @@ object Foo:
2121
// here @nowarn is effective without -Wfatal-warnings (i.e. no warning)
2222
// But with -Wfatal-warnings we get an error
2323
messageBuilder.append("\n").append(s): @nowarn("msg=discarded non-Unit value*")
24-
messageBuilder.result()
24+
messageBuilder.result()

tests/pos-special/fatal-warnings/i17735a.scala renamed to tests/pos/i17735a.scala

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// scalac: -Wvalue-discard -Wconf:msg=non-Unit:s
1+
//> using options -Xfatal-warnings -Wvalue-discard -Wconf:msg=non-Unit:s
22

33
import scala.collection.mutable
44
import scala.annotation.nowarn

tests/pos-special/fatal-warnings/i17741.scala renamed to tests/pos/i17741.scala

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// scalac: -Wnonunit-statement
1+
//> using options -Xfatal-warnings -Wnonunit-statement
22

33
class Node()
44
class Elem(
@@ -29,4 +29,4 @@ object Main {
2929
)
3030
}
3131
}: @annotation.nowarn()
32-
}
32+
}

tests/pos-special/fatal-warnings/nowarnannot.scala renamed to tests/pos/nowarnannot.scala

+2
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
//> using options -Xfatal-warnings -Wvalue-discard
2+
13
case class F(i: Int)
24

35
object Main {

0 commit comments

Comments
 (0)