Merge pull request #21217 from hvitved/rust/type-inference-perf

Rust: Rework call disambiguation logic
This commit is contained in:
Tom Hvitved
2026-02-10 08:52:01 +01:00
committed by GitHub
13 changed files with 9238 additions and 8476 deletions

View File

@@ -77,32 +77,23 @@ pragma[nomagic]
private predicate implHasSibling(ImplItemNode impl, Trait trait) { implSiblings(trait, impl, _) }
/**
* Holds if type parameter `tp` of `trait` occurs in the function `f` with the name
* `functionName` at position `pos` and path `path`.
*
* Note that `pos` can also be the special `return` position, which is sometimes
* needed to disambiguate associated function calls like `Default::default()`
* (in this case, `tp` is the special `Self` type parameter).
*/
bindingset[trait]
pragma[inline_late]
predicate traitTypeParameterOccurrence(
TraitItemNode trait, Function f, string functionName, FunctionPosition pos, TypePath path,
TypeParameter tp
) {
f = trait.getASuccessor(functionName) and
tp = getAssocFunctionTypeAt(f, trait, pos, path) and
tp = trait.(TraitTypeAbstraction).getATypeParameter()
}
/**
* Holds if resolving the function `f` in `impl` with the name `functionName`
* requires inspecting the type of applied _arguments_ at position `pos` in
* order to determine whether it is the correct resolution.
* Holds if `f` is a function declared inside `trait`, and the type of `f` at
* `pos` and `path` is `traitTp`, which is a type parameter of `trait`.
*/
pragma[nomagic]
predicate functionResolutionDependsOnArgument(
ImplItemNode impl, Function f, FunctionPosition pos, TypePath path, Type type
predicate traitTypeParameterOccurrence(
TraitItemNode trait, Function f, string functionName, FunctionPosition pos, TypePath path,
TypeParameter traitTp
) {
f = trait.getAssocItem(functionName) and
traitTp = getAssocFunctionTypeInclNonMethodSelfAt(f, trait, pos, path) and
traitTp = trait.(TraitTypeAbstraction).getATypeParameter()
}
pragma[nomagic]
private predicate functionResolutionDependsOnArgumentCand(
ImplItemNode impl, Function f, string functionName, TypeParameter traitTp, FunctionPosition pos,
TypePath path
) {
/*
* As seen in the example below, when an implementation has a sibling for a
@@ -129,11 +120,114 @@ predicate functionResolutionDependsOnArgument(
* method. In that case we will still resolve several methods.
*/
exists(TraitItemNode trait, string functionName |
exists(TraitItemNode trait |
implHasSibling(impl, trait) and
traitTypeParameterOccurrence(trait, _, functionName, pos, path, _) and
type = getAssocFunctionTypeAt(f, impl, pos, path) and
traitTypeParameterOccurrence(trait, _, functionName, pos, path, traitTp) and
f = impl.getASuccessor(functionName) and
not pos.isSelf()
)
}
private predicate functionResolutionDependsOnPositionalArgumentCand(
ImplItemNode impl, Function f, string functionName, TypeParameter traitTp
) {
exists(FunctionPosition pos |
functionResolutionDependsOnArgumentCand(impl, f, functionName, traitTp, pos, _) and
pos.isPosition()
)
}
pragma[nomagic]
private Type getAssocFunctionNonTypeParameterTypeAt(
ImplItemNode impl, Function f, FunctionPosition pos, TypePath path
) {
result = getAssocFunctionTypeInclNonMethodSelfAt(f, impl, pos, path) and
not result instanceof TypeParameter
}
/**
* Holds if `f` inside `impl` has a sibling implementation inside `sibling`, where
* those two implementations agree on the instantiation of `traitTp`, which occurs
* in a positional position inside `f`.
*/
pragma[nomagic]
private predicate hasEquivalentPositionalSibling(
ImplItemNode impl, ImplItemNode sibling, Function f, TypeParameter traitTp
) {
exists(string functionName, FunctionPosition pos, TypePath path |
functionResolutionDependsOnArgumentCand(impl, f, functionName, traitTp, pos, path) and
pos.isPosition()
|
exists(Function f1 |
implSiblings(_, impl, sibling) and
f1 = sibling.getASuccessor(functionName)
|
forall(TypePath path0, Type t |
t = getAssocFunctionNonTypeParameterTypeAt(impl, f, pos, path0) and
path = path0.getAPrefix()
|
t = getAssocFunctionNonTypeParameterTypeAt(sibling, f1, pos, path0)
) and
forall(TypePath path0, Type t |
t = getAssocFunctionNonTypeParameterTypeAt(sibling, f1, pos, path0) and
path = path0.getAPrefix()
|
t = getAssocFunctionNonTypeParameterTypeAt(impl, f, pos, path0)
)
)
)
}
/**
* Holds if resolving the function `f` in `impl` requires inspecting the type
* of applied _arguments_ or possibly knowing the return type.
*
* `traitTp` is a type parameter of the trait being implemented by `impl`, and
* we need to check that the type of `f` corresponding to `traitTp` is satisfied
* at any one of the positions `pos` in which that type occurs in `f`.
*
* Type parameters that only occur in return positions are only included when
* all other type parameters that occur in a positional position are insufficient
* to disambiguate.
*
* Example:
*
* ```rust
* trait Trait1<T1> {
* fn f(self, x: T1) -> T1;
* }
*
* impl Trait1<i32> for i32 {
* fn f(self, x: i32) -> i32 { 0 } // f1
* }
*
* impl Trait1<i64> for i32 {
* fn f(self, x: i64) -> i64 { 0 } // f2
* }
* ```
*
* The type for `T1` above occurs in both a positional position and a return position
* in `f`, so both may be used to disambiguate between `f1` and `f2`. That is, `f(0i32)`
* is sufficient to resolve to `f1`, and so is `let y: i64 = f(Default::default())`.
*/
pragma[nomagic]
predicate functionResolutionDependsOnArgument(
ImplItemNode impl, Function f, TypeParameter traitTp, FunctionPosition pos
) {
exists(string functionName |
functionResolutionDependsOnArgumentCand(impl, f, functionName, traitTp, pos, _)
|
if functionResolutionDependsOnPositionalArgumentCand(impl, f, functionName, traitTp)
then any()
else
// `traitTp` only occurs in return position; check that it is indeed needed for disambiguation
exists(ImplItemNode sibling |
implSiblings(_, impl, sibling) and
forall(TypeParameter otherTraitTp |
functionResolutionDependsOnPositionalArgumentCand(impl, f, functionName, otherTraitTp)
|
hasEquivalentPositionalSibling(impl, sibling, f, otherTraitTp)
)
)
)
}

View File

@@ -82,7 +82,9 @@ private newtype TAssocFunctionType =
// through `i`. This ensures that `parent` is either a supertrait of `i` or
// `i` in an `impl` block implementing `parent`.
(parent = i or BaseTypes::rootTypesSatisfaction(_, TTrait(parent), i, _, _)) and
exists(pos.getTypeMention(f))
// We always include the `self` position, even for non-methods, where it is used
// to match type qualifiers against the `impl` or trait type, such as in `Vec::new`.
(exists(pos.getTypeMention(f)) or pos.isSelf())
}
bindingset[abs, constraint, tp]
@@ -116,6 +118,22 @@ Type getAssocFunctionTypeAt(Function f, ImplOrTraitItemNode i, FunctionPosition
)
}
/**
* Same as `getAssocFunctionTypeAt`, but also includes types at the `self` position
* for non-methods.
*/
pragma[nomagic]
Type getAssocFunctionTypeInclNonMethodSelfAt(
Function f, ImplOrTraitItemNode i, FunctionPosition pos, TypePath path
) {
result = getAssocFunctionTypeAt(f, i, pos, path)
or
f = i.getASuccessor(_) and
not f.hasSelfParam() and
pos.isSelf() and
result = resolveImplOrTraitType(i, path)
}
/**
* The type of an associated function at a given position, when its implicit
* `Self` type parameter is specialized to a given trait or `impl` block.
@@ -174,7 +192,7 @@ class AssocFunctionType extends MkAssocFunctionType {
Type getTypeAt(TypePath path) {
exists(Function f, FunctionPosition pos, ImplOrTraitItemNode i, Type t |
this.appliesTo(f, i, pos) and
t = getAssocFunctionTypeAt(f, i, pos, path)
t = getAssocFunctionTypeInclNonMethodSelfAt(f, i, pos, path)
|
not t instanceof SelfTypeParameter and
result = t
@@ -183,16 +201,19 @@ class AssocFunctionType extends MkAssocFunctionType {
)
}
private TypeMention getTypeMention() {
exists(Function f, FunctionPosition pos |
this.appliesTo(f, _, pos) and
private AstNode getIdentifyingNode() {
exists(Function f, ImplOrTraitItemNode i, FunctionPosition pos | this.appliesTo(f, i, pos) |
result = pos.getTypeMention(f)
or
pos.isSelf() and
not f.hasSelfParam() and
result = [i.(Impl).getSelfTy().(AstNode), i.(Trait).getName()]
)
}
string toString() { result = this.getTypeMention().toString() }
string toString() { result = this.getIdentifyingNode().toString() }
Location getLocation() { result = this.getTypeMention().getLocation() }
Location getLocation() { result = this.getIdentifyingNode().getLocation() }
}
pragma[nomagic]
@@ -294,10 +315,15 @@ module ArgIsInstantiationOf<
*/
signature module ArgsAreInstantiationsOfInputSig {
/**
* Holds if types need to be matched against the type `t` at position `pos` of
* `f` inside `i`.
* Holds if `f` inside `i` needs to have the type corresponding to type parameter
* `tp` checked.
*
* If `i` is an inherent implementation, `tp` is a type parameter of the type being
* implemented, otherwise `tp` is a type parameter of the trait (being implemented).
*
* `pos` is one of the positions in `f` in which the relevant type occours.
*/
predicate toCheck(ImplOrTraitItemNode i, Function f, FunctionPosition pos, AssocFunctionType t);
predicate toCheck(ImplOrTraitItemNode i, Function f, TypeParameter tp, FunctionPosition pos);
/** A call whose argument types are to be checked. */
class Call {
@@ -318,23 +344,27 @@ signature module ArgsAreInstantiationsOfInputSig {
*/
module ArgsAreInstantiationsOf<ArgsAreInstantiationsOfInputSig Input> {
pragma[nomagic]
private predicate toCheckRanked(ImplOrTraitItemNode i, Function f, FunctionPosition pos, int rnk) {
Input::toCheck(i, f, pos, _) and
pos =
rank[rnk + 1](FunctionPosition pos0, int j |
Input::toCheck(i, f, pos0, _) and
(
j = pos0.asPosition()
or
pos0.isSelf() and j = -1
or
pos0.isReturn() and j = -2
)
private predicate toCheckRanked(
ImplOrTraitItemNode i, Function f, TypeParameter tp, FunctionPosition pos, int rnk
) {
Input::toCheck(i, f, tp, pos) and
tp =
rank[rnk + 1](TypeParameter tp0, int j |
Input::toCheck(i, f, tp0, _) and
j = getTypeParameterId(tp0)
|
pos0 order by j
tp0 order by j
)
}
pragma[nomagic]
private predicate toCheck(
ImplOrTraitItemNode i, Function f, TypeParameter tp, FunctionPosition pos, AssocFunctionType t
) {
Input::toCheck(i, f, tp, pos) and
t.appliesTo(f, i, pos)
}
private newtype TCallAndPos =
MkCallAndPos(Input::Call call, FunctionPosition pos) { exists(call.getArgType(pos, _)) }
@@ -356,26 +386,26 @@ module ArgsAreInstantiationsOf<ArgsAreInstantiationsOfInputSig Input> {
string toString() { result = call.toString() + " [arg " + pos + "]" }
}
pragma[nomagic]
private predicate potentialInstantiationOf0(
CallAndPos cp, Input::Call call, TypeParameter tp, FunctionPosition pos, Function f,
TypeAbstraction abs, AssocFunctionType constraint
) {
cp = MkCallAndPos(call, pragma[only_bind_into](pos)) and
call.hasTargetCand(abs, f) and
toCheck(abs, f, tp, pragma[only_bind_into](pos), constraint)
}
private module ArgIsInstantiationOfToIndexInput implements
IsInstantiationOfInputSig<CallAndPos, AssocFunctionType>
{
pragma[nomagic]
private predicate potentialInstantiationOf0(
CallAndPos cp, Input::Call call, FunctionPosition pos, int rnk, Function f,
TypeAbstraction abs, AssocFunctionType constraint
) {
cp = MkCallAndPos(call, pragma[only_bind_into](pos)) and
call.hasTargetCand(abs, f) and
toCheckRanked(abs, f, pragma[only_bind_into](pos), rnk) and
Input::toCheck(abs, f, pragma[only_bind_into](pos), constraint)
}
pragma[nomagic]
predicate potentialInstantiationOf(
CallAndPos cp, TypeAbstraction abs, AssocFunctionType constraint
) {
exists(Input::Call call, int rnk, Function f |
potentialInstantiationOf0(cp, call, _, rnk, f, abs, constraint)
exists(Input::Call call, TypeParameter tp, FunctionPosition pos, int rnk, Function f |
potentialInstantiationOf0(cp, call, tp, pos, f, abs, constraint) and
toCheckRanked(abs, f, tp, pos, rnk)
|
rnk = 0
or
@@ -383,9 +413,7 @@ module ArgsAreInstantiationsOf<ArgsAreInstantiationsOfInputSig Input> {
)
}
predicate relevantConstraint(AssocFunctionType constraint) {
Input::toCheck(_, _, _, constraint)
}
predicate relevantConstraint(AssocFunctionType constraint) { toCheck(_, _, _, _, constraint) }
}
private module ArgIsInstantiationOfToIndex =
@@ -398,39 +426,63 @@ module ArgsAreInstantiationsOf<ArgsAreInstantiationsOfInputSig Input> {
exists(FunctionPosition pos |
ArgIsInstantiationOfToIndex::argIsInstantiationOf(MkCallAndPos(call, pos), i, _) and
call.hasTargetCand(i, f) and
toCheckRanked(i, f, pos, rnk)
toCheckRanked(i, f, _, pos, rnk)
|
rnk = 0
or
argsAreInstantiationsOfToIndex(call, i, f, rnk - 1)
)
}
/**
* Holds if all arguments of `call` have types that are instantiations of the
* types of the corresponding parameters of `f` inside `i`.
*
* TODO: Check type parameter constraints as well.
*/
pragma[nomagic]
predicate argsAreInstantiationsOf(Input::Call call, ImplOrTraitItemNode i, Function f) {
exists(int rnk |
argsAreInstantiationsOfToIndex(call, i, f, rnk) and
rnk = max(int r | toCheckRanked(i, f, _, r))
rnk = max(int r | toCheckRanked(i, f, _, _, r))
)
}
private module ArgsAreNotInstantiationOfInput implements
IsInstantiationOfInputSig<CallAndPos, AssocFunctionType>
{
pragma[nomagic]
predicate potentialInstantiationOf(
CallAndPos cp, TypeAbstraction abs, AssocFunctionType constraint
) {
potentialInstantiationOf0(cp, _, _, _, _, abs, constraint)
}
predicate relevantConstraint(AssocFunctionType constraint) { toCheck(_, _, _, _, constraint) }
}
private module ArgsAreNotInstantiationOf =
ArgIsInstantiationOf<CallAndPos, ArgsAreNotInstantiationOfInput>;
pragma[nomagic]
private predicate argsAreNotInstantiationsOf0(
Input::Call call, FunctionPosition pos, ImplOrTraitItemNode i
) {
ArgIsInstantiationOfToIndex::argIsNotInstantiationOf(MkCallAndPos(call, pos), i, _, _)
ArgsAreNotInstantiationOf::argIsNotInstantiationOf(MkCallAndPos(call, pos), i, _, _)
}
/**
* Holds if _some_ argument of `call` has a type that is not an instantiation of the
* type of the corresponding parameter of `f` inside `i`.
*
* TODO: Check type parameter constraints as well.
*/
pragma[nomagic]
predicate argsAreNotInstantiationsOf(Input::Call call, ImplOrTraitItemNode i, Function f) {
exists(FunctionPosition pos |
argsAreNotInstantiationsOf0(call, pos, i) and
call.hasTargetCand(i, f) and
Input::toCheck(i, f, pos, _)
Input::toCheck(i, f, _, pos)
)
}
}

View File

@@ -30,7 +30,7 @@ private newtype TTypeArgumentPosition =
} or
TTypeParamTypeArgumentPosition(TypeParam tp)
private module Input1 implements InputSig1<Location> {
private module Input implements InputSig1<Location>, InputSig2<PreTypeMention> {
private import Type as T
private import codeql.rust.elements.internal.generated.Raw
private import codeql.rust.elements.internal.generated.Synth
@@ -120,21 +120,7 @@ private module Input1 implements InputSig1<Location> {
}
int getTypePathLimit() { result = 10 }
}
private import Input1
private module M1 = Make1<Location, Input1>;
import M1
predicate getTypePathLimit = Input1::getTypePathLimit/0;
class TypePath = M1::TypePath;
module TypePath = M1::TypePath;
private module Input2 implements InputSig2<PreTypeMention> {
PreTypeMention getABaseTypeMention(Type t) { none() }
Type getATypeParameterConstraint(TypeParameter tp, TypePath path) {
@@ -208,7 +194,21 @@ private module Input2 implements InputSig2<PreTypeMention> {
}
}
private module M2 = Make2<PreTypeMention, Input2>;
private import Input
private module M1 = Make1<Location, Input>;
import M1
predicate getTypePathLimit = Input::getTypePathLimit/0;
predicate getTypeParameterId = Input::getTypeParameterId/1;
class TypePath = M1::TypePath;
module TypePath = M1::TypePath;
private module M2 = Make2<PreTypeMention, Input>;
import M2
@@ -286,11 +286,13 @@ private class FunctionDeclaration extends Function {
}
pragma[nomagic]
Type getParameterType(ImplOrTraitItemNodeOption i, FunctionPosition pos, TypePath path) {
Type getParameterTypeInclNonMethodSelf(
ImplOrTraitItemNodeOption i, FunctionPosition pos, TypePath path
) {
i = parent and
(
not pos.isReturn() and
result = getAssocFunctionTypeAt(this, i.asSome(), pos, path)
result = getAssocFunctionTypeInclNonMethodSelfAt(this, i.asSome(), pos, path)
or
i.isNone() and
result = this.getParam(pos.asPosition()).getTypeRepr().(TypeMention).getTypeAt(path)
@@ -322,13 +324,6 @@ private class FunctionDeclaration extends Function {
else result = this.resolveRetType(i, path)
}
Type getDeclaredType(ImplOrTraitItemNodeOption i, FunctionPosition pos, TypePath path) {
result = this.getParameterType(i, pos, path)
or
pos.isReturn() and
result = this.getReturnType(i, path)
}
string toStringExt(ImplOrTraitItemNode i) {
i = parent.asSome() and
if this = i.getAnAssocItem()
@@ -1058,14 +1053,29 @@ private Path getCallExprPathQualifier(CallExpr ce) {
* Gets the type qualifier of function call `ce`, if any.
*
* For example, the type qualifier of `Foo::<i32>::default()` is `Foo::<i32>`,
* but only when `Foo` is not a trait.
* but only when `Foo` is not a trait. The type qualifier of `<Foo as Bar>::baz()`
* is `Foo`.
*
* `isDefaultTypeArg` indicates whether the returned type is a default type
* argument, for example in `Vec::new()` the default type for the type parameter
* `A` of `Vec` is `Global`.
*/
pragma[nomagic]
private Type getCallExprTypeQualifier(CallExpr ce, TypePath path) {
exists(TypeMention tm |
tm = getCallExprPathQualifier(ce) and
private Type getCallExprTypeQualifier(CallExpr ce, TypePath path, boolean isDefaultTypeArg) {
exists(Path p, TypeMention tm |
p = getCallExprPathQualifier(ce) and
tm = [p.(AstNode), p.getSegment().getTypeRepr()]
|
result = tm.getTypeAt(path) and
not resolvePath(tm) instanceof Trait
not resolvePath(tm) instanceof Trait and
isDefaultTypeArg = false
or
exists(TypeParameter tp, TypePath suffix |
result =
tm.(NonAliasPathTypeMention).getDefaultTypeForTypeParameterInNonAnnotationAt(tp, suffix) and
path = TypePath::cons(tp, suffix) and
isDefaultTypeArg = true
)
)
}
@@ -1148,7 +1158,7 @@ private module ContextTyping {
) and
not (
tp instanceof TSelfTypeParameter and
exists(getCallExprTypeQualifier(this, _))
exists(getCallExprTypeQualifier(this, _, _))
)
)
}
@@ -1172,15 +1182,10 @@ private module ContextTyping {
*/
module CheckContextTyping<inferCallTypeSig/3 inferCallType> {
pragma[nomagic]
private Type inferCallTypeFromContextCand(AstNode n, TypePath path, TypePath prefix) {
private Type inferCallTypeFromContextCand(AstNode n, TypePath prefix, TypePath path) {
result = inferCallType(n, false, path) and
hasUnknownType(n) and
prefix = path
or
exists(TypePath mid |
result = inferCallTypeFromContextCand(n, path, mid) and
mid.isSnoc(prefix, _)
)
prefix = path.getAPrefix()
}
pragma[nomagic]
@@ -1188,7 +1193,7 @@ private module ContextTyping {
result = inferCallType(n, true, path)
or
exists(TypePath prefix |
result = inferCallTypeFromContextCand(n, path, prefix) and
result = inferCallTypeFromContextCand(n, prefix, path) and
hasUnknownTypeAt(n, prefix)
)
}
@@ -1285,13 +1290,6 @@ private class BorrowKind extends TBorrowKind {
}
}
// for now, we do not handle ambiguous targets when one of the types is itself
// a constrained type parameter; we should be checking the constraints in this case
private predicate typeCanBeUsedForDisambiguation(Type t) {
not t instanceof TypeParameter or
t.(TypeParamTypeParameter).getTypeParam() = any(TypeParam tp | not tp.hasTypeBound())
}
/**
* Provides logic for resolving calls to methods.
*
@@ -1422,9 +1420,7 @@ private module MethodResolution {
private module MethodTraitIsVisible = TraitIsVisible<methodCallTraitCandidate/2>;
private predicate methodCallVisibleTraitCandidate(MethodCall mc, Trait trait) {
MethodTraitIsVisible::traitIsVisible(mc, trait)
}
private predicate methodCallVisibleTraitCandidate = MethodTraitIsVisible::traitIsVisible/2;
bindingset[mc, impl]
pragma[inline_late]
@@ -1949,9 +1945,23 @@ private module MethodResolution {
forall(ItemNode i | i = CallExprImpl::getResolvedFunction(this) | i instanceof Method)
}
bindingset[this, f]
pragma[inline_late]
private predicate hasTypeQualifiedCandidateFilter(Function f, ImplItemNode impl) {
f = impl.getAnAssocItem()
or
exists(TraitItemNode trait |
f = trait.getAnAssocItem() and
methodCallVisibleTraitCandidate(this, trait) and
impl.resolveTraitTy() = trait
)
}
/**
* Holds if this call has a type qualifier, and we are able to resolve,
* using path resolution, the method to a member of `impl`.
* using path resolution, the method to a member of `impl` or the trait
* being implemented by `impl` (when this call os of the kind
* `<Foo as Bar>::f()`).
*
* When this is the case, we still want to check that the type qualifier
* is an instance of the type being implemented, which is done in
@@ -1959,8 +1969,11 @@ private module MethodResolution {
*/
pragma[nomagic]
predicate hasTypeQualifiedCandidate(ImplItemNode impl) {
exists(getCallExprTypeQualifier(this, _)) and
CallExprImpl::getResolvedFunction(this) = impl.getADescendant()
exists(Function f |
exists(getCallExprTypeQualifier(this, _, _)) and
f = CallExprImpl::getResolvedFunction(this) and
this.hasTypeQualifiedCandidateFilter(f, impl)
)
}
pragma[nomagic]
@@ -1978,7 +1991,13 @@ private module MethodResolution {
// needed for `TypeQualifierIsInstantiationOfImplSelfInput`
Type getTypeAt(TypePath path) {
result = substituteLookupTraits(getCallExprTypeQualifier(this, path))
result = substituteLookupTraits(getCallExprTypeQualifier(this, path, _))
}
pragma[nomagic]
predicate hasNoInherentTarget() {
// `_` is fine below, because auto-deref/borrow is not supported
MkMethodCallCand(this, _, _).(MethodCallCand).hasNoInherentTarget()
}
override predicate supportsAutoDerefAndBorrow() { none() }
@@ -2109,6 +2128,8 @@ private module MethodResolution {
*/
pragma[nomagic]
predicate hasNoInherentTarget() {
mc_.hasTrait()
or
exists(TypePath strippedTypePath, Type strippedType, string name, int arity |
this.hasSignature(_, strippedTypePath, strippedType, name, arity) and
forall(Impl i |
@@ -2121,7 +2142,7 @@ private module MethodResolution {
}
pragma[nomagic]
private predicate typeQualifierIsInstantiationOf(ImplOrTraitItemNode i) {
private predicate typeQualifierIsInstantiationOf(ImplItemNode i) {
TypeQualifierIsInstantiationOfImplSelf::isInstantiationOf(mc_, i, _)
}
@@ -2147,7 +2168,7 @@ private module MethodResolution {
pragma[nomagic]
Method resolveCallTarget(ImplOrTraitItemNode i) {
result = this.resolveCallTargetCand(i) and
not FunctionOverloading::functionResolutionDependsOnArgument(i, _, _, _, _)
not FunctionOverloading::functionResolutionDependsOnArgument(i, result, _, _)
or
MethodArgsAreInstantiationsOf::argsAreInstantiationsOf(this, i, result)
}
@@ -2345,7 +2366,7 @@ private module MethodResolution {
then
// inherent methods take precedence over trait methods, so only allow
// trait methods when there are no matching inherent methods
MkMethodCallCand(ce, _, _).(MethodCallCand).hasNoInherentTarget()
ce.hasNoInherentTarget()
else any()
}
@@ -2381,16 +2402,15 @@ private module MethodResolution {
* types of parameters, when needed to disambiguate the call.
*/
private module MethodArgsAreInstantiationsOfInput implements ArgsAreInstantiationsOfInputSig {
predicate toCheck(ImplOrTraitItemNode i, Function f, FunctionPosition pos, AssocFunctionType t) {
exists(TypePath path, Type t0 |
FunctionOverloading::functionResolutionDependsOnArgument(i, f, pos, path, t0) and
t.appliesTo(f, i, pos) and
typeCanBeUsedForDisambiguation(t0)
)
predicate toCheck(ImplOrTraitItemNode i, Function f, TypeParameter traitTp, FunctionPosition pos) {
FunctionOverloading::functionResolutionDependsOnArgument(i, f, traitTp, pos)
}
class Call extends MethodCallCand {
Type getArgType(FunctionPosition pos, TypePath path) {
result = mc_.getArgumentTypeAt(pos.asArgumentPosition(), path)
or
pos.isReturn() and
result = inferType(mc_.getNodeAt(pos), path)
}
@@ -2435,7 +2455,10 @@ private module MethodCallMatchingInput implements MatchingWithEnvironmentInputSi
}
Type getDeclaredType(DeclarationPosition dpos, TypePath path) {
result = m.getDeclaredType(someParent, dpos, path)
result = m.getParameterTypeInclNonMethodSelf(someParent, dpos, path)
or
dpos.isReturn() and
result = m.getReturnType(someParent, path)
}
string toString() { result = m.toStringExt(parent) }
@@ -2647,38 +2670,46 @@ private predicate inferMethodCallType =
*/
private module NonMethodResolution {
/**
* Holds if the associated function `implFunction` at `impl` implements
* `traitFunction`, which belongs to `trait`, and resolving the function
* `implFunction` requires inspecting the type at position `pos` in order
* to determine whether it is the correct resolution.
* Holds if resolving the function `implFunction` in `impl` requires inspecting
* the type of applied _arguments_ or possibly knowing the return type.
*
* `type` is the type at `pos` of `implFunction` which mathces a type parameter of
* `traitFunction` at `pos`.
* `traitTp` is a type parameter of the trait being implemented by `impl`, and
* we need to check that the type of `f` corresponding to `traitTp` is satisfied
* at any one of the positions `pos` in which that type occurs in `f` (at `path`).
*
* As for method resolution, we always check the type being implemented (corresponding
* to `traitTp` being the special `Self` type parameter).
*/
pragma[nomagic]
private predicate traitFunctionDependsOnPos(
TraitItemNode trait, NonMethodFunction traitFunction, FunctionPosition pos, Type type,
ImplItemNode impl, NonMethodFunction implFunction
private predicate traitFunctionResolutionDependsOnArgument(
TraitItemNode trait, NonMethodFunction traitFunction, FunctionPosition pos, ImplItemNode impl,
NonMethodFunction implFunction, TypePath path, TypeParameter traitTp
) {
exists(TypePath path |
type = getAssocFunctionTypeAt(implFunction, impl, pos, path) and
implFunction.implements(traitFunction) and
FunctionOverloading::traitTypeParameterOccurrence(trait, traitFunction, _, pos, path, _)
|
if pos.isReturn()
then
// We only check that the context of the call provides relevant type information
// when no argument can
not exists(FunctionPosition pos0 |
FunctionOverloading::traitTypeParameterOccurrence(trait, traitFunction, _, pos0, _, _) and
not pos0.isReturn()
or
FunctionOverloading::functionResolutionDependsOnArgument(impl, implFunction, pos0, _, _)
)
else any()
implFunction = impl.getAnAssocItem() and
implFunction.implements(traitFunction) and
FunctionOverloading::traitTypeParameterOccurrence(trait, traitFunction, _, pos, path, traitTp) and
(
traitTp = TSelfTypeParameter(trait)
or
FunctionOverloading::functionResolutionDependsOnArgument(impl, implFunction, traitTp, pos)
)
}
pragma[nomagic]
private predicate functionResolutionDependsOnArgument(
ImplItemNode impl, NonMethodFunction f, FunctionPosition pos, TypeParameter tp
) {
traitFunctionResolutionDependsOnArgument(_, _, pos, impl, f, _, tp)
or
// For inherent implementations of generic types, we also need to check the type being
// implemented. We arbitrarily choose the first type parameter of the type being implemented
// to represent this case.
f = impl.getAnAssocItem() and
not impl.(Impl).hasTrait() and
tp = TTypeParamTypeParameter(impl.resolveSelfTy().getTypeParam(0)) and
pos.isSelf()
}
pragma[nomagic]
private predicate functionInfoBlanketLikeRelevantPos(
NonMethodFunction f, string name, int arity, ImplItemNode impl, Trait trait,
@@ -2740,6 +2771,16 @@ private module NonMethodResolution {
not result.(Function).hasSelfParam()
}
/**
* Gets the associated function that this function call resolves to using path
* resolution, if any.
*/
pragma[nomagic]
NonMethodFunction getPathResolutionResolved(ImplOrTraitItemNode i) {
result = this.getPathResolutionResolved() and
result = i.getAnAssocItem()
}
/**
* Gets the blanket function that this call may resolve to, if any.
*/
@@ -2758,14 +2799,6 @@ private module NonMethodResolution {
/** Holds if this call targets a trait. */
predicate hasTrait() { exists(this.getTrait()) }
pragma[nomagic]
NonMethodFunction resolveCallTargetNonBlanketCand(ImplItemNode i) {
not this.hasTrait() and
result = this.getPathResolutionResolved() and
result = i.getASuccessor(_) and
FunctionOverloading::functionResolutionDependsOnArgument(_, result, _, _, _)
}
AstNode getNodeAt(FunctionPosition pos) {
result = this.getSyntacticArgument(pos.asArgumentPosition())
or
@@ -2777,7 +2810,15 @@ private module NonMethodResolution {
}
pragma[nomagic]
predicate resolveCallTargetBlanketLikeCandidate(
NonMethodFunction resolveCallTargetNonBlanketCand(ImplItemNode i) {
not this.hasTrait() and
result = this.getPathResolutionResolved(i) and
not exists(this.resolveCallTargetViaPathResolution()) and
functionResolutionDependsOnArgument(i, result, _, _)
}
pragma[nomagic]
predicate resolveCallTargetBlanketLikeCand(
ImplItemNode impl, FunctionPosition pos, TypePath blanketPath, TypeParam blanketTypeParam
) {
exists(string name, int arity, Trait trait, AssocFunctionType t |
@@ -2786,6 +2827,10 @@ private module NonMethodResolution {
functionInfoBlanketLikeRelevantPos(_, name, arity, impl, trait, pos, t, blanketPath,
blanketTypeParam) and
BlanketTraitIsVisible::traitIsVisible(this, trait)
|
not this.hasTrait()
or
trait = this.getTrait()
)
}
@@ -2801,7 +2846,7 @@ private module NonMethodResolution {
*/
pragma[nomagic]
predicate hasNoCompatibleNonBlanketTarget() {
this.resolveCallTargetBlanketLikeCandidate(_, _, _, _) and
this.resolveCallTargetBlanketLikeCand(_, _, _, _) and
not exists(this.resolveCallTargetViaPathResolution()) and
forall(ImplOrTraitItemNode i, Function f |
this.(NonMethodArgsAreInstantiationsOfNonBlanketInput::Call).hasTargetCand(i, f)
@@ -2817,7 +2862,7 @@ private module NonMethodResolution {
ItemNode resolveCallTargetViaPathResolution() {
not this.hasTrait() and
result = this.getPathResolutionResolved() and
not FunctionOverloading::functionResolutionDependsOnArgument(_, result, _, _, _)
not functionResolutionDependsOnArgument(_, result, _, _)
}
/**
@@ -2826,24 +2871,17 @@ private module NonMethodResolution {
pragma[nomagic]
NonMethodFunction resolveCallTargetViaTypeInference(ImplOrTraitItemNode i) {
result = this.resolveCallTargetBlanketCand(i) and
not FunctionOverloading::functionResolutionDependsOnArgument(_, result, _, _, _)
not FunctionOverloading::functionResolutionDependsOnArgument(_, result, _, _)
or
NonMethodArgsAreInstantiationsOfBlanket::argsAreInstantiationsOf(this, i, result)
or
NonMethodArgsAreInstantiationsOfNonBlanket::argsAreInstantiationsOf(this, i, result)
}
pragma[nomagic]
NonMethodFunction resolveTraitFunctionViaPathResolution(TraitItemNode trait) {
this.hasTrait() and
result = this.getPathResolutionResolved() and
result = trait.getASuccessor(_)
}
}
private newtype TCallAndBlanketPos =
MkCallAndBlanketPos(NonMethodCall fc, FunctionPosition pos) {
fc.resolveCallTargetBlanketLikeCandidate(_, pos, _, _)
fc.resolveCallTargetBlanketLikeCand(_, pos, _, _)
}
/** A call tagged with a position. */
@@ -2869,7 +2907,7 @@ private module NonMethodResolution {
) {
exists(NonMethodCall fc, FunctionPosition pos |
fcp = MkCallAndBlanketPos(fc, pos) and
fc.resolveCallTargetBlanketLikeCandidate(impl, pos, blanketPath, blanketTypeParam) and
fc.resolveCallTargetBlanketLikeCand(impl, pos, blanketPath, blanketTypeParam) and
// Only apply blanket implementations when no other implementations are possible;
// this is to account for codebases that use the (unstable) specialization feature
// (https://rust-lang.github.io/rfcs/1210-impl-specialization.html), as well as
@@ -2909,41 +2947,30 @@ private module NonMethodResolution {
private module ArgIsInstantiationOfBlanketParam =
ArgIsInstantiationOf<CallAndBlanketPos, ArgIsInstantiationOfBlanketParamInput>;
private Type getArgType(
NonMethodCall call, FunctionPosition pos, TypePath path, boolean isDefaultTypeArg
) {
result = inferType(call.getNodeAt(pos), path) and
isDefaultTypeArg = false
or
result = getCallExprTypeQualifier(call, path, isDefaultTypeArg) and
pos.isSelf()
}
private module NonMethodArgsAreInstantiationsOfBlanketInput implements
ArgsAreInstantiationsOfInputSig
{
predicate toCheck(ImplOrTraitItemNode i, Function f, FunctionPosition pos, AssocFunctionType t) {
t.appliesTo(f, i, pos) and
exists(Type t0 | typeCanBeUsedForDisambiguation(t0) |
FunctionOverloading::functionResolutionDependsOnArgument(i, f, pos, _, t0)
or
traitFunctionDependsOnPos(_, _, pos, t0, i, f)
)
predicate toCheck(ImplOrTraitItemNode i, Function f, TypeParameter tp, FunctionPosition pos) {
functionResolutionDependsOnArgument(i, f, pos, tp)
}
final class Call extends NonMethodCall {
Type getArgType(FunctionPosition pos, TypePath path) {
result = inferType(this.getNodeAt(pos), path)
}
predicate hasTraitResolvedCand(ImplOrTraitItemNode i, Function f) {
exists(TraitItemNode trait, NonMethodFunction resolved, ImplItemNode i1, Function f1 |
this.hasTraitResolved(trait, resolved) and
traitFunctionDependsOnPos(trait, resolved, _, _, i1, f1)
|
f = f1 and
i = i1
or
f = resolved and
i = trait
)
result = getArgType(this, pos, path, false)
}
predicate hasTargetCand(ImplOrTraitItemNode i, Function f) {
f = this.resolveCallTargetBlanketCand(i)
or
this.hasTraitResolvedCand(i, f) and
BlanketImplementation::isBlanketLike(i, _, _)
}
}
}
@@ -2954,23 +2981,43 @@ private module NonMethodResolution {
private module NonMethodArgsAreInstantiationsOfNonBlanketInput implements
ArgsAreInstantiationsOfInputSig
{
predicate toCheck(ImplOrTraitItemNode i, Function f, FunctionPosition pos, AssocFunctionType t) {
NonMethodArgsAreInstantiationsOfBlanketInput::toCheck(i, f, pos, t)
predicate toCheck(ImplOrTraitItemNode i, Function f, TypeParameter traitTp, FunctionPosition pos) {
functionResolutionDependsOnArgument(i, f, pos, traitTp)
or
// match against the trait function itself
t.appliesTo(f, i, pos) and
exists(Trait trait |
FunctionOverloading::traitTypeParameterOccurrence(trait, f, _, pos, _,
TSelfTypeParameter(trait))
)
// Also match against the trait function itself
FunctionOverloading::traitTypeParameterOccurrence(i, f, _, pos, _, traitTp) and
traitTp = TSelfTypeParameter(i)
}
class Call extends NonMethodArgsAreInstantiationsOfBlanketInput::Call {
class Call extends NonMethodCall {
Type getArgType(FunctionPosition pos, TypePath path) {
result = getArgType(this, pos, path, _)
}
predicate hasTargetCand(ImplOrTraitItemNode i, Function f) {
f = this.resolveCallTargetNonBlanketCand(i)
or
this.hasTraitResolvedCand(i, f) and
not BlanketImplementation::isBlanketLike(i, _, _)
exists(TraitItemNode trait, NonMethodFunction resolved, ImplItemNode i1, Function f1 |
this.hasTraitResolved(trait, resolved) and
traitFunctionResolutionDependsOnArgument(trait, resolved, _, i1, f1, _, _) and
not BlanketImplementation::isBlanketLike(i, _, _)
|
f = resolved and
i = trait
or
f = f1 and
i = i1 and
// Exclude functions where we cannot resolve all relevant type mentions; this allows
// for blanket implementations to be applied in those cases
forall(TypeParameter traitTp |
traitFunctionResolutionDependsOnArgument(trait, resolved, _, i1, f1, _, traitTp)
|
exists(FunctionPosition pos, TypePath path |
traitFunctionResolutionDependsOnArgument(trait, resolved, pos, i1, f1, path, traitTp) and
exists(getAssocFunctionTypeInclNonMethodSelfAt(f, i, pos, path))
)
)
)
}
}
}
@@ -3076,26 +3123,7 @@ private module NonMethodCallMatchingInput implements MatchingInputSig {
}
override Type getParameterType(DeclarationPosition dpos, TypePath path) {
// For associated functions, we may also need to match type arguments against
// the `Self` type. For example, in
//
// ```rust
// struct Foo<T>(T);
//
// impl<T : Default> Foo<T> {
// fn default() -> Self {
// Foo(Default::default())
// }
// }
//
// Foo::<i32>::default();
// ```
//
// we need to match `i32` against the type parameter `T` of the `impl` block.
dpos.isSelf() and
result = resolveImplOrTraitType(i.asSome(), path)
or
result = f.getParameterType(i, dpos, path)
result = f.getParameterTypeInclNonMethodSelf(i, dpos, path)
}
override Type getReturnType(TypePath path) { result = f.getReturnType(i, path) }
@@ -3140,7 +3168,7 @@ private module NonMethodCallMatchingInput implements MatchingInputSig {
pragma[nomagic]
Type getInferredType(AccessPosition apos, TypePath path) {
apos.isSelf() and
result = getCallExprTypeQualifier(this, path)
result = getCallExprTypeQualifier(this, path, false)
or
result = inferType(this.getNodeAt(apos), path)
}
@@ -3152,8 +3180,6 @@ private module NonMethodCallMatchingInput implements MatchingInputSig {
|
f = this.resolveCallTargetViaTypeInference(i.asSome()) // mutual recursion; resolving some associated function calls requires resolving types
or
f = this.resolveTraitFunctionViaPathResolution(i.asSome())
or
f = this.resolveCallTargetViaPathResolution() and
f.isDirectlyFor(i)
)
@@ -3174,6 +3200,12 @@ private module NonMethodCallMatchingInput implements MatchingInputSig {
this.hasUnknownTypeAt(i.asSome(), f, pos, path)
)
or
forex(ImplOrTraitItemNode i, NonMethodFunctionDeclaration f |
f = this.getPathResolutionResolved(i)
|
this.hasUnknownTypeAt(i, f, pos, path)
)
or
// Tuple declarations, such as `Result::Ok(...)`, may also be context typed
exists(TupleLikeConstructor tc, TypeParameter tp |
tc = this.resolveCallTargetViaPathResolution() and
@@ -4152,7 +4184,7 @@ private module Debug {
TypeAbstraction abs, TypeMention condition, TypeMention constraint, boolean transitive
) {
abs = getRelevantLocatable() and
Input2::conditionSatisfiesConstraint(abs, condition, constraint, transitive)
Input::conditionSatisfiesConstraint(abs, condition, constraint, transitive)
}
predicate debugInferShorthandSelfType(ShorthandSelfParameterMention self, TypePath path, Type t) {

View File

@@ -206,12 +206,11 @@ private module MkTypeMention<getAdditionalPathTypeAtSig/2 getAdditionalPathTypeA
exists(TypeParamItemNode tp | this = tp.getABoundPath() and result = tp)
}
pragma[nomagic]
private Type getDefaultPositionalTypeArgument(int i, TypePath path) {
// If a type argument is not given in the path, then we use the default for
// the type parameter if one exists for the type.
not exists(getPathTypeArgument(this, i)) and
// Defaults only apply to type mentions in type annotations
this = any(PathTypeRepr ptp).getPath().getQualifier*() and
exists(Type ty, TypePath prefix |
ty = this.resolveRootType().getTypeParameterDefault(i).(TypeMention).getTypeAt(prefix) and
if not ty = TSelfTypeParameter(resolved)
@@ -226,10 +225,37 @@ private module MkTypeMention<getAdditionalPathTypeAtSig/2 getAdditionalPathTypeA
)
}
private predicate isInTypeAnnotation() {
this = any(PathTypeRepr ptp).getPath().getQualifier*()
}
pragma[nomagic]
private TypeParameter getPositionalTypeParameter(int i) {
result = this.resolveRootType().getPositionalTypeParameter(i)
}
/**
* Gets the default type for the type parameter `tp` at `path`, if any.
*
* This predicate is restricted to mentions that are _not_ part of a type
* annotation, such as a qualifier in a call, `Vec::new()`, where the
* default type for type parameter `A` of `Vec` is `Global`.
*
* In these cases, whether or not the default type actually applies may
* depend on the types of arguments.
*/
pragma[nomagic]
Type getDefaultTypeForTypeParameterInNonAnnotationAt(TypeParameter tp, TypePath path) {
not this.isInTypeAnnotation() and
exists(int i |
result = this.getDefaultPositionalTypeArgument(i, path) and
tp = this.getPositionalTypeParameter(i)
)
}
pragma[nomagic]
private Type getPositionalTypeArgument(int i, TypePath path) {
result = getPathTypeArgument(this, i).getTypeAt(path)
or
result = this.getDefaultPositionalTypeArgument(i, path)
}
/**
@@ -237,9 +263,12 @@ private module MkTypeMention<getAdditionalPathTypeAtSig/2 getAdditionalPathTypeA
* type parameter does not correspond directly to a type mention.
*/
private Type getTypeForTypeParameterAt(TypeParameter tp, TypePath path) {
exists(int i |
result = this.getPositionalTypeArgument(pragma[only_bind_into](i), path) and
tp = this.resolveRootType().getPositionalTypeParameter(pragma[only_bind_into](i))
exists(int i | tp = this.getPositionalTypeParameter(i) |
result = this.getPositionalTypeArgument(i, path)
or
// Defaults only apply to type mentions in type annotations
this.isInTypeAnnotation() and
result = this.getDefaultPositionalTypeArgument(i, path)
)
or
// Handle the special syntactic sugar for function traits. The syntactic
@@ -299,7 +328,7 @@ private module MkTypeMention<getAdditionalPathTypeAtSig/2 getAdditionalPathTypeA
/** Gets the type mention in this path for the type parameter `tp`, if any. */
pragma[nomagic]
private TypeMention getTypeMentionImplForTypeParameter(TypeParameter tp) {
private TypeMention getTypeMentionForAssociatedTypeTypeParameter(AssociatedTypeTypeParameter tp) {
exists(TypeAlias alias, string name |
result = this.getAssocTypeArg(name) and
tp = TAssociatedTypeTypeParameter(resolved, alias) and
@@ -352,7 +381,7 @@ private module MkTypeMention<getAdditionalPathTypeAtSig/2 getAdditionalPathTypeA
exists(TypeParameter tp, TypePath suffix | typePath = TypePath::cons(tp, suffix) |
result = this.getTypeForTypeParameterAt(tp, suffix)
or
result = this.getTypeMentionImplForTypeParameter(tp).getTypeAt(suffix)
result = this.getTypeMentionForAssociatedTypeTypeParameter(tp).getTypeAt(suffix)
)
or
// When the path refers to a trait, then the implicit `Self` type parameter

View File

@@ -1,5 +1,2 @@
multipleResolvedTargets
| main.rs:126:9:126:11 | f(...) |
| main.rs:566:9:567:15 | ...::Assoc(...) |
| main.rs:569:9:570:12 | ...::f1(...) |
| main.rs:572:9:573:12 | ...::f1(...) |

View File

@@ -564,13 +564,13 @@ mod m16 {
#[rustfmt::skip]
fn foo() {
S3::<i32>:: // $ item=i32
Assoc(); // $ item=S3i32AssocFunc $ SPURIOUS: item=S3boolAssocFunc
Assoc(); // $ item=S3i32AssocFunc $ SPURIOUS: item=S3boolAssocFunc (the spurious target is later filtered away by type inference)
S3::<bool>:: // $ item=bool
f1(); // $ item=S3boolf1 $ SPURIOUS: item=S3i32f1
f1(); // $ item=S3boolf1 $ SPURIOUS: item=S3i32f1 (the spurious target is later filtered away by type inference)
S3::<i32>:: // $ item=i32
f1(); // $ item=S3i32f1 $ SPURIOUS: item=S3boolf1
f1(); // $ item=S3i32f1 $ SPURIOUS: item=S3boolf1 (the spurious target is later filtered away by type inference)
}
}

View File

@@ -1,2 +1,4 @@
multipleResolvedTargets
| main.rs:2902:13:2902:17 | x.f() |
| main.rs:2223:9:2223:31 | ... .my_add(...) |
| main.rs:2225:9:2225:29 | ... .my_add(...) |
| main.rs:2723:13:2723:17 | x.f() |

View File

@@ -184,34 +184,6 @@ mod trait_visibility {
}
}
mod method_call_trait_path_disambig {
trait FirstTrait {
// FirstTrait::method
fn method(&self) -> bool {
true
}
}
trait SecondTrait {
// SecondTrait::method
fn method(&self) -> i64 {
1
}
}
struct S;
impl FirstTrait for S {}
impl SecondTrait for S {}
fn _test() {
let s = S;
let _b1 = FirstTrait::method(&s); // $ type=_b1:bool target=FirstTrait::method
let _b2 = <S as FirstTrait>::method(&s); // $ type=_b2:bool target=FirstTrait::method
let _n1 = SecondTrait::method(&s); // $ type=_n1:i64 target=SecondTrait::method
let _n2 = <S as SecondTrait>::method(&s); // $ type=_n2:i64 target=SecondTrait::method
}
}
mod method_non_parametric_impl {
#[derive(Debug)]
struct MyThing<A> {
@@ -484,158 +456,7 @@ mod method_non_parametric_trait_impl {
}
}
mod impl_overlap {
#[derive(Debug, Clone, Copy)]
struct S1;
trait OverlappingTrait {
fn common_method(self) -> S1;
fn common_method_2(self, s1: S1) -> S1;
}
impl OverlappingTrait for S1 {
// <S1_as_OverlappingTrait>::common_method
fn common_method(self) -> S1 {
S1
}
// <S1_as_OverlappingTrait>::common_method_2
fn common_method_2(self, s1: S1) -> S1 {
S1
}
}
impl S1 {
// S1::common_method
fn common_method(self) -> S1 {
self
}
// S1::common_method_2
fn common_method_2(self) -> S1 {
self
}
}
struct S2<T2>(T2);
impl S2<i32> {
// S2<i32>::common_method
fn common_method(self) -> S1 {
S1
}
// S2<i32>::common_method
fn common_method_2(self) -> S1 {
S1
}
}
impl OverlappingTrait for S2<i32> {
// <S2<i32>_as_OverlappingTrait>::common_method
fn common_method(self) -> S1 {
S1
}
// <S2<i32>_as_OverlappingTrait>::common_method_2
fn common_method_2(self, s1: S1) -> S1 {
S1
}
}
impl OverlappingTrait for S2<S1> {
// <S2<S1>_as_OverlappingTrait>::common_method
fn common_method(self) -> S1 {
S1
}
// <S2<S1>_as_OverlappingTrait>::common_method_2
fn common_method_2(self, s1: S1) -> S1 {
S1
}
}
#[derive(Debug)]
struct S3<T3>(T3);
trait OverlappingTrait2<T> {
fn m(&self, x: &T) -> &Self;
}
impl<T> OverlappingTrait2<T> for S3<T> {
// <S3<T>_as_OverlappingTrait2<T>>::m
fn m(&self, x: &T) -> &Self {
self
}
}
impl<T> S3<T> {
// S3<T>::m
fn m(&self, x: T) -> &Self {
self
}
}
trait MyTrait1 {
// MyTrait1::m
fn m(&self) {}
}
trait MyTrait2: MyTrait1 {}
#[derive(Debug)]
struct S4;
impl MyTrait1 for S4 {
// <S4_as_MyTrait1>::m
fn m(&self) {}
}
impl MyTrait2 for S4 {}
#[derive(Debug)]
struct S5<T5>(T5);
impl MyTrait1 for S5<i32> {
// <S5<i32>_as_MyTrait1>::m
fn m(&self) {}
}
impl MyTrait2 for S5<i32> {}
impl MyTrait1 for S5<bool> {}
impl MyTrait2 for S5<bool> {}
pub fn f() {
let x = S1;
println!("{:?}", x.common_method()); // $ target=S1::common_method
println!("{:?}", S1::common_method(x)); // $ target=S1::common_method
println!("{:?}", x.common_method_2()); // $ target=S1::common_method_2
println!("{:?}", S1::common_method_2(x)); // $ target=S1::common_method_2
let y = S2(S1);
println!("{:?}", y.common_method()); // $ target=<S2<S1>_as_OverlappingTrait>::common_method
println!("{:?}", S2::<S1>::common_method(S2(S1))); // $ target=<S2<S1>_as_OverlappingTrait>::common_method
let z = S2(0);
println!("{:?}", z.common_method()); // $ target=S2<i32>::common_method
println!("{:?}", S2::common_method(S2(0))); // $ target=S2<i32>::common_method
println!("{:?}", S2::<i32>::common_method(S2(0))); // $ target=S2<i32>::common_method
let w = S3(S1);
println!("{:?}", w.m(x)); // $ target=S3<T>::m
println!("{:?}", S3::m(&w, x)); // $ target=S3<T>::m
S4.m(); // $ target=<S4_as_MyTrait1>::m
S4::m(&S4); // $ target=<S4_as_MyTrait1>::m
S5(0i32).m(); // $ target=<S5<i32>_as_MyTrait1>::m
S5::m(&S5(0i32)); // $ target=<S5<i32>_as_MyTrait1>::m
S5(true).m(); // $ target=MyTrait1::m
S5::m(&S5(true)); // $ target=MyTrait1::m
}
}
mod overloading;
mod type_parameter_bounds {
use std::fmt::Debug;
@@ -2399,9 +2220,9 @@ mod method_determined_by_argument_type {
x.my_add(&5i64); // $ target=MyAdd<&i64>::my_add
x.my_add(true); // $ target=MyAdd<bool>::my_add
S(1i64).my_add(S(2i64)); // $ target=S::my_add1
S(1i64).my_add(3i64); // $ MISSING: target=S::my_add2
S(1i64).my_add(&3i64); // $ target=S::my_add3
S(1i64).my_add(S(2i64)); // $ target=S::my_add1 $ SPURIOUS: target=S::my_add2 -- we do not check the `T: MyAdd` constraint yet
S(1i64).my_add(3i64); // $ target=S::my_add2
S(1i64).my_add(&3i64); // $ target=S::my_add3 $ SPURIOUS: target=S::my_add2 -- we do not check the `T: MyAdd` constraint yet
let x = i64::my_from(73i64); // $ target=MyFrom<i64>::my_from
let y = i64::my_from(true); // $ target=MyFrom<bool>::my_from

View File

@@ -0,0 +1,402 @@
mod method_call_trait_path_disambig {
trait FirstTrait {
// FirstTrait::method
fn method(&self) -> bool {
true
}
fn method2(&self) -> bool;
fn function() -> bool;
}
trait SecondTrait {
// SecondTrait::method
fn method(&self) -> i64 {
1
}
fn method2(&self) -> i64;
}
#[derive(Default)]
struct S;
impl FirstTrait for S {
// S_as_FirstTrait::method2
fn method2(&self) -> bool {
true
}
// S::function
fn function() -> bool {
true
}
}
impl SecondTrait for S {
// S_as_SecondTrait::method2
fn method2(&self) -> i64 {
1
}
}
struct S2;
impl FirstTrait for S2 {
// S2::method2
fn method2(&self) -> bool {
false
}
// S2::function
fn function() -> bool {
false
}
}
fn _test() {
let s = S;
let _b1 = FirstTrait::method(&s); // $ type=_b1:bool target=FirstTrait::method
let _b2 = <S as FirstTrait>::method(&s); // $ type=_b2:bool target=FirstTrait::method
let _b3 = <S as FirstTrait>::method(&Default::default()); // $ type=_b3:bool target=FirstTrait::method target=default
let _b4 = <S as FirstTrait>::method2(&s); // $ type=_b4:bool target=S_as_FirstTrait::method2
let _b5 = <S as FirstTrait>::method2(&Default::default()); // $ type=_b5:bool target=S_as_FirstTrait::method2 target=default
let _n1 = SecondTrait::method(&s); // $ type=_n1:i64 target=SecondTrait::method
let _n2 = <S as SecondTrait>::method(&s); // $ type=_n2:i64 target=SecondTrait::method
let _n3 = <S as SecondTrait>::method(&Default::default()); // $ type=_n3:i64 target=SecondTrait::method target=default
let _n4 = <S as SecondTrait>::method2(&s); // $ type=_n4:i64 target=S_as_SecondTrait::method2
let _n5 = <S as SecondTrait>::method2(&Default::default()); // $ type=_n5:i64 target=S_as_SecondTrait::method2 target=default
<S as FirstTrait>::function(); // $ target=S::function
<S2 as FirstTrait>::function(); // $ target=S2::function
}
}
pub mod impl_overlap {
#[derive(Debug, Clone, Copy)]
struct S1;
trait OverlappingTrait {
fn common_method(self) -> S1;
fn common_method_2(self, s1: S1) -> S1;
}
impl OverlappingTrait for S1 {
// <S1_as_OverlappingTrait>::common_method
fn common_method(self) -> S1 {
S1
}
// <S1_as_OverlappingTrait>::common_method_2
fn common_method_2(self, s1: S1) -> S1 {
S1
}
}
impl S1 {
// S1::common_method
fn common_method(self) -> S1 {
self
}
// S1::common_method_2
fn common_method_2(self) -> S1 {
self
}
}
struct S2<T2>(T2);
impl S2<i32> {
// S2<i32>::common_method
fn common_method(self) -> S1 {
S1
}
// S2<i32>::common_method
fn common_method_2(self) -> S1 {
S1
}
}
impl OverlappingTrait for S2<i32> {
// <S2<i32>_as_OverlappingTrait>::common_method
fn common_method(self) -> S1 {
S1
}
// <S2<i32>_as_OverlappingTrait>::common_method_2
fn common_method_2(self, s1: S1) -> S1 {
S1
}
}
impl OverlappingTrait for S2<S1> {
// <S2<S1>_as_OverlappingTrait>::common_method
fn common_method(self) -> S1 {
S1
}
// <S2<S1>_as_OverlappingTrait>::common_method_2
fn common_method_2(self, s1: S1) -> S1 {
S1
}
}
#[derive(Debug)]
struct S3<T3>(T3);
trait OverlappingTrait2<T> {
fn m(&self, x: &T) -> &Self;
}
impl<T> OverlappingTrait2<T> for S3<T> {
// <S3<T>_as_OverlappingTrait2<T>>::m
fn m(&self, x: &T) -> &Self {
self
}
}
impl<T> S3<T> {
// S3<T>::m
fn m(&self, x: T) -> &Self {
self
}
}
trait MyTrait1 {
// MyTrait1::m
fn m(&self) {}
}
trait MyTrait2: MyTrait1 {}
#[derive(Debug)]
struct S4;
impl MyTrait1 for S4 {
// <S4_as_MyTrait1>::m
fn m(&self) {}
}
impl MyTrait2 for S4 {}
#[derive(Debug)]
struct S5<T5>(T5);
impl MyTrait1 for S5<i32> {
// <S5<i32>_as_MyTrait1>::m
fn m(&self) {}
}
impl MyTrait2 for S5<i32> {}
impl MyTrait1 for S5<bool> {}
impl MyTrait2 for S5<bool> {}
pub fn f() {
let x = S1;
println!("{:?}", x.common_method()); // $ target=S1::common_method
println!("{:?}", S1::common_method(x)); // $ target=S1::common_method
println!("{:?}", x.common_method_2()); // $ target=S1::common_method_2
println!("{:?}", S1::common_method_2(x)); // $ target=S1::common_method_2
let y = S2(S1);
println!("{:?}", y.common_method()); // $ target=<S2<S1>_as_OverlappingTrait>::common_method
println!("{:?}", S2::<S1>::common_method(S2(S1))); // $ target=<S2<S1>_as_OverlappingTrait>::common_method
let z = S2(0);
println!("{:?}", z.common_method()); // $ target=S2<i32>::common_method
println!("{:?}", S2::common_method(S2(0))); // $ target=S2<i32>::common_method
println!("{:?}", S2::<i32>::common_method(S2(0))); // $ target=S2<i32>::common_method
let w = S3(S1);
println!("{:?}", w.m(x)); // $ target=S3<T>::m
println!("{:?}", S3::m(&w, x)); // $ target=S3<T>::m
S4.m(); // $ target=<S4_as_MyTrait1>::m
S4::m(&S4); // $ target=<S4_as_MyTrait1>::m
S5(0i32).m(); // $ target=<S5<i32>_as_MyTrait1>::m
S5::m(&S5(0i32)); // $ target=<S5<i32>_as_MyTrait1>::m
S5(true).m(); // $ target=MyTrait1::m
S5::m(&S5(true)); // $ target=MyTrait1::m
}
}
mod impl_overlap2 {
trait Trait1<T1> {
fn f(self, x: T1) -> T1;
}
impl Trait1<i32> for i32 {
// f1
fn f(self, x: i32) -> i32 {
0
}
}
impl Trait1<i64> for i32 {
// f2
fn f(self, x: i64) -> i64 {
0
}
}
trait Trait2<T1, T2> {
fn g(self, x: T1) -> T2;
}
impl Trait2<i32, i32> for i32 {
// g3
fn g(self, x: i32) -> i32 {
0
}
}
impl Trait2<i32, i64> for i32 {
// g4
fn g(self, x: i32) -> i64 {
0
}
}
fn f() {
let x = 0;
let y = x.f(0i32); // $ target=f1
let z: i32 = x.f(Default::default()); // $ target=f1 target=default
let z = x.f(0i64); // $ target=f2
let z: i64 = x.f(Default::default()); // $ target=f2 target=default
let z: i64 = x.g(0i32); // $ target=g4
}
}
mod impl_overlap3 {
trait Trait {
type Assoc;
fn Assoc() -> Self::Assoc;
}
struct S<T>(T);
impl Trait for S<i32> {
type Assoc = i32;
// S3i32AssocFunc
fn Assoc() -> Self::Assoc {
0
}
}
impl Trait for S<bool> {
type Assoc = bool;
// S3boolAssocFunc
fn Assoc() -> Self::Assoc {
true
}
}
impl S<i32> {
// S3i32f
fn f(x: i32) -> i32 {
0
}
}
impl S<bool> {
// S3boolf
fn f(x: bool) -> bool {
true
}
}
fn f() {
S::<i32>::Assoc(); // $ target=S3i32AssocFunc
S::<bool>::Assoc(); // $ target=S3boolAssocFunc
// `S::f(true)` results in "multiple applicable items in scope", even though the argument is actually enough to disambiguate
S::<bool>::f(true); // $ target=S3boolf
S::<i32>::f(0); // $ target=S3i32f
}
}
mod default_type_args {
struct S<T = i64>(T);
trait MyTrait {
type AssocType;
fn g(self) -> Self::AssocType;
}
impl S {
fn f(self) -> i64 {
self.0 // $ fieldof=S
}
fn g(self) -> i64 {
self.0 // $ fieldof=S
}
}
impl S<bool> {
fn g(self) -> bool {
self.0 // $ fieldof=S
}
}
impl<T> MyTrait for S<T> {
type AssocType = S;
fn g(self) -> S {
let x = S::f(S(Default::default())); // $ target=f target=default type=x:i64
let x = Self::AssocType::f(S(Default::default())); // $ target=f target=default type=x:i64
let x = S::<bool>::g(S(Default::default())); // $ target=g target=default type=x:bool
let x = S::<i64>::g(S(Default::default())); // $ target=g target=default type=x:i64
let x = Self::AssocType::g(S(Default::default())); // $ target=g target=default type=x:i64
S(0)
}
}
}
mod from_default {
#[derive(Default)]
struct S;
fn f() -> S {
let x = Default::default(); // $ target=default type=x:S
From::from(x) // $ target=from
}
struct S1;
struct S2;
impl From<S> for S1 {
// from1
fn from(_: S) -> Self {
S1
}
}
impl From<S2> for S1 {
// from2
fn from(_: S2) -> Self {
S1
}
}
impl From<S> for S2 {
// from3
fn from(_: S) -> Self {
S2
}
}
fn g(b: bool) -> S1 {
let s = if b { S } else { Default::default() }; // $ target=default type=s:S
let x = From::from(s); // $ target=from1 type=x:S1
x
}
}

View File

@@ -1,2 +1,2 @@
multipleResolvedTargets
| test_storage.rs:36:45:36:57 | text.as_ref() |
| test_storage.rs:40:5:40:31 | combined.extend(...) |

View File

@@ -980,6 +980,11 @@ module Make1<LocationSig Location, InputSig1<Location> Input1> {
not t = abs.getATypeParameter()
}
pragma[nomagic]
private predicate hasTypeConstraint(HasTypeTree term, Type constraint) {
hasTypeConstraint(term, constraint, constraint)
}
/**
* Holds if the type tree at `tt` satisfies the constraint `constraint`
* with the type `t` at `path`.
@@ -994,7 +999,7 @@ module Make1<LocationSig Location, InputSig1<Location> Input1> {
path = prefix0.append(suffix)
)
or
hasTypeConstraint(tt, constraint, constraint) and
hasTypeConstraint(tt, constraint) and
t = getTypeAt(tt, path)
}

View File

@@ -115,7 +115,7 @@ module Make<LocationSig Location, InputSig<Location> Input> {
/** Holds if this list starts with `e`, followed by `suffix`. */
bindingset[this]
predicate isCons(Element e, UnboundList suffix) {
exists(string regexp | regexp = "([0-9]+)\\.(.*)" |
exists(string regexp | regexp = "^([0-9]+)\\.(.*)$" |
e = decode(this.regexpCapture(regexp, 1)) and
suffix = this.regexpCapture(regexp, 2)
)
@@ -124,7 +124,7 @@ module Make<LocationSig Location, InputSig<Location> Input> {
/** Holds if this list starts with `prefix`, followed by `e`. */
bindingset[this]
predicate isSnoc(UnboundList prefix, Element e) {
exists(string regexp | regexp = "(|.+\\.)([0-9]+)\\." |
exists(string regexp | regexp = "^(|.+\\.)([0-9]+)\\.$" |
prefix = this.regexpCapture(regexp, 1) and
e = decode(this.regexpCapture(regexp, 2))
)
@@ -133,6 +133,33 @@ module Make<LocationSig Location, InputSig<Location> Input> {
/** Gets the head of this list, if any. */
bindingset[this]
Element getHead() { result = this.getElement(0) }
/**
* Gets the `i`th prefix of this list, if any.
*
* Only holds when this list is non-empty, and only returns proper prefixes.
*/
bindingset[this]
UnboundList getProperPrefix(int i) {
exists(string regexp, int occurrenceOffset | regexp = "[0-9]+\\." |
exists(this.regexpFind(regexp, i, occurrenceOffset)) and
result = this.prefix(occurrenceOffset)
)
}
/**
* Gets a prefix of this list, if any.
*
* Only holds when this list is non-empty, and only returns proper prefixes.
*/
bindingset[this]
UnboundList getAProperPrefix() { result = this.getProperPrefix(_) }
/**
* Gets a prefix of this list, including the list itself.
*/
bindingset[this]
UnboundList getAPrefix() { result = [this, this.getAProperPrefix()] }
}
/** Provides predicates for constructing `UnboundList`s. */