@@ -1033,14 +1033,24 @@ object RefChecks {
1033
1033
end checkUnaryMethods
1034
1034
1035
1035
/** Check that an extension method is not hidden, i.e., that it is callable as an extension method.
1036
+ *
1037
+ * For example, it is not possible to define a type-safe extension `contains` for `Set`,
1038
+ * since for any parameter type, the existing `contains` method will compile and would be used.
1036
1039
*
1037
1040
* An extension method is hidden if it does not offer a parameter that is not subsumed
1038
1041
* by the corresponding parameter of the member with the same name (or of all alternatives of an overload).
1039
1042
*
1040
- * This check is suppressed if this method is an override.
1043
+ * This check is suppressed if the method is an override. (Because the type of the receiver
1044
+ * may be narrower in the override.)
1041
1045
*
1042
- * For example, it is not possible to define a type-safe extension `contains` for `Set`,
1043
- * since for any parameter type, the existing `contains` method will compile and would be used.
1046
+ * If the extension method is nullary, it is always hidden by a member of the same name.
1047
+ * (Either the member is nullary, or the reference is taken as the eta-expansion of the member.)
1048
+ *
1049
+ * This check is in lieu of a more expensive use-site check that an application failed to use an extension.
1050
+ * That check would account for accessibility and opacity. As a limitation, this check considers
1051
+ * only public members for which corresponding method parameters are either both opaque types or both not.
1052
+ * It is intended to warn if the receiver type from a third-party library has been augmented with a member
1053
+ * that nullifies an existing extension.
1044
1054
*
1045
1055
* If the member has a leading implicit parameter list, then the extension method must also have
1046
1056
* a leading implicit parameter list. The reason is that if the implicit arguments are inferred,
@@ -1051,42 +1061,31 @@ object RefChecks {
1051
1061
* If the member does not have a leading implicit parameter list, then the argument cannot be explicitly
1052
1062
* supplied with `using`, as typechecking would fail. But the extension method may have leading implicit
1053
1063
* parameters, which are necessarily supplied implicitly in the application. The first non-implicit
1054
- * parameters of the extension method must be distinguishable from the member parameters, as described.
1055
- *
1056
- * If the extension method is nullary, it is always hidden by a member of the same name.
1057
- * (Either the member is nullary, or the reference is taken as the eta-expansion of the member.)
1058
- *
1059
- * This check is in lieu of a more expensive use-site check that an application failed to use an extension.
1060
- * That check would account for accessibility and opacity. As a limitation, this check considers
1061
- * only public members, a target receiver that is not an alias, and corresponding method parameters
1062
- * that are either both opaque types or both not.
1064
+ * parameters of the extension method must be distinguishable from the member parameters, as described above.
1063
1065
*/
1064
1066
def checkExtensionMethods (sym : Symbol )(using Context ): Unit =
1065
1067
if sym.is(Extension ) then
1066
1068
extension (tp : Type )
1067
1069
def explicit = Applications .stripImplicit(tp.stripPoly, wildcardOnly = true )
1068
1070
def hasImplicitParams = tp.stripPoly match { case mt : MethodType => mt.isImplicitMethod case _ => false }
1069
1071
val explicitInfo = sym.info.explicit // consider explicit value params
1070
- val target = explicitInfo.firstParamTypes.head.typeSymbol.info // required for extension method, the putative receiver
1072
+ val target0 = explicitInfo.firstParamTypes.head // required for extension method, the putative receiver
1073
+ val target = target0.dealiasKeepOpaques.typeSymbol.info
1071
1074
val methTp = explicitInfo.resultType // skip leading implicits and the "receiver" parameter
1075
+ def memberMatchesMethod (member : Denotation ) =
1076
+ val memberIsImplicit = member.info.hasImplicitParams
1077
+ val paramTps =
1078
+ if memberIsImplicit then methTp.stripPoly.firstParamTypes
1079
+ else methTp.explicit.firstParamTypes
1080
+ inline def paramsCorrespond =
1081
+ val memberParamTps = member.info.stripPoly.firstParamTypes
1082
+ memberParamTps.corresponds(paramTps): (m, x) =>
1083
+ m.typeSymbol.denot.isOpaqueAlias == x.typeSymbol.denot.isOpaqueAlias && (x frozen_<:< m)
1084
+ paramTps.isEmpty || memberIsImplicit && ! methTp.hasImplicitParams || paramsCorrespond
1072
1085
def hidden =
1073
1086
target.nonPrivateMember(sym.name)
1074
1087
.filterWithPredicate: member =>
1075
- member.symbol.isPublic && {
1076
- val memberIsImplicit = member.info.hasImplicitParams
1077
- val paramTps =
1078
- if memberIsImplicit then methTp.stripPoly.firstParamTypes
1079
- else methTp.explicit.firstParamTypes
1080
-
1081
- paramTps.isEmpty || memberIsImplicit && ! methTp.hasImplicitParams || {
1082
- val memberParamTps = member.info.stripPoly.firstParamTypes
1083
- ! memberParamTps.isEmpty
1084
- && memberParamTps.lengthCompare(paramTps) == 0
1085
- && memberParamTps.lazyZip(paramTps).forall: (m, x) =>
1086
- m.typeSymbol.denot.isOpaqueAlias == x.typeSymbol.denot.isOpaqueAlias
1087
- && (x frozen_<:< m)
1088
- }
1089
- }
1088
+ member.symbol.isPublic && memberMatchesMethod(member)
1090
1089
.exists
1091
1090
if sym.is(HasDefaultParams ) then
1092
1091
val getterDenot =
0 commit comments