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
59 changes: 29 additions & 30 deletions rust/ql/lib/codeql/rust/internal/typeinference/TypeInference.qll
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,12 @@ private module Input implements InputSig1<Location>, InputSig2<PreTypeMention> {

class TypeAbstraction = TA::TypeAbstraction;

predicate typeAbstractionHasAmbigousConstraintAt(
TypeAbstraction abs, Type constraint, TypePath path
) {
FunctionOverloading::implHasAmbigousSiblingAt(abs, constraint.(TraitType).getTrait(), path)
}

class TypeArgumentPosition extends TTypeArgumentPosition {
int asMethodTypeArgumentPosition() { this = TMethodTypeArgumentPosition(result) }

Expand Down Expand Up @@ -127,17 +133,15 @@ private module Input implements InputSig1<Location>, InputSig2<PreTypeMention> {

PreTypeMention getABaseTypeMention(Type t) { none() }

Type getATypeParameterConstraint(TypeParameter tp, TypePath path) {
exists(TypeMention tm | result = tm.getTypeAt(path) |
tm = tp.(TypeParamTypeParameter).getTypeParam().getATypeBound().getTypeRepr() or
tm = tp.(SelfTypeParameter).getTrait() or
tm =
tp.(ImplTraitTypeTypeParameter)
.getImplTraitTypeRepr()
.getTypeBoundList()
.getABound()
.getTypeRepr()
)
PreTypeMention getATypeParameterConstraint(TypeParameter tp) {
result = tp.(TypeParamTypeParameter).getTypeParam().getATypeBound().getTypeRepr() or
result = tp.(SelfTypeParameter).getTrait() or
result =
tp.(ImplTraitTypeTypeParameter)
.getImplTraitTypeRepr()
.getTypeBoundList()
.getABound()
.getTypeRepr()
}

/**
Expand Down Expand Up @@ -1170,7 +1174,7 @@ private module ContextTyping {
or
exists(TypeParameter mid |
assocFunctionMentionsTypeParameterAtNonRetPos(i, f, mid) and
tp = getATypeParameterConstraint(mid, _)
tp = getATypeParameterConstraint(mid).getTypeAt(_)
)
}

Expand Down Expand Up @@ -2544,8 +2548,7 @@ private module AssocFunctionResolution {
Location getLocation() { result = afc.getLocation() }
}

private module CallSatisfiesDerefConstraintInput implements
SatisfiesConstraintInputSig<CallDerefCand>
private module CallSatisfiesDerefConstraintInput implements SatisfiesTypeInputSig<CallDerefCand>
{
pragma[nomagic]
predicate relevantConstraint(CallDerefCand mc, Type constraint) {
Expand All @@ -2555,7 +2558,7 @@ private module AssocFunctionResolution {
}

private module CallSatisfiesDerefConstraint =
SatisfiesConstraint<CallDerefCand, CallSatisfiesDerefConstraintInput>;
SatisfiesType<CallDerefCand, CallSatisfiesDerefConstraintInput>;

pragma[nomagic]
private AssociatedTypeTypeParameter getDerefTargetTypeParameter() {
Expand Down Expand Up @@ -3586,21 +3589,20 @@ final private class AwaitTarget extends Expr {
Type getTypeAt(TypePath path) { result = inferType(this, path) }
}

private module AwaitSatisfiesConstraintInput implements SatisfiesConstraintInputSig<AwaitTarget> {
private module AwaitSatisfiesTypeInput implements SatisfiesTypeInputSig<AwaitTarget> {
pragma[nomagic]
predicate relevantConstraint(AwaitTarget term, Type constraint) {
exists(term) and
constraint.(TraitType).getTrait() instanceof FutureTrait
}
}

private module AwaitSatisfiesConstraint =
SatisfiesConstraint<AwaitTarget, AwaitSatisfiesConstraintInput>;
private module AwaitSatisfiesType = SatisfiesType<AwaitTarget, AwaitSatisfiesTypeInput>;

pragma[nomagic]
private Type inferAwaitExprType(AstNode n, TypePath path) {
exists(TypePath exprPath |
AwaitSatisfiesConstraint::satisfiesConstraintType(n.(AwaitExpr).getExpr(), _, exprPath, result) and
AwaitSatisfiesType::satisfiesConstraintType(n.(AwaitExpr).getExpr(), _, exprPath, result) and
exprPath.isCons(getFutureOutputTypeParameter(), path)
)
}
Expand Down Expand Up @@ -3779,9 +3781,7 @@ final private class ForIterableExpr extends Expr {
Type getTypeAt(TypePath path) { result = inferType(this, path) }
}

private module ForIterableSatisfiesConstraintInput implements
SatisfiesConstraintInputSig<ForIterableExpr>
{
private module ForIterableSatisfiesTypeInput implements SatisfiesTypeInputSig<ForIterableExpr> {
predicate relevantConstraint(ForIterableExpr term, Type constraint) {
exists(term) and
exists(Trait t | t = constraint.(TraitType).getTrait() |
Expand All @@ -3802,15 +3802,15 @@ private AssociatedTypeTypeParameter getIntoIteratorItemTypeParameter() {
result = getAssociatedTypeTypeParameter(any(IntoIteratorTrait t).getItemType())
}

private module ForIterableSatisfiesConstraint =
SatisfiesConstraint<ForIterableExpr, ForIterableSatisfiesConstraintInput>;
private module ForIterableSatisfiesType =
SatisfiesType<ForIterableExpr, ForIterableSatisfiesTypeInput>;

pragma[nomagic]
private Type inferForLoopExprType(AstNode n, TypePath path) {
// type of iterable -> type of pattern (loop variable)
exists(ForExpr fe, TypePath exprPath, AssociatedTypeTypeParameter tp |
n = fe.getPat() and
ForIterableSatisfiesConstraint::satisfiesConstraintType(fe.getIterable(), _, exprPath, result) and
ForIterableSatisfiesType::satisfiesConstraintType(fe.getIterable(), _, exprPath, result) and
exprPath.isCons(tp, path)
|
tp = getIntoIteratorItemTypeParameter()
Expand All @@ -3836,21 +3836,20 @@ final private class InvokedClosureExpr extends Expr {
CallExpr getCall() { result = call }
}

private module InvokedClosureSatisfiesConstraintInput implements
SatisfiesConstraintInputSig<InvokedClosureExpr>
private module InvokedClosureSatisfiesTypeInput implements SatisfiesTypeInputSig<InvokedClosureExpr>
{
predicate relevantConstraint(InvokedClosureExpr term, Type constraint) {
exists(term) and
constraint.(TraitType).getTrait() instanceof FnOnceTrait
}
}

private module InvokedClosureSatisfiesConstraint =
SatisfiesConstraint<InvokedClosureExpr, InvokedClosureSatisfiesConstraintInput>;
private module InvokedClosureSatisfiesType =
SatisfiesType<InvokedClosureExpr, InvokedClosureSatisfiesTypeInput>;

/** Gets the type of `ce` when viewed as an implementation of `FnOnce`. */
private Type invokedClosureFnTypeAt(InvokedClosureExpr ce, TypePath path) {
InvokedClosureSatisfiesConstraint::satisfiesConstraintType(ce, _, path, result)
InvokedClosureSatisfiesType::satisfiesConstraintType(ce, _, path, result)
}

/**
Expand Down
Loading
Loading