@@ -1137,14 +1137,24 @@ object RefChecks {
1137
1137
end checkUnaryMethods
1138
1138
1139
1139
/** 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.
1140
1143
*
1141
1144
* An extension method is hidden if it does not offer a parameter that is not subsumed
1142
1145
* by the corresponding parameter of the member with the same name (or of all alternatives of an overload).
1143
1146
*
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.)
1145
1149
*
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.
1148
1158
*
1149
1159
* If the member has a leading implicit parameter list, then the extension method must also have
1150
1160
* a leading implicit parameter list. The reason is that if the implicit arguments are inferred,
@@ -1155,42 +1165,31 @@ object RefChecks {
1155
1165
* If the member does not have a leading implicit parameter list, then the argument cannot be explicitly
1156
1166
* supplied with `using`, as typechecking would fail. But the extension method may have leading implicit
1157
1167
* 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.
1167
1169
*/
1168
1170
def checkExtensionMethods (sym : Symbol )(using Context ): Unit =
1169
1171
if sym.is(Extension ) then
1170
1172
extension (tp : Type )
1171
1173
def explicit = Applications .stripImplicit(tp.stripPoly, wildcardOnly = true )
1172
1174
def hasImplicitParams = tp.stripPoly match { case mt : MethodType => mt.isImplicitMethod case _ => false }
1173
1175
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
1175
1178
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
1176
1189
def hidden =
1177
1190
target.nonPrivateMember(sym.name)
1178
1191
.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)
1194
1193
.exists
1195
1194
if sym.is(HasDefaultParams ) then
1196
1195
val getterDenot =
0 commit comments