Skip to content

Commit 8edcb7c

Browse files
committed
Consider more upper bounds in extension lint
1 parent 2cfa9b9 commit 8edcb7c

File tree

3 files changed

+34
-9
lines changed

3 files changed

+34
-9
lines changed

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

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1158,26 +1158,36 @@ object RefChecks {
11581158
*/
11591159
def checkExtensionMethods(sym: Symbol)(using Context): Unit =
11601160
if sym.is(Extension) then atPhase(typerPhase):
1161-
extension (tp: Type) def explicit = Applications.stripImplicit(tp.widen, wildcardOnly = false)
1161+
extension (tp: Type)
1162+
def explicit = Applications.stripImplicit(tp.widen, wildcardOnly = false)
1163+
def hiBoundOpaque =
1164+
if tp.typeSymbol.isOpaqueAlias then
1165+
tp.typeSymbol.info match
1166+
case TypeBounds(lo, hi) if lo ne hi => hi
1167+
case _ => tp
1168+
else tp.hiBound
11621169
val explicitInfo = sym.info.explicit // consider explicit value params
11631170
def memberHidesMethod(member: Denotation): Boolean =
11641171
val methTp = explicitInfo.stripPoly.resultType // skip leading implicits and the "receiver" parameter
11651172
val memberIsImplicit = member.info.isImplicitMethod
1166-
inline def paramsCorrespond =
1173+
// are the params of the extension subsumed by the params of the member?
1174+
// an unbounded type param always subsumes.
1175+
// the params must have the same opacity to conclude the extension is hidden.
1176+
inline def paramsCorrespond = {
11671177
val paramTps =
1168-
if memberIsImplicit then methTp.stripPoly.firstParamTypes
1178+
if memberIsImplicit then methTp.firstParamTypes
11691179
else methTp.explicit.firstParamTypes
1170-
val memberParamTps = member.info.stripPoly.firstParamTypes
1180+
val memberParamTps = member.info.firstParamTypes
11711181
memberParamTps.corresponds(paramTps): (m, x) =>
11721182
m.typeSymbol.denot.isOpaqueAlias == x.typeSymbol.denot.isOpaqueAlias
1173-
&& (x frozen_<:< m)
1174-
1183+
&& (m.isInstanceOf[ParamRef] && (m eq m.hiBound) || (x.hiBound frozen_<:< m.hiBound))
1184+
}
11751185
methTp.isParameterless // extension without parens is always hidden by a member of same name
11761186
|| memberIsImplicit && !methTp.isImplicitMethod // see above
11771187
|| paramsCorrespond // match by type and opacity
11781188
def targetOfHiddenExtension: Symbol =
11791189
val target = explicitInfo.firstParamTypes.head // required for extension method; the nominal receiver
1180-
.typeSymbol.typeRef.dealiasKeepOpaques
1190+
.hiBound.typeSymbol.typeRef.dealiasKeepOpaques.hiBoundOpaque
11811191
val member = target.nonPrivateMember(sym.name)
11821192
.filterWithPredicate: member =>
11831193
member.symbol.isPublic && memberHidesMethod(member)

tests/warn/i22232.check

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,15 @@
1919
| because String already has a member with the same name and compatible parameter types.
2020
|
2121
| longer explanation available when compiling with `-explain`
22-
-- [E194] Potential Issue Warning: tests/warn/i22232.scala:17:46 -------------------------------------------------------
23-
17 | extension [T <: MyString[Byte]](arr: T) def length: Int = 0 // warn
22+
-- [E194] Potential Issue Warning: tests/warn/i22232.scala:21:39 -------------------------------------------------------
23+
21 | extension (arr: MyStuff[String]) def add(s: String): MyStuff[String] = ??? // warn
24+
| ^
25+
| Extension method add will never be selected from type MyList
26+
| because MyList already has a member with the same name and compatible parameter types.
27+
|
28+
| longer explanation available when compiling with `-explain`
29+
-- [E194] Potential Issue Warning: tests/warn/i22232.scala:25:46 -------------------------------------------------------
30+
25 | extension [T <: MyString[Byte]](arr: T) def length: Int = 0 // warn
2431
| ^
2532
| Extension method length will never be selected from type String
2633
| because String already has a member with the same name and compatible parameter types.

tests/warn/i22232.scala

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,14 @@ object Upperbound1:
1212
opaque type MyString[+T] <: String = String
1313
extension (arr: MyString[Byte]) def length: Int = 0 // warn
1414

15+
object Upperbound1a:
16+
trait MyList[+A]:
17+
def add[B >: A](x: B): MyList[B]
18+
class MyListImpl[+A] extends MyList[A]:
19+
def add[B >: A](x: B): MyList[B] = ???
20+
opaque type MyStuff[+T] <: MyList[T] = MyListImpl[T]
21+
extension (arr: MyStuff[String]) def add(s: String): MyStuff[String] = ??? // warn
22+
1523
object Upperbound2:
1624
opaque type MyString[+T] <: String = String
1725
extension [T <: MyString[Byte]](arr: T) def length: Int = 0 // warn

0 commit comments

Comments
 (0)