@@ -1231,11 +1231,126 @@ private Function getTypeParameterMethod(TypeParameter tp, string name) {
1231
1231
result = getMethodSuccessor ( tp .( ImplTraitTypeTypeParameter ) .getImplTraitTypeRepr ( ) , name )
1232
1232
}
1233
1233
1234
+ bindingset [ t1, t2]
1235
+ private predicate typeMentionEqual ( TypeMention t1 , TypeMention t2 ) {
1236
+ forex ( TypePath path , Type type | t1 .resolveTypeAt ( path ) = type | t2 .resolveTypeAt ( path ) = type )
1237
+ }
1238
+
1239
+ pragma [ nomagic]
1240
+ private predicate implSiblingCandidate (
1241
+ Impl impl , TraitItemNode trait , Type rootType , TypeMention selfTy
1242
+ ) {
1243
+ trait = impl .( ImplItemNode ) .resolveTraitTy ( ) and
1244
+ not exists ( impl .getAttributeMacroExpansion ( ) ) and
1245
+ // We use this for resolving methods, so exclude traits that do not have methods.
1246
+ exists ( Function f | f = trait .getASuccessor ( _) and f .getParamList ( ) .hasSelfParam ( ) ) and
1247
+ selfTy = impl .getSelfTy ( ) and
1248
+ rootType = selfTy .resolveType ( )
1249
+ }
1250
+
1251
+ /**
1252
+ * Holds if `impl1` and `impl2` are a sibling implementations of `trait`. We
1253
+ * consider implementations to be siblings if they implement the same trait for
1254
+ * the same type. In that case `Self` is the same type in both implementations,
1255
+ * and method calls to the implementations cannot be resolved unambiguously
1256
+ * based only on the receiver type.
1257
+ */
1258
+ pragma [ inline]
1259
+ private predicate implSiblings ( TraitItemNode trait , Impl impl1 , Impl impl2 ) {
1260
+ exists ( Type rootType , TypeMention selfTy1 , TypeMention selfTy2 |
1261
+ impl1 != impl2 and
1262
+ implSiblingCandidate ( impl1 , trait , rootType , selfTy1 ) and
1263
+ implSiblingCandidate ( impl2 , trait , rootType , selfTy2 ) and
1264
+ // In principle the second conjunct below should be superflous, but we still
1265
+ // have ill-formed type mentions for types that we don't understand. For
1266
+ // those checking both directions restricts further. Note also that we check
1267
+ // syntactic equality, whereas equality up to renaming would be more
1268
+ // correct.
1269
+ typeMentionEqual ( selfTy1 , selfTy2 ) and
1270
+ typeMentionEqual ( selfTy2 , selfTy1 )
1271
+ )
1272
+ }
1273
+
1274
+ /**
1275
+ * Holds if `impl` is an implementation of `trait` and if another implementation
1276
+ * exists for the same type.
1277
+ */
1278
+ pragma [ nomagic]
1279
+ private predicate implHasSibling ( Impl impl , Trait trait ) { implSiblings ( trait , impl , _) }
1280
+
1281
+ /**
1282
+ * Holds if a type parameter of `trait` occurs in the method with the name
1283
+ * `methodName` at the `pos`th parameter at `path`.
1284
+ */
1285
+ bindingset [ trait]
1286
+ pragma [ inline_late]
1287
+ private predicate traitTypeParameterOccurrence (
1288
+ TraitItemNode trait , string methodName , int pos , TypePath path
1289
+ ) {
1290
+ exists ( Function f | f = trait .getASuccessor ( methodName ) |
1291
+ f .getParam ( pos ) .getTypeRepr ( ) .( TypeMention ) .resolveTypeAt ( path ) =
1292
+ trait .( TraitTypeAbstraction ) .getATypeParameter ( )
1293
+ )
1294
+ }
1295
+
1296
+ bindingset [ f, pos, path]
1297
+ pragma [ inline_late]
1298
+ private predicate methodTypeAtPath ( Function f , int pos , TypePath path , Type type ) {
1299
+ f .getParam ( pos ) .getTypeRepr ( ) .( TypeMention ) .resolveTypeAt ( path ) = type
1300
+ }
1301
+
1302
+ /**
1303
+ * Holds if resolving the method in `impl` with the name `methodName` requires
1304
+ * inspecting the types of applied _arguments_ in order to determine whether it
1305
+ * is the correct resolution.
1306
+ */
1307
+ private predicate methodResolutionDependsOnArgument (
1308
+ Impl impl , string methodName , int pos , TypePath path , Type type
1309
+ ) {
1310
+ /*
1311
+ * As seen in the example below, when an implementation has a sibling for a
1312
+ * trait we find occurrences of a type parameter of the trait in a method
1313
+ * signature in the trait. We then find the type given in the implementation
1314
+ * at the same position, which is a position that might disambiguate the
1315
+ * method from its siblings.
1316
+ *
1317
+ * ```rust
1318
+ * trait MyTrait<T> {
1319
+ * fn method(&self, value: Foo<T>) -> Self;
1320
+ * // ^^^^^^^^^^^^^ `pos` = 0
1321
+ * // ^ `path` = "T"
1322
+ * }
1323
+ * impl MyAdd<i64> for i64 {
1324
+ * fn method(&self, value: i64) -> Self { ... }
1325
+ * // ^^^ `type` = i64
1326
+ * }
1327
+ * ```
1328
+ *
1329
+ * Note that we only check the root type symbol at the position. If the type
1330
+ * at that position is a type constructor (for instance `Vec<..>`) then
1331
+ * inspecting the entire type tree could be necessary to disambiguate the
1332
+ * method. In that case we will still resolve several methods.
1333
+ */
1334
+
1335
+ exists ( TraitItemNode trait |
1336
+ implHasSibling ( impl , trait ) and
1337
+ traitTypeParameterOccurrence ( trait , methodName , pos , path ) and
1338
+ methodTypeAtPath ( getMethodSuccessor ( impl , methodName ) , pos , path , type )
1339
+ )
1340
+ }
1341
+
1234
1342
/** Gets a method from an `impl` block that matches the method call `mc`. */
1235
1343
private Function getMethodFromImpl ( MethodCall mc ) {
1236
1344
exists ( Impl impl |
1237
1345
IsInstantiationOf< MethodCall , IsInstantiationOfInput > :: isInstantiationOf ( mc , impl , _) and
1238
1346
result = getMethodSuccessor ( impl , mc .getMethodName ( ) )
1347
+ |
1348
+ not methodResolutionDependsOnArgument ( impl , _, _, _, _)
1349
+ or
1350
+ exists ( int pos , TypePath path , Type type |
1351
+ methodResolutionDependsOnArgument ( impl , mc .getMethodName ( ) , pos , path , type ) and
1352
+ inferType ( mc .getArgument ( pos ) , path ) = type
1353
+ )
1239
1354
)
1240
1355
}
1241
1356
0 commit comments