Rust: Infer argument types based on trait bounds on parameters

This commit is contained in:
Tom Hvitved
2026-01-19 14:53:22 +01:00
parent b8a8a160c5
commit 6dc98cfd01
9 changed files with 653 additions and 242 deletions

View File

@@ -134,7 +134,7 @@ module SatisfiesBlanketConstraint<
exists(ArgumentTypeAndBlanketOffset ato, Trait traitBound |
ato = MkArgumentTypeAndBlanketOffset(at, _) and
SatisfiesBlanketConstraintInput::relevantConstraint(ato, impl, traitBound) and
SatisfiesBlanketConstraint::satisfiesConstraintType(ato, TTrait(traitBound), _, _)
SatisfiesBlanketConstraint::satisfiesConstraint(ato, TTrait(traitBound), _, _)
)
or
exists(TypeParam blanketTypeParam |

View File

@@ -467,6 +467,41 @@ private predicate isPanicMacroCall(MacroExpr me) {
me.getMacroCall().resolveMacro().(MacroRules).getName().getText() = "panic"
}
// Due to "binding modes" the type of the pattern is not necessarily the
// same as the type of the initializer. However, when the pattern is an
// identifier pattern, its type is guaranteed to be the same as the type of the
// initializer.
private predicate identLetStmt(LetStmt let, IdentPat lhs, Expr rhs) {
let.getPat() = lhs and
let.getInitializer() = rhs
}
/**
* Gets the root type of a closure.
*
* We model closures as `dyn Fn` trait object types. A closure might implement
* only `Fn`, `FnMut`, or `FnOnce`. But since `Fn` is a subtrait of the others,
* giving closures the type `dyn Fn` works well in practice -- even if not
* entirely accurate.
*/
private DynTraitType closureRootType() {
result = TDynTraitType(any(FnTrait t)) // always exists because of the mention in `builtins/mentions.rs`
}
/** Gets the path to a closure's return type. */
private TypePath closureReturnPath() {
result =
TypePath::singleton(TDynTraitTypeParameter(any(FnTrait t), any(FnOnceTrait t).getOutputType()))
}
/** Gets the path to a closure's `index`th parameter type, where the arity is `arity`. */
pragma[nomagic]
private TypePath closureParameterPath(int arity, int index) {
result =
TypePath::cons(TDynTraitTypeParameter(_, any(FnTrait t).getTypeParam()),
TypePath::singleton(getTupleTypeParameter(arity, index)))
}
/** Module for inferring certain type information. */
module CertainTypeInference {
pragma[nomagic]
@@ -544,11 +579,7 @@ module CertainTypeInference {
// is not a certain type equality.
exists(LetStmt let |
not let.hasTypeRepr() and
// Due to "binding modes" the type of the pattern is not necessarily the
// same as the type of the initializer. The pattern being an identifier
// pattern is sufficient to ensure that this is not the case.
let.getPat().(IdentPat) = n1 and
let.getInitializer() = n2
identLetStmt(let, n1, n2)
)
or
exists(LetExpr let |
@@ -572,6 +603,21 @@ module CertainTypeInference {
)
else prefix2.isEmpty()
)
or
exists(CallExprImpl::DynamicCallExpr dce, TupleType tt, int i |
n1 = dce.getArgList() and
tt.getArity() = dce.getNumberOfSyntacticArguments() and
n2 = dce.getSyntacticPositionalArgument(i) and
prefix1 = TypePath::singleton(tt.getPositionalTypeParameter(i)) and
prefix2.isEmpty()
)
or
exists(ClosureExpr ce, int index |
n1 = ce and
n2 = ce.getParam(index).getPat() and
prefix1 = closureParameterPath(ce.getNumberOfParams(), index) and
prefix2.isEmpty()
)
}
pragma[nomagic]
@@ -636,6 +682,10 @@ module CertainTypeInference {
path.isEmpty() and
result instanceof NeverType
or
n instanceof ClosureExpr and
path.isEmpty() and
result = closureRootType()
or
infersCertainTypeAt(n, path, result.getATypeParameter())
}
@@ -835,17 +885,6 @@ private predicate typeEquality(AstNode n1, TypePath prefix1, AstNode n2, TypePat
n1.(ArrayRepeatExpr).getRepeatOperand() = n2 and
prefix1 = TypePath::singleton(getArrayTypeParameter()) and
prefix2.isEmpty()
or
exists(ClosureExpr ce, int index |
n1 = ce and
n2 = ce.getParam(index).getPat() and
prefix1 = closureParameterPath(ce.getNumberOfParams(), index) and
prefix2.isEmpty()
)
or
n1.(ClosureExpr).getClosureBody() = n2 and
prefix1 = closureReturnPath() and
prefix2.isEmpty()
}
/**
@@ -881,6 +920,9 @@ private predicate lubCoercion(AstNode parent, AstNode child, TypePath prefix) {
strictcount(Expr e | bodyReturns(parent, e)) > 1 and
prefix.isEmpty()
or
parent = any(ClosureExpr ce | not ce.hasRetType() and ce.getClosureBody() = child) and
prefix = closureReturnPath()
or
exists(Struct s |
child = [parent.(RangeExpr).getStart(), parent.(RangeExpr).getEnd()] and
prefix = TypePath::singleton(TTypeParamTypeParameter(s.getGenericParamList().getATypeParam())) and
@@ -888,6 +930,19 @@ private predicate lubCoercion(AstNode parent, AstNode child, TypePath prefix) {
)
}
private Type inferUnknownTypeFromAnnotation(AstNode n, TypePath path) {
inferType(n, path) = TUnknownType() and
// Normally, these are coercion sites, but in case a type is unknown we
// allow for type information to flow from the type annotation.
exists(TypeMention tm | result = tm.getTypeAt(path) |
tm = any(LetStmt let | identLetStmt(let, _, n)).getTypeRepr()
or
tm = any(ClosureExpr ce | n = ce.getBody()).getRetType().getTypeRepr()
or
tm = getReturnTypeMention(any(Function f | n = f.getBody()))
)
}
/**
* Holds if the type tree of `n1` at `prefix1` should be equal to the type tree
* of `n2` at `prefix2`, but type information should only propagate from `n1` to
@@ -1545,12 +1600,14 @@ private module AssocFunctionResolution {
*
* This is either:
*
* 1. `AssocFunctionCallMethodCallExpr`: a method call, `x.m()`;
* 2. `AssocFunctionCallIndexExpr`: an index expression, `x[i]`, which is [syntactic sugar][1]
* 1. `MethodCallExprAssocFunctionCall`: a method call, `x.m()`;
* 2. `IndexExprAssocFunctionCall`: an index expression, `x[i]`, which is [syntactic sugar][1]
* for `*x.index(i)`;
* 3. `AssocFunctionCallCallExpr`: a qualified function call, `Q::f(x)`; or
* 4. `AssocFunctionCallOperation`: an operation expression, `x + y`, which is syntactic sugar
* 3. `CallExprAssocFunctionCall`: a qualified function call, `Q::f(x)`; or
* 4. `OperationAssocFunctionCall`: an operation expression, `x + y`, which is syntactic sugar
* for `Add::add(x, y)`.
* 5. `DynamicAssocFunctionCall`: a call to a closure, `c(x)`, which is syntactic sugar for
* `c.call_once(x)`, `c.call_mut(x)`, or `c.call(x)`.
*
* Note that only in case 1 and 2 is auto-dereferencing and borrowing allowed.
*
@@ -1567,7 +1624,7 @@ private module AssocFunctionResolution {
pragma[nomagic]
abstract predicate hasNameAndArity(string name, int arity);
abstract Expr getNonReturnNodeAt(FunctionPosition pos);
abstract AstNode getNonReturnNodeAt(FunctionPosition pos);
AstNode getNodeAt(FunctionPosition pos) {
result = this.getNonReturnNodeAt(pos)
@@ -2101,7 +2158,7 @@ private module AssocFunctionResolution {
}
}
private class AssocFunctionCallMethodCallExpr extends AssocFunctionCall instanceof MethodCallExpr {
private class MethodCallExprAssocFunctionCall extends AssocFunctionCall instanceof MethodCallExpr {
override predicate hasNameAndArity(string name, int arity) {
name = super.getIdentifier().getText() and
arity = super.getNumberOfSyntacticArguments()
@@ -2121,7 +2178,7 @@ private module AssocFunctionResolution {
override Trait getTrait() { none() }
}
private class AssocFunctionCallIndexExpr extends AssocFunctionCall, IndexExpr {
private class IndexExprAssocFunctionCall extends AssocFunctionCall, IndexExpr {
private predicate isInMutableContext() {
// todo: does not handle all cases yet
VariableImpl::assignmentOperationDescendant(_, this)
@@ -2151,8 +2208,8 @@ private module AssocFunctionResolution {
}
}
private class AssocFunctionCallCallExpr extends AssocFunctionCall, CallExpr {
AssocFunctionCallCallExpr() {
private class CallExprAssocFunctionCall extends AssocFunctionCall, CallExpr {
CallExprAssocFunctionCall() {
exists(getCallExprPathQualifier(this)) and
// even if a target cannot be resolved by path resolution, it may still
// be possible to resolve a blanket implementation (so not `forex`)
@@ -2184,7 +2241,7 @@ private module AssocFunctionResolution {
override Trait getTrait() { result = getCallExprTraitQualifier(this) }
}
final class AssocFunctionCallOperation extends AssocFunctionCall, Operation {
final class OperationAssocFunctionCall extends AssocFunctionCall, Operation {
override predicate hasNameAndArity(string name, int arity) {
this.isOverloaded(_, name, _) and
arity = this.getNumberOfOperands()
@@ -2242,6 +2299,29 @@ private module AssocFunctionResolution {
override Trait getTrait() { this.isOverloaded(result, _, _) }
}
private class DynamicAssocFunctionCall extends AssocFunctionCall instanceof CallExprImpl::DynamicCallExpr
{
pragma[nomagic]
override predicate hasNameAndArity(string name, int arity) {
name = "call_once" and // todo: handle call_mut and call
arity = 2 // args are passed in a tuple
}
override predicate hasReceiver() { any() }
override AstNode getNonReturnNodeAt(FunctionPosition pos) {
pos.asPosition() = 0 and
result = super.getFunction()
or
pos.asPosition() = 1 and
result = super.getArgList()
}
override predicate supportsAutoDerefAndBorrow() { any() }
override Trait getTrait() { result instanceof AnyFnTrait }
}
pragma[nomagic]
private AssocFunctionDeclaration getAssocFunctionSuccessor(
ImplOrTraitItemNode i, string name, int arity
@@ -2445,7 +2525,7 @@ private module AssocFunctionResolution {
) {
exists(CallDerefCand cdc, TypePath exprPath |
cdc = MkCallDerefCand(afc, selfPos, derefChain) and
CallSatisfiesDerefConstraint::satisfiesConstraintTypeThrough(cdc, impl, _, exprPath, result) and
CallSatisfiesDerefConstraint::satisfiesConstraintThrough(cdc, impl, _, exprPath, result) and
exprPath.isCons(getDerefTargetTypeParameter(), path)
)
}
@@ -3198,7 +3278,7 @@ private module OperationMatchingInput implements MatchingInputSig {
}
}
class Access extends AssocFunctionResolution::AssocFunctionCallOperation {
class Access extends AssocFunctionResolution::OperationAssocFunctionCall {
Type getTypeArgument(TypeArgumentPosition apos, TypePath path) { none() }
pragma[nomagic]
@@ -3566,7 +3646,7 @@ private module AwaitSatisfiesType = SatisfiesType<AwaitTarget, AwaitSatisfiesTyp
pragma[nomagic]
private Type inferAwaitExprType(AstNode n, TypePath path) {
exists(TypePath exprPath |
AwaitSatisfiesType::satisfiesConstraintType(n.(AwaitExpr).getExpr(), _, exprPath, result) and
AwaitSatisfiesType::satisfiesConstraint(n.(AwaitExpr).getExpr(), _, exprPath, result) and
exprPath.isCons(getFutureOutputTypeParameter(), path)
)
}
@@ -3733,7 +3813,7 @@ 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
ForIterableSatisfiesType::satisfiesConstraintType(fe.getIterable(), _, exprPath, result) and
ForIterableSatisfiesType::satisfiesConstraint(fe.getIterable(), _, exprPath, result) and
exprPath.isCons(tp, path)
|
tp = getIntoIteratorItemTypeParameter()
@@ -3744,130 +3824,36 @@ private Type inferForLoopExprType(AstNode n, TypePath path) {
)
}
/**
* An invoked expression, the target of a call that is either a local variable
* or a non-path expression. This means that the expression denotes a
* first-class function.
*/
final private class InvokedClosureExpr extends Expr {
private CallExprImpl::DynamicCallExpr call;
InvokedClosureExpr() { call.getFunction() = this }
Type getTypeAt(TypePath path) { result = inferType(this, path) }
CallExpr getCall() { result = call }
}
private module InvokedClosureSatisfiesTypeInput implements SatisfiesTypeInputSig<InvokedClosureExpr>
{
predicate relevantConstraint(InvokedClosureExpr term, Type constraint) {
exists(term) and
constraint.(TraitType).getTrait() instanceof FnOnceTrait
}
}
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) {
InvokedClosureSatisfiesType::satisfiesConstraintType(ce, _, path, result)
}
/**
* Gets the root type of a closure.
*
* We model closures as `dyn Fn` trait object types. A closure might implement
* only `Fn`, `FnMut`, or `FnOnce`. But since `Fn` is a subtrait of the others,
* giving closures the type `dyn Fn` works well in practice -- even if not
* entirely accurate.
*/
private DynTraitType closureRootType() {
result = TDynTraitType(any(FnTrait t)) // always exists because of the mention in `builtins/mentions.rs`
}
/** Gets the path to a closure's return type. */
private TypePath closureReturnPath() {
result =
TypePath::singleton(TDynTraitTypeParameter(any(FnTrait t), any(FnOnceTrait t).getOutputType()))
}
/** Gets the path to a closure with arity `arity`'s `index`th parameter type. */
pragma[nomagic]
private TypePath closureParameterPath(int arity, int index) {
result =
TypePath::cons(TDynTraitTypeParameter(_, any(FnTrait t).getTypeParam()),
TypePath::singleton(getTupleTypeParameter(arity, index)))
}
/** Gets the path to the return type of the `FnOnce` trait. */
private TypePath fnReturnPath() {
result = TypePath::singleton(getAssociatedTypeTypeParameter(any(FnOnceTrait t).getOutputType()))
}
/**
* Gets the path to the parameter type of the `FnOnce` trait with arity `arity`
* and index `index`.
*/
pragma[nomagic]
private TypePath fnParameterPath(int arity, int index) {
result =
TypePath::cons(TTypeParamTypeParameter(any(FnOnceTrait t).getTypeParam()),
TypePath::singleton(getTupleTypeParameter(arity, index)))
}
pragma[nomagic]
private Type inferDynamicCallExprType(Expr n, TypePath path) {
exists(InvokedClosureExpr ce |
// Propagate the function's return type to the call expression
exists(TypePath path0 | result = invokedClosureFnTypeAt(ce, path0) |
n = ce.getCall() and
path = path0.stripPrefix(fnReturnPath())
private Type inferClosureExprType(AstNode n, TypePath path) {
exists(ClosureExpr ce |
n = ce and
(
path = TypePath::singleton(TDynTraitTypeParameter(_, any(FnTrait t).getTypeParam())) and
result.(TupleType).getArity() = ce.getNumberOfParams()
or
// Propagate the function's parameter type to the arguments
exists(int index |
n = ce.getCall().getSyntacticPositionalArgument(index) and
path =
path0.stripPrefix(fnParameterPath(ce.getCall().getArgList().getNumberOfArgs(), index))
exists(TypePath path0 |
result = ce.getRetType().getTypeRepr().(TypeMention).getTypeAt(path0) and
path = closureReturnPath().append(path0)
)
)
or
// _If_ the invoked expression has the type of a closure, then we propagate
// the surrounding types into the closure.
exists(int arity, TypePath path0 | ce.getTypeAt(TypePath::nil()) = closureRootType() |
// Propagate the type of arguments to the parameter types of closure
exists(int index, ArgList args |
n = ce and
args = ce.getCall().getArgList() and
arity = args.getNumberOfArgs() and
result = inferType(args.getArg(index), path0) and
path = closureParameterPath(arity, index).append(path0)
)
or
// Propagate the type of the call expression to the return type of the closure
n = ce and
arity = ce.getCall().getArgList().getNumberOfArgs() and
result = inferType(ce.getCall(), path0) and
path = closureReturnPath().append(path0)
exists(Param p |
p = ce.getAParam() and
not p.hasTypeRepr() and
n = p.getPat() and
result = TUnknownType() and
path.isEmpty()
)
)
}
pragma[nomagic]
private Type inferClosureExprType(AstNode n, TypePath path) {
exists(ClosureExpr ce |
n = ce and
path.isEmpty() and
result = closureRootType()
or
n = ce and
path = TypePath::singleton(TDynTraitTypeParameter(_, any(FnTrait t).getTypeParam())) and
result.(TupleType).getArity() = ce.getNumberOfParams()
or
// Propagate return type annotation to body
n = ce.getClosureBody() and
result = ce.getRetType().getTypeRepr().(TypeMention).getTypeAt(path)
private TupleType inferArgList(ArgList args, TypePath path) {
exists(CallExprImpl::DynamicCallExpr dce |
args = dce.getArgList() and
result.getArity() = dce.getNumberOfSyntacticArguments() and
path.isEmpty()
)
}
@@ -3915,7 +3901,8 @@ private module Cached {
or
i instanceof ImplItemNode and dispatch = false
|
result = call.(AssocFunctionResolution::AssocFunctionCall).resolveCallTarget(i, _, _, _)
result = call.(AssocFunctionResolution::AssocFunctionCall).resolveCallTarget(i, _, _, _) and
not call instanceof CallExprImpl::DynamicCallExpr
)
}
@@ -4023,11 +4010,13 @@ private module Cached {
or
result = inferForLoopExprType(n, path)
or
result = inferDynamicCallExprType(n, path)
or
result = inferClosureExprType(n, path)
or
result = inferArgList(n, path)
or
result = inferDeconstructionPatType(n, path)
or
result = inferUnknownTypeFromAnnotation(n, path)
)
}
}

View File

@@ -769,7 +769,7 @@ private Type getPathConcreteAssocTypeAt(Path path, TypePath typePath) {
TypeAlias alias, TypePath path0
|
pathConcreteTypeAssocType(path, tm, trait, traitOrTmTrait, alias) and
PathSatisfiesConstraint::satisfiesConstraintTypeThrough(tm, impl, traitOrTmTrait, path0, result) and
PathSatisfiesConstraint::satisfiesConstraintThrough(tm, impl, traitOrTmTrait, path0, result) and
path0.isCons(TAssociatedTypeTypeParameter(trait, alias), typePath)
)
}

View File

@@ -63,7 +63,7 @@ mod fn_once_trait {
};
let _r = apply(f, true); // $ target=apply type=_r:i64
let f = |x| x + 1; // $ MISSING: type=x:i64 target=add
let f = |x| x + 1; // $ type=x:i64 $ MISSING: target=add
let _r2 = apply_two(f); // $ target=apply_two certainType=_r2:i64
}
}
@@ -100,7 +100,7 @@ mod fn_mut_trait {
};
let _r = apply(f, true); // $ target=apply type=_r:i64
let f = |x| x + 1; // $ MISSING: type=x:i64 target=add
let f = |x| x + 1; // $ type=x:i64 $ MISSING: target=add
let _r2 = apply_two(f); // $ target=apply_two certainType=_r2:i64
}
}
@@ -137,7 +137,7 @@ mod fn_trait {
};
let _r = apply(f, true); // $ target=apply type=_r:i64
let f = |x| x + 1; // $ MISSING: type=x:i64 target=add
let f = |x| x + 1; // $ type=x:i64 $ MISSING: target=add
let _r2 = apply_two(f); // $ target=apply_two certainType=_r2:i64
}
}
@@ -183,25 +183,25 @@ mod closure_infer_param {
}
fn test() {
let f = |x| x; // $ MISSING: type=x:i64
let f = |x| x; // $ type=x:i64
let _r = apply1(f, 1i64); // $ target=apply1
let f = |x| x; // $ MISSING: type=x:i64
let f = |x| x; // $ type=x:i64
let _r = apply2(f, 2i64); // $ target=apply2
let f = |x| x; // $ MISSING: type=x:i64
let f = |x| x; // $ type=x:i64
let _r = apply3(&f, 3i64); // $ target=apply3
let f = |x| x; // $ MISSING: type=x:i64
let f = |x| x; // $ type=x:i64
let _r = apply4(f, 4i64); // $ target=apply4
let mut f = |x| x; // $ MISSING: type=x:i64
let _r = apply5(&mut f, 5i64); // $ target=apply5
let f = |x| x; // $ MISSING: type=x:i64
let f = |x| x; // $ type=x:i64
let _r = apply6(f, 6i64); // $ target=apply6
let f = |x| x; // $ MISSING: type=x:i64
let f = |x| x; // $ type=x:i64
let _r = apply7(f, 7i64); // $ target=apply7
}
}
@@ -221,15 +221,15 @@ mod implicit_deref {
pub fn test() {
let x = 0i64;
let v = Default::default(); // $ MISSING: type=v:i64 target=default
let v = Default::default(); // $ type=v:i64 target=default
let s = S(v);
let _ret = s(x); // $ MISSING: type=_ret:bool
let _ret = s(x); // $ type=_ret:bool
let x = 0i32;
let v = Default::default(); // $ MISSING: type=v:i32 target=default
let v = Default::default(); // $ type=v:i32 target=default
let s = S(v);
let s_ref = &s;
let _ret = s_ref(x); // $ MISSING: type=_ret:bool
let _ret = s_ref(x); // $ type=_ret:bool
// The call below is not an implicit deref, instead it will target
// `impl<A, F> FnOnce<A> for &F` from

View File

@@ -2259,7 +2259,7 @@ mod loops {
// for loops with arrays
for i in [1, 2, 3] {} // $ type=i:i32
for i in [1, 2, 3].map(|x| x + 1) {} // $ target=map MISSING: type=i:i32
for i in [1, 2, 3].map(|x| x + 1) {} // $ target=map target=add type=i:i32
for i in [1, 2, 3].into_iter() {} // $ target=into_iter type=i:i32
let vals1 = [1u8, 2, 3]; // $ type=vals1:TArray.u8
@@ -2777,7 +2777,7 @@ mod arg_trait_bounds {
}
fn test() {
let v = Default::default(); // $ MISSING: type=v:i64 target=default
let v = Default::default(); // $ type=v:i64 target=default
let g = Gen(v);
let _ = my_get(&g); // $ target=my_get
}

View File

@@ -149,7 +149,7 @@ mod regression5 {
fn foo() -> S2<S1> {
let x = S1.into(); // $ target=into
x
x // $ SPURIOUS: type=x:T2.TRef.S1 -- happens because we currently do not consider the two `impl` blocks to be siblings
}
}

View File

@@ -149,6 +149,8 @@ impl<T> MyOption<T> {
// summary=<test::option::MyOption>::map;Argument[0].ReturnValue;ReturnValue.Field[test::option::MyOption::MySome(0)];value;dfc-generated
// summary=<test::option::MyOption>::map;Argument[self].Field[test::option::MyOption::MySome(0)];Argument[0].Parameter[0];value;dfc-generated
// The spurious model below happens because `f` incorrectly resolves to the closure passed in from `as_deref`
// SPURIOUS-summary=<test::option::MyOption>::map;Argument[self].Field[test::option::MyOption::MySome(0)].Reference;ReturnValue.Field[test::option::MyOption::MySome(0)].Reference;taint;dfc-generated
pub fn map<U, F>(self, f: F) -> MyOption<U>
where
F: FnOnce(T) -> U,
@@ -217,7 +219,7 @@ impl<T> MyOption<T> {
}
}
// MISSING: `Deref` trait
// summary=<test::option::MyOption>::as_deref;Argument[self].Reference.Field[test::option::MyOption::MySome(0)];ReturnValue.Field[test::option::MyOption::MySome(0)].Reference;taint;dfc-generated
pub fn as_deref(&self) -> MyOption<&T::Target>
where
T: Deref,

View File

@@ -964,7 +964,7 @@ module Make1<LocationSig Location, InputSig1<Location> Input1> {
/**
* Holds if `term` does not satisfy `constraint`.
*
* This predicate is an approximation of `not hasConstraintMention(term, constraint)`.
* This predicate is an approximation of `not hasConstraintMention(term, _, _, constraint, _, _)`.
*/
pragma[nomagic]
private predicate hasNotConstraintMention(
@@ -1072,7 +1072,7 @@ module Make1<LocationSig Location, InputSig1<Location> Input1> {
}
pragma[nomagic]
private predicate satisfiesConstraintTypeMention1(
private predicate satisfiesConstraint0(
Term term, Constraint constraint, TypeAbstraction abs, TypeMention sub, TypePath path,
Type t
) {
@@ -1100,37 +1100,55 @@ module Make1<LocationSig Location, InputSig1<Location> Input1> {
}
pragma[inline]
private predicate satisfiesConstraintTypeMentionInline(
Term term, Constraint constraint, TypeAbstraction abs, TypePath path,
TypePath pathToTypeParamInSub
private predicate satisfiesConstraintInline(
Term term, Constraint constraint, TypeAbstraction abs, TypePath pathToTypeParamInConstraint,
TypePath pathToTypeParamInSub, TypeParameter tp
) {
exists(TypeMention sub, TypeParameter tp |
satisfiesConstraintTypeMention1(term, constraint, abs, sub, path, tp) and
exists(TypeMention sub |
satisfiesConstraint0(term, constraint, abs, sub, pathToTypeParamInConstraint, tp) and
tp = abs.getATypeParameter() and
sub.getTypeAt(pathToTypeParamInSub) = tp
)
}
/**
* Holds if `term` satisfies the constraint `constraint` with _some_ type
* parameter at `pathToTypeParamInConstraint`, and the type parameter occurs
* at `pathToTypeParamInSub` in a satisfying condition.
*
* Example:
*
* ```rust
* struct MyThing<A> { ... }
*
* trait MyTrait<B> { ... }
*
* impl<T> MyTrait<T> for MyThing<T> { ... }
*
* fn foo<T: MyTrait<i32>>(x: T) { ... }
*
* let x = MyThing(Default::default());
* foo(x);
* ```
*
* At `term` = `foo(x)`, we have `constraint = MyTrait<i32>`, and because of the
* `impl` block, `pathToTypeParamInConstraint` = `"B"`, and
* `pathToTypeParamInSub` = `"A"`.
*/
pragma[nomagic]
private predicate satisfiesConstraintTypeMention(
Term term, Constraint constraint, TypePath path, TypePath pathToTypeParamInSub
) {
satisfiesConstraintTypeMentionInline(term, constraint, _, path, pathToTypeParamInSub)
}
pragma[nomagic]
private predicate satisfiesConstraintTypeMentionThrough(
Term term, Constraint constraint, TypeAbstraction abs, TypePath path,
predicate satisfiesConstraintAtTypeParameter(
Term term, Constraint constraint, TypePath pathToTypeParamInConstraint,
TypePath pathToTypeParamInSub
) {
satisfiesConstraintTypeMentionInline(term, constraint, abs, path, pathToTypeParamInSub)
satisfiesConstraintInline(term, constraint, _, pathToTypeParamInConstraint,
pathToTypeParamInSub, _)
}
pragma[inline]
private predicate satisfiesConstraintTypeNonTypeParamInline(
private predicate satisfiesConstraintNonTypeParamInline(
Term term, TypeAbstraction abs, Constraint constraint, TypePath path, Type t
) {
satisfiesConstraintTypeMention1(term, constraint, abs, _, path, t) and
satisfiesConstraint0(term, constraint, abs, _, path, t) and
not t = abs.getATypeParameter()
}
@@ -1142,15 +1160,14 @@ module Make1<LocationSig Location, InputSig1<Location> Input1> {
}
/**
* Holds if the type tree at `term` satisfies the constraint `constraint`
* with the type `t` at `path`.
* Holds if `term` satisfies the constraint `constraint` with the type `t` at `path`.
*/
pragma[nomagic]
predicate satisfiesConstraintType(Term term, Constraint constraint, TypePath path, Type t) {
satisfiesConstraintTypeNonTypeParamInline(term, _, constraint, path, t)
predicate satisfiesConstraint(Term term, Constraint constraint, TypePath path, Type t) {
satisfiesConstraintNonTypeParamInline(term, _, constraint, path, t)
or
exists(TypePath prefix0, TypePath pathToTypeParamInSub, TypePath suffix |
satisfiesConstraintTypeMention(term, constraint, prefix0, pathToTypeParamInSub) and
satisfiesConstraintAtTypeParameter(term, constraint, prefix0, pathToTypeParamInSub) and
getTypeAt(term, pathToTypeParamInSub.appendInverse(suffix)) = t and
path = prefix0.append(suffix)
)
@@ -1159,25 +1176,34 @@ module Make1<LocationSig Location, InputSig1<Location> Input1> {
t = getTypeAt(term, path)
}
pragma[nomagic]
private predicate satisfiesConstraintThrough0(
Term term, Constraint constraint, TypeAbstraction abs, TypePath pathToTypeParamInConstraint,
TypePath pathToTypeParamInSub
) {
satisfiesConstraintInline(term, constraint, abs, pathToTypeParamInConstraint,
pathToTypeParamInSub, _)
}
/**
* Holds if the type tree at `term` satisfies the constraint `constraint`
* through `abs` with the type `t` at `path`.
* Holds if `term` satisfies the constraint `constraint` through `abs` with
* the type `t` at `path`.
*/
pragma[nomagic]
predicate satisfiesConstraintTypeThrough(
predicate satisfiesConstraintThrough(
Term term, TypeAbstraction abs, Constraint constraint, TypePath path, Type t
) {
satisfiesConstraintTypeNonTypeParamInline(term, abs, constraint, path, t)
satisfiesConstraintNonTypeParamInline(term, abs, constraint, path, t)
or
exists(TypePath prefix0, TypePath pathToTypeParamInSub, TypePath suffix |
satisfiesConstraintTypeMentionThrough(term, constraint, abs, prefix0, pathToTypeParamInSub) and
satisfiesConstraintThrough0(term, constraint, abs, prefix0, pathToTypeParamInSub) and
getTypeAt(term, pathToTypeParamInSub.appendInverse(suffix)) = t and
path = prefix0.append(suffix)
)
}
/**
* Holds if the type tree at `term` does _not_ satisfy the constraint `constraint`.
* Holds if `term` does _not_ satisfy the constraint `constraint`.
*
* This is an approximation of `not satisfiesConstraintType(term, constraint, _, _)`,
* but defined without a negative occurrence of `satisfiesConstraintType`.
@@ -1495,7 +1521,7 @@ module Make1<LocationSig Location, InputSig1<Location> Input1> {
TypeMention constraint
) {
target = a.getTarget(e) and
typeParameterConstraintHasTypeParameter(target, apos, path, constraint, _, _)
typeParameterHasConstraint(target, apos, _, path, constraint)
}
private newtype TRelevantAccess =
@@ -1556,13 +1582,40 @@ module Make1<LocationSig Location, InputSig1<Location> Input1> {
SatisfiesTypeParameterConstraintInput>;
pragma[nomagic]
predicate satisfiesConstraintType(
predicate satisfiesConstraintAtTypeParameter(
Access a, AccessEnvironment e, Declaration target, AccessPosition apos, TypePath prefix,
TypeMention constraint, TypePath pathToTypeParamInConstraint,
TypePath pathToTypeParamInSub
) {
exists(RelevantAccess ra |
ra = MkRelevantAccess(a, apos, e, prefix) and
SatisfiesTypeParameterConstraint::satisfiesConstraintAtTypeParameter(ra, constraint,
pathToTypeParamInConstraint, pathToTypeParamInSub) and
constraint = ra.getConstraint(target)
)
}
pragma[nomagic]
predicate satisfiesConstraint(
Access a, AccessEnvironment e, Declaration target, AccessPosition apos, TypePath prefix,
TypeMention constraint, TypePath path, Type t
) {
exists(RelevantAccess ra |
ra = MkRelevantAccess(a, apos, e, prefix) and
SatisfiesTypeParameterConstraint::satisfiesConstraintType(ra, constraint, path, t) and
SatisfiesTypeParameterConstraint::satisfiesConstraint(ra, constraint, path, t) and
constraint = ra.getConstraint(target)
)
}
pragma[nomagic]
predicate satisfiesConstraintThrough(
Access a, AccessEnvironment e, Declaration target, AccessPosition apos, TypePath prefix,
TypeAbstraction abs, TypeMention constraint, TypePath path, Type t
) {
exists(RelevantAccess ra |
ra = MkRelevantAccess(a, apos, e, prefix) and
SatisfiesTypeParameterConstraint::satisfiesConstraintThrough(ra, abs, constraint, path,
t) and
constraint = ra.getConstraint(target)
)
}
@@ -1707,7 +1760,7 @@ module Make1<LocationSig Location, InputSig1<Location> Input1> {
not exists(getTypeArgument(a, target, tp, _)) and
exists(TypeMention constraint, AccessPosition apos, TypePath pathToTp, TypePath pathToTp2 |
typeParameterConstraintHasTypeParameter(target, apos, pathToTp2, constraint, pathToTp, tp) and
AccessConstraint::satisfiesConstraintType(a, e, target, apos, pathToTp2, constraint,
AccessConstraint::satisfiesConstraint(a, e, target, apos, pathToTp2, constraint,
pathToTp.appendInverse(path), t)
)
}
@@ -1785,6 +1838,82 @@ module Make1<LocationSig Location, InputSig1<Location> Input1> {
not result instanceof TypeParameter
)
)
or
exists(
Declaration target, TypePath prefix, TypeMention constraint,
TypePath pathToTypeParamInConstraint, TypePath pathToTypeParamInSub
|
AccessConstraint::satisfiesConstraintAtTypeParameter(a, e, target, apos, prefix,
constraint, pathToTypeParamInConstraint, pathToTypeParamInSub)
|
exists(TypePath suffix |
/*
* Example:
*
* ```rust
* struct MyThing<A> { ... }
*
* trait MyTrait<B> { ... }
*
* impl<T> MyTrait<T> for MyThing<T> { ... }
*
* fn foo<T: MyTrait<i32>>(x: T) { ... }
*
* let x = MyThing(Default::default());
* foo(x);
* ```
*
* At `term` = `foo(x)`, we have
* - `constraint = MyTrait<i32>`,
* - `pathToTypeParamInConstraint` = `"B"`,
* - `pathToTypeParamInSub` = `"A"`,
* - `prefix` = `suffix` = `""`, and
* - `result` = `i32`.
*
* That is, it allows us to infer that the type of `x` is `MyThing<i32>`.
*/
result = constraint.getTypeAt(pathToTypeParamInConstraint.appendInverse(suffix)) and
not result instanceof TypeParameter and
path = prefix.append(pathToTypeParamInSub.append(suffix))
)
or
exists(TypeParameter tp, TypePath suffix, TypePath mid, TypePath pathToTp |
/*
* Example:
*
* ```rust
* struct MyThing<A> { ... }
*
* trait MyTrait<B> { ... }
*
* impl<T> MyTrait<T> for MyThing<T> { ... }
*
* fn bar<T1, T2: MyTrait<T1>>(x: T1, y: T2) {}
*
* let x : i32 = ...;
* let y = MyThing(Default::default());
* bar(x, y);
* ```
*
* At `term` = `bar(x, y)`, we have
* - `constraint = MyTrait<T1>`,
* - `pathToTypeParamInConstraint` = `"B"`,
* - `pathToTypeParamInSub` = `"A"`,
* - `prefix` = `suffix` = `mid` = `""`,
* - `tp = T1`,
* - `pathToTp` = `"B"`, and
* - `result` = `i32`.
*
* That is, it allows us to infer that the type of `y` is `MyThing<i32>`.
*/
typeMatch(a, e, target, suffix, result, tp) and
typeParameterConstraintHasTypeParameter(target, apos, _, constraint, pathToTp, tp) and
pathToTp = pathToTypeParamInConstraint.appendInverse(mid) and
path = prefix.append(pathToTypeParamInSub.append(mid).append(suffix))
)
)
}
}