Skip to content

Commit ca0fe44

Browse files
committed
Rust: Only infer types from trait bounds when their implementation is unambiguous
1 parent 16613d1 commit ca0fe44

File tree

8 files changed

+257
-155
lines changed

8 files changed

+257
-155
lines changed

rust/ql/lib/codeql/rust/internal/typeinference/BlanketImplementation.qll

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ module SatisfiesBlanketConstraint<
103103
}
104104

105105
private module SatisfiesBlanketConstraintInput implements
106-
SatisfiesConstraintInputSig<ArgumentTypeAndBlanketOffset>
106+
SatisfiesTypeInputSig<ArgumentTypeAndBlanketOffset>
107107
{
108108
pragma[nomagic]
109109
additional predicate relevantConstraint(
@@ -123,7 +123,7 @@ module SatisfiesBlanketConstraint<
123123
}
124124

125125
private module SatisfiesBlanketConstraint =
126-
SatisfiesConstraint<ArgumentTypeAndBlanketOffset, SatisfiesBlanketConstraintInput>;
126+
SatisfiesType<ArgumentTypeAndBlanketOffset, SatisfiesBlanketConstraintInput>;
127127

128128
/**
129129
* Holds if the argument type `at` satisfies the first non-trivial blanket

rust/ql/lib/codeql/rust/internal/typeinference/FunctionOverloading.qll

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,21 +14,21 @@ private import TypeInference
1414
private import FunctionType
1515

1616
pragma[nomagic]
17-
private Type resolveNonTypeParameterTypeAt(TypeMention tm, TypePath path) {
17+
private Type resolveNonTypeParameterTypeAt(PreTypeMention tm, TypePath path) {
1818
result = tm.getTypeAt(path) and
1919
not result instanceof TypeParameter
2020
}
2121

2222
bindingset[t1, t2]
23-
private predicate typeMentionEqual(TypeMention t1, TypeMention t2) {
23+
private predicate typeMentionEqual(PreTypeMention t1, PreTypeMention t2) {
2424
forex(TypePath path, Type type | resolveNonTypeParameterTypeAt(t1, path) = type |
2525
resolveNonTypeParameterTypeAt(t2, path) = type
2626
)
2727
}
2828

2929
pragma[nomagic]
3030
private predicate implSiblingCandidate(
31-
Impl impl, TraitItemNode trait, Type rootType, TypeMention selfTy
31+
Impl impl, TraitItemNode trait, Type rootType, PreTypeMention selfTy
3232
) {
3333
trait = impl.(ImplItemNode).resolveTraitTy() and
3434
selfTy = impl.getSelfTy() and
@@ -52,7 +52,7 @@ pragma[inline]
5252
private predicate implSiblings(TraitItemNode trait, Impl impl1, Impl impl2) {
5353
impl1 != impl2 and
5454
(
55-
exists(Type rootType, TypeMention selfTy1, TypeMention selfTy2 |
55+
exists(Type rootType, PreTypeMention selfTy1, PreTypeMention selfTy2 |
5656
implSiblingCandidate(impl1, trait, rootType, selfTy1) and
5757
implSiblingCandidate(impl2, trait, rootType, selfTy2) and
5858
// In principle the second conjunct below should be superflous, but we still
@@ -76,6 +76,15 @@ private predicate implSiblings(TraitItemNode trait, Impl impl1, Impl impl2) {
7676
pragma[nomagic]
7777
private predicate implHasSibling(ImplItemNode impl, Trait trait) { implSiblings(trait, impl, _) }
7878

79+
pragma[nomagic]
80+
predicate implHasAmbigousSiblingAt(ImplItemNode impl, Trait trait, TypePath path) {
81+
exists(ImplItemNode impl2 |
82+
implSiblings(trait, impl, impl2) and
83+
resolveNonTypeParameterTypeAt(impl.getTraitPath(), path) !=
84+
resolveNonTypeParameterTypeAt(impl2.getTraitPath(), path)
85+
)
86+
}
87+
7988
/**
8089
* Holds if `f` is a function declared inside `trait`, and the type of `f` at
8190
* `pos` and `path` is `traitTp`, which is a type parameter of `trait`.

rust/ql/lib/codeql/rust/internal/typeinference/Type.qll

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -439,11 +439,11 @@ class TypeParamTypeParameter extends TypeParameter, TTypeParamTypeParameter {
439439
*/
440440
class AssociatedTypeTypeParameter extends TypeParameter, TAssociatedTypeTypeParameter {
441441
private Trait trait;
442-
private TypeAlias typeAlias;
442+
private AssocType typeAlias;
443443

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

446-
TypeAlias getTypeAlias() { result = typeAlias }
446+
AssocType getTypeAlias() { result = typeAlias }
447447

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

459459
override string toString() {
460-
result = typeAlias.getName().getText() + "[" + trait.getName().toString() + "]"
460+
exists(string fromString, TraitItemNode trait2 |
461+
result = typeAlias.getName().getText() + "[" + trait.getName() + fromString + "]" and
462+
trait2 = typeAlias.getTrait() and
463+
if trait = trait2
464+
then fromString = ""
465+
else fromString = " (inherited from " + trait2.getName() + ")"
466+
)
461467
}
462468

463469
override Location getLocation() { result = typeAlias.getLocation() }

rust/ql/lib/codeql/rust/internal/typeinference/TypeInference.qll

Lines changed: 29 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,12 @@ private module Input implements InputSig1<Location>, InputSig2<PreTypeMention> {
4141

4242
class TypeAbstraction = TA::TypeAbstraction;
4343

44+
predicate typeAbstractionHasAmbigousConstraintAt(
45+
TypeAbstraction abs, Type constraint, TypePath path
46+
) {
47+
FunctionOverloading::implHasAmbigousSiblingAt(abs, constraint.(TraitType).getTrait(), path)
48+
}
49+
4450
class TypeArgumentPosition extends TTypeArgumentPosition {
4551
int asMethodTypeArgumentPosition() { this = TMethodTypeArgumentPosition(result) }
4652

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

128134
PreTypeMention getABaseTypeMention(Type t) { none() }
129135

130-
Type getATypeParameterConstraint(TypeParameter tp, TypePath path) {
131-
exists(TypeMention tm | result = tm.getTypeAt(path) |
132-
tm = tp.(TypeParamTypeParameter).getTypeParam().getATypeBound().getTypeRepr() or
133-
tm = tp.(SelfTypeParameter).getTrait() or
134-
tm =
135-
tp.(ImplTraitTypeTypeParameter)
136-
.getImplTraitTypeRepr()
137-
.getTypeBoundList()
138-
.getABound()
139-
.getTypeRepr()
140-
)
136+
PreTypeMention getATypeParameterConstraint(TypeParameter tp) {
137+
result = tp.(TypeParamTypeParameter).getTypeParam().getATypeBound().getTypeRepr() or
138+
result = tp.(SelfTypeParameter).getTrait() or
139+
result =
140+
tp.(ImplTraitTypeTypeParameter)
141+
.getImplTraitTypeRepr()
142+
.getTypeBoundList()
143+
.getABound()
144+
.getTypeRepr()
141145
}
142146

143147
/**
@@ -1170,7 +1174,7 @@ private module ContextTyping {
11701174
or
11711175
exists(TypeParameter mid |
11721176
assocFunctionMentionsTypeParameterAtNonRetPos(i, f, mid) and
1173-
tp = getATypeParameterConstraint(mid, _)
1177+
tp = getATypeParameterConstraint(mid).getTypeAt(_)
11741178
)
11751179
}
11761180

@@ -2544,8 +2548,7 @@ private module AssocFunctionResolution {
25442548
Location getLocation() { result = afc.getLocation() }
25452549
}
25462550

2547-
private module CallSatisfiesDerefConstraintInput implements
2548-
SatisfiesConstraintInputSig<CallDerefCand>
2551+
private module CallSatisfiesDerefConstraintInput implements SatisfiesTypeInputSig<CallDerefCand>
25492552
{
25502553
pragma[nomagic]
25512554
predicate relevantConstraint(CallDerefCand mc, Type constraint) {
@@ -2555,7 +2558,7 @@ private module AssocFunctionResolution {
25552558
}
25562559

25572560
private module CallSatisfiesDerefConstraint =
2558-
SatisfiesConstraint<CallDerefCand, CallSatisfiesDerefConstraintInput>;
2561+
SatisfiesType<CallDerefCand, CallSatisfiesDerefConstraintInput>;
25592562

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

3589-
private module AwaitSatisfiesConstraintInput implements SatisfiesConstraintInputSig<AwaitTarget> {
3592+
private module AwaitSatisfiesTypeInput implements SatisfiesTypeInputSig<AwaitTarget> {
35903593
pragma[nomagic]
35913594
predicate relevantConstraint(AwaitTarget term, Type constraint) {
35923595
exists(term) and
35933596
constraint.(TraitType).getTrait() instanceof FutureTrait
35943597
}
35953598
}
35963599

3597-
private module AwaitSatisfiesConstraint =
3598-
SatisfiesConstraint<AwaitTarget, AwaitSatisfiesConstraintInput>;
3600+
private module AwaitSatisfiesType = SatisfiesType<AwaitTarget, AwaitSatisfiesTypeInput>;
35993601

36003602
pragma[nomagic]
36013603
private Type inferAwaitExprType(AstNode n, TypePath path) {
36023604
exists(TypePath exprPath |
3603-
AwaitSatisfiesConstraint::satisfiesConstraintType(n.(AwaitExpr).getExpr(), _, exprPath, result) and
3605+
AwaitSatisfiesType::satisfiesConstraintType(n.(AwaitExpr).getExpr(), _, exprPath, result) and
36043606
exprPath.isCons(getFutureOutputTypeParameter(), path)
36053607
)
36063608
}
@@ -3779,9 +3781,7 @@ final private class ForIterableExpr extends Expr {
37793781
Type getTypeAt(TypePath path) { result = inferType(this, path) }
37803782
}
37813783

3782-
private module ForIterableSatisfiesConstraintInput implements
3783-
SatisfiesConstraintInputSig<ForIterableExpr>
3784-
{
3784+
private module ForIterableSatisfiesTypeInput implements SatisfiesTypeInputSig<ForIterableExpr> {
37853785
predicate relevantConstraint(ForIterableExpr term, Type constraint) {
37863786
exists(term) and
37873787
exists(Trait t | t = constraint.(TraitType).getTrait() |
@@ -3802,15 +3802,15 @@ private AssociatedTypeTypeParameter getIntoIteratorItemTypeParameter() {
38023802
result = getAssociatedTypeTypeParameter(any(IntoIteratorTrait t).getItemType())
38033803
}
38043804

3805-
private module ForIterableSatisfiesConstraint =
3806-
SatisfiesConstraint<ForIterableExpr, ForIterableSatisfiesConstraintInput>;
3805+
private module ForIterableSatisfiesType =
3806+
SatisfiesType<ForIterableExpr, ForIterableSatisfiesTypeInput>;
38073807

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

3839-
private module InvokedClosureSatisfiesConstraintInput implements
3840-
SatisfiesConstraintInputSig<InvokedClosureExpr>
3839+
private module InvokedClosureSatisfiesTypeInput implements SatisfiesTypeInputSig<InvokedClosureExpr>
38413840
{
38423841
predicate relevantConstraint(InvokedClosureExpr term, Type constraint) {
38433842
exists(term) and
38443843
constraint.(TraitType).getTrait() instanceof FnOnceTrait
38453844
}
38463845
}
38473846

3848-
private module InvokedClosureSatisfiesConstraint =
3849-
SatisfiesConstraint<InvokedClosureExpr, InvokedClosureSatisfiesConstraintInput>;
3847+
private module InvokedClosureSatisfiesType =
3848+
SatisfiesType<InvokedClosureExpr, InvokedClosureSatisfiesTypeInput>;
38503849

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

38563855
/**

rust/ql/lib/codeql/rust/internal/typeinference/TypeMention.qll

Lines changed: 35 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,13 @@ private module MkTypeMention<getAdditionalPathTypeAtSig/2 getAdditionalPathTypeA
205205
)
206206
or
207207
exists(TypeParamItemNode tp | this = tp.getABoundPath() and result = tp)
208+
or
209+
// `<result as this>::...`
210+
exists(PathTypeRepr typeRepr, PathTypeRepr traitRepr |
211+
pathTypeAsTraitAssoc(_, typeRepr, traitRepr, _, _) and
212+
this = traitRepr.getPath() and
213+
result = typeRepr.getPath()
214+
)
208215
}
209216

210217
pragma[nomagic]
@@ -696,6 +703,16 @@ private module PreTypeMention = MkTypeMention<preGetAdditionalPathTypeAt/2>;
696703

697704
class PreTypeMention = PreTypeMention::TypeMention;
698705

706+
private class TraitOrTmTrait extends AstNode {
707+
Type getTypeAt(TypePath path) {
708+
pathTypeAsTraitAssoc(_, _, this, _, _) and
709+
result = this.(PreTypeMention).getTypeAt(path)
710+
or
711+
result = TTrait(this) and
712+
path.isEmpty()
713+
}
714+
}
715+
699716
/**
700717
* Holds if `path` accesses an associated type `alias` from `trait` on a
701718
* concrete type given by `tm`.
@@ -705,7 +722,7 @@ class PreTypeMention = PreTypeMention::TypeMention;
705722
* when `path` is of the form `Self::AssocType`.
706723
*/
707724
private predicate pathConcreteTypeAssocType(
708-
Path path, PreTypeMention tm, TraitItemNode trait, AstNode implOrTmTrait, TypeAlias alias
725+
Path path, PreTypeMention tm, TraitItemNode trait, TraitOrTmTrait traitOrTmTrait, TypeAlias alias
709726
) {
710727
exists(Path qualifier |
711728
qualifier = path.getQualifier() and
@@ -714,57 +731,45 @@ private predicate pathConcreteTypeAssocType(
714731
// path of the form `<Type as Trait>::AssocType`
715732
// ^^^ tm ^^^^^^^^^ name
716733
exists(string name |
717-
pathTypeAsTraitAssoc(path, tm, implOrTmTrait, trait, name) and
734+
pathTypeAsTraitAssoc(path, tm, traitOrTmTrait, trait, name) and
718735
getTraitAssocType(trait, name) = alias
719736
)
720737
or
721738
// path of the form `Self::AssocType` within an `impl` block
722739
// tm ^^^^ ^^^^^^^^^ name
723-
implOrTmTrait =
724-
any(ImplItemNode impl |
725-
alias = resolvePath(path) and
726-
qualifier = impl.getASelfPath() and
727-
tm = impl.(Impl).getSelfTy() and
728-
trait.getAnAssocItem() = alias
729-
)
740+
exists(ImplItemNode impl |
741+
alias = resolvePath(path) and
742+
qualifier = impl.getASelfPath() and
743+
tm = impl.(Impl).getSelfTy() and
744+
trait.getAnAssocItem() = alias and
745+
traitOrTmTrait = trait
746+
)
730747
)
731748
}
732749

733-
private module PathSatisfiesConstraintInput implements SatisfiesConstraintInputSig<PreTypeMention> {
734-
predicate relevantConstraint(PreTypeMention tm, Type constraint) {
735-
pathConcreteTypeAssocType(_, tm, constraint.(TraitType).getTrait(), _, _)
750+
private module PathSatisfiesConstraintInput implements
751+
SatisfiesConstraintInputSig<PreTypeMention, TraitOrTmTrait>
752+
{
753+
predicate relevantConstraint(PreTypeMention tm, TraitOrTmTrait constraint) {
754+
pathConcreteTypeAssocType(_, tm, _, constraint, _)
736755
}
737756
}
738757

739758
private module PathSatisfiesConstraint =
740-
SatisfiesConstraint<PreTypeMention, PathSatisfiesConstraintInput>;
759+
SatisfiesConstraint<PreTypeMention, TraitOrTmTrait, PathSatisfiesConstraintInput>;
741760

742761
/**
743762
* Gets the type of `path` at `typePath` when `path` accesses an associated type
744763
* on a concrete type.
745764
*/
746765
private Type getPathConcreteAssocTypeAt(Path path, TypePath typePath) {
747766
exists(
748-
PreTypeMention tm, ImplItemNode impl, TraitItemNode trait, TraitType t, AstNode implOrTmTrait,
767+
PreTypeMention tm, ImplItemNode impl, TraitItemNode trait, TraitOrTmTrait traitOrTmTrait,
749768
TypeAlias alias, TypePath path0
750769
|
751-
pathConcreteTypeAssocType(path, tm, trait, implOrTmTrait, alias) and
752-
t = TTrait(trait) and
753-
PathSatisfiesConstraint::satisfiesConstraintTypeThrough(tm, impl, t, path0, result) and
770+
pathConcreteTypeAssocType(path, tm, trait, traitOrTmTrait, alias) and
771+
PathSatisfiesConstraint::satisfiesConstraintTypeThrough(tm, impl, traitOrTmTrait, path0, result) and
754772
path0.isCons(TAssociatedTypeTypeParameter(trait, alias), typePath)
755-
|
756-
implOrTmTrait instanceof Impl
757-
or
758-
// When `path` is of the form `<Type as Trait>::AssocType` we need to check
759-
// that `impl` is not more specific than the mentioned trait
760-
implOrTmTrait =
761-
any(PreTypeMention tmTrait |
762-
not exists(TypePath path1, Type t1 |
763-
t1 = impl.getTraitPath().(PreTypeMention).getTypeAt(path1) and
764-
not t1 instanceof TypeParameter and
765-
t1 != tmTrait.getTypeAt(path1)
766-
)
767-
)
768773
)
769774
}
770775

rust/ql/test/library-tests/type-inference/overloading.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -509,15 +509,15 @@ mod trait_bound_impl_overlap {
509509

510510
fn test() {
511511
let x = S(0);
512-
let y = call_f(x); // $ target=call_f type=y:i32 $ SPURIOUS: type=y:i64
512+
let y = call_f(x); // $ target=call_f type=y:i32
513513
let z: i32 = y;
514514

515515
let x = S(0);
516516
let y = call_f::<i32, _>(x); // $ target=call_f type=y:i32
517517

518518
let x = S(0);
519-
let y = call_f2(S(0i32), x); // $ target=call_f2 type=y:i32 $ SPURIOUS: type=y:i64
519+
let y = call_f2(S(0i32), x); // $ target=call_f2 $ MISSING: type=y:i32
520520
let x = S(0);
521-
let y = call_f2(S(0i64), x); // $ target=call_f2 type=y:i64 $ SPURIOUS: type=y:i32
521+
let y = call_f2(S(0i64), x); // $ target=call_f2 $ MISSING: type=y:i64
522522
}
523523
}

0 commit comments

Comments
 (0)