@@ -1137,14 +1137,24 @@ object RefChecks {
11371137 end checkUnaryMethods
11381138
11391139 /** Check that an extension method is not hidden, i.e., that it is callable as an extension method.
1140+ *
1141+ * For example, it is not possible to define a type-safe extension `contains` for `Set`,
1142+ * since for any parameter type, the existing `contains` method will compile and would be used.
11401143 *
11411144 * An extension method is hidden if it does not offer a parameter that is not subsumed
11421145 * by the corresponding parameter of the member with the same name (or of all alternatives of an overload).
11431146 *
1144- * This check is suppressed if this method is an override.
1147+ * This check is suppressed if the method is an override. (Because the type of the receiver
1148+ * may be narrower in the override.)
11451149 *
1146- * For example, it is not possible to define a type-safe extension `contains` for `Set`,
1147- * since for any parameter type, the existing `contains` method will compile and would be used.
1150+ * If the extension method is nullary, it is always hidden by a member of the same name.
1151+ * (Either the member is nullary, or the reference is taken as the eta-expansion of the member.)
1152+ *
1153+ * This check is in lieu of a more expensive use-site check that an application failed to use an extension.
1154+ * That check would account for accessibility and opacity. As a limitation, this check considers
1155+ * only public members for which corresponding method parameters are either both opaque types or both not.
1156+ * It is intended to warn if the receiver type from a third-party library has been augmented with a member
1157+ * that nullifies an existing extension.
11481158 *
11491159 * If the member has a leading implicit parameter list, then the extension method must also have
11501160 * a leading implicit parameter list. The reason is that if the implicit arguments are inferred,
@@ -1155,42 +1165,31 @@ object RefChecks {
11551165 * If the member does not have a leading implicit parameter list, then the argument cannot be explicitly
11561166 * supplied with `using`, as typechecking would fail. But the extension method may have leading implicit
11571167 * parameters, which are necessarily supplied implicitly in the application. The first non-implicit
1158- * parameters of the extension method must be distinguishable from the member parameters, as described.
1159- *
1160- * If the extension method is nullary, it is always hidden by a member of the same name.
1161- * (Either the member is nullary, or the reference is taken as the eta-expansion of the member.)
1162- *
1163- * This check is in lieu of a more expensive use-site check that an application failed to use an extension.
1164- * That check would account for accessibility and opacity. As a limitation, this check considers
1165- * only public members, a target receiver that is not an alias, and corresponding method parameters
1166- * that are either both opaque types or both not.
1168+ * parameters of the extension method must be distinguishable from the member parameters, as described above.
11671169 */
11681170 def checkExtensionMethods (sym : Symbol )(using Context ): Unit =
11691171 if sym.is(Extension ) then
11701172 extension (tp : Type )
11711173 def explicit = Applications .stripImplicit(tp.stripPoly, wildcardOnly = true )
11721174 def hasImplicitParams = tp.stripPoly match { case mt : MethodType => mt.isImplicitMethod case _ => false }
11731175 val explicitInfo = sym.info.explicit // consider explicit value params
1174- val target = explicitInfo.firstParamTypes.head.typeSymbol.info // required for extension method, the putative receiver
1176+ val target0 = explicitInfo.firstParamTypes.head // required for extension method, the putative receiver
1177+ val target = target0.dealiasKeepOpaques.typeSymbol.info
11751178 val methTp = explicitInfo.resultType // skip leading implicits and the "receiver" parameter
1179+ def memberMatchesMethod (member : Denotation ) =
1180+ val memberIsImplicit = member.info.hasImplicitParams
1181+ val paramTps =
1182+ if memberIsImplicit then methTp.stripPoly.firstParamTypes
1183+ else methTp.explicit.firstParamTypes
1184+ inline def paramsCorrespond =
1185+ val memberParamTps = member.info.stripPoly.firstParamTypes
1186+ memberParamTps.corresponds(paramTps): (m, x) =>
1187+ m.typeSymbol.denot.isOpaqueAlias == x.typeSymbol.denot.isOpaqueAlias && (x frozen_<:< m)
1188+ paramTps.isEmpty || memberIsImplicit && ! methTp.hasImplicitParams || paramsCorrespond
11761189 def hidden =
11771190 target.nonPrivateMember(sym.name)
11781191 .filterWithPredicate: member =>
1179- member.symbol.isPublic && {
1180- val memberIsImplicit = member.info.hasImplicitParams
1181- val paramTps =
1182- if memberIsImplicit then methTp.stripPoly.firstParamTypes
1183- else methTp.explicit.firstParamTypes
1184-
1185- paramTps.isEmpty || memberIsImplicit && ! methTp.hasImplicitParams || {
1186- val memberParamTps = member.info.stripPoly.firstParamTypes
1187- ! memberParamTps.isEmpty
1188- && memberParamTps.lengthCompare(paramTps) == 0
1189- && memberParamTps.lazyZip(paramTps).forall: (m, x) =>
1190- m.typeSymbol.denot.isOpaqueAlias == x.typeSymbol.denot.isOpaqueAlias
1191- && (x frozen_<:< m)
1192- }
1193- }
1192+ member.symbol.isPublic && memberMatchesMethod(member)
11941193 .exists
11951194 if sym.is(HasDefaultParams ) then
11961195 val getterDenot =
0 commit comments