-
Notifications
You must be signed in to change notification settings - Fork 1.9k
Rust: Support blanket implementations #20133
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
69a1c7e
d10cdfb
29ba013
12dcd75
e2e6fd0
875c7da
3543829
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -1915,6 +1915,10 @@ private predicate methodCandidateTrait(Type type, Trait trait, string name, int | |
| methodCandidate(type, name, arity, impl) | ||
| } | ||
|
|
||
| /** | ||
| * Holds if `mc` has `rootType` as the root type of the receiver and the target | ||
| * method is named `name` and has arity `arity` | ||
| */ | ||
| pragma[nomagic] | ||
| private predicate isMethodCall(MethodCall mc, Type rootType, string name, int arity) { | ||
| rootType = mc.getTypeAt(TypePath::nil()) and | ||
|
|
@@ -2153,6 +2157,130 @@ private predicate methodCallHasImplCandidate(MethodCall mc, Impl impl) { | |
| else any() | ||
| } | ||
|
|
||
| private module BlanketImplementation { | ||
| private ImplItemNode getPotentialDuplicated( | ||
| string fileName, string traitName, int arity, string tpName | ||
| ) { | ||
| tpName = result.getBlanketImplementationTypeParam().getName() and | ||
| fileName = result.getLocation().getFile().getBaseName() and | ||
| traitName = result.resolveTraitTy().getName() and | ||
| arity = result.resolveTraitTy().(Trait).getNumberOfGenericParams() | ||
| } | ||
|
|
||
| private predicate duplicatedImpl(Impl impl1, Impl impl2) { | ||
| exists(string fileName, string traitName, int arity, string tpName | | ||
| impl1 = getPotentialDuplicated(fileName, traitName, arity, tpName) and | ||
| impl2 = getPotentialDuplicated(fileName, traitName, arity, tpName) and | ||
| impl1.getLocation().getFile().getAbsolutePath() < | ||
| impl2.getLocation().getFile().getAbsolutePath() | ||
| ) | ||
| } | ||
|
||
|
|
||
|
||
| /** | ||
| * Holds if `impl` is a canonical blanket implementation. | ||
| * | ||
| * Libraries can often occur several times in the database for different | ||
| * library versions. This causes the same blanket implementations to exist | ||
| * multiple times, and these add no useful information. | ||
| * | ||
| * We detect these duplicates based on some simple heuristics (same trait | ||
| * name, file name, etc.). For these duplicates we select the one with the | ||
| * greatest file name (which usually is also the one with the greatest library | ||
| * version in the path) as the "canonical" implementation. | ||
| */ | ||
| private predicate isCanonicalImpl(Impl impl) { | ||
| not duplicatedImpl(impl, _) and impl.(ImplItemNode).isBlanketImplementation() | ||
| } | ||
|
|
||
| /** | ||
| * Holds if `impl` is a blanket implementation for a type parameter and | ||
| * `traitBound` is the first non-trivial trait bound of that type parameter. | ||
| */ | ||
| private predicate blanketImplementationTraitBound(ImplItemNode impl, Trait traitBound) { | ||
| traitBound = | ||
| min(Trait trait, int i | | ||
| trait = impl.getBlanketImplementationTypeParam().resolveBound(i) and | ||
| // Exclude traits that are known to not narrow things down very much. | ||
| not trait.getName().getText() = | ||
| [ | ||
| "Sized", "Clone", | ||
| // The auto traits | ||
| "Send", "Sync", "Unpin", "UnwindSafe", "RefUnwindSafe" | ||
| ] | ||
|
Comment on lines
+2204
to
+2209
|
||
| | | ||
| trait order by i | ||
| ) | ||
| } | ||
|
|
||
| /** | ||
| * Holds if `impl` is a relevant blanket implementation that requires the | ||
| * trait `traitBound` and provides `f`, a method with name `name` and arity | ||
| * `arity`. | ||
| */ | ||
| private predicate blanketImplementationMethod( | ||
| ImplItemNode impl, Trait traitBound, string name, int arity, Function f | ||
| ) { | ||
| isCanonicalImpl(impl) and | ||
| blanketImplementationTraitBound(impl, traitBound) and | ||
| f.getParamList().hasSelfParam() and | ||
| arity = f.getParamList().getNumberOfParams() and | ||
| ( | ||
| f = impl.getAssocItem(name) | ||
| or | ||
| // If the trait has a method with a default implementation, then that | ||
| // target is interesting as well. | ||
| not exists(impl.getAssocItem(name)) and | ||
| f = impl.resolveTraitTy().getAssocItem(name) | ||
|
Comment on lines
+2232
to
+2233
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. How come this case is not filtered away by the constraint below?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If we have
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
But
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You're right, my example doesn't add up. I think I added the filter for cases like this: trait ApplicationImpl: ApplicationImplExt {
fn activate(&self) { /* body */ }
}
impl<T: ApplicationImpl> ApplicationImplExt for T {}Here we can get the |
||
| ) and | ||
| // If the method is already available through one of the trait bounds on the | ||
| // type parameter (because they implement the trait targeted by the impl | ||
| // block) then ignore it. | ||
| not impl.getBlanketImplementationTypeParam().resolveABound().(TraitItemNode).getASuccessor(name) = | ||
| f | ||
| } | ||
|
|
||
| pragma[nomagic] | ||
| predicate methodCallMatchesBlanketImpl( | ||
| MethodCall mc, Type t, ImplItemNode impl, Trait traitBound, Trait traitImpl, Function f | ||
| ) { | ||
| // Only check method calls where we have ruled out inherent method targets. | ||
| // Ideally we would also check if non-blanket method targets have been ruled | ||
| // out. | ||
| methodCallHasNoInherentTarget(mc) and | ||
| exists(string name, int arity | | ||
| isMethodCall(mc, t, name, arity) and | ||
| blanketImplementationMethod(impl, traitBound, name, arity, f) | ||
| ) and | ||
| traitImpl = impl.resolveTraitTy() | ||
| } | ||
|
|
||
| private predicate relevantTraitVisible(Element mc, Trait trait) { | ||
| methodCallMatchesBlanketImpl(mc, _, _, _, trait, _) | ||
| } | ||
|
|
||
| module SatisfiesConstraintInput implements SatisfiesConstraintInputSig<MethodCall> { | ||
| pragma[nomagic] | ||
| predicate relevantConstraint(MethodCall mc, Type constraint) { | ||
| exists(Trait traitBound, Trait traitImpl | | ||
| methodCallMatchesBlanketImpl(mc, _, _, traitBound, traitImpl, _) and | ||
| TraitIsVisible<relevantTraitVisible/2>::traitIsVisible(mc, traitImpl) and | ||
| traitBound = constraint.(TraitType).getTrait() | ||
| ) | ||
| } | ||
|
|
||
| predicate useUniversalConditions() { none() } | ||
| } | ||
|
|
||
| predicate hasBlanketImpl(MethodCall mc, Type t, Impl impl, Trait traitBound, Function f) { | ||
| SatisfiesConstraint<MethodCall, SatisfiesConstraintInput>::satisfiesConstraintType(mc, | ||
|
||
| TTrait(traitBound), _, _) and | ||
| methodCallMatchesBlanketImpl(mc, t, impl, traitBound, _, f) | ||
| } | ||
|
|
||
| pragma[nomagic] | ||
|
||
| Function getMethodFromBlanketImpl(MethodCall mc) { hasBlanketImpl(mc, _, _, _, result) } | ||
| } | ||
|
|
||
| /** Gets a method from an `impl` block that matches the method call `mc`. */ | ||
| pragma[nomagic] | ||
| private Function getMethodFromImpl(MethodCall mc) { | ||
|
|
@@ -2188,6 +2316,8 @@ private Function resolveMethodCallTarget(MethodCall mc) { | |
| // The method comes from an `impl` block targeting the type of the receiver. | ||
| result = getMethodFromImpl(mc) | ||
| or | ||
| result = BlanketImplementation::getMethodFromBlanketImpl(mc) | ||
| or | ||
| // The type of the receiver is a type parameter and the method comes from a | ||
| // trait bound on the type parameter. | ||
| result = getTypeParameterMethod(mc.getTypeAt(TypePath::nil()), mc.getMethodName()) | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.