Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 22 additions & 15 deletions compiler/src/dotty/tools/dotc/typer/RefChecks.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1159,28 +1159,35 @@ object RefChecks {
def checkExtensionMethods(sym: Symbol)(using Context): Unit =
if sym.is(Extension) then atPhase(typerPhase):
extension (tp: Type)
def explicit = Applications.stripImplicit(tp.stripPoly, wildcardOnly = true)
def hasImplicitParams = tp.stripPoly match { case mt: MethodType => mt.isImplicitMethod case _ => false }
def isParamLess = tp.stripPoly match { case mt: MethodType => false case _ => true }
def explicit = Applications.stripImplicit(tp.widen, wildcardOnly = true)
def hiBoundOpaque =
if tp.typeSymbol.isOpaqueAlias then
tp.typeSymbol.info match
case TypeBounds(lo, hi) if lo ne hi => hi
case _ => tp
else tp.hiBound
val explicitInfo = sym.info.explicit // consider explicit value params
def memberHidesMethod(member: Denotation): Boolean =
val methTp = explicitInfo.resultType // skip leading implicits and the "receiver" parameter
if methTp.isParamLess then
return true // extension without parens is always hidden by a member of same name
val memberIsImplicit = member.info.hasImplicitParams
inline def paramsCorrespond =
val methTp = explicitInfo.stripPoly.resultType // skip leading implicits and the "receiver" parameter
val memberIsImplicit = member.info.isImplicitMethod
// are the params of the extension subsumed by the params of the member?
// an unbounded type param always subsumes.
// the params must have the same opacity to conclude the extension is hidden.
inline def paramsCorrespond = {
val paramTps =
if memberIsImplicit then methTp.stripPoly.firstParamTypes
if memberIsImplicit then methTp.firstParamTypes
else methTp.explicit.firstParamTypes
val memberParamTps = member.info.stripPoly.firstParamTypes
val memberParamTps = member.info.firstParamTypes
memberParamTps.corresponds(paramTps): (m, x) =>
m.typeSymbol.denot.isOpaqueAlias == x.typeSymbol.denot.isOpaqueAlias
&& (x frozen_<:< m)
memberIsImplicit && !methTp.hasImplicitParams || paramsCorrespond
&& (m.isInstanceOf[ParamRef] && (m eq m.hiBound) || (x.hiBound frozen_<:< m.hiBound))
}
methTp.isParameterless // extension without parens is always hidden by a member of same name
|| memberIsImplicit && !methTp.isImplicitMethod // see above
|| paramsCorrespond // match by type and opacity
def targetOfHiddenExtension: Symbol =
val target =
val target0 = explicitInfo.firstParamTypes.head // required for extension method, the putative receiver
target0.dealiasKeepOpaques.typeSymbol.info
val target = explicitInfo.firstParamTypes.head // required for extension method; the nominal receiver
.hiBound.typeSymbol.typeRef.dealiasKeepOpaques.hiBoundOpaque
val member = target.nonPrivateMember(sym.name)
.filterWithPredicate: member =>
member.symbol.isPublic && memberHidesMethod(member)
Expand Down
11 changes: 9 additions & 2 deletions tests/warn/i22232.check
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,15 @@
| because String already has a member with the same name and compatible parameter types.
|
| longer explanation available when compiling with `-explain`
-- [E194] Potential Issue Warning: tests/warn/i22232.scala:17:46 -------------------------------------------------------
17 | extension [T <: MyString[Byte]](arr: T) def length: Int = 0 // warn
-- [E194] Potential Issue Warning: tests/warn/i22232.scala:21:39 -------------------------------------------------------
21 | extension (arr: MyStuff[String]) def add(s: String): MyStuff[String] = ??? // warn
| ^
| Extension method add will never be selected from type MyList
| because MyList already has a member with the same name and compatible parameter types.
|
| longer explanation available when compiling with `-explain`
-- [E194] Potential Issue Warning: tests/warn/i22232.scala:25:46 -------------------------------------------------------
25 | extension [T <: MyString[Byte]](arr: T) def length: Int = 0 // warn
| ^
| Extension method length will never be selected from type String
| because String already has a member with the same name and compatible parameter types.
Expand Down
8 changes: 8 additions & 0 deletions tests/warn/i22232.scala
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,14 @@ object Upperbound1:
opaque type MyString[+T] <: String = String
extension (arr: MyString[Byte]) def length: Int = 0 // warn

object Upperbound1a:
trait MyList[+A]:
def add[B >: A](x: B): MyList[B]
class MyListImpl[+A] extends MyList[A]:
def add[B >: A](x: B): MyList[B] = ???
opaque type MyStuff[+T] <: MyList[T] = MyListImpl[T]
extension (arr: MyStuff[String]) def add(s: String): MyStuff[String] = ??? // warn

object Upperbound2:
opaque type MyString[+T] <: String = String
extension [T <: MyString[Byte]](arr: T) def length: Int = 0 // warn
Expand Down
15 changes: 15 additions & 0 deletions tests/warn/i23666.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@

type Tuple = scala.Tuple

infix type =:= [A, B] = A => B

object `=:=` :
given [A] => A =:= A = a => a

extension [T <: Tuple] (tuple: T)

def reverse[A, B](using ev: T =:= (A, B)): (B, A) = // warn
val ab = ev(tuple)
(ab._2, ab._1)

def toList: List[Nothing] = Nil // warn
Loading