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