Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ module SatisfiesBlanketConstraint<
}

private module SatisfiesBlanketConstraintInput implements
SatisfiesConstraintInputSig<ArgumentTypeAndBlanketOffset>
SatisfiesTypeInputSig<ArgumentTypeAndBlanketOffset>
{
pragma[nomagic]
additional predicate relevantConstraint(
Expand All @@ -123,7 +123,7 @@ module SatisfiesBlanketConstraint<
}

private module SatisfiesBlanketConstraint =
SatisfiesConstraint<ArgumentTypeAndBlanketOffset, SatisfiesBlanketConstraintInput>;
SatisfiesType<ArgumentTypeAndBlanketOffset, SatisfiesBlanketConstraintInput>;

/**
* Holds if the argument type `at` satisfies the first non-trivial blanket
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,68 +13,105 @@ private import TypeMention
private import TypeInference
private import FunctionType

pragma[nomagic]
private Type resolveNonTypeParameterTypeAt(TypeMention tm, TypePath path) {
result = tm.getTypeAt(path) and
not result instanceof TypeParameter
}
private signature Type resolveTypeMentionAtSig(AstNode tm, TypePath path);

bindingset[t1, t2]
private predicate typeMentionEqual(TypeMention t1, TypeMention t2) {
forex(TypePath path, Type type | resolveNonTypeParameterTypeAt(t1, path) = type |
resolveNonTypeParameterTypeAt(t2, path) = type
)
}
/**
* Provides logic for identifying sibling implementations, parameterized over
* how to resolve type mentions (`PreTypeMention` vs. `TypeMention`).
*/
private module MkSiblingImpls<resolveTypeMentionAtSig/2 resolveTypeMentionAt> {
pragma[nomagic]
private Type resolveNonTypeParameterTypeAt(AstNode tm, TypePath path) {
result = resolveTypeMentionAt(tm, path) and
not result instanceof TypeParameter
}

pragma[nomagic]
private predicate implSiblingCandidate(
Impl impl, TraitItemNode trait, Type rootType, TypeMention selfTy
) {
trait = impl.(ImplItemNode).resolveTraitTy() and
selfTy = impl.getSelfTy() and
rootType = selfTy.getType()
bindingset[t1, t2]
private predicate typeMentionEqual(AstNode t1, AstNode t2) {
forex(TypePath path, Type type | resolveNonTypeParameterTypeAt(t1, path) = type |
resolveNonTypeParameterTypeAt(t2, path) = type
)
}

pragma[nomagic]
private predicate implSiblingCandidate(
Impl impl, TraitItemNode trait, Type rootType, AstNode selfTy
) {
trait = impl.(ImplItemNode).resolveTraitTy() and
selfTy = impl.getSelfTy() and
rootType = resolveTypeMentionAt(selfTy, TypePath::nil())
}

pragma[nomagic]
private predicate blanketImplSiblingCandidate(ImplItemNode impl, Trait trait) {
impl.isBlanketImplementation() and
trait = impl.resolveTraitTy()
}

/**
* Holds if `impl1` and `impl2` are a sibling implementations of `trait`. We
* consider implementations to be siblings if they implement the same trait for
* the same type. In that case `Self` is the same type in both implementations,
* and method calls to the implementations cannot be resolved unambiguously
* based only on the receiver type.
*/
pragma[inline]
predicate implSiblings(TraitItemNode trait, Impl impl1, Impl impl2) {
impl1 != impl2 and
(
exists(Type rootType, AstNode selfTy1, AstNode selfTy2 |
implSiblingCandidate(impl1, trait, rootType, selfTy1) and
implSiblingCandidate(impl2, trait, rootType, selfTy2) and
// In principle the second conjunct below should be superflous, but we still
// have ill-formed type mentions for types that we don't understand. For
// those checking both directions restricts further. Note also that we check
// syntactic equality, whereas equality up to renaming would be more
// correct.
typeMentionEqual(selfTy1, selfTy2) and
typeMentionEqual(selfTy2, selfTy1)
)
or
blanketImplSiblingCandidate(impl1, trait) and
blanketImplSiblingCandidate(impl2, trait)
)
}

/**
* Holds if `impl` is an implementation of `trait` and if another implementation
* exists for the same type.
*/
pragma[nomagic]
predicate implHasSibling(ImplItemNode impl, Trait trait) { implSiblings(trait, impl, _) }

pragma[nomagic]
predicate implHasAmbigousSiblingAt(ImplItemNode impl, Trait trait, TypePath path) {
exists(ImplItemNode impl2, Type t1, Type t2 |
implSiblings(trait, impl, impl2) and
t1 = resolveTypeMentionAt(impl.getTraitPath(), path) and
t2 = resolveTypeMentionAt(impl2.getTraitPath(), path) and
t1 != t2
|
not t1 instanceof TypeParameter or
not t2 instanceof TypeParameter
)
}
}

pragma[nomagic]
private predicate blanketImplSiblingCandidate(ImplItemNode impl, Trait trait) {
impl.isBlanketImplementation() and
trait = impl.resolveTraitTy()
private Type resolvePreTypeMention(AstNode tm, TypePath path) {
result = tm.(PreTypeMention).getTypeAt(path)
}

/**
* Holds if `impl1` and `impl2` are a sibling implementations of `trait`. We
* consider implementations to be siblings if they implement the same trait for
* the same type. In that case `Self` is the same type in both implementations,
* and method calls to the implementations cannot be resolved unambiguously
* based only on the receiver type.
*/
pragma[inline]
private predicate implSiblings(TraitItemNode trait, Impl impl1, Impl impl2) {
impl1 != impl2 and
(
exists(Type rootType, TypeMention selfTy1, TypeMention selfTy2 |
implSiblingCandidate(impl1, trait, rootType, selfTy1) and
implSiblingCandidate(impl2, trait, rootType, selfTy2) and
// In principle the second conjunct below should be superflous, but we still
// have ill-formed type mentions for types that we don't understand. For
// those checking both directions restricts further. Note also that we check
// syntactic equality, whereas equality up to renaming would be more
// correct.
typeMentionEqual(selfTy1, selfTy2) and
typeMentionEqual(selfTy2, selfTy1)
)
or
blanketImplSiblingCandidate(impl1, trait) and
blanketImplSiblingCandidate(impl2, trait)
)
private module PreSiblingImpls = MkSiblingImpls<resolvePreTypeMention/2>;

predicate preImplHasAmbigousSiblingAt = PreSiblingImpls::implHasAmbigousSiblingAt/3;

private Type resolveTypeMention(AstNode tm, TypePath path) {
result = tm.(TypeMention).getTypeAt(path)
}

/**
* Holds if `impl` is an implementation of `trait` and if another implementation
* exists for the same type.
*/
pragma[nomagic]
private predicate implHasSibling(ImplItemNode impl, Trait trait) { implSiblings(trait, impl, _) }
private module SiblingImpls = MkSiblingImpls<resolveTypeMention/2>;

import SiblingImpls

/**
* Holds if `f` is a function declared inside `trait`, and the type of `f` at
Expand Down
12 changes: 9 additions & 3 deletions rust/ql/lib/codeql/rust/internal/typeinference/Type.qll
Original file line number Diff line number Diff line change
Expand Up @@ -439,11 +439,11 @@ class TypeParamTypeParameter extends TypeParameter, TTypeParamTypeParameter {
*/
class AssociatedTypeTypeParameter extends TypeParameter, TAssociatedTypeTypeParameter {
private Trait trait;
private TypeAlias typeAlias;
private AssocType typeAlias;

AssociatedTypeTypeParameter() { this = TAssociatedTypeTypeParameter(trait, typeAlias) }

TypeAlias getTypeAlias() { result = typeAlias }
AssocType getTypeAlias() { result = typeAlias }

/** Gets the trait that contains this associated type declaration. */
TraitItemNode getTrait() { result = trait }
Expand All @@ -457,7 +457,13 @@ class AssociatedTypeTypeParameter extends TypeParameter, TAssociatedTypeTypePara
override ItemNode getDeclaringItem() { result = trait }

override string toString() {
result = typeAlias.getName().getText() + "[" + trait.getName().toString() + "]"
exists(string fromString, TraitItemNode trait2 |
result = typeAlias.getName().getText() + "[" + trait.getName() + fromString + "]" and
trait2 = typeAlias.getTrait() and
if trait = trait2
then fromString = ""
else fromString = " (inherited from " + trait2.getName() + ")"
)
}

override Location getLocation() { result = typeAlias.getLocation() }
Expand Down
Loading
Loading