Skip to content

Commit

Permalink
Suppress "extension method will never be selected" for overrides
Browse files Browse the repository at this point in the history
When we're overriding an existing extension method, we don't have the liberty
of renaming the method, so we shouldn't get warnings we can't do anything
about.
  • Loading branch information
smarter committed Apr 11, 2024
1 parent 0ee804f commit c7570c8
Show file tree
Hide file tree
Showing 2 changed files with 40 additions and 25 deletions.
53 changes: 28 additions & 25 deletions compiler/src/dotty/tools/dotc/typer/RefChecks.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1108,6 +1108,8 @@ 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 with the same name (or of all alternatives of an overload).
*
* This check is suppressed if this method is an override.
*
* For example, it is not possible to define a type-safe extension `contains` for `Set`,
* since for any parameter type, the existing `contains` method will compile and would be used.
*
Expand All @@ -1125,31 +1127,32 @@ object RefChecks {
* If the extension method is nullary, it is always hidden by a member of the same name.
* (Either the member is nullary, or the reference is taken as the eta-expansion of the member.)
*/
def checkExtensionMethods(sym: Symbol)(using Context): Unit = if sym.is(Extension) then
extension (tp: Type)
def strippedResultType = Applications.stripImplicit(tp.stripPoly, wildcardOnly = true).resultType
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.firstExplicitParamTypes.head // required for extension method, the putative receiver
val methTp = sym.info.strippedResultType // skip leading implicits and the "receiver" parameter
def hidden =
target.nonPrivateMember(sym.name)
.filterWithPredicate:
member =>
val memberIsImplicit = member.info.hasImplicitParams
val paramTps =
if memberIsImplicit then methTp.stripPoly.firstParamTypes
else methTp.firstExplicitParamTypes

paramTps.isEmpty || memberIsImplicit && !methTp.hasImplicitParams || {
val memberParamTps = member.info.stripPoly.firstParamTypes
!memberParamTps.isEmpty
&& memberParamTps.lengthCompare(paramTps) == 0
&& memberParamTps.lazyZip(paramTps).forall((m, x) => x frozen_<:< m)
}
.exists
if !target.typeSymbol.denot.isAliasType && !target.typeSymbol.denot.isOpaqueAlias && hidden
then report.warning(ExtensionNullifiedByMember(sym, target.typeSymbol), sym.srcPos)
def checkExtensionMethods(sym: Symbol)(using Context): Unit =
if sym.is(Extension) && !sym.nextOverriddenSymbol.exists then
extension (tp: Type)
def strippedResultType = Applications.stripImplicit(tp.stripPoly, wildcardOnly = true).resultType
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.firstExplicitParamTypes.head // required for extension method, the putative receiver
val methTp = sym.info.strippedResultType // skip leading implicits and the "receiver" parameter
def hidden =
target.nonPrivateMember(sym.name)
.filterWithPredicate:
member =>
val memberIsImplicit = member.info.hasImplicitParams
val paramTps =
if memberIsImplicit then methTp.stripPoly.firstParamTypes
else methTp.firstExplicitParamTypes

paramTps.isEmpty || memberIsImplicit && !methTp.hasImplicitParams || {
val memberParamTps = member.info.stripPoly.firstParamTypes
!memberParamTps.isEmpty
&& memberParamTps.lengthCompare(paramTps) == 0
&& memberParamTps.lazyZip(paramTps).forall((m, x) => x frozen_<:< m)
}
.exists
if !target.typeSymbol.denot.isAliasType && !target.typeSymbol.denot.isOpaqueAlias && hidden
then report.warning(ExtensionNullifiedByMember(sym, target.typeSymbol), sym.srcPos)
end checkExtensionMethods

/** Verify that references in the user-defined `@implicitNotFound` message are valid.
Expand Down
12 changes: 12 additions & 0 deletions tests/pos/ext-override.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
//> using options -Xfatal-warnings

trait Foo[T]:
extension (x: T)
def hi: String

class Bla:
def hi: String = "hi"
object Bla:
given Foo[Bla] with
extension (x: Bla)
def hi: String = x.hi

0 comments on commit c7570c8

Please sign in to comment.