mirror of
https://github.com/github/codeql.git
synced 2026-02-11 20:51:06 +01:00
Merge pull request #21217 from hvitved/rust/type-inference-perf
Rust: Rework call disambiguation logic
This commit is contained in:
@@ -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)
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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(...) |
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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() |
|
||||
|
||||
@@ -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
|
||||
|
||||
402
rust/ql/test/library-tests/type-inference/overloading.rs
Normal file
402
rust/ql/test/library-tests/type-inference/overloading.rs
Normal 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
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,2 +1,2 @@
|
||||
multipleResolvedTargets
|
||||
| test_storage.rs:36:45:36:57 | text.as_ref() |
|
||||
| test_storage.rs:40:5:40:31 | combined.extend(...) |
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
|
||||
@@ -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. */
|
||||
|
||||
Reference in New Issue
Block a user