From bad223cb8d4f383fc968e0c6a880cb3f2656727e Mon Sep 17 00:00:00 2001 From: Tom Hvitved Date: Tue, 5 May 2026 12:47:32 +0200 Subject: [PATCH 01/18] Rust: Move more type inference logic into shared library --- .../internal/typeinference/TypeInference.qll | 632 +++++++++--------- .../typeinference/internal/TypeInference.qll | 335 +++++++++- 2 files changed, 657 insertions(+), 310 deletions(-) diff --git a/rust/ql/lib/codeql/rust/internal/typeinference/TypeInference.qll b/rust/ql/lib/codeql/rust/internal/typeinference/TypeInference.qll index 423ad21ae4ac..7ecf2276f7b9 100644 --- a/rust/ql/lib/codeql/rust/internal/typeinference/TypeInference.qll +++ b/rust/ql/lib/codeql/rust/internal/typeinference/TypeInference.qll @@ -37,10 +37,7 @@ private module Input1 implements InputSig1 { class Type = T::Type; - predicate isPseudoType(Type t) { - t instanceof UnknownType or - t instanceof NeverType - } + class UnknownType = T::UnknownType; class TypeParameter = T::TypeParameter; @@ -276,6 +273,93 @@ private module M2 = Make2; import M2 +private module Input3 implements InputSig3 { + private import rust as Rust + + predicate cachedStageRef = CachedStage::ref/0; + + predicate cachedStageRevRef() { + (implicitDerefChainBorrow(_, _, _) implies any()) + or + (exists(resolveCallTarget(_, _)) implies any()) + or + (exists(resolveStructFieldExpr(_, _)) implies any()) + or + (exists(resolveTupleFieldExpr(_, _)) implies any()) + } + + class AstNode = Rust::AstNode; + + predicate getTypeAnnotation = getTypeAnnotation_/1; + + /** A variable, for example a local variable or a field. */ + class Variable extends Rust::Variable { + AstNode getDefiningNode() { + result = this.getPat().getName() or + result = this.getParameter().(SelfParam) + } + + AstNode getAnAccess() { result = super.getAnAccess() } + } + + abstract class Assignment extends AstNode { + abstract predicate isCoercionSite(); + + abstract AstNode getLeftOperand(); + + abstract AstNode getRightOperand(); + } + + private class LetExprAssignment extends Assignment, LetExpr { + override predicate isCoercionSite() { not this.getPat() instanceof IdentPat } + + override AstNode getLeftOperand() { result = this.getPat() } + + override AstNode getRightOperand() { result = this.getScrutinee() } + } + + private class LetStmtAssignment extends Assignment, LetStmt { + override predicate isCoercionSite() { + this.hasTypeRepr() or + not identLetStmt(this, _, _) + } + + override AstNode getLeftOperand() { result = this.getPat() } + + override AstNode getRightOperand() { result = this.getInitializer() } + } + + class ParenExpr extends AstNode, Rust::ParenExpr { + AstNode getExpr() { result = super.getExpr() } + } + + /** A ternary conditional expression. */ + class ConditionalExpr extends AstNode, IfExpr { + AstNode getCondition() { result = super.getCondition() } + + AstNode getThen() { result = super.getThen() } + + AstNode getElse() { result = super.getElse() } + } + + predicate certainTypeEqualityInput = CertainTypeInference_::certainTypeEquality_/4; + + predicate inferCertainTypeInput = CertainTypeInference_::inferCertainType_/2; + + predicate lubCoercionInput = lubCoercion_/3; + + predicate typeEqualityAsymmetricInput = typeEqualityAsymmetric_/4; + + predicate typeEqualityInput = typeEquality_/4; + + predicate inferTypeInput = inferType_/2; +} + +// private import Input3 +private module M3 = Make3; + +import M3 + module Consistency { import M2::Consistency @@ -428,7 +512,7 @@ private Type getCallExprTypeArgument(CallExpr ce, TypeArgumentPosition apos, Typ } /** Gets the type annotation that applies to `n`, if any. */ -private TypeMention getTypeAnnotation(AstNode n) { +private TypeMention getTypeAnnotation_(AstNode n) { exists(LetStmt let | n = let.getPat() and result = let.getTypeRepr() @@ -440,16 +524,15 @@ private TypeMention getTypeAnnotation(AstNode n) { n = p.getPat() and result = p.getTypeRepr() ) -} - -/** Gets the type of `n`, which has an explicit type annotation. */ -pragma[nomagic] -private Type inferAnnotatedType(AstNode n, TypePath path) { - result = getTypeAnnotation(n).getTypeAt(path) or - result = n.(ShorthandSelfParameterMention).getTypeAt(path) + result = n.(ShorthandSelfParameterMention) } +// /** Gets the type of `n`, which has an explicit type annotation. */ +// pragma[nomagic] +// private Type inferAnnotatedType(AstNode n, TypePath path) { +// result = getTypeAnnotation(n).getTypeAt(path) +// } pragma[nomagic] private Type inferFunctionBodyType(AstNode n, TypePath path) { exists(Function f | @@ -508,7 +591,7 @@ private TypePath closureParameterPath(int arity, int index) { } /** Module for inferring certain type information. */ -module CertainTypeInference { +module CertainTypeInference_ { pragma[nomagic] private predicate callResolvesTo(CallExpr ce, Path p, Function f) { p = CallExprImpl::getFunctionPath(ce) and @@ -572,31 +655,31 @@ module CertainTypeInference { result = sp.getPath().(TypeMention).getTypeAt(path) } - predicate certainTypeEquality(AstNode n1, TypePath prefix1, AstNode n2, TypePath prefix2) { - prefix1.isEmpty() and - prefix2.isEmpty() and - ( - exists(Variable v | n1 = v.getAnAccess() | - n2 = v.getPat().getName() or n2 = v.getParameter().(SelfParam) - ) - or - // A `let` statement with a type annotation is a coercion site and hence - // is not a certain type equality. - exists(LetStmt let | - not let.hasTypeRepr() and - identLetStmt(let, n1, n2) - ) - or - exists(LetExpr let | - // Similarly as for let statements, we need to rule out binding modes - // changing the type. - let.getPat().(IdentPat) = n1 and - let.getScrutinee() = n2 - ) - or - n1 = n2.(ParenExpr).getExpr() - ) - or + predicate certainTypeEquality_(AstNode n1, TypePath prefix1, AstNode n2, TypePath prefix2) { + // prefix1.isEmpty() and + // prefix2.isEmpty() and + // ( + // exists(Variable v | n1 = v.getAnAccess() | + // n2 = v.getPat().getName() or n2 = v.getParameter().(SelfParam) + // ) + // or + // // // A `let` statement with a type annotation is a coercion site and hence + // // // is not a certain type equality. + // // exists(LetStmt let | + // // not let.hasTypeRepr() and + // // identLetStmt(let, n1, n2) + // // ) + // // or + // // exists(LetExpr let | + // // // Similarly as for let statements, we need to rule out binding modes + // // // changing the type. + // // let.getPat().(IdentPat) = n1 and + // // let.getScrutinee() = n2 + // // ) + // // or + // n1 = n2.(ParenExpr).getExpr() + // ) + // or n1 = any(IdentPat ip | n2 = ip.getName() and @@ -625,33 +708,31 @@ module CertainTypeInference { ) } - pragma[nomagic] - private Type inferCertainTypeEquality(AstNode n, TypePath path) { - exists(TypePath prefix1, AstNode n2, TypePath prefix2, TypePath suffix | - result = inferCertainType(n2, prefix2.appendInverse(suffix)) and - path = prefix1.append(suffix) - | - certainTypeEquality(n, prefix1, n2, prefix2) - or - certainTypeEquality(n2, prefix2, n, prefix1) - ) - } - + // pragma[nomagic] + // private Type inferCertainTypeEquality(AstNode n, TypePath path) { + // exists(TypePath prefix1, AstNode n2, TypePath prefix2, TypePath suffix | + // result = inferCertainType(n2, prefix2.appendInverse(suffix)) and + // path = prefix1.append(suffix) + // | + // certainTypeEquality(n, prefix1, n2, prefix2) + // or + // certainTypeEquality(n2, prefix2, n, prefix1) + // ) + // } /** * Holds if `n` has complete and certain type information and if `n` has the * resulting type at `path`. */ - cached - Type inferCertainType(AstNode n, TypePath path) { - result = inferAnnotatedType(n, path) and - Stages::TypeInferenceStage::ref() - or + Type inferCertainType_(AstNode n, TypePath path) { + // result = inferAnnotatedType(n, path) and + // Stages::TypeInferenceStage::ref() + // or result = inferFunctionBodyType(n, path) or result = inferCertainCallExprType(n, path) or - result = inferCertainTypeEquality(n, path) - or + // result = inferCertainTypeEquality(n, path) + // or result = inferLiteralType(n, path, true) or result = inferRefPatType(n) and @@ -690,51 +771,48 @@ module CertainTypeInference { n instanceof ClosureExpr and path.isEmpty() and result = closureRootType() - or - infersCertainTypeAt(n, path, result.getATypeParameter()) - } - - /** - * Holds if `n` has complete and certain type information at the type path - * `prefix.tp`. This entails that the type at `prefix` must be the type - * that declares `tp`. - */ - pragma[nomagic] - private predicate infersCertainTypeAt(AstNode n, TypePath prefix, TypeParameter tp) { - exists(TypePath path | - exists(inferCertainType(n, path)) and - path.isSnoc(prefix, tp) - ) - } - - /** - * Holds if `n` has complete and certain type information at `path`. - */ - pragma[nomagic] - predicate hasInferredCertainType(AstNode n, TypePath path) { exists(inferCertainType(n, path)) } - - /** - * Holds if `n` having type `t` at `path` conflicts with certain type information - * at `prefix`. - */ - bindingset[n, prefix, path, t] - pragma[inline_late] - predicate certainTypeConflict(AstNode n, TypePath prefix, TypePath path, Type t) { - inferCertainType(n, path) != t - or - // If we infer that `n` has _some_ type at `T1.T2....Tn`, and we also - // know that `n` certainly has type `certainType` at `T1.T2...Ti`, `0 <= i < n`, - // then it must be the case that `T(i+1)` is a type parameter of `certainType`, - // otherwise there is a conflict. - // - // Below, `prefix` is `T1.T2...Ti` and `tp` is `T(i+1)`. - exists(TypePath suffix, TypeParameter tp, Type certainType | - path = prefix.appendInverse(suffix) and - tp = suffix.getHead() and - inferCertainType(n, prefix) = certainType and - not certainType.getATypeParameter() = tp - ) - } + // or + // infersCertainTypeAt(n, path, result.getATypeParameter()) + } + // /** + // * Holds if `n` has complete and certain type information at the type path + // * `prefix.tp`. This entails that the type at `prefix` must be the type + // * that declares `tp`. + // */ + // pragma[nomagic] + // private predicate infersCertainTypeAt(AstNode n, TypePath prefix, TypeParameter tp) { + // exists(TypePath path | + // exists(inferCertainType(n, path)) and + // path.isSnoc(prefix, tp) + // ) + // } + // /** + // * Holds if `n` has complete and certain type information at `path`. + // */ + // pragma[nomagic] + // predicate hasInferredCertainType(AstNode n, TypePath path) { exists(inferCertainType(n, path)) } + // /** + // * Holds if `n` having type `t` at `path` conflicts with certain type information + // * at `prefix`. + // */ + // bindingset[n, prefix, path, t] + // pragma[inline_late] + // predicate certainTypeConflict(AstNode n, TypePath prefix, TypePath path, Type t) { + // inferCertainType(n, path) != t + // or + // // If we infer that `n` has _some_ type at `T1.T2....Tn`, and we also + // // know that `n` certainly has type `certainType` at `T1.T2...Ti`, `0 <= i < n`, + // // then it must be the case that `T(i+1)` is a type parameter of `certainType`, + // // otherwise there is a conflict. + // // + // // Below, `prefix` is `T1.T2...Ti` and `tp` is `T(i+1)`. + // exists(TypePath suffix, TypeParameter tp, Type certainType | + // path = prefix.appendInverse(suffix) and + // tp = suffix.getHead() and + // inferCertainType(n, prefix) = certainType and + // not certainType.getATypeParameter() = tp + // ) + // } } private Type inferLogicalOperationType(AstNode n, TypePath path) { @@ -785,28 +863,28 @@ private predicate bodyReturns(Expr body, Expr e) { * of `n2` at `prefix2` and type information should propagate in both directions * through the type equality. */ -private predicate typeEquality(AstNode n1, TypePath prefix1, AstNode n2, TypePath prefix2) { - CertainTypeInference::certainTypeEquality(n1, prefix1, n2, prefix2) - or +private predicate typeEquality_(AstNode n1, TypePath prefix1, AstNode n2, TypePath prefix2) { + // CertainTypeInference::certainTypeEquality(n1, prefix1, n2, prefix2) + // or prefix1.isEmpty() and prefix2.isEmpty() and ( - exists(LetStmt let | - let.getPat() = n1 and - let.getInitializer() = n2 - ) - or + // exists(LetStmt let | + // let.getPat() = n1 and + // let.getInitializer() = n2 + // ) + // or n2 = any(MatchExpr me | n1 = me.getAnArm().getExpr() and me.getNumberOfArms() = 1 ) or - exists(LetExpr let | - n1 = let.getScrutinee() and - n2 = let.getPat() - ) - or + // exists(LetExpr let | + // n1 = let.getScrutinee() and + // n2 = let.getPat() + // ) + // or exists(MatchExpr me | n1 = me.getScrutinee() and n2 = me.getAnArm().getPat() @@ -903,10 +981,10 @@ private predicate typeEquality(AstNode n1, TypePath prefix1, AstNode n2, TypePat * * [1]: https://doc.rust-lang.org/reference/type-coercions.html#r-coerce.least-upper-bound */ -private predicate lubCoercion(AstNode parent, AstNode child, TypePath prefix) { - child = parent.(IfExpr).getABranch() and - prefix.isEmpty() - or +private predicate lubCoercion_(AstNode parent, AstNode child, TypePath prefix) { + // child = parent.(IfExpr).getABranch() and + // prefix.isEmpty() + // or parent = any(MatchExpr me | child = me.getAnArm().getExpr() and @@ -953,19 +1031,19 @@ private Type inferUnknownTypeFromAnnotation(AstNode n, TypePath path) { * of `n2` at `prefix2`, but type information should only propagate from `n1` to * `n2`. */ -private predicate typeEqualityAsymmetric(AstNode n1, TypePath prefix1, AstNode n2, TypePath prefix2) { - lubCoercion(n2, n1, prefix2) and - prefix1.isEmpty() - or - exists(AstNode mid, TypePath prefixMid, TypePath suffix | - typeEquality(n1, prefixMid, mid, prefix2) or - typeEquality(mid, prefix2, n1, prefixMid) - | - lubCoercion(mid, n2, suffix) and - not lubCoercion(mid, n1, _) and - prefix1 = prefixMid.append(suffix) - ) - or +private predicate typeEqualityAsymmetric_(AstNode n1, TypePath prefix1, AstNode n2, TypePath prefix2) { + // lubCoercion(n2, n1, prefix2) and + // prefix1.isEmpty() + // or + // exists(AstNode mid, TypePath prefixMid, TypePath suffix | + // typeEquality(n1, prefixMid, mid, prefix2) or + // typeEquality(mid, prefix2, n1, prefixMid) + // | + // lubCoercion(mid, n2, suffix) and + // not lubCoercion(mid, n1, _) and + // prefix1 = prefixMid.append(suffix) + // ) + // or // When `n2` is `*n1` propagate type information from a raw pointer type // parameter at `n1`. The other direction is handled in // `inferDereferencedExprPtrType`. @@ -974,20 +1052,19 @@ private predicate typeEqualityAsymmetric(AstNode n1, TypePath prefix1, AstNode n prefix2.isEmpty() } -pragma[nomagic] -private Type inferTypeEquality(AstNode n, TypePath path) { - exists(TypePath prefix1, AstNode n2, TypePath prefix2, TypePath suffix | - result = inferType(n2, prefix2.appendInverse(suffix)) and - path = prefix1.append(suffix) - | - typeEquality(n, prefix1, n2, prefix2) - or - typeEquality(n2, prefix2, n, prefix1) - or - typeEqualityAsymmetric(n2, prefix2, n, prefix1) - ) -} - +// pragma[nomagic] +// private Type inferTypeEquality(AstNode n, TypePath path) { +// exists(TypePath prefix1, AstNode n2, TypePath prefix2, TypePath suffix | +// result = inferType(n2, prefix2.appendInverse(suffix)) and +// path = prefix1.append(suffix) +// | +// typeEquality(n, prefix1, n2, prefix2) +// or +// typeEquality(n2, prefix2, n, prefix1) +// or +// typeEqualityAsymmetric(n2, prefix2, n, prefix1) +// ) +// } pragma[nomagic] private TupleType inferTupleRootType(AstNode n) { // `typeEquality` handles the non-root cases @@ -3776,170 +3853,121 @@ private Type inferCastExprType(CastExpr ce, TypePath path) { result = ce.getTypeRepr().(TypeMention).getTypeAt(path) } +/** Holds if `n` is implicitly dereferenced and/or borrowed. */ cached -private module Cached { - /** Holds if `n` is implicitly dereferenced and/or borrowed. */ - cached - predicate implicitDerefChainBorrow(Expr e, DerefChain derefChain, boolean borrow) { - exists(BorrowKind bk | - any(AssocFunctionResolution::AssocFunctionCall afc) - .argumentHasImplicitDerefChainBorrow(e, derefChain, bk) and - if bk.isNoBorrow() then borrow = false else borrow = true - ) - or - e = - any(FieldExpr fe | - exists(resolveStructFieldExpr(fe, derefChain)) - or - exists(resolveTupleFieldExpr(fe, derefChain)) - ).getContainer() and - not derefChain.isEmpty() and - borrow = false - } - - /** - * Gets an item (function or tuple struct/variant) that `call` resolves to, if - * any. - * - * The parameter `dispatch` is `true` if and only if the resolved target is a - * trait item because a precise target could not be determined from the - * types (for instance in the presence of generics or `dyn` types) - */ - cached - Addressable resolveCallTarget(InvocationExpr call, boolean dispatch) { - dispatch = false and - result = call.(NonAssocCallExpr).resolveCallTargetViaPathResolution() - or - exists(ImplOrTraitItemNode i | - i instanceof TraitItemNode and dispatch = true +predicate implicitDerefChainBorrow(Expr e, DerefChain derefChain, boolean borrow) { + CachedStage::ref() and + exists(BorrowKind bk | + any(AssocFunctionResolution::AssocFunctionCall afc) + .argumentHasImplicitDerefChainBorrow(e, derefChain, bk) and + if bk.isNoBorrow() then borrow = false else borrow = true + ) + or + e = + any(FieldExpr fe | + exists(resolveStructFieldExpr(fe, derefChain)) or - i instanceof ImplItemNode and dispatch = false - | - result = call.(AssocFunctionResolution::AssocFunctionCall).resolveCallTarget(i, _, _, _) and - not call instanceof CallExprImpl::DynamicCallExpr and - not i instanceof Builtins::BuiltinImpl - ) - } - - /** - * Gets the struct field that the field expression `fe` resolves to, if any. - */ - cached - StructField resolveStructFieldExpr(FieldExpr fe, DerefChain derefChain) { - exists(string name, DataType ty | - ty = getFieldExprLookupType(fe, pragma[only_bind_into](name), derefChain) - | - result = ty.(StructType).getTypeItem().getStructField(pragma[only_bind_into](name)) or - result = ty.(UnionType).getTypeItem().getStructField(pragma[only_bind_into](name)) - ) - } - - /** - * Gets the tuple field that the field expression `fe` resolves to, if any. - */ - cached - TupleField resolveTupleFieldExpr(FieldExpr fe, DerefChain derefChain) { - exists(int i | - result = - getTupleFieldExprLookupType(fe, pragma[only_bind_into](i), derefChain) - .(StructType) - .getTypeItem() - .getTupleField(pragma[only_bind_into](i)) - ) - } + exists(resolveTupleFieldExpr(fe, derefChain)) + ).getContainer() and + not derefChain.isEmpty() and + borrow = false +} - /** - * Gets a type at `path` that `n` infers to, if any. - * - * The type inference implementation works by computing all possible types, so - * the result is not necessarily unique. For example, in - * - * ```rust - * trait MyTrait { - * fn foo(&self) -> &Self; - * - * fn bar(&self) -> &Self { - * self.foo() - * } - * } - * - * struct MyStruct; - * - * impl MyTrait for MyStruct { - * fn foo(&self) -> &MyStruct { - * self - * } - * } - * - * fn baz() { - * let x = MyStruct; - * x.bar(); - * } - * ``` - * - * the type inference engine will roughly make the following deductions: - * - * 1. `MyStruct` has type `MyStruct`. - * 2. `x` has type `MyStruct` (via 1.). - * 3. The return type of `bar` is `&Self`. - * 3. `x.bar()` has type `&MyStruct` (via 2 and 3, by matching the implicit `Self` - * type parameter with `MyStruct`.). - * 4. The return type of `bar` is `&MyTrait`. - * 5. `x.bar()` has type `&MyTrait` (via 2 and 4). - */ - cached - Type inferType(AstNode n, TypePath path) { - Stages::TypeInferenceStage::ref() and - result = CertainTypeInference::inferCertainType(n, path) +/** + * Gets an item (function or tuple struct/variant) that `call` resolves to, if + * any. + * + * The parameter `dispatch` is `true` if and only if the resolved target is a + * trait item because a precise target could not be determined from the + * types (for instance in the presence of generics or `dyn` types) + */ +cached +Addressable resolveCallTarget(InvocationExpr call, boolean dispatch) { + dispatch = false and + result = call.(NonAssocCallExpr).resolveCallTargetViaPathResolution() + or + exists(ImplOrTraitItemNode i | + i instanceof TraitItemNode and dispatch = true or - // Don't propagate type information into a node which conflicts with certain - // type information. - forall(TypePath prefix | - CertainTypeInference::hasInferredCertainType(n, prefix) and - prefix.isPrefixOf(path) - | - not CertainTypeInference::certainTypeConflict(n, prefix, path, result) - ) and - ( - result = inferAssignmentOperationType(n, path) - or - result = inferTypeEquality(n, path) - or - result = inferFunctionCallType(n, path) - or - result = inferConstructionType(n, path) - or - result = inferOperationType(n, path) - or - result = inferFieldExprType(n, path) - or - result = inferTryExprType(n, path) - or - result = inferLiteralType(n, path, false) - or - result = inferAwaitExprType(n, path) - or - result = inferDereferencedExprPtrType(n, path) - or - result = inferForLoopExprType(n, path) - or - result = inferClosureExprType(n, path) - or - result = inferArgList(n, path) - or - result = inferDeconstructionPatType(n, path) - or - result = inferUnknownTypeFromAnnotation(n, path) - ) - } + i instanceof ImplItemNode and dispatch = false + | + result = call.(AssocFunctionResolution::AssocFunctionCall).resolveCallTarget(i, _, _, _) and + not call instanceof CallExprImpl::DynamicCallExpr and + not i instanceof Builtins::BuiltinImpl + ) } -import Cached +/** + * Gets the struct field that the field expression `fe` resolves to, if any. + */ +cached +StructField resolveStructFieldExpr(FieldExpr fe, DerefChain derefChain) { + exists(string name, DataType ty | + ty = getFieldExprLookupType(fe, pragma[only_bind_into](name), derefChain) + | + result = ty.(StructType).getTypeItem().getStructField(pragma[only_bind_into](name)) or + result = ty.(UnionType).getTypeItem().getStructField(pragma[only_bind_into](name)) + ) +} /** - * Gets a type that `n` infers to, if any. + * Gets the tuple field that the field expression `fe` resolves to, if any. */ -Type inferType(AstNode n) { result = inferType(n, TypePath::nil()) } +cached +TupleField resolveTupleFieldExpr(FieldExpr fe, DerefChain derefChain) { + exists(int i | + result = + getTupleFieldExprLookupType(fe, pragma[only_bind_into](i), derefChain) + .(StructType) + .getTypeItem() + .getTupleField(pragma[only_bind_into](i)) + ) +} + +private Type inferType_(AstNode n, TypePath path) { + // // Stages::TypeInferenceStage::ref() and + // // result = CertainTypeInference::inferCertainType(n, path) + // // or + // // Don't propagate type information into a node which conflicts with certain + // // type information. + // forall(TypePath prefix | + // CertainTypeInference::hasInferredCertainType(n, prefix) and + // prefix.isPrefixOf(path) + // | + // not CertainTypeInference::certainTypeConflict(n, prefix, path, result) + // ) and + // ( + result = inferAssignmentOperationType(n, path) + or + // result = inferTypeEquality(n, path) + // or + result = inferFunctionCallType(n, path) + or + result = inferConstructionType(n, path) + or + result = inferOperationType(n, path) + or + result = inferFieldExprType(n, path) + or + result = inferTryExprType(n, path) + or + result = inferLiteralType(n, path, false) + or + result = inferAwaitExprType(n, path) + or + result = inferDereferencedExprPtrType(n, path) + or + result = inferForLoopExprType(n, path) + or + result = inferClosureExprType(n, path) + or + result = inferArgList(n, path) + or + result = inferDeconstructionPatType(n, path) + or + result = inferUnknownTypeFromAnnotation(n, path) + // ) +} /** Provides predicates for debugging the type inference implementation. */ private module Debug { @@ -3990,7 +4018,7 @@ private module Debug { Type debugInferAnnotatedType(AstNode n, TypePath path) { n = getRelevantLocatable() and - result = inferAnnotatedType(n, path) + result = CertainTypeInference::inferAnnotatedType(n, path) } pragma[nomagic] diff --git a/shared/typeinference/codeql/typeinference/internal/TypeInference.qll b/shared/typeinference/codeql/typeinference/internal/TypeInference.qll index cf82d77b5e1d..18998d3d974f 100644 --- a/shared/typeinference/codeql/typeinference/internal/TypeInference.qll +++ b/shared/typeinference/codeql/typeinference/internal/TypeInference.qll @@ -146,17 +146,25 @@ signature module InputSig1 { } /** - * Holds if `t` is a pseudo type. Pseudo types are skipped when checking for - * non-instantiations in `isNotInstantiationOf`. + * A special pseudo type used to represent cases where the actual type needs + * to be inferred from the context. For example, in + * + * ```rust + * let x = Vec::new(); + * x.push(42); + * ``` + * + * the element type of `x` is assigned an unknown type, which allows for type + * information to flow into `x` from the call to `push`. */ - predicate isPseudoType(Type t); + class UnknownType extends Type; /** A type parameter. */ class TypeParameter extends Type; /** - * A type abstraction. I.e., a place in the program where type variables are - * introduced. + * A type abstraction. I.e., a place in the program where type variables may + * be introduced. * * Example in C#: * ```csharp @@ -171,7 +179,7 @@ signature module InputSig1 { * ``` */ class TypeAbstraction { - /** Gets a type parameter introduced by this abstraction. */ + /** Gets a type parameter introduced by this abstraction, if any. */ TypeParameter getATypeParameter(); /** Gets a textual representation of this type abstraction. */ @@ -332,6 +340,8 @@ module Make1 Input1> { * code. For example, in * * ```csharp + * class Base { } + * * class C : Base, Interface { } * ``` * @@ -341,7 +351,7 @@ module Make1 Input1> { * `TypePath` | `Type` * ---------- | ------- * `""` | ``Base`1`` - * `"0"` | `T` + * `"B"` | `T` */ signature module InputSig2 { /** @@ -666,7 +676,8 @@ module Make1 Input1> { } private Type getNonPseudoTypeAt(App app, TypePath path) { - result = app.getTypeAt(path) and not isPseudoType(result) + result = app.getTypeAt(path) and + not result instanceof UnknownType } pragma[nomagic] @@ -2127,5 +2138,313 @@ module Make1 Input1> { not exists(tm.getTypeAt(TypePath::nil())) and exists(tm.getLocation()) } } + + /** + * Provides the input to `Make3`. + */ + signature module InputSig3 { + /** + * Reference to the cached stage of type inference. Should be instantiated + * with `CachedStage::ref()`. + */ + predicate cachedStageRef(); + + /** + * Reference to the cached stage of type inference. Should be instantiated + * with `CachedStage::ref()`. + */ + default predicate cachedStageRevRef() { none() } + + /** An AST node. */ + class AstNode { + /** Gets a textual representation of this AST node. */ + string toString(); + + /** Gets the location of this AST node. */ + Location getLocation(); + } + + /** Gets the type annotation that applies to `n`, if any. */ + TypeMention getTypeAnnotation(AstNode n); + + /** A variable, for example a local variable or a field. */ + class Variable { + AstNode getDefiningNode(); + + AstNode getAnAccess(); + + /** Gets a textual representation of this element. */ + string toString(); + + /** Gets the location of this element. */ + Location getLocation(); + } + + /** + * An assignment where type information can flow from one operand to the + * other. + */ + class Assignment extends AstNode { + /** + * Holds if this assignment is a coercion site, meaning that the type of the right + * operand may have to be coerced to the type of the left operand. + */ + predicate isCoercionSite(); + + /** Gets the left operand of this binary expression. */ + AstNode getLeftOperand(); + + /** Gets the right operand of this binary expression. */ + AstNode getRightOperand(); + } + + /** A parenthesized expression. */ + class ParenExpr extends AstNode { + AstNode getExpr(); + } + + /** A ternary conditional expression. */ + class ConditionalExpr extends AstNode { + /** Gets the condition of this expression. */ + AstNode getCondition(); + + /** Gets the true branch of this expression. */ + AstNode getThen(); + + /** Gets the false branch of this expression. */ + AstNode getElse(); + } + + /** + * Holds if the types of `n1` at `path1` and `n2` at `path2` are certainly equal. + */ + predicate certainTypeEqualityInput(AstNode n1, TypePath path1, AstNode n2, TypePath path2); + + /** Gets the inferred certain type of `n` at `path`. */ + Type inferCertainTypeInput(AstNode n, TypePath path); + + /** + * Holds if `child` is a child of `parent`, and a least upper bound (LUB) coercion + * may be applied to infer the type of `parent` from the type of `child`. + * + * In this case, we want type information to only flow from `child` to `parent`, + * to avoid (a) either having to model LUB coercions, or (b) risk combinatorial + * explosion in inferred types. + */ + predicate lubCoercionInput(AstNode parent, AstNode child, TypePath prefix); + + /** + * Holds if the type tree of `n1` at `path1` should be equal to the type tree + * of `n2` at `prefix2`, but type information should only propagate from `n1` to + * `n2`. + */ + predicate typeEqualityAsymmetricInput(AstNode n1, TypePath path1, AstNode n2, TypePath path2); + + /** + * Holds if the types of `n1` at `path1` and `n2` at `path2` are possibly equal. + */ + predicate typeEqualityInput(AstNode n1, TypePath path1, AstNode n2, TypePath path2); + + /** Gets the inferred type of `n` at `path`. */ + Type inferTypeInput(AstNode n, TypePath path); + } + + module Make3 { + private import Input3 + + /** Provides logic for inferring certain type information. */ + module CertainTypeInference { + /** Gets the type of `n`, which has an explicit type annotation. */ + pragma[nomagic] + Type inferAnnotatedType(AstNode n, TypePath path) { + result = getTypeAnnotation(n).getTypeAt(path) + } + + predicate certainTypeEquality(AstNode n1, TypePath path1, AstNode n2, TypePath path2) { + path1.isEmpty() and + path2.isEmpty() and + ( + exists(Variable v | n1 = v.getAnAccess() and n2 = v.getDefiningNode()) + or + exists(Assignment a | + not a.isCoercionSite() and + n1 = a.getLeftOperand() and + n2 = a.getRightOperand() + ) + or + n1 = n2.(ParenExpr).getExpr() + ) + or + certainTypeEqualityInput(n1, path1, n2, path2) + } + + pragma[nomagic] + private Type inferCertainTypeEquality(AstNode n, TypePath path) { + exists(TypePath prefix1, AstNode n2, TypePath prefix2, TypePath suffix | + result = inferCertainType(n2, prefix2.appendInverse(suffix)) and + path = prefix1.append(suffix) + | + certainTypeEquality(n, prefix1, n2, prefix2) + or + certainTypeEquality(n2, prefix2, n, prefix1) + ) + } + + /** Gets the inferred certain type of `n` at `path`. */ + cached + Type inferCertainType(AstNode n, TypePath path) { + result = inferAnnotatedType(n, path) and + cachedStageRef() + or + result = inferCertainTypeEquality(n, path) + or + result = inferCertainTypeInput(n, path) + or + infersCertainTypeAt(n, path, result.getATypeParameter()) + } + + /** + * Holds if `n` has complete and certain type information at the type path + * `prefix.tp`. This entails that the type at `prefix` must be the type + * that declares `tp`. + */ + pragma[nomagic] + private predicate infersCertainTypeAt(AstNode n, TypePath prefix, TypeParameter tp) { + exists(TypePath path | + exists(inferCertainType(n, path)) and + path.isSnoc(prefix, tp) + ) + } + + /** + * Holds if `n` has complete and certain type information at `path`. + */ + pragma[nomagic] + predicate hasInferredCertainType(AstNode n, TypePath path) { + exists(inferCertainType(n, path)) + } + + /** + * Holds if `n` having type `t` at `path` conflicts with certain type information + * at `prefix`. + */ + bindingset[n, prefix, path, t] + pragma[inline_late] + predicate certainTypeConflict(AstNode n, TypePath prefix, TypePath path, Type t) { + inferCertainType(n, path) != t + or + // If we infer that `n` has _some_ type at `T1.T2....Tn`, and we also + // know that `n` certainly has type `certainType` at `T1.T2...Ti`, `0 <= i < n`, + // then it must be the case that `T(i+1)` is a type parameter of `certainType`, + // otherwise there is a conflict. + // + // Below, `prefix` is `T1.T2...Ti` and `tp` is `T(i+1)`. + exists(TypePath suffix, TypeParameter tp, Type certainType | + path = prefix.appendInverse(suffix) and + tp = suffix.getHead() and + inferCertainType(n, prefix) = certainType and + not certainType.getATypeParameter() = tp + ) + } + } + + private predicate typeEquality(AstNode n1, TypePath path1, AstNode n2, TypePath path2) { + CertainTypeInference::certainTypeEquality(n1, path1, n2, path2) + or + path1.isEmpty() and + path2.isEmpty() and + exists(Assignment a | + a.getLeftOperand() = n1 and + a.getRightOperand() = n2 + ) + or + typeEqualityInput(n1, path1, n2, path2) + } + + private predicate lubCoercion(AstNode parent, AstNode child, TypePath prefix) { + parent = any(ConditionalExpr ce | child = [ce.getThen(), ce.getElse()]) and + prefix.isEmpty() + or + lubCoercionInput(parent, child, prefix) + } + + private predicate typeEqualityAsymmetric( + AstNode n1, TypePath path1, AstNode n2, TypePath path2 + ) { + lubCoercion(n2, n1, path2) and + path1.isEmpty() + or + exists(AstNode mid, TypePath pathMid, TypePath suffix | + typeEquality(n1, pathMid, mid, path2) or + typeEquality(mid, path2, n1, pathMid) + | + lubCoercion(mid, n2, suffix) and + not lubCoercion(mid, n1, _) and + path1 = pathMid.append(suffix) + ) + or + typeEqualityAsymmetricInput(n1, path1, n2, path2) + } + + pragma[nomagic] + private Type inferTypeEquality(AstNode n, TypePath path) { + exists(TypePath prefix1, AstNode n2, TypePath prefix2, TypePath suffix | + result = inferType(n2, prefix2.appendInverse(suffix)) and + path = prefix1.append(suffix) + | + typeEquality(n, prefix1, n2, prefix2) + or + typeEquality(n2, prefix2, n, prefix1) + or + typeEqualityAsymmetric(n2, prefix2, n, prefix1) + ) + } + + /** + * Gets the inferred type of `n` at `path`. + */ + cached + Type inferType(AstNode n, TypePath path) { + cachedStageRef() and + result = CertainTypeInference::inferCertainType(n, path) + or + // Don't propagate type information into a node which conflicts with certain + // type information. + forall(TypePath prefix | + CertainTypeInference::hasInferredCertainType(n, prefix) and + prefix.isPrefixOf(path) + | + not CertainTypeInference::certainTypeConflict(n, prefix, path, result) + ) and + ( + result = inferTypeEquality(n, path) + or + result = inferTypeInput(n, path) + ) + } + + /** + * Gets the inferred root type of `n`, if any. + */ + Type inferType(AstNode n) { result = inferType(n, TypePath::nil()) } + + /** The cached stage of type inference. */ + cached + module CachedStage { + /** Reference to the cached stage of type inference. */ + cached + predicate ref() { any() } + + /** Reverse references to the predicates that reference `ref()`. */ + cached + predicate revRef() { + (exists(CertainTypeInference::inferCertainType(_, _)) implies any()) + or + (exists(inferType(_, _)) implies any()) + or + cachedStageRevRef() + } + } + } } } From 2896852e47defc4c04884e4ec9b112f0576d13c4 Mon Sep 17 00:00:00 2001 From: Tom Hvitved Date: Tue, 5 May 2026 13:27:39 +0200 Subject: [PATCH 02/18] wip --- .../internal/typeinference/TypeInference.qll | 231 ++++-------------- .../typeinference/internal/TypeInference.qll | 2 +- 2 files changed, 44 insertions(+), 189 deletions(-) diff --git a/rust/ql/lib/codeql/rust/internal/typeinference/TypeInference.qll b/rust/ql/lib/codeql/rust/internal/typeinference/TypeInference.qll index 7ecf2276f7b9..e1cbbda6f4f4 100644 --- a/rust/ql/lib/codeql/rust/internal/typeinference/TypeInference.qll +++ b/rust/ql/lib/codeql/rust/internal/typeinference/TypeInference.qll @@ -290,9 +290,22 @@ private module Input3 implements InputSig3 { class AstNode = Rust::AstNode; - predicate getTypeAnnotation = getTypeAnnotation_/1; + TypeMention getTypeAnnotation(AstNode n) { + exists(LetStmt let | + n = let.getPat() and + result = let.getTypeRepr() + ) + or + result = n.(SelfParam).getTypeRepr() + or + exists(Param p | + n = p.getPat() and + result = p.getTypeRepr() + ) + or + result = n.(ShorthandSelfParameterMention) + } - /** A variable, for example a local variable or a field. */ class Variable extends Rust::Variable { AstNode getDefiningNode() { result = this.getPat().getName() or @@ -511,28 +524,6 @@ private Type getCallExprTypeArgument(CallExpr ce, TypeArgumentPosition apos, Typ ) } -/** Gets the type annotation that applies to `n`, if any. */ -private TypeMention getTypeAnnotation_(AstNode n) { - exists(LetStmt let | - n = let.getPat() and - result = let.getTypeRepr() - ) - or - result = n.(SelfParam).getTypeRepr() - or - exists(Param p | - n = p.getPat() and - result = p.getTypeRepr() - ) - or - result = n.(ShorthandSelfParameterMention) -} - -// /** Gets the type of `n`, which has an explicit type annotation. */ -// pragma[nomagic] -// private Type inferAnnotatedType(AstNode n, TypePath path) { -// result = getTypeAnnotation(n).getTypeAt(path) -// } pragma[nomagic] private Type inferFunctionBodyType(AstNode n, TypePath path) { exists(Function f | @@ -656,30 +647,6 @@ module CertainTypeInference_ { } predicate certainTypeEquality_(AstNode n1, TypePath prefix1, AstNode n2, TypePath prefix2) { - // prefix1.isEmpty() and - // prefix2.isEmpty() and - // ( - // exists(Variable v | n1 = v.getAnAccess() | - // n2 = v.getPat().getName() or n2 = v.getParameter().(SelfParam) - // ) - // or - // // // A `let` statement with a type annotation is a coercion site and hence - // // // is not a certain type equality. - // // exists(LetStmt let | - // // not let.hasTypeRepr() and - // // identLetStmt(let, n1, n2) - // // ) - // // or - // // exists(LetExpr let | - // // // Similarly as for let statements, we need to rule out binding modes - // // // changing the type. - // // let.getPat().(IdentPat) = n1 and - // // let.getScrutinee() = n2 - // // ) - // // or - // n1 = n2.(ParenExpr).getExpr() - // ) - // or n1 = any(IdentPat ip | n2 = ip.getName() and @@ -708,31 +675,15 @@ module CertainTypeInference_ { ) } - // pragma[nomagic] - // private Type inferCertainTypeEquality(AstNode n, TypePath path) { - // exists(TypePath prefix1, AstNode n2, TypePath prefix2, TypePath suffix | - // result = inferCertainType(n2, prefix2.appendInverse(suffix)) and - // path = prefix1.append(suffix) - // | - // certainTypeEquality(n, prefix1, n2, prefix2) - // or - // certainTypeEquality(n2, prefix2, n, prefix1) - // ) - // } /** * Holds if `n` has complete and certain type information and if `n` has the * resulting type at `path`. */ Type inferCertainType_(AstNode n, TypePath path) { - // result = inferAnnotatedType(n, path) and - // Stages::TypeInferenceStage::ref() - // or result = inferFunctionBodyType(n, path) or result = inferCertainCallExprType(n, path) or - // result = inferCertainTypeEquality(n, path) - // or result = inferLiteralType(n, path, true) or result = inferRefPatType(n) and @@ -771,48 +722,7 @@ module CertainTypeInference_ { n instanceof ClosureExpr and path.isEmpty() and result = closureRootType() - // or - // infersCertainTypeAt(n, path, result.getATypeParameter()) - } - // /** - // * Holds if `n` has complete and certain type information at the type path - // * `prefix.tp`. This entails that the type at `prefix` must be the type - // * that declares `tp`. - // */ - // pragma[nomagic] - // private predicate infersCertainTypeAt(AstNode n, TypePath prefix, TypeParameter tp) { - // exists(TypePath path | - // exists(inferCertainType(n, path)) and - // path.isSnoc(prefix, tp) - // ) - // } - // /** - // * Holds if `n` has complete and certain type information at `path`. - // */ - // pragma[nomagic] - // predicate hasInferredCertainType(AstNode n, TypePath path) { exists(inferCertainType(n, path)) } - // /** - // * Holds if `n` having type `t` at `path` conflicts with certain type information - // * at `prefix`. - // */ - // bindingset[n, prefix, path, t] - // pragma[inline_late] - // predicate certainTypeConflict(AstNode n, TypePath prefix, TypePath path, Type t) { - // inferCertainType(n, path) != t - // or - // // If we infer that `n` has _some_ type at `T1.T2....Tn`, and we also - // // know that `n` certainly has type `certainType` at `T1.T2...Ti`, `0 <= i < n`, - // // then it must be the case that `T(i+1)` is a type parameter of `certainType`, - // // otherwise there is a conflict. - // // - // // Below, `prefix` is `T1.T2...Ti` and `tp` is `T(i+1)`. - // exists(TypePath suffix, TypeParameter tp, Type certainType | - // path = prefix.appendInverse(suffix) and - // tp = suffix.getHead() and - // inferCertainType(n, prefix) = certainType and - // not certainType.getATypeParameter() = tp - // ) - // } + } } private Type inferLogicalOperationType(AstNode n, TypePath path) { @@ -864,27 +774,15 @@ private predicate bodyReturns(Expr body, Expr e) { * through the type equality. */ private predicate typeEquality_(AstNode n1, TypePath prefix1, AstNode n2, TypePath prefix2) { - // CertainTypeInference::certainTypeEquality(n1, prefix1, n2, prefix2) - // or prefix1.isEmpty() and prefix2.isEmpty() and ( - // exists(LetStmt let | - // let.getPat() = n1 and - // let.getInitializer() = n2 - // ) - // or n2 = any(MatchExpr me | n1 = me.getAnArm().getExpr() and me.getNumberOfArms() = 1 ) or - // exists(LetExpr let | - // n1 = let.getScrutinee() and - // n2 = let.getPat() - // ) - // or exists(MatchExpr me | n1 = me.getScrutinee() and n2 = me.getAnArm().getPat() @@ -982,9 +880,6 @@ private predicate typeEquality_(AstNode n1, TypePath prefix1, AstNode n2, TypePa * [1]: https://doc.rust-lang.org/reference/type-coercions.html#r-coerce.least-upper-bound */ private predicate lubCoercion_(AstNode parent, AstNode child, TypePath prefix) { - // child = parent.(IfExpr).getABranch() and - // prefix.isEmpty() - // or parent = any(MatchExpr me | child = me.getAnArm().getExpr() and @@ -1032,18 +927,6 @@ private Type inferUnknownTypeFromAnnotation(AstNode n, TypePath path) { * `n2`. */ private predicate typeEqualityAsymmetric_(AstNode n1, TypePath prefix1, AstNode n2, TypePath prefix2) { - // lubCoercion(n2, n1, prefix2) and - // prefix1.isEmpty() - // or - // exists(AstNode mid, TypePath prefixMid, TypePath suffix | - // typeEquality(n1, prefixMid, mid, prefix2) or - // typeEquality(mid, prefix2, n1, prefixMid) - // | - // lubCoercion(mid, n2, suffix) and - // not lubCoercion(mid, n1, _) and - // prefix1 = prefixMid.append(suffix) - // ) - // or // When `n2` is `*n1` propagate type information from a raw pointer type // parameter at `n1`. The other direction is handled in // `inferDereferencedExprPtrType`. @@ -1052,19 +935,6 @@ private predicate typeEqualityAsymmetric_(AstNode n1, TypePath prefix1, AstNode prefix2.isEmpty() } -// pragma[nomagic] -// private Type inferTypeEquality(AstNode n, TypePath path) { -// exists(TypePath prefix1, AstNode n2, TypePath prefix2, TypePath suffix | -// result = inferType(n2, prefix2.appendInverse(suffix)) and -// path = prefix1.append(suffix) -// | -// typeEquality(n, prefix1, n2, prefix2) -// or -// typeEquality(n2, prefix2, n, prefix1) -// or -// typeEqualityAsymmetric(n2, prefix2, n, prefix1) -// ) -// } pragma[nomagic] private TupleType inferTupleRootType(AstNode n) { // `typeEquality` handles the non-root cases @@ -3362,6 +3232,19 @@ private Type getFieldExprLookupType(FieldExpr fe, string name, DerefChain derefC ) } +/** + * Gets the struct field that the field expression `fe` resolves to, if any. + */ +cached +StructField resolveStructFieldExpr(FieldExpr fe, DerefChain derefChain) { + exists(string name, DataType ty | + ty = getFieldExprLookupType(fe, pragma[only_bind_into](name), derefChain) + | + result = ty.(StructType).getTypeItem().getStructField(pragma[only_bind_into](name)) or + result = ty.(UnionType).getTypeItem().getStructField(pragma[only_bind_into](name)) + ) +} + pragma[nomagic] private Type getTupleFieldExprLookupType(FieldExpr fe, int pos, DerefChain derefChain) { exists(string name | @@ -3370,6 +3253,20 @@ private Type getTupleFieldExprLookupType(FieldExpr fe, int pos, DerefChain deref ) } +/** + * Gets the tuple field that the field expression `fe` resolves to, if any. + */ +cached +TupleField resolveTupleFieldExpr(FieldExpr fe, DerefChain derefChain) { + exists(int i | + result = + getTupleFieldExprLookupType(fe, pragma[only_bind_into](i), derefChain) + .(StructType) + .getTypeItem() + .getTupleField(pragma[only_bind_into](i)) + ) +} + /** * A matching configuration for resolving types of field expressions like `x.field`. */ @@ -3897,50 +3794,9 @@ Addressable resolveCallTarget(InvocationExpr call, boolean dispatch) { ) } -/** - * Gets the struct field that the field expression `fe` resolves to, if any. - */ -cached -StructField resolveStructFieldExpr(FieldExpr fe, DerefChain derefChain) { - exists(string name, DataType ty | - ty = getFieldExprLookupType(fe, pragma[only_bind_into](name), derefChain) - | - result = ty.(StructType).getTypeItem().getStructField(pragma[only_bind_into](name)) or - result = ty.(UnionType).getTypeItem().getStructField(pragma[only_bind_into](name)) - ) -} - -/** - * Gets the tuple field that the field expression `fe` resolves to, if any. - */ -cached -TupleField resolveTupleFieldExpr(FieldExpr fe, DerefChain derefChain) { - exists(int i | - result = - getTupleFieldExprLookupType(fe, pragma[only_bind_into](i), derefChain) - .(StructType) - .getTypeItem() - .getTupleField(pragma[only_bind_into](i)) - ) -} - private Type inferType_(AstNode n, TypePath path) { - // // Stages::TypeInferenceStage::ref() and - // // result = CertainTypeInference::inferCertainType(n, path) - // // or - // // Don't propagate type information into a node which conflicts with certain - // // type information. - // forall(TypePath prefix | - // CertainTypeInference::hasInferredCertainType(n, prefix) and - // prefix.isPrefixOf(path) - // | - // not CertainTypeInference::certainTypeConflict(n, prefix, path, result) - // ) and - // ( result = inferAssignmentOperationType(n, path) or - // result = inferTypeEquality(n, path) - // or result = inferFunctionCallType(n, path) or result = inferConstructionType(n, path) @@ -3966,7 +3822,6 @@ private Type inferType_(AstNode n, TypePath path) { result = inferDeconstructionPatType(n, path) or result = inferUnknownTypeFromAnnotation(n, path) - // ) } /** Provides predicates for debugging the type inference implementation. */ diff --git a/shared/typeinference/codeql/typeinference/internal/TypeInference.qll b/shared/typeinference/codeql/typeinference/internal/TypeInference.qll index 18998d3d974f..b544e8495483 100644 --- a/shared/typeinference/codeql/typeinference/internal/TypeInference.qll +++ b/shared/typeinference/codeql/typeinference/internal/TypeInference.qll @@ -2235,7 +2235,7 @@ module Make1 Input1> { /** * Holds if the type tree of `n1` at `path1` should be equal to the type tree - * of `n2` at `prefix2`, but type information should only propagate from `n1` to + * of `n2` at `path2`, but type information should only propagate from `n1` to * `n2`. */ predicate typeEqualityAsymmetricInput(AstNode n1, TypePath path1, AstNode n2, TypePath path2); From 4f168a4ddb0a888e8bbb3b61f3180026868029e5 Mon Sep 17 00:00:00 2001 From: Tom Hvitved Date: Tue, 5 May 2026 13:45:25 +0200 Subject: [PATCH 03/18] wip2 --- .../internal/typeinference/TypeInference.qll | 428 +++++++++--------- .../typeinference/internal/TypeInference.qll | 18 +- 2 files changed, 211 insertions(+), 235 deletions(-) diff --git a/rust/ql/lib/codeql/rust/internal/typeinference/TypeInference.qll b/rust/ql/lib/codeql/rust/internal/typeinference/TypeInference.qll index e1cbbda6f4f4..269e4835803e 100644 --- a/rust/ql/lib/codeql/rust/internal/typeinference/TypeInference.qll +++ b/rust/ql/lib/codeql/rust/internal/typeinference/TypeInference.qll @@ -276,8 +276,6 @@ import M2 private module Input3 implements InputSig3 { private import rust as Rust - predicate cachedStageRef = CachedStage::ref/0; - predicate cachedStageRevRef() { (implicitDerefChainBorrow(_, _, _) implies any()) or @@ -342,6 +340,14 @@ private module Input3 implements InputSig3 { override AstNode getRightOperand() { result = this.getInitializer() } } + private class AssignmentExprAssignment extends Assignment, AssignmentExpr { + override predicate isCoercionSite() { any() } + + override AstNode getLeftOperand() { result = this.getLhs() } + + override AstNode getRightOperand() { result = this.getRhs() } + } + class ParenExpr extends AstNode, Rust::ParenExpr { AstNode getExpr() { result = super.getExpr() } } @@ -355,20 +361,207 @@ private module Input3 implements InputSig3 { AstNode getElse() { result = super.getElse() } } - predicate certainTypeEqualityInput = CertainTypeInference_::certainTypeEquality_/4; + predicate certainTypeEqualityInput(AstNode n1, TypePath prefix1, AstNode n2, TypePath prefix2) { + n1 = + any(IdentPat ip | + n2 = ip.getName() and + prefix1.isEmpty() and + if ip.isRef() + then + exists(boolean isMutable | if ip.isMut() then isMutable = true else isMutable = false | + prefix2 = TypePath::singleton(getRefTypeParameter(isMutable)) + ) + 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() + ) + } - predicate inferCertainTypeInput = CertainTypeInference_::inferCertainType_/2; + predicate inferCertainTypeInput = CertainTypeInferenceInput::inferCertainTypeInput/2; - predicate lubCoercionInput = lubCoercion_/3; + /** + * Holds if `child` is a child of `parent`, and the Rust compiler applies [least + * upper bound (LUB) coercion][1] to infer the type of `parent` from the type of + * `child`. + * + * In this case, we want type information to only flow from `child` to `parent`, + * to avoid (a) either having to model LUB coercions, or (b) risk combinatorial + * explosion in inferred types. + * + * [1]: https://doc.rust-lang.org/reference/type-coercions.html#r-coerce.least-upper-bound + */ + predicate lubCoercionInput(AstNode parent, AstNode child, TypePath prefix) { + parent = + any(MatchExpr me | + child = me.getAnArm().getExpr() and + me.getNumberOfArms() > 1 + ) and + prefix.isEmpty() + or + parent = + any(ArrayListExpr ale | + child = ale.getAnExpr() and + ale.getNumberOfExprs() > 1 + ) and + prefix = TypePath::singleton(getArrayTypeParameter()) + or + bodyReturns(parent, child) and + 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 + s = getRangeType(parent) + ) + } - predicate typeEqualityAsymmetricInput = typeEqualityAsymmetric_/4; + predicate typeEqualityAsymmetricInput(AstNode n1, TypePath prefix1, AstNode n2, TypePath prefix2) { + // When `n2` is `*n1` propagate type information from a raw pointer type + // parameter at `n1`. The other direction is handled in + // `inferDereferencedExprPtrType`. + n1 = n2.(DerefExpr).getExpr() and + prefix1 = TypePath::singleton(getPtrTypeParameter()) and + prefix2.isEmpty() + } - predicate typeEqualityInput = typeEquality_/4; + predicate typeEqualityInput(AstNode n1, TypePath prefix1, AstNode n2, TypePath prefix2) { + prefix1.isEmpty() and + prefix2.isEmpty() and + ( + n2 = + any(MatchExpr me | + n1 = me.getAnArm().getExpr() and + me.getNumberOfArms() = 1 + ) + or + exists(MatchExpr me | + n1 = me.getScrutinee() and + n2 = me.getAnArm().getPat() + ) + or + n1 = n2.(OrPat).getAPat() + or + n1 = n2.(ParenPat).getPat() + or + n1 = n2.(LiteralPat).getLiteral() + or + exists(BreakExpr break | + break.getExpr() = n1 and + break.getTarget() = n2.(LoopExpr) + ) + or + n1 = n2.(MacroExpr).getMacroCall().getMacroCallExpansion() and + not isPanicMacroCall(n2) + or + n1 = n2.(MacroPat).getMacroCall().getMacroCallExpansion() + or + bodyReturns(n1, n2) and + strictcount(Expr e | bodyReturns(n1, e)) = 1 + ) + or + n2 = + any(RefExpr re | + n1 = re.getExpr() and + prefix1.isEmpty() and + prefix2 = TypePath::singleton(inferRefExprType(re).getPositionalTypeParameter(0)) + ) + or + n2 = + any(RefPat rp | + n1 = rp.getPat() and + prefix1.isEmpty() and + exists(boolean isMutable | if rp.isMut() then isMutable = true else isMutable = false | + prefix2 = TypePath::singleton(getRefTypeParameter(isMutable)) + ) + ) + or + exists(int i, int arity | + prefix1.isEmpty() and + prefix2 = TypePath::singleton(getTupleTypeParameter(arity, i)) + | + arity = n2.(TupleExpr).getNumberOfFields() and + n1 = n2.(TupleExpr).getField(i) + or + arity = n2.(TuplePat).getTupleArity() and + n1 = n2.(TuplePat).getField(i) + ) + or + exists(BlockExpr be | + n1 = be and + n2 = be.getStmtList().getTailExpr() and + if be.isAsync() + then + prefix1 = TypePath::singleton(getDynFutureOutputTypeParameter()) and + prefix2.isEmpty() + else ( + prefix1.isEmpty() and + prefix2.isEmpty() + ) + ) + or + // an array list expression with only one element (such as `[1]`) has type from that element + n1 = + any(ArrayListExpr ale | + ale.getAnExpr() = n2 and + ale.getNumberOfExprs() = 1 + ) and + prefix1 = TypePath::singleton(getArrayTypeParameter()) and + prefix2.isEmpty() + or + // an array repeat expression (`[1; 3]`) has the type of the repeat operand + n1.(ArrayRepeatExpr).getRepeatOperand() = n2 and + prefix1 = TypePath::singleton(getArrayTypeParameter()) and + prefix2.isEmpty() + } - predicate inferTypeInput = inferType_/2; + Type inferTypeInput(AstNode n, TypePath path) { + result = inferAssignmentOperationType(n, path) + or + result = inferFunctionCallType(n, path) + or + result = inferConstructionType(n, path) + or + result = inferOperationType(n, path) + or + result = inferFieldExprType(n, path) + or + result = inferTryExprType(n, path) + or + result = inferLiteralType(n, path, false) + or + result = inferAwaitExprType(n, path) + or + result = inferDereferencedExprPtrType(n, path) + or + result = inferForLoopExprType(n, path) + or + result = inferClosureExprType(n, path) + or + result = inferArgList(n, path) + or + result = inferDeconstructionPatType(n, path) + or + result = inferUnknownTypeFromAnnotation(n, path) + } } -// private import Input3 private module M3 = Make3; import M3 @@ -582,7 +775,7 @@ private TypePath closureParameterPath(int arity, int index) { } /** Module for inferring certain type information. */ -module CertainTypeInference_ { +private module CertainTypeInferenceInput { pragma[nomagic] private predicate callResolvesTo(CallExpr ce, Path p, Function f) { p = CallExprImpl::getFunctionPath(ce) and @@ -646,40 +839,11 @@ module CertainTypeInference_ { result = sp.getPath().(TypeMention).getTypeAt(path) } - predicate certainTypeEquality_(AstNode n1, TypePath prefix1, AstNode n2, TypePath prefix2) { - n1 = - any(IdentPat ip | - n2 = ip.getName() and - prefix1.isEmpty() and - if ip.isRef() - then - exists(boolean isMutable | if ip.isMut() then isMutable = true else isMutable = false | - prefix2 = TypePath::singleton(getRefTypeParameter(isMutable)) - ) - 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() - ) - } - /** * Holds if `n` has complete and certain type information and if `n` has the * resulting type at `path`. */ - Type inferCertainType_(AstNode n, TypePath path) { + Type inferCertainTypeInput(AstNode n, TypePath path) { result = inferFunctionBodyType(n, path) or result = inferCertainCallExprType(n, path) @@ -768,146 +932,6 @@ private predicate bodyReturns(Expr body, Expr e) { ) } -/** - * Holds if the type tree of `n1` at `prefix1` should be equal to the type tree - * of `n2` at `prefix2` and type information should propagate in both directions - * through the type equality. - */ -private predicate typeEquality_(AstNode n1, TypePath prefix1, AstNode n2, TypePath prefix2) { - prefix1.isEmpty() and - prefix2.isEmpty() and - ( - n2 = - any(MatchExpr me | - n1 = me.getAnArm().getExpr() and - me.getNumberOfArms() = 1 - ) - or - exists(MatchExpr me | - n1 = me.getScrutinee() and - n2 = me.getAnArm().getPat() - ) - or - n1 = n2.(OrPat).getAPat() - or - n1 = n2.(ParenPat).getPat() - or - n1 = n2.(LiteralPat).getLiteral() - or - exists(BreakExpr break | - break.getExpr() = n1 and - break.getTarget() = n2.(LoopExpr) - ) - or - exists(AssignmentExpr be | - n1 = be.getLhs() and - n2 = be.getRhs() - ) - or - n1 = n2.(MacroExpr).getMacroCall().getMacroCallExpansion() and - not isPanicMacroCall(n2) - or - n1 = n2.(MacroPat).getMacroCall().getMacroCallExpansion() - or - bodyReturns(n1, n2) and - strictcount(Expr e | bodyReturns(n1, e)) = 1 - ) - or - n2 = - any(RefExpr re | - n1 = re.getExpr() and - prefix1.isEmpty() and - prefix2 = TypePath::singleton(inferRefExprType(re).getPositionalTypeParameter(0)) - ) - or - n2 = - any(RefPat rp | - n1 = rp.getPat() and - prefix1.isEmpty() and - exists(boolean isMutable | if rp.isMut() then isMutable = true else isMutable = false | - prefix2 = TypePath::singleton(getRefTypeParameter(isMutable)) - ) - ) - or - exists(int i, int arity | - prefix1.isEmpty() and - prefix2 = TypePath::singleton(getTupleTypeParameter(arity, i)) - | - arity = n2.(TupleExpr).getNumberOfFields() and - n1 = n2.(TupleExpr).getField(i) - or - arity = n2.(TuplePat).getTupleArity() and - n1 = n2.(TuplePat).getField(i) - ) - or - exists(BlockExpr be | - n1 = be and - n2 = be.getStmtList().getTailExpr() and - if be.isAsync() - then - prefix1 = TypePath::singleton(getDynFutureOutputTypeParameter()) and - prefix2.isEmpty() - else ( - prefix1.isEmpty() and - prefix2.isEmpty() - ) - ) - or - // an array list expression with only one element (such as `[1]`) has type from that element - n1 = - any(ArrayListExpr ale | - ale.getAnExpr() = n2 and - ale.getNumberOfExprs() = 1 - ) and - prefix1 = TypePath::singleton(getArrayTypeParameter()) and - prefix2.isEmpty() - or - // an array repeat expression (`[1; 3]`) has the type of the repeat operand - n1.(ArrayRepeatExpr).getRepeatOperand() = n2 and - prefix1 = TypePath::singleton(getArrayTypeParameter()) and - prefix2.isEmpty() -} - -/** - * Holds if `child` is a child of `parent`, and the Rust compiler applies [least - * upper bound (LUB) coercion][1] to infer the type of `parent` from the type of - * `child`. - * - * In this case, we want type information to only flow from `child` to `parent`, - * to avoid (a) either having to model LUB coercions, or (b) risk combinatorial - * explosion in inferred types. - * - * [1]: https://doc.rust-lang.org/reference/type-coercions.html#r-coerce.least-upper-bound - */ -private predicate lubCoercion_(AstNode parent, AstNode child, TypePath prefix) { - parent = - any(MatchExpr me | - child = me.getAnArm().getExpr() and - me.getNumberOfArms() > 1 - ) and - prefix.isEmpty() - or - parent = - any(ArrayListExpr ale | - child = ale.getAnExpr() and - ale.getNumberOfExprs() > 1 - ) and - prefix = TypePath::singleton(getArrayTypeParameter()) - or - bodyReturns(parent, child) and - 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 - s = getRangeType(parent) - ) -} - 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 @@ -921,20 +945,6 @@ private Type inferUnknownTypeFromAnnotation(AstNode n, TypePath path) { ) } -/** - * 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 - * `n2`. - */ -private predicate typeEqualityAsymmetric_(AstNode n1, TypePath prefix1, AstNode n2, TypePath prefix2) { - // When `n2` is `*n1` propagate type information from a raw pointer type - // parameter at `n1`. The other direction is handled in - // `inferDereferencedExprPtrType`. - n1 = n2.(DerefExpr).getExpr() and - prefix1 = TypePath::singleton(getPtrTypeParameter()) and - prefix2.isEmpty() -} - pragma[nomagic] private TupleType inferTupleRootType(AstNode n) { // `typeEquality` handles the non-root cases @@ -3794,36 +3804,6 @@ Addressable resolveCallTarget(InvocationExpr call, boolean dispatch) { ) } -private Type inferType_(AstNode n, TypePath path) { - result = inferAssignmentOperationType(n, path) - or - result = inferFunctionCallType(n, path) - or - result = inferConstructionType(n, path) - or - result = inferOperationType(n, path) - or - result = inferFieldExprType(n, path) - or - result = inferTryExprType(n, path) - or - result = inferLiteralType(n, path, false) - or - result = inferAwaitExprType(n, path) - or - result = inferDereferencedExprPtrType(n, path) - or - result = inferForLoopExprType(n, path) - or - result = inferClosureExprType(n, path) - or - result = inferArgList(n, path) - or - result = inferDeconstructionPatType(n, path) - or - result = inferUnknownTypeFromAnnotation(n, path) -} - /** Provides predicates for debugging the type inference implementation. */ private module Debug { Locatable getRelevantLocatable() { diff --git a/shared/typeinference/codeql/typeinference/internal/TypeInference.qll b/shared/typeinference/codeql/typeinference/internal/TypeInference.qll index b544e8495483..b3298834b8ce 100644 --- a/shared/typeinference/codeql/typeinference/internal/TypeInference.qll +++ b/shared/typeinference/codeql/typeinference/internal/TypeInference.qll @@ -2144,14 +2144,8 @@ module Make1 Input1> { */ signature module InputSig3 { /** - * Reference to the cached stage of type inference. Should be instantiated - * with `CachedStage::ref()`. - */ - predicate cachedStageRef(); - - /** - * Reference to the cached stage of type inference. Should be instantiated - * with `CachedStage::ref()`. + * References to cached predicates that should be included to the cached + * stage of type inference. Such predicates should reference `CachedStage::ref`. */ default predicate cachedStageRevRef() { none() } @@ -2169,8 +2163,10 @@ module Make1 Input1> { /** A variable, for example a local variable or a field. */ class Variable { + /** Gets the AST node that defines this variable. */ AstNode getDefiningNode(); + /** Gets an access to this variable. */ AstNode getAnAccess(); /** Gets a textual representation of this element. */ @@ -2293,8 +2289,8 @@ module Make1 Input1> { /** Gets the inferred certain type of `n` at `path`. */ cached Type inferCertainType(AstNode n, TypePath path) { - result = inferAnnotatedType(n, path) and - cachedStageRef() + CachedStage::ref() and + result = inferAnnotatedType(n, path) or result = inferCertainTypeEquality(n, path) or @@ -2405,7 +2401,7 @@ module Make1 Input1> { */ cached Type inferType(AstNode n, TypePath path) { - cachedStageRef() and + CachedStage::ref() and result = CertainTypeInference::inferCertainType(n, path) or // Don't propagate type information into a node which conflicts with certain From 52beb6f450509be187a4d28746b9d92df26d632d Mon Sep 17 00:00:00 2001 From: Tom Hvitved Date: Tue, 5 May 2026 15:30:10 +0200 Subject: [PATCH 04/18] wip3 --- .../internal/typeinference/TypeInference.qll | 120 +++++++----------- .../typeinference/internal/TypeInference.qll | 38 ++---- 2 files changed, 58 insertions(+), 100 deletions(-) diff --git a/rust/ql/lib/codeql/rust/internal/typeinference/TypeInference.qll b/rust/ql/lib/codeql/rust/internal/typeinference/TypeInference.qll index 269e4835803e..c679c0ffaa17 100644 --- a/rust/ql/lib/codeql/rust/internal/typeinference/TypeInference.qll +++ b/rust/ql/lib/codeql/rust/internal/typeinference/TypeInference.qll @@ -352,15 +352,6 @@ private module Input3 implements InputSig3 { AstNode getExpr() { result = super.getExpr() } } - /** A ternary conditional expression. */ - class ConditionalExpr extends AstNode, IfExpr { - AstNode getCondition() { result = super.getCondition() } - - AstNode getThen() { result = super.getThen() } - - AstNode getElse() { result = super.getElse() } - } - predicate certainTypeEqualityInput(AstNode n1, TypePath prefix1, AstNode n2, TypePath prefix2) { n1 = any(IdentPat ip | @@ -392,65 +383,10 @@ private module Input3 implements InputSig3 { predicate inferCertainTypeInput = CertainTypeInferenceInput::inferCertainTypeInput/2; - /** - * Holds if `child` is a child of `parent`, and the Rust compiler applies [least - * upper bound (LUB) coercion][1] to infer the type of `parent` from the type of - * `child`. - * - * In this case, we want type information to only flow from `child` to `parent`, - * to avoid (a) either having to model LUB coercions, or (b) risk combinatorial - * explosion in inferred types. - * - * [1]: https://doc.rust-lang.org/reference/type-coercions.html#r-coerce.least-upper-bound - */ - predicate lubCoercionInput(AstNode parent, AstNode child, TypePath prefix) { - parent = - any(MatchExpr me | - child = me.getAnArm().getExpr() and - me.getNumberOfArms() > 1 - ) and - prefix.isEmpty() - or - parent = - any(ArrayListExpr ale | - child = ale.getAnExpr() and - ale.getNumberOfExprs() > 1 - ) and - prefix = TypePath::singleton(getArrayTypeParameter()) - or - bodyReturns(parent, child) and - 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 - s = getRangeType(parent) - ) - } - - predicate typeEqualityAsymmetricInput(AstNode n1, TypePath prefix1, AstNode n2, TypePath prefix2) { - // When `n2` is `*n1` propagate type information from a raw pointer type - // parameter at `n1`. The other direction is handled in - // `inferDereferencedExprPtrType`. - n1 = n2.(DerefExpr).getExpr() and - prefix1 = TypePath::singleton(getPtrTypeParameter()) and - prefix2.isEmpty() - } - predicate typeEqualityInput(AstNode n1, TypePath prefix1, AstNode n2, TypePath prefix2) { prefix1.isEmpty() and prefix2.isEmpty() and ( - n2 = - any(MatchExpr me | - n1 = me.getAnArm().getExpr() and - me.getNumberOfArms() = 1 - ) - or exists(MatchExpr me | n1 = me.getScrutinee() and n2 = me.getAnArm().getPat() @@ -471,9 +407,6 @@ private module Input3 implements InputSig3 { not isPanicMacroCall(n2) or n1 = n2.(MacroPat).getMacroCall().getMacroCallExpansion() - or - bodyReturns(n1, n2) and - strictcount(Expr e | bodyReturns(n1, e)) = 1 ) or n2 = @@ -516,21 +449,56 @@ private module Input3 implements InputSig3 { ) ) or - // an array list expression with only one element (such as `[1]`) has type from that element - n1 = - any(ArrayListExpr ale | - ale.getAnExpr() = n2 and - ale.getNumberOfExprs() = 1 - ) and - prefix1 = TypePath::singleton(getArrayTypeParameter()) and - prefix2.isEmpty() - or // an array repeat expression (`[1; 3]`) has the type of the repeat operand n1.(ArrayRepeatExpr).getRepeatOperand() = n2 and prefix1 = TypePath::singleton(getArrayTypeParameter()) and prefix2.isEmpty() } + predicate typeEqualityAsymmetricInput(AstNode n1, TypePath prefix1, AstNode n2, TypePath prefix2) { + // When `n2` is `*n1` propagate type information from a raw pointer type + // parameter at `n1`. The other direction is handled in + // `inferDereferencedExprPtrType`. + n1 = n2.(DerefExpr).getExpr() and + prefix1 = TypePath::singleton(getPtrTypeParameter()) and + prefix2.isEmpty() + or + n2 = any(ClosureExpr ce | not ce.hasRetType() and ce.getClosureBody() = n1) and + prefix2 = closureReturnPath() and + prefix1.isEmpty() + } + + /** + * Holds if `child` is a child of `parent`, and the Rust compiler applies [least + * upper bound (LUB) coercion][1] to infer the type of `parent` from the type of + * `child`. + * + * In this case, we want type information to only flow from `child` to `parent`, + * to avoid (a) either having to model LUB coercions, or (b) risk combinatorial + * explosion in inferred types. + * + * [1]: https://doc.rust-lang.org/reference/type-coercions.html#r-coerce.least-upper-bound + */ + predicate parentChildType(AstNode parent, AstNode child, TypePath prefix) { + child = parent.(IfExpr).getABranch() and + prefix.isEmpty() + or + parent = any(MatchExpr me | child = me.getAnArm().getExpr()) and + prefix.isEmpty() + or + parent = any(ArrayListExpr ale | child = ale.getAnExpr()) and + prefix = TypePath::singleton(getArrayTypeParameter()) + or + bodyReturns(parent, child) and + prefix.isEmpty() + or + exists(Struct s | + child = [parent.(RangeExpr).getStart(), parent.(RangeExpr).getEnd()] and + prefix = TypePath::singleton(TTypeParamTypeParameter(s.getGenericParamList().getATypeParam())) and + s = getRangeType(parent) + ) + } + Type inferTypeInput(AstNode n, TypePath path) { result = inferAssignmentOperationType(n, path) or diff --git a/shared/typeinference/codeql/typeinference/internal/TypeInference.qll b/shared/typeinference/codeql/typeinference/internal/TypeInference.qll index b3298834b8ce..c8c11e7de78e 100644 --- a/shared/typeinference/codeql/typeinference/internal/TypeInference.qll +++ b/shared/typeinference/codeql/typeinference/internal/TypeInference.qll @@ -2199,18 +2199,6 @@ module Make1 Input1> { AstNode getExpr(); } - /** A ternary conditional expression. */ - class ConditionalExpr extends AstNode { - /** Gets the condition of this expression. */ - AstNode getCondition(); - - /** Gets the true branch of this expression. */ - AstNode getThen(); - - /** Gets the false branch of this expression. */ - AstNode getElse(); - } - /** * Holds if the types of `n1` at `path1` and `n2` at `path2` are certainly equal. */ @@ -2220,14 +2208,10 @@ module Make1 Input1> { Type inferCertainTypeInput(AstNode n, TypePath path); /** - * Holds if `child` is a child of `parent`, and a least upper bound (LUB) coercion - * may be applied to infer the type of `parent` from the type of `child`. - * - * In this case, we want type information to only flow from `child` to `parent`, - * to avoid (a) either having to model LUB coercions, or (b) risk combinatorial - * explosion in inferred types. + * Holds if the types of `n1` at `path1` and `n2` at `path2` are possibly equal, + * and type information should be allowed to flow in both directions between them. */ - predicate lubCoercionInput(AstNode parent, AstNode child, TypePath prefix); + predicate typeEqualityInput(AstNode n1, TypePath path1, AstNode n2, TypePath path2); /** * Holds if the type tree of `n1` at `path1` should be equal to the type tree @@ -2237,9 +2221,12 @@ module Make1 Input1> { predicate typeEqualityAsymmetricInput(AstNode n1, TypePath path1, AstNode n2, TypePath path2); /** - * Holds if the types of `n1` at `path1` and `n2` at `path2` are possibly equal. + * Holds if `child` is a child of `parent` and the type of `parent` at `prefix` can be + * inferred from the type of `child`. + * + * When `child` is unique, we also allow type information to flow from `parent` to `child`. */ - predicate typeEqualityInput(AstNode n1, TypePath path1, AstNode n2, TypePath path2); + predicate parentChildType(AstNode parent, AstNode child, TypePath prefix); /** Gets the inferred type of `n` at `path`. */ Type inferTypeInput(AstNode n, TypePath path); @@ -2355,13 +2342,16 @@ module Make1 Input1> { ) or typeEqualityInput(n1, path1, n2, path2) + or + n2 = unique(AstNode child | parentChildType(n1, child, path1) | child) and + path2.isEmpty() } private predicate lubCoercion(AstNode parent, AstNode child, TypePath prefix) { - parent = any(ConditionalExpr ce | child = [ce.getThen(), ce.getElse()]) and - prefix.isEmpty() + parentChildType(parent, child, prefix) and + strictcount(AstNode child0 | parentChildType(parent, child0, prefix) | child0) > 1 or - lubCoercionInput(parent, child, prefix) + typeEqualityAsymmetricInput(child, TypePath::nil(), parent, prefix) } private predicate typeEqualityAsymmetric( From 23108631fd1e622b216f6611c48078dd78410a08 Mon Sep 17 00:00:00 2001 From: Tom Hvitved Date: Wed, 6 May 2026 10:22:00 +0200 Subject: [PATCH 05/18] wip4 --- .../internal/typeinference/TypeInference.qll | 62 +++++----- .../typeinference/internal/TypeInference.qll | 106 ++++++++++++++---- 2 files changed, 123 insertions(+), 45 deletions(-) diff --git a/rust/ql/lib/codeql/rust/internal/typeinference/TypeInference.qll b/rust/ql/lib/codeql/rust/internal/typeinference/TypeInference.qll index c679c0ffaa17..fb8a78fa05f1 100644 --- a/rust/ql/lib/codeql/rust/internal/typeinference/TypeInference.qll +++ b/rust/ql/lib/codeql/rust/internal/typeinference/TypeInference.qll @@ -286,6 +286,10 @@ private module Input3 implements InputSig3 { (exists(resolveTupleFieldExpr(_, _)) implies any()) } + class BoolType extends DataType { + BoolType() { this.getTypeItem() instanceof Builtins::Bool } + } + class AstNode = Rust::AstNode; TypeMention getTypeAnnotation(AstNode n) { @@ -304,16 +308,44 @@ private module Input3 implements InputSig3 { result = n.(ShorthandSelfParameterMention) } + class Expr = Rust::Expr; + + class ConditionalExpr extends AstNode, IfExpr { + Expr getCondition() { result = super.getCondition() } + + Expr getThen() { result = super.getThen() } + + Expr getElse() { result = super.getElse() } + } + + class BinaryExpr extends AstNode, Rust::BinaryExpr { + Expr getLeftOperand() { result = super.getLhs() } + + Expr getRightOperand() { result = super.getRhs() } + } + + class LogicalAndExpr extends BinaryExpr, Rust::LogicalAndExpr { } + + class LogicalOrExpr extends BinaryExpr, Rust::LogicalOrExpr { } + + abstract class Assignment extends BinaryExpr { } + + class AssignExpr extends Assignment, Rust::AssignmentExpr { } + + class ParenExpr extends AstNode, Rust::ParenExpr { + AstNode getExpr() { result = super.getExpr() } + } + class Variable extends Rust::Variable { AstNode getDefiningNode() { result = this.getPat().getName() or result = this.getParameter().(SelfParam) } - AstNode getAnAccess() { result = super.getAnAccess() } + Expr getAnAccess() { result = super.getAnAccess() } } - abstract class Assignment extends AstNode { + abstract class LetDeclaration extends AstNode { abstract predicate isCoercionSite(); abstract AstNode getLeftOperand(); @@ -321,7 +353,7 @@ private module Input3 implements InputSig3 { abstract AstNode getRightOperand(); } - private class LetExprAssignment extends Assignment, LetExpr { + private class LetExprLetDeclaration extends LetDeclaration, LetExpr { override predicate isCoercionSite() { not this.getPat() instanceof IdentPat } override AstNode getLeftOperand() { result = this.getPat() } @@ -329,7 +361,7 @@ private module Input3 implements InputSig3 { override AstNode getRightOperand() { result = this.getScrutinee() } } - private class LetStmtAssignment extends Assignment, LetStmt { + private class LetStmtLetDeclaration extends LetDeclaration, LetStmt { override predicate isCoercionSite() { this.hasTypeRepr() or not identLetStmt(this, _, _) @@ -340,18 +372,6 @@ private module Input3 implements InputSig3 { override AstNode getRightOperand() { result = this.getInitializer() } } - private class AssignmentExprAssignment extends Assignment, AssignmentExpr { - override predicate isCoercionSite() { any() } - - override AstNode getLeftOperand() { result = this.getLhs() } - - override AstNode getRightOperand() { result = this.getRhs() } - } - - class ParenExpr extends AstNode, Rust::ParenExpr { - AstNode getExpr() { result = super.getExpr() } - } - predicate certainTypeEqualityInput(AstNode n1, TypePath prefix1, AstNode n2, TypePath prefix2) { n1 = any(IdentPat ip | @@ -824,8 +844,6 @@ private module CertainTypeInferenceInput { result = inferRefExprType(n) and path.isEmpty() or - result = inferLogicalOperationType(n, path) - or result = inferCertainStructExprType(n, path) or result = inferCertainStructPatType(n, path) @@ -857,14 +875,6 @@ private module CertainTypeInferenceInput { } } -private Type inferLogicalOperationType(AstNode n, TypePath path) { - exists(Builtins::Bool t, BinaryLogicalOperation be | - n = [be, be.getLhs(), be.getRhs()] and - path.isEmpty() and - result = TDataType(t) - ) -} - private Type inferAssignmentOperationType(AstNode n, TypePath path) { n instanceof AssignmentOperation and path.isEmpty() and diff --git a/shared/typeinference/codeql/typeinference/internal/TypeInference.qll b/shared/typeinference/codeql/typeinference/internal/TypeInference.qll index c8c11e7de78e..5dad50dac2ee 100644 --- a/shared/typeinference/codeql/typeinference/internal/TypeInference.qll +++ b/shared/typeinference/codeql/typeinference/internal/TypeInference.qll @@ -2141,6 +2141,8 @@ module Make1 Input1> { /** * Provides the input to `Make3`. + * + * TODO: Eventually align the AST signature with that of the shared CFG library. */ signature module InputSig3 { /** @@ -2149,6 +2151,9 @@ module Make1 Input1> { */ default predicate cachedStageRevRef() { none() } + /** A boolean type. */ + class BoolType extends Type; + /** An AST node. */ class AstNode { /** Gets a textual representation of this AST node. */ @@ -2161,13 +2166,63 @@ module Make1 Input1> { /** Gets the type annotation that applies to `n`, if any. */ TypeMention getTypeAnnotation(AstNode n); + /** An expression. */ + class Expr extends AstNode; + + /** A ternary conditional expression. */ + class ConditionalExpr extends Expr { + /** Gets the condition of this expression. */ + Expr getCondition(); + + /** Gets the true branch of this expression. */ + Expr getThen(); + + /** Gets the false branch of this expression. */ + Expr getElse(); + } + + /** A binary expression. */ + class BinaryExpr extends Expr { + /** Gets the left operand of this binary expression. */ + Expr getLeftOperand(); + + /** Gets the right operand of this binary expression. */ + Expr getRightOperand(); + } + + /** A short-circuiting logical AND expression. */ + class LogicalAndExpr extends BinaryExpr; + + /** A short-circuiting logical OR expression. */ + class LogicalOrExpr extends BinaryExpr; + + /** + * An assignment expression, either compound or simple. + * + * Examples: + * + * ``` + * x = y + * sum += element + * ``` + */ + class Assignment extends BinaryExpr; + + /** A simple assignment expression, for example `x = y`. */ + class AssignExpr extends Assignment; + + /** A parenthesized expression. */ + class ParenExpr extends AstNode { + AstNode getExpr(); + } + /** A variable, for example a local variable or a field. */ class Variable { /** Gets the AST node that defines this variable. */ AstNode getDefiningNode(); /** Gets an access to this variable. */ - AstNode getAnAccess(); + Expr getAnAccess(); /** Gets a textual representation of this element. */ string toString(); @@ -2177,28 +2232,22 @@ module Make1 Input1> { } /** - * An assignment where type information can flow from one operand to the - * other. + * A `let` declaration, for example a local variable declaration. */ - class Assignment extends AstNode { + class LetDeclaration extends AstNode { /** - * Holds if this assignment is a coercion site, meaning that the type of the right + * Holds if this declaration is a coercion site, meaning that the type of the right * operand may have to be coerced to the type of the left operand. */ predicate isCoercionSite(); - /** Gets the left operand of this binary expression. */ + /** Gets the left operand of this declaration. */ AstNode getLeftOperand(); - /** Gets the right operand of this binary expression. */ + /** Gets the right operand of this declaration. */ AstNode getRightOperand(); } - /** A parenthesized expression. */ - class ParenExpr extends AstNode { - AstNode getExpr(); - } - /** * Holds if the types of `n1` at `path1` and `n2` at `path2` are certainly equal. */ @@ -2249,10 +2298,10 @@ module Make1 Input1> { ( exists(Variable v | n1 = v.getAnAccess() and n2 = v.getDefiningNode()) or - exists(Assignment a | - not a.isCoercionSite() and - n1 = a.getLeftOperand() and - n2 = a.getRightOperand() + exists(LetDeclaration let | + not let.isCoercionSite() and + n1 = let.getLeftOperand() and + n2 = let.getRightOperand() ) or n1 = n2.(ParenExpr).getExpr() @@ -2273,6 +2322,16 @@ module Make1 Input1> { ) } + private Type inferLogicalOperationType(AstNode n, TypePath path) { + ( + exists(LogicalAndExpr lae | n = [lae, lae.getLeftOperand(), lae.getRightOperand()]) or + exists(LogicalOrExpr loe | n = [loe, loe.getLeftOperand(), loe.getRightOperand()]) //or + // exists(LogicalNotExpr lne | n = [lne, lne.getOperand()]) + ) and + result instanceof BoolType and + path.isEmpty() + } + /** Gets the inferred certain type of `n` at `path`. */ cached Type inferCertainType(AstNode n, TypePath path) { @@ -2283,6 +2342,8 @@ module Make1 Input1> { or result = inferCertainTypeInput(n, path) or + result = inferLogicalOperationType(n, path) + or infersCertainTypeAt(n, path, result.getATypeParameter()) } @@ -2336,9 +2397,16 @@ module Make1 Input1> { or path1.isEmpty() and path2.isEmpty() and - exists(Assignment a | - a.getLeftOperand() = n1 and - a.getRightOperand() = n2 + ( + exists(Assignment a | + a.getLeftOperand() = n1 and + a.getRightOperand() = n2 + ) + or + exists(LetDeclaration let | + let.getLeftOperand() = n1 and + let.getRightOperand() = n2 + ) ) or typeEqualityInput(n1, path1, n2, path2) From 414ae96be2152d37ce7c7d55b2d13f628d9c51e0 Mon Sep 17 00:00:00 2001 From: Tom Hvitved Date: Wed, 6 May 2026 11:41:30 +0200 Subject: [PATCH 06/18] wip6 --- .../internal/typeinference/TypeInference.qll | 143 +++++++++--------- .../typeinference/internal/TypeInference.qll | 55 ++++++- .../type-inference/type-inference.expected | 11 ++ 3 files changed, 136 insertions(+), 73 deletions(-) diff --git a/rust/ql/lib/codeql/rust/internal/typeinference/TypeInference.qll b/rust/ql/lib/codeql/rust/internal/typeinference/TypeInference.qll index fb8a78fa05f1..4d2df25c7fe9 100644 --- a/rust/ql/lib/codeql/rust/internal/typeinference/TypeInference.qll +++ b/rust/ql/lib/codeql/rust/internal/typeinference/TypeInference.qll @@ -310,15 +310,11 @@ private module Input3 implements InputSig3 { class Expr = Rust::Expr; - class ConditionalExpr extends AstNode, IfExpr { - Expr getCondition() { result = super.getCondition() } - + class ConditionalExpr extends IfExpr { Expr getThen() { result = super.getThen() } - - Expr getElse() { result = super.getElse() } } - class BinaryExpr extends AstNode, Rust::BinaryExpr { + class BinaryExpr extends Rust::BinaryExpr { Expr getLeftOperand() { result = super.getLhs() } Expr getRightOperand() { result = super.getRhs() } @@ -332,9 +328,7 @@ private module Input3 implements InputSig3 { class AssignExpr extends Assignment, Rust::AssignmentExpr { } - class ParenExpr extends AstNode, Rust::ParenExpr { - AstNode getExpr() { result = super.getExpr() } - } + class ParenExpr = Rust::ParenExpr; class Variable extends Rust::Variable { AstNode getDefiningNode() { @@ -372,6 +366,43 @@ private module Input3 implements InputSig3 { override AstNode getRightOperand() { result = this.getInitializer() } } + class CallTarget extends FunctionCallMatchingInput::Declaration { + TypeMention getAdditionalTypeParameterConstraint(TypeParameter tp) { + result = + tp.(TypeParamTypeParameter) + .getTypeParam() + .getAdditionalTypeBound(this.getFunction(), _) + .getTypeRepr() + } + + Type getReturnType(TypePath path) { + exists(FunctionPosition pos | + pos.isReturn() and + result = super.getDeclaredType(pos, path) + ) + } + + Type getParameterType(int index, TypePath path) { + none() // todo + } + } + + class Call extends Expr instanceof FunctionCallMatchingInput::Access { + Type getTypeArgument(TypeArgumentPosition apos, TypePath path) { + result = super.getTypeArgument(apos, path) + } + + /** Gets the target of this call. */ + CallTarget getTargetCertain() { + exists(ImplOrTraitItemNodeOption i, FunctionDeclaration f, Path p | + result.isFunction(i, f) and + p = CallExprImpl::getFunctionPath(this) and + f = resolvePath(p) and + f.isDirectlyFor(i) + ) + } + } + predicate certainTypeEqualityInput(AstNode n1, TypePath prefix1, AstNode n2, TypePath prefix2) { n1 = any(IdentPat ip | @@ -686,16 +717,38 @@ private class AssocFunctionDeclaration extends FunctionDeclaration { } pragma[nomagic] -private TypeMention getCallExprTypeMentionArgument(CallExpr ce, TypeArgumentPosition apos) { - exists(Path p, int i | p = CallExprImpl::getFunctionPath(ce) | - apos.asTypeParam() = resolvePath(p).getTypeParam(pragma[only_bind_into](i)) and - result = getPathTypeArgument(p, pragma[only_bind_into](i)) +private TypePath getPathToImplSelfTypeParam(TypeParam tp) { + exists(ImplItemNode impl | + tp = impl.getTypeParam(_) and + TTypeParamTypeParameter(tp) = impl.(Impl).getSelfTy().(TypeMention).getTypeAt(result) ) } pragma[nomagic] private Type getCallExprTypeArgument(CallExpr ce, TypeArgumentPosition apos, TypePath path) { - result = getCallExprTypeMentionArgument(ce, apos).getTypeAt(path) + exists(Path p, ItemNode resolved, TypeParam tp | + p = CallExprImpl::getFunctionPath(ce) and + resolved = resolvePath(p) and + apos.asTypeParam() = tp + | + // For type parameters of the function we must resolve their + // instantiation from the path. For instance, for `fn bar(a: A) -> A` + // and the path `bar`, we must resolve `A` to `i64`. + exists(int i | + tp = resolved.getTypeParam(pragma[only_bind_into](i)) and + result = getPathTypeArgument(p, pragma[only_bind_into](i)).getTypeAt(path) + ) + or + // For type parameters of the `impl` block we must resolve their + // instantiation from the path. For instance, for `impl for Foo` + // and the path `Foo::bar` we must resolve `A` to `i64`. + exists(ImplItemNode impl, TypePath pathToTp | + resolved = impl.getASuccessor(_) and + tp = impl.getTypeParam(_) and + pathToTp = getPathToImplSelfTypeParam(tp) and + result = p.getQualifier().(TypeMention).getTypeAt(pathToTp.appendInverse(path)) + ) + ) or // Handle constructions that use `Self(...)` syntax exists(Path p, TypePath path0 | @@ -764,61 +817,6 @@ private TypePath closureParameterPath(int arity, int index) { /** Module for inferring certain type information. */ private module CertainTypeInferenceInput { - pragma[nomagic] - private predicate callResolvesTo(CallExpr ce, Path p, Function f) { - p = CallExprImpl::getFunctionPath(ce) and - f = resolvePath(p) - } - - pragma[nomagic] - private Type getCallExprType(CallExpr ce, Path p, FunctionDeclaration f, TypePath path) { - exists(ImplOrTraitItemNodeOption i | - callResolvesTo(ce, p, f) and - result = f.getReturnType(i, path) and - f.isDirectlyFor(i) - ) - } - - pragma[nomagic] - private Type getCertainCallExprType(CallExpr ce, Path p, TypePath tp) { - forex(Function f | callResolvesTo(ce, p, f) | result = getCallExprType(ce, p, f, tp)) - } - - pragma[nomagic] - private TypePath getPathToImplSelfTypeParam(TypeParam tp) { - exists(ImplItemNode impl | - tp = impl.getTypeParam(_) and - TTypeParamTypeParameter(tp) = impl.(Impl).getSelfTy().(TypeMention).getTypeAt(result) - ) - } - - pragma[nomagic] - private Type inferCertainCallExprType(CallExpr ce, TypePath path) { - exists(Type ty, TypePath prefix, Path p | ty = getCertainCallExprType(ce, p, prefix) | - exists(TypePath suffix, TypeParam tp | - tp = ty.(TypeParamTypeParameter).getTypeParam() and - path = prefix.append(suffix) - | - // For type parameters of the `impl` block we must resolve their - // instantiation from the path. For instance, for `impl for Foo` - // and the path `Foo::bar` we must resolve `A` to `i64`. - exists(TypePath pathToTp | - pathToTp = getPathToImplSelfTypeParam(tp) and - result = p.getQualifier().(TypeMention).getTypeAt(pathToTp.appendInverse(suffix)) - ) - or - // For type parameters of the function we must resolve their - // instantiation from the path. For instance, for `fn bar(a: A) -> A` - // and the path `bar`, we must resolve `A` to `i64`. - result = getCallExprTypeArgument(ce, TTypeParamTypeArgumentPosition(tp), suffix) - ) - or - not ty instanceof TypeParameter and - result = ty and - path = prefix - ) - } - private Type inferCertainStructExprType(StructExpr se, TypePath path) { result = se.getPath().(TypeMention).getTypeAt(path) } @@ -834,8 +832,6 @@ private module CertainTypeInferenceInput { Type inferCertainTypeInput(AstNode n, TypePath path) { result = inferFunctionBodyType(n, path) or - result = inferCertainCallExprType(n, path) - or result = inferLiteralType(n, path, true) or result = inferRefPatType(n) and @@ -2612,6 +2608,11 @@ private module FunctionCallMatchingInput implements MatchingWithEnvironmentInput FunctionDeclaration getFunction() { result = f } + predicate isFunction(ImplOrTraitItemNodeOption i_, Function f_) { + i_ = i and + f_ = f + } + predicate isAssocFunction(ImplOrTraitItemNode i_, Function f_) { i_ = i.asSome() and f_ = f diff --git a/shared/typeinference/codeql/typeinference/internal/TypeInference.qll b/shared/typeinference/codeql/typeinference/internal/TypeInference.qll index 5dad50dac2ee..4d5f078505c3 100644 --- a/shared/typeinference/codeql/typeinference/internal/TypeInference.qll +++ b/shared/typeinference/codeql/typeinference/internal/TypeInference.qll @@ -2212,8 +2212,8 @@ module Make1 Input1> { class AssignExpr extends Assignment; /** A parenthesized expression. */ - class ParenExpr extends AstNode { - AstNode getExpr(); + class ParenExpr extends Expr { + Expr getExpr(); } /** A variable, for example a local variable or a field. */ @@ -2248,6 +2248,29 @@ module Make1 Input1> { AstNode getRightOperand(); } + class CallTarget { + TypeParameter getTypeParameter(TypeParameterPosition ppos); + + TypeMention getAdditionalTypeParameterConstraint(TypeParameter tp); + + Type getReturnType(TypePath path); + + Type getParameterType(int index, TypePath path); + + /** Gets a textual representation of this element. */ + string toString(); + + /** Gets the location of this element. */ + Location getLocation(); + } + + class Call extends Expr { + Type getTypeArgument(TypeArgumentPosition apos, TypePath path); + + /** Gets the target of this call. */ + CallTarget getTargetCertain(); + } + /** * Holds if the types of `n1` at `path1` and `n2` at `path2` are certainly equal. */ @@ -2332,6 +2355,32 @@ module Make1 Input1> { path.isEmpty() } + pragma[nomagic] + private Type getCertainCallExprType(Call call, TypePath path) { + forex(CallTarget target | target = call.getTargetCertain() | + result = target.getReturnType(path) + ) + } + + pragma[nomagic] + private Type inferCertainCallExprType(Call call, TypePath path) { + exists(Type ty, TypePath prefix | ty = getCertainCallExprType(call, prefix) | + exists( + CallTarget target, TypePath suffix, TypeParameterPosition tppos, + TypeArgumentPosition tapos + | + ty = target.getTypeParameter(tppos) and + path = prefix.append(suffix) and + result = call.getTypeArgument(tapos, suffix) and + typeArgumentParameterPositionMatch(tapos, tppos) + ) + or + not ty instanceof TypeParameter and + result = ty and + path = prefix + ) + } + /** Gets the inferred certain type of `n` at `path`. */ cached Type inferCertainType(AstNode n, TypePath path) { @@ -2344,6 +2393,8 @@ module Make1 Input1> { or result = inferLogicalOperationType(n, path) or + result = inferCertainCallExprType(n, path) + or infersCertainTypeAt(n, path, result.getATypeParameter()) } diff --git a/swift/ql/test/library-tests/type-inference/type-inference.expected b/swift/ql/test/library-tests/type-inference/type-inference.expected index e69de29bb2d1..79a3599aa1f3 100644 --- a/swift/ql/test/library-tests/type-inference/type-inference.expected +++ b/swift/ql/test/library-tests/type-inference/type-inference.expected @@ -0,0 +1,11 @@ +| context.swift:17:10:17:10 | C.init() | Unexpected result: target=init() | +| context.swift:17:10:17:12 | call to C.init() | Unexpected result: target=init() | +| context.swift:17:14:18:1 | // $ type=C\n | Missing result: type=C | +| context.swift:25:11:25:11 | A.init() | Unexpected result: target=init() | +| context.swift:25:11:25:13 | call to A.init() | Unexpected result: target=init() | +| context.swift:26:19:26:19 | D.init() | Unexpected result: target=init() | +| context.swift:26:19:26:21 | call to D.init() | Unexpected result: target=init() | +| context.swift:26:25:26:25 | B.init() | Unexpected result: target=init() | +| context.swift:26:25:26:27 | call to B.init() | Unexpected result: target=init() | +| file://:0:0:0:0 | A.init() | Unexpected result: target=init() | +| file://:0:0:0:0 | call to A.init() | Unexpected result: target=init() | From 3de907c34a391894ab26c588c4422034534a3204 Mon Sep 17 00:00:00 2001 From: Tom Hvitved Date: Wed, 6 May 2026 15:55:24 +0200 Subject: [PATCH 07/18] wip7 --- .../internal/typeinference/TypeInference.qll | 161 +++++------------- .../type-inference/type-inference.expected | 5 + .../typeinference/internal/TypeInference.qll | 154 +++++++++++++++-- 3 files changed, 191 insertions(+), 129 deletions(-) diff --git a/rust/ql/lib/codeql/rust/internal/typeinference/TypeInference.qll b/rust/ql/lib/codeql/rust/internal/typeinference/TypeInference.qll index 4d2df25c7fe9..a4ba95f30253 100644 --- a/rust/ql/lib/codeql/rust/internal/typeinference/TypeInference.qll +++ b/rust/ql/lib/codeql/rust/internal/typeinference/TypeInference.qll @@ -286,6 +286,8 @@ private module Input3 implements InputSig3 { (exists(resolveTupleFieldExpr(_, _)) implies any()) } + predicate inferType = M3::inferType/2; + class BoolType extends DataType { BoolType() { this.getTypeItem() instanceof Builtins::Bool } } @@ -366,7 +368,11 @@ private module Input3 implements InputSig3 { override AstNode getRightOperand() { result = this.getInitializer() } } - class CallTarget extends FunctionCallMatchingInput::Declaration { + class CallResolutionContext = FunctionCallMatchingInput::AccessEnvironment; + + class TypePosition = FunctionPosition; + + class Callable extends FunctionCallMatchingInput::Declaration { TypeMention getAdditionalTypeParameterConstraint(TypeParameter tp) { result = tp.(TypeParamTypeParameter) @@ -374,17 +380,6 @@ private module Input3 implements InputSig3 { .getAdditionalTypeBound(this.getFunction(), _) .getTypeRepr() } - - Type getReturnType(TypePath path) { - exists(FunctionPosition pos | - pos.isReturn() and - result = super.getDeclaredType(pos, path) - ) - } - - Type getParameterType(int index, TypePath path) { - none() // todo - } } class Call extends Expr instanceof FunctionCallMatchingInput::Access { @@ -392,8 +387,10 @@ private module Input3 implements InputSig3 { result = super.getTypeArgument(apos, path) } + AstNode getNodeAt(TypePosition pos) { result = super.getNodeAt(pos) } + /** Gets the target of this call. */ - CallTarget getTargetCertain() { + Callable getTargetCertain() { exists(ImplOrTraitItemNodeOption i, FunctionDeclaration f, Path p | result.isFunction(i, f) and p = CallExprImpl::getFunctionPath(this) and @@ -401,6 +398,24 @@ private module Input3 implements InputSig3 { f.isDirectlyFor(i) ) } + + Callable getTarget(string derefChainBorrow) { result = super.getTarget(derefChainBorrow) } + } + + bindingset[derefChainBorrow] + Type inferCallTypeIn(Call call, string derefChainBorrow, FunctionPosition pos, TypePath path) { + result = call.(FunctionCallMatchingInput::Access).getInferredType(derefChainBorrow, pos, path) + } + + Type inferCallTypeOut(AstNode n, TypePosition pos, TypePath path) { + result = inferFunctionCallTypeNonSelf(n, pos, path) + or + exists(FunctionCallMatchingInput::Access a | + result = inferFunctionCallTypeSelf(a, n, DerefChain::nil(), path) and + if a.(AssocFunctionResolution::AssocFunctionCall).hasReceiver() + then not path.isEmpty() + else any() + ) } predicate certainTypeEqualityInput(AstNode n1, TypePath prefix1, AstNode n2, TypePath prefix2) { @@ -553,8 +568,6 @@ private module Input3 implements InputSig3 { Type inferTypeInput(AstNode n, TypePath path) { result = inferAssignmentOperationType(n, path) or - result = inferFunctionCallType(n, path) - or result = inferConstructionType(n, path) or result = inferOperationType(n, path) @@ -1094,53 +1107,6 @@ private module ContextTyping { ) } } - - pragma[nomagic] - private predicate hasUnknownTypeAt(AstNode n, TypePath path) { - inferType(n, path) = TUnknownType() - } - - pragma[nomagic] - private predicate hasUnknownType(AstNode n) { hasUnknownTypeAt(n, _) } - - newtype FunctionPositionKind = - SelfKind() or - ReturnKind() or - PositionalKind() - - signature Type inferCallTypeSig(AstNode n, FunctionPositionKind kind, TypePath path); - - /** - * Given a predicate `inferCallType` for inferring the type of a call at a given - * position, this module exposes the predicate `check`, which wraps the input - * predicate and checks that types are only propagated into arguments when they - * are context-typed. - */ - module CheckContextTyping { - pragma[nomagic] - private Type inferCallNonReturnType( - AstNode n, FunctionPositionKind kind, TypePath prefix, TypePath path - ) { - result = inferCallType(n, kind, path) and - hasUnknownType(n) and - kind != ReturnKind() and - prefix = path.getAPrefix() - } - - pragma[nomagic] - Type check(AstNode n, TypePath path) { - result = inferCallType(n, ReturnKind(), path) - or - exists(FunctionPositionKind kind, TypePath prefix | - result = inferCallNonReturnType(n, kind, prefix, path) and - hasUnknownTypeAt(n, prefix) - | - // Never propagate type information directly into the receiver, since its type - // must already have been known in order to resolve the call - if kind = SelfKind() then not prefix.isEmpty() else any() - ) - } - } } /** @@ -2836,22 +2802,20 @@ private module FunctionCallMatchingInput implements MatchingWithEnvironmentInput } } -private module FunctionCallMatching = MatchingWithEnvironment; - pragma[nomagic] private Type inferFunctionCallType0( FunctionCallMatchingInput::Access call, FunctionPosition pos, AstNode n, DerefChain derefChain, BorrowKind borrow, TypePath path ) { exists(TypePath path0 | - n = call.getNodeAt(pos) and exists(string derefChainBorrow | FunctionCallMatchingInput::decodeDerefChainBorrow(derefChainBorrow, derefChain, borrow) | - result = FunctionCallMatching::inferAccessType(call, derefChainBorrow, pos, path0) - or + n = call.getNodeAt(pos) and call.hasUnknownTypeAt(derefChainBorrow, pos, path0) and result = TUnknownType() + or + result = inferCallTypeOut(call, pos, n, derefChainBorrow, path0) ) | if @@ -2919,31 +2883,6 @@ private Type inferFunctionCallTypeSelf( ) } -private Type inferFunctionCallTypePreCheck( - AstNode n, ContextTyping::FunctionPositionKind kind, TypePath path -) { - exists(FunctionPosition pos | - result = inferFunctionCallTypeNonSelf(n, pos, path) and - if pos.isPosition() - then kind = ContextTyping::PositionalKind() - else kind = ContextTyping::ReturnKind() - ) - or - exists(FunctionCallMatchingInput::Access a | - result = inferFunctionCallTypeSelf(a, n, DerefChain::nil(), path) and - if a.(AssocFunctionResolution::AssocFunctionCall).hasReceiver() - then kind = ContextTyping::SelfKind() - else kind = ContextTyping::PositionalKind() - ) -} - -/** - * Gets the type of `n` at `path`, where `n` is either a function call or an - * argument/receiver of a function call. - */ -private predicate inferFunctionCallType = - ContextTyping::CheckContextTyping::check/2; - abstract private class Constructor extends Addressable { final TypeParameter getTypeParameter(TypeParameterPosition ppos) { typeParamMatchPosition(this.getTypeItem().getGenericParamList().getATypeParam(), result, ppos) @@ -3102,15 +3041,8 @@ private module ConstructionMatchingInput implements MatchingInputSig { private module ConstructionMatching = Matching; pragma[nomagic] -private Type inferConstructionTypePreCheck( - AstNode n, ContextTyping::FunctionPositionKind kind, TypePath path -) { - exists(ConstructionMatchingInput::Access a, FunctionPosition pos | - n = a.getNodeAt(pos) and - if pos.isPosition() - then kind = ContextTyping::PositionalKind() - else kind = ContextTyping::ReturnKind() - | +private Type inferConstructionTypePreCheck(AstNode n, FunctionPosition pos, TypePath path) { + exists(ConstructionMatchingInput::Access a | n = a.getNodeAt(pos) | result = ConstructionMatching::inferAccessType(a, pos, path) or a.hasUnknownTypeAt(pos, path) and @@ -3119,7 +3051,7 @@ private Type inferConstructionTypePreCheck( } private predicate inferConstructionType = - ContextTyping::CheckContextTyping::check/2; + CheckContextTyping::check/2; /** * A matching configuration for resolving types of operations like `a + b`. @@ -3184,23 +3116,15 @@ private module OperationMatchingInput implements MatchingInputSig { private module OperationMatching = Matching; pragma[nomagic] -private Type inferOperationTypePreCheck( - AstNode n, ContextTyping::FunctionPositionKind kind, TypePath path -) { - exists(OperationMatchingInput::Access a, FunctionPosition pos | +private Type inferOperationTypePreCheck(AstNode n, FunctionPosition pos, TypePath path) { + exists(OperationMatchingInput::Access a | n = a.getNodeAt(pos) and result = OperationMatching::inferAccessType(a, pos, path) and - if pos.asPosition() = 0 - then kind = ContextTyping::SelfKind() - else - if pos.isPosition() - then kind = ContextTyping::PositionalKind() - else kind = ContextTyping::ReturnKind() + if pos.asPosition() = 0 then not path.isEmpty() else any() ) } -private predicate inferOperationType = - ContextTyping::CheckContextTyping::check/2; +private predicate inferOperationType = CheckContextTyping::check/2; pragma[nomagic] private Type getFieldExprLookupType(FieldExpr fe, string name, DerefChain derefChain) { @@ -3815,11 +3739,10 @@ private module Debug { t = self.getTypeAt(path) } - predicate debugInferFunctionCallType(AstNode n, TypePath path, Type t) { - n = getRelevantLocatable() and - t = inferFunctionCallType(n, path) - } - + // predicate debugInferFunctionCallType(AstNode n, TypePath path, Type t) { + // n = getRelevantLocatable() and + // t = inferFunctionCallType(n, path) + // } predicate debugInferConstructionType(AstNode n, TypePath path, Type t) { n = getRelevantLocatable() and t = inferConstructionType(n, path) diff --git a/rust/ql/test/library-tests/type-inference/type-inference.expected b/rust/ql/test/library-tests/type-inference/type-inference.expected index 3344fc45f74f..f30a2c064d12 100644 --- a/rust/ql/test/library-tests/type-inference/type-inference.expected +++ b/rust/ql/test/library-tests/type-inference/type-inference.expected @@ -10252,6 +10252,7 @@ inferType | main.rs:1412:17:1412:20 | self | TRef.TSlice | main.rs:1410:14:1410:23 | T | | main.rs:1412:17:1412:27 | self.get(...) | | {EXTERNAL LOCATION} | Option | | main.rs:1412:17:1412:27 | self.get(...) | T | {EXTERNAL LOCATION} | & | +| main.rs:1412:17:1412:27 | self.get(...) | T.TRef | main.rs:1410:14:1410:23 | T | | main.rs:1412:17:1412:36 | ... .unwrap() | | {EXTERNAL LOCATION} | & | | main.rs:1412:17:1412:36 | ... .unwrap() | TRef | main.rs:1410:14:1410:23 | T | | main.rs:1412:26:1412:26 | 0 | | {EXTERNAL LOCATION} | i32 | @@ -11600,6 +11601,8 @@ inferType | main.rs:2221:18:2221:21 | true | | {EXTERNAL LOCATION} | bool | | main.rs:2223:9:2223:15 | S(...) | | main.rs:2107:5:2107:19 | S | | main.rs:2223:9:2223:15 | S(...) | T | {EXTERNAL LOCATION} | i64 | +| main.rs:2223:9:2223:15 | S(...) | T | main.rs:2107:5:2107:19 | S | +| main.rs:2223:9:2223:15 | S(...) | T.T | {EXTERNAL LOCATION} | i64 | | main.rs:2223:9:2223:31 | ... .my_add(...) | | main.rs:2107:5:2107:19 | S | | main.rs:2223:9:2223:31 | ... .my_add(...) | T | {EXTERNAL LOCATION} | i64 | | main.rs:2223:9:2223:31 | ... .my_add(...) | T | main.rs:2107:5:2107:19 | S | @@ -11618,6 +11621,8 @@ inferType | main.rs:2224:24:2224:27 | 3i64 | | {EXTERNAL LOCATION} | i64 | | main.rs:2225:9:2225:15 | S(...) | | main.rs:2107:5:2107:19 | S | | main.rs:2225:9:2225:15 | S(...) | T | {EXTERNAL LOCATION} | i64 | +| main.rs:2225:9:2225:15 | S(...) | T | {EXTERNAL LOCATION} | & | +| main.rs:2225:9:2225:15 | S(...) | T.TRef | {EXTERNAL LOCATION} | i64 | | main.rs:2225:9:2225:29 | ... .my_add(...) | | main.rs:2107:5:2107:19 | S | | main.rs:2225:9:2225:29 | ... .my_add(...) | T | {EXTERNAL LOCATION} | i64 | | main.rs:2225:11:2225:14 | 1i64 | | {EXTERNAL LOCATION} | i64 | diff --git a/shared/typeinference/codeql/typeinference/internal/TypeInference.qll b/shared/typeinference/codeql/typeinference/internal/TypeInference.qll index 4d5f078505c3..aeb6cc6ed3b1 100644 --- a/shared/typeinference/codeql/typeinference/internal/TypeInference.qll +++ b/shared/typeinference/codeql/typeinference/internal/TypeInference.qll @@ -2151,6 +2151,13 @@ module Make1 Input1> { */ default predicate cachedStageRevRef() { none() } + /** + * Point this predicate to the `inferType` predicate in the output of this module. + * + * Needed to be able to refer to `inferType` in default signature implementations. + */ + Type inferType(AstNode n, TypePath path); + /** A boolean type. */ class BoolType extends Type; @@ -2248,29 +2255,64 @@ module Make1 Input1> { AstNode getRightOperand(); } - class CallTarget { + /** + * A position where a callable can have a declared type. + */ + class TypePosition { + /** Holds if this position represents the return type of a callable. */ + predicate isReturn(); + + /** Gets a textual representation of this position. */ + string toString(); + } + + /** A context needed to resolve calls. */ + bindingset[this] + class CallResolutionContext { + /** Gets a textual representation of this context. */ + bindingset[this] + string toString(); + } + + /** A callable. */ + class Callable { TypeParameter getTypeParameter(TypeParameterPosition ppos); TypeMention getAdditionalTypeParameterConstraint(TypeParameter tp); - Type getReturnType(TypePath path); + /* Gets the declared type of this callable at `path` for position `pos`. */ + Type getDeclaredType(TypePosition pos, TypePath path); - Type getParameterType(int index, TypePath path); - - /** Gets a textual representation of this element. */ + /** Gets a textual representation of this callable. */ string toString(); - /** Gets the location of this element. */ + /** Gets the location of this callable. */ Location getLocation(); } class Call extends Expr { Type getTypeArgument(TypeArgumentPosition apos, TypePath path); + AstNode getNodeAt(TypePosition pos); + /** Gets the target of this call. */ - CallTarget getTargetCertain(); + Callable getTargetCertain(); + + /** Gets the target of this call. */ + Callable getTarget(CallResolutionContext ctx); } + /** Gets the inferred type `call` at `path` for position `pos` in context `ctx`. */ + bindingset[ctx] + default Type inferCallTypeIn( + Call call, CallResolutionContext ctx, TypePosition pos, TypePath path + ) { + result = inferType(call.getNodeAt(pos), path) and + exists(ctx) + } + + Type inferCallTypeOut(AstNode n, TypePosition pos, TypePath path); + /** * Holds if the types of `n1` at `path1` and `n2` at `path2` are certainly equal. */ @@ -2357,8 +2399,11 @@ module Make1 Input1> { pragma[nomagic] private Type getCertainCallExprType(Call call, TypePath path) { - forex(CallTarget target | target = call.getTargetCertain() | - result = target.getReturnType(path) + exists(TypePosition ret | + ret.isReturn() and + forex(Callable target | target = call.getTargetCertain() | + result = target.getDeclaredType(ret, path) + ) ) } @@ -2366,7 +2411,7 @@ module Make1 Input1> { private Type inferCertainCallExprType(Call call, TypePath path) { exists(Type ty, TypePath prefix | ty = getCertainCallExprType(call, prefix) | exists( - CallTarget target, TypePath suffix, TypeParameterPosition tppos, + Callable target, TypePath suffix, TypeParameterPosition tppos, TypeArgumentPosition tapos | ty = target.getTypeParameter(tppos) and @@ -2524,15 +2569,104 @@ module Make1 Input1> { ( result = inferTypeEquality(n, path) or + result = CheckContextTyping::check(n, path) + or result = inferTypeInput(n, path) ) } + private module TypePositionMatchingInput { + class DeclarationPosition = TypePosition; + + class AccessPosition = DeclarationPosition; + + predicate accessDeclarationPositionMatch(AccessPosition apos, DeclarationPosition dpos) { + apos = dpos + } + } + + /** + * A matching configuration for resolving types of calls. + */ + private module CallMatchingInput implements MatchingWithEnvironmentInputSig { + import TypePositionMatchingInput + + class Declaration = Callable; + + bindingset[decl] + TypeMention getATypeParameterConstraint(TypeParameter tp, Declaration decl) { + result = Input2::getATypeParameterConstraint(tp) and + exists(decl) + or + result = decl.getAdditionalTypeParameterConstraint(tp) + } + + class AccessEnvironment = CallResolutionContext; + + final private class CallFinal = Call; + + class Access extends CallFinal { + bindingset[e] + Type getInferredType(AccessEnvironment e, AccessPosition apos, TypePath path) { + result = inferCallTypeIn(this, e, apos, path) + } + } + } + + private module CallMatching = MatchingWithEnvironment; + + pragma[nomagic] + Type inferCallTypeOut( + Call call, TypePosition pos, AstNode n, CallResolutionContext ctx, TypePath path + ) { + n = call.getNodeAt(pos) and + result = CallMatching::inferAccessType(call, ctx, pos, path) + } + + pragma[nomagic] + private predicate hasUnknownTypeAt(AstNode n, TypePath path) { + inferType(n, path) instanceof UnknownType + } + + pragma[nomagic] + private predicate hasUnknownType(AstNode n) { hasUnknownTypeAt(n, _) } + + signature Type inferCallTypeSig(AstNode n, TypePosition pos, TypePath path); + + /** + * Given a predicate `inferCallType` for inferring the type of a call at a given + * position, this module exposes the predicate `check`, which wraps the input + * predicate and checks that types are only propagated into arguments when they + * are context-typed. + */ + module CheckContextTyping { + pragma[nomagic] + private Type inferCallNonReturnType(AstNode n, TypePath prefix, TypePath path) { + exists(TypePosition pos | + result = inferCallType(n, pos, path) and + hasUnknownType(n) and + not pos.isReturn() and + prefix = path.getAPrefix() + ) + } + + pragma[nomagic] + Type check(AstNode n, TypePath path) { + result = inferCallType(n, any(TypePosition pos | pos.isReturn()), path) + or + exists(TypePath prefix | + result = inferCallNonReturnType(n, prefix, path) and + hasUnknownTypeAt(n, prefix) + ) + } + } + /** * Gets the inferred root type of `n`, if any. */ Type inferType(AstNode n) { result = inferType(n, TypePath::nil()) } + // todo: consistency checks /** The cached stage of type inference. */ cached module CachedStage { From 7a2933169eba2f9e38a8b4368df1c36bb9e1b8aa Mon Sep 17 00:00:00 2001 From: Tom Hvitved Date: Thu, 7 May 2026 09:03:20 +0200 Subject: [PATCH 08/18] wip8 --- .../internal/typeinference/TypeInference.qll | 111 +++++++++--------- .../typeinference/internal/TypeInference.qll | 22 ++++ 2 files changed, 79 insertions(+), 54 deletions(-) diff --git a/rust/ql/lib/codeql/rust/internal/typeinference/TypeInference.qll b/rust/ql/lib/codeql/rust/internal/typeinference/TypeInference.qll index a4ba95f30253..1b24e8210408 100644 --- a/rust/ql/lib/codeql/rust/internal/typeinference/TypeInference.qll +++ b/rust/ql/lib/codeql/rust/internal/typeinference/TypeInference.qll @@ -312,6 +312,18 @@ private module Input3 implements InputSig3 { class Expr = Rust::Expr; + class SwitchExpr extends Rust::MatchExpr { + Expr getExpr() { result = this.getScrutinee() } + + Case getCase(int index) { result = this.getArm(index) } + } + + class Case extends Rust::MatchArm { + AstNode getAPattern() { result = this.getPat() } + + Expr getBody() { result = this.getExpr() } + } + class ConditionalExpr extends IfExpr { Expr getThen() { result = super.getThen() } } @@ -447,7 +459,46 @@ private module Input3 implements InputSig3 { ) } - predicate inferCertainTypeInput = CertainTypeInferenceInput::inferCertainTypeInput/2; + Type inferCertainTypeInput(AstNode n, TypePath path) { + result = inferFunctionBodyType(n, path) + or + result = inferLiteralType(n, path, true) + or + result = inferRefPatType(n) and + path.isEmpty() + or + result = inferRefExprType(n) and + path.isEmpty() + or + result = inferCertainStructExprType(n, path) + or + result = inferCertainStructPatType(n, path) + or + result = inferRangeExprType(n) and + path.isEmpty() + or + result = inferTupleRootType(n) and + path.isEmpty() + or + result = inferBlockExprType(n, path) + or + result = inferArrayExprType(n) and + path.isEmpty() + or + result = inferCastExprType(n, path) + or + exprHasUnitType(n) and + path.isEmpty() and + result instanceof UnitType + or + isPanicMacroCall(n) and + path.isEmpty() and + result instanceof NeverType + or + n instanceof ClosureExpr and + path.isEmpty() and + result = closureRootType() + } predicate typeEqualityInput(AstNode n1, TypePath prefix1, AstNode n2, TypePath prefix2) { prefix1.isEmpty() and @@ -828,60 +879,12 @@ private TypePath closureParameterPath(int arity, int index) { TypePath::singleton(getTupleTypeParameter(arity, index))) } -/** Module for inferring certain type information. */ -private module CertainTypeInferenceInput { - private Type inferCertainStructExprType(StructExpr se, TypePath path) { - result = se.getPath().(TypeMention).getTypeAt(path) - } - - private Type inferCertainStructPatType(StructPat sp, TypePath path) { - result = sp.getPath().(TypeMention).getTypeAt(path) - } +private Type inferCertainStructExprType(StructExpr se, TypePath path) { + result = se.getPath().(TypeMention).getTypeAt(path) +} - /** - * Holds if `n` has complete and certain type information and if `n` has the - * resulting type at `path`. - */ - Type inferCertainTypeInput(AstNode n, TypePath path) { - result = inferFunctionBodyType(n, path) - or - result = inferLiteralType(n, path, true) - or - result = inferRefPatType(n) and - path.isEmpty() - or - result = inferRefExprType(n) and - path.isEmpty() - or - result = inferCertainStructExprType(n, path) - or - result = inferCertainStructPatType(n, path) - or - result = inferRangeExprType(n) and - path.isEmpty() - or - result = inferTupleRootType(n) and - path.isEmpty() - or - result = inferBlockExprType(n, path) - or - result = inferArrayExprType(n) and - path.isEmpty() - or - result = inferCastExprType(n, path) - or - exprHasUnitType(n) and - path.isEmpty() and - result instanceof UnitType - or - isPanicMacroCall(n) and - path.isEmpty() and - result instanceof NeverType - or - n instanceof ClosureExpr and - path.isEmpty() and - result = closureRootType() - } +private Type inferCertainStructPatType(StructPat sp, TypePath path) { + result = sp.getPath().(TypeMention).getTypeAt(path) } private Type inferAssignmentOperationType(AstNode n, TypePath path) { diff --git a/shared/typeinference/codeql/typeinference/internal/TypeInference.qll b/shared/typeinference/codeql/typeinference/internal/TypeInference.qll index aeb6cc6ed3b1..c3adee01c7f8 100644 --- a/shared/typeinference/codeql/typeinference/internal/TypeInference.qll +++ b/shared/typeinference/codeql/typeinference/internal/TypeInference.qll @@ -2176,6 +2176,28 @@ module Make1 Input1> { /** An expression. */ class Expr extends AstNode; + /** + * A switch expression. + */ + class SwitchExpr extends Expr { + /** + * Gets the expression being switched on. + */ + Expr getExpr(); + + /** Gets the case at the specified (zero-based) `index`. */ + Case getCase(int index); + } + + /** A case in a switch expression. */ + class Case extends AstNode { + /** Gets a pattern being matched by this case. */ + AstNode getAPattern(); + + /** Gets the body of this case. */ + Expr getBody(); + } + /** A ternary conditional expression. */ class ConditionalExpr extends Expr { /** Gets the condition of this expression. */ From 64a4dac84b6103c5e5d91a012178ec98f3d31184 Mon Sep 17 00:00:00 2001 From: Tom Hvitved Date: Thu, 7 May 2026 10:59:57 +0200 Subject: [PATCH 09/18] wip9 --- .../internal/typeinference/TypeInference.qll | 100 ++++---- .../type-inference/type-inference.expected | 24 -- .../type-inference/type-inference.ql | 4 +- .../typeinference/internal/TypeInference.qll | 220 ++++++++++++------ 4 files changed, 203 insertions(+), 145 deletions(-) diff --git a/rust/ql/lib/codeql/rust/internal/typeinference/TypeInference.qll b/rust/ql/lib/codeql/rust/internal/typeinference/TypeInference.qll index 1b24e8210408..02b98f9990a3 100644 --- a/rust/ql/lib/codeql/rust/internal/typeinference/TypeInference.qll +++ b/rust/ql/lib/codeql/rust/internal/typeinference/TypeInference.qll @@ -312,7 +312,7 @@ private module Input3 implements InputSig3 { class Expr = Rust::Expr; - class SwitchExpr extends Rust::MatchExpr { + class Switch extends Rust::MatchExpr { Expr getExpr() { result = this.getScrutinee() } Case getCase(int index) { result = this.getArm(index) } @@ -321,7 +321,7 @@ private module Input3 implements InputSig3 { class Case extends Rust::MatchArm { AstNode getAPattern() { result = this.getPat() } - Expr getBody() { result = this.getExpr() } + AstNode getBody() { result = this.getExpr() } } class ConditionalExpr extends IfExpr { @@ -430,7 +430,7 @@ private module Input3 implements InputSig3 { ) } - predicate certainTypeEqualityInput(AstNode n1, TypePath prefix1, AstNode n2, TypePath prefix2) { + predicate inferStepSymmetricCertain(AstNode n1, TypePath prefix1, AstNode n2, TypePath prefix2) { n1 = any(IdentPat ip | n2 = ip.getName() and @@ -459,7 +459,7 @@ private module Input3 implements InputSig3 { ) } - Type inferCertainTypeInput(AstNode n, TypePath path) { + Type inferTypeCertainInput(AstNode n, TypePath path) { result = inferFunctionBodyType(n, path) or result = inferLiteralType(n, path, true) @@ -500,15 +500,10 @@ private module Input3 implements InputSig3 { result = closureRootType() } - predicate typeEqualityInput(AstNode n1, TypePath prefix1, AstNode n2, TypePath prefix2) { + predicate inferStepSymmetric(AstNode n1, TypePath prefix1, AstNode n2, TypePath prefix2) { prefix1.isEmpty() and prefix2.isEmpty() and ( - exists(MatchExpr me | - n1 = me.getScrutinee() and - n2 = me.getAnArm().getPat() - ) - or n1 = n2.(OrPat).getAPat() or n1 = n2.(ParenPat).getPat() @@ -572,7 +567,7 @@ private module Input3 implements InputSig3 { prefix2.isEmpty() } - predicate typeEqualityAsymmetricInput(AstNode n1, TypePath prefix1, AstNode n2, TypePath prefix2) { + predicate inferStep(AstNode n1, TypePath prefix1, AstNode n2, TypePath prefix2) { // When `n2` is `*n1` propagate type information from a raw pointer type // parameter at `n1`. The other direction is handled in // `inferDereferencedExprPtrType`. @@ -596,23 +591,21 @@ private module Input3 implements InputSig3 { * * [1]: https://doc.rust-lang.org/reference/type-coercions.html#r-coerce.least-upper-bound */ - predicate parentChildType(AstNode parent, AstNode child, TypePath prefix) { - child = parent.(IfExpr).getABranch() and - prefix.isEmpty() - or - parent = any(MatchExpr me | child = me.getAnArm().getExpr()) and - prefix.isEmpty() - or - parent = any(ArrayListExpr ale | child = ale.getAnExpr()) and - prefix = TypePath::singleton(getArrayTypeParameter()) - or - bodyReturns(parent, child) and - prefix.isEmpty() - or - exists(Struct s | - child = [parent.(RangeExpr).getStart(), parent.(RangeExpr).getEnd()] and - prefix = TypePath::singleton(TTypeParamTypeParameter(s.getGenericParamList().getATypeParam())) and - s = getRangeType(parent) + predicate inferLubStep(AstNode child, TypePath path1, AstNode parent, TypePath prefix) { + path1.isEmpty() and + ( + parent = any(ArrayListExpr ale | child = ale.getAnExpr()) and + prefix = TypePath::singleton(getArrayTypeParameter()) + or + bodyReturns(parent, child) and + prefix.isEmpty() + or + exists(Struct s | + child = [parent.(RangeExpr).getStart(), parent.(RangeExpr).getEnd()] and + prefix = + TypePath::singleton(TTypeParamTypeParameter(s.getGenericParamList().getATypeParam())) and + s = getRangeType(parent) + ) ) } @@ -652,14 +645,14 @@ import M3 module Consistency { import M2::Consistency - private Type inferCertainTypeAdj(AstNode n, TypePath path) { - result = CertainTypeInference::inferCertainType(n, path) and + private Type inferTypeCertainAdj(AstNode n, TypePath path) { + result = inferTypeCertain(n, path) and not result = TNeverType() } predicate nonUniqueCertainType(AstNode n, TypePath path, Type t) { - strictcount(inferCertainTypeAdj(n, path)) > 1 and - t = inferCertainTypeAdj(n, path) and + strictcount(inferTypeCertainAdj(n, path)) > 1 and + t = inferTypeCertainAdj(n, path) and // Suppress the inconsistency if `n` is a self parameter and the type // mention for the self type has multiple types for a path. not exists(ImplItemNode impl, TypePath selfTypePath | @@ -922,8 +915,8 @@ private predicate bodyReturns(Expr body, Expr e) { ) } -private Type inferUnknownTypeFromAnnotation(AstNode n, TypePath path) { - inferType(n, path) = TUnknownType() and +pragma[nomagic] +private Type inferUnknownTypeFromAnnotationCand(AstNode n, TypePath path, TypePath prefix) { // 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) | @@ -932,6 +925,14 @@ private Type inferUnknownTypeFromAnnotation(AstNode n, TypePath path) { tm = any(ClosureExpr ce | n = ce.getBody()).getRetType().getTypeRepr() or tm = getReturnTypeMention(any(Function f | n = f.getBody())) + ) and + prefix = path.getAPrefix() +} + +private Type inferUnknownTypeFromAnnotation(AstNode n, TypePath path) { + exists(TypePath prefix | + result = inferUnknownTypeFromAnnotationCand(n, path, prefix) and + hasUnknownTypeAt(n, prefix) ) } @@ -3019,7 +3020,7 @@ private module ConstructionMatchingInput implements MatchingInputSig { or exists(TypePath suffix | suffix.isCons(TTypeParamTypeParameter(apos.asTypeParam()), path) and - result = CertainTypeInference::inferCertainType(this, suffix) + result = inferTypeCertain(this, suffix) ) } @@ -3628,6 +3629,15 @@ private Type inferForLoopExprType(AstNode n, TypePath path) { ) } +pragma[nomagic] +private Type inferClosureExprBodyTypeCand(AstNode n, TypePath path, TypePath prefix) { + exists(ClosureExpr ce | + n = ce.getClosureBody() and + result = inferType(ce, closureReturnPath().appendInverse(path)) and + prefix = path.getAPrefix() + ) +} + pragma[nomagic] private Type inferClosureExprType(AstNode n, TypePath path) { exists(ClosureExpr ce | @@ -3650,6 +3660,11 @@ private Type inferClosureExprType(AstNode n, TypePath path) { path.isEmpty() ) ) + or + exists(TypePath prefix | + result = inferClosureExprBodyTypeCand(n, path, prefix) and + hasUnknownTypeAt(n, prefix) + ) } pragma[nomagic] @@ -3716,7 +3731,7 @@ private module Debug { exists(string filepath, int startline, int startcolumn, int endline, int endcolumn | result.getLocation().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) and filepath.matches("%/main.rs") and - startline = 103 + startline = 1102 ) } @@ -3756,11 +3771,10 @@ private module Debug { tm.getTypeAt(path) = type } - Type debugInferAnnotatedType(AstNode n, TypePath path) { - n = getRelevantLocatable() and - result = CertainTypeInference::inferAnnotatedType(n, path) - } - + // Type debugInferAnnotatedType(AstNode n, TypePath path) { + // n = getRelevantLocatable() and + // result = inferAnnotatedType(n, path) + // } pragma[nomagic] private int countTypesAtPath(AstNode n, TypePath path, Type t) { t = inferType(n, path) and @@ -3809,9 +3823,9 @@ private module Debug { c = max(countTypePaths(_, _, _)) } - Type debugInferCertainType(AstNode n, TypePath path) { + Type debuginferTypeCertain(AstNode n, TypePath path) { n = getRelevantLocatable() and - result = CertainTypeInference::inferCertainType(n, path) + result = inferTypeCertain(n, path) } Type debugInferCertainNonUniqueType(AstNode n, TypePath path) { diff --git a/rust/ql/test/library-tests/type-inference/type-inference.expected b/rust/ql/test/library-tests/type-inference/type-inference.expected index f30a2c064d12..9075fc17f3eb 100644 --- a/rust/ql/test/library-tests/type-inference/type-inference.expected +++ b/rust/ql/test/library-tests/type-inference/type-inference.expected @@ -8961,10 +8961,8 @@ inferType | main.rs:826:16:826:16 | 3 | | {EXTERNAL LOCATION} | i32 | | main.rs:826:16:826:20 | ... > ... | | {EXTERNAL LOCATION} | bool | | main.rs:826:20:826:20 | 2 | | {EXTERNAL LOCATION} | i32 | -| main.rs:826:22:828:13 | { ... } | | main.rs:820:20:820:22 | Tr2 | | main.rs:827:17:827:20 | self | | {EXTERNAL LOCATION} | & | | main.rs:827:17:827:20 | self | TRef | main.rs:820:5:832:5 | Self [trait MyTrait2] | -| main.rs:827:17:827:25 | self.m1() | | main.rs:820:20:820:22 | Tr2 | | main.rs:828:20:830:13 | { ... } | | main.rs:820:20:820:22 | Tr2 | | main.rs:829:17:829:31 | ...::m1(...) | | main.rs:820:20:820:22 | Tr2 | | main.rs:829:26:829:30 | * ... | | main.rs:820:5:832:5 | Self [trait MyTrait2] | @@ -11482,13 +11480,9 @@ inferType | main.rs:2099:13:2103:13 | if value {...} else {...} | | {EXTERNAL LOCATION} | i64 | | main.rs:2099:16:2099:20 | value | | {EXTERNAL LOCATION} | bool | | main.rs:2099:22:2101:13 | { ... } | | {EXTERNAL LOCATION} | i32 | -| main.rs:2099:22:2101:13 | { ... } | | {EXTERNAL LOCATION} | i64 | | main.rs:2100:17:2100:17 | 1 | | {EXTERNAL LOCATION} | i32 | -| main.rs:2100:17:2100:17 | 1 | | {EXTERNAL LOCATION} | i64 | | main.rs:2101:20:2103:13 | { ... } | | {EXTERNAL LOCATION} | i32 | -| main.rs:2101:20:2103:13 | { ... } | | {EXTERNAL LOCATION} | i64 | | main.rs:2102:17:2102:17 | 0 | | {EXTERNAL LOCATION} | i32 | -| main.rs:2102:17:2102:17 | 0 | | {EXTERNAL LOCATION} | i64 | | main.rs:2113:19:2113:22 | SelfParam | | main.rs:2107:5:2107:19 | S | | main.rs:2113:19:2113:22 | SelfParam | T | main.rs:2109:10:2109:17 | T | | main.rs:2113:25:2113:29 | other | | main.rs:2107:5:2107:19 | S | @@ -11543,13 +11537,9 @@ inferType | main.rs:2154:13:2158:13 | if value {...} else {...} | | {EXTERNAL LOCATION} | i64 | | main.rs:2154:16:2154:20 | value | | {EXTERNAL LOCATION} | bool | | main.rs:2154:22:2156:13 | { ... } | | {EXTERNAL LOCATION} | i32 | -| main.rs:2154:22:2156:13 | { ... } | | {EXTERNAL LOCATION} | i64 | | main.rs:2155:17:2155:17 | 1 | | {EXTERNAL LOCATION} | i32 | -| main.rs:2155:17:2155:17 | 1 | | {EXTERNAL LOCATION} | i64 | | main.rs:2156:20:2158:13 | { ... } | | {EXTERNAL LOCATION} | i32 | -| main.rs:2156:20:2158:13 | { ... } | | {EXTERNAL LOCATION} | i64 | | main.rs:2157:17:2157:17 | 0 | | {EXTERNAL LOCATION} | i32 | -| main.rs:2157:17:2157:17 | 0 | | {EXTERNAL LOCATION} | i64 | | main.rs:2164:21:2164:25 | value | | main.rs:2162:19:2162:19 | T | | main.rs:2164:31:2164:31 | x | | main.rs:2162:5:2165:5 | Self [trait MyFrom2] | | main.rs:2169:21:2169:25 | value | | {EXTERNAL LOCATION} | i64 | @@ -11715,9 +11705,7 @@ inferType | main.rs:2265:21:2265:31 | [...] | TArray | {EXTERNAL LOCATION} | u8 | | main.rs:2265:22:2265:24 | 1u8 | | {EXTERNAL LOCATION} | u8 | | main.rs:2265:27:2265:27 | 2 | | {EXTERNAL LOCATION} | i32 | -| main.rs:2265:27:2265:27 | 2 | | {EXTERNAL LOCATION} | u8 | | main.rs:2265:30:2265:30 | 3 | | {EXTERNAL LOCATION} | i32 | -| main.rs:2265:30:2265:30 | 3 | | {EXTERNAL LOCATION} | u8 | | main.rs:2266:9:2266:25 | for ... in ... { ... } | | {EXTERNAL LOCATION} | () | | main.rs:2266:13:2266:13 | u | | {EXTERNAL LOCATION} | i32 | | main.rs:2266:13:2266:13 | u | | {EXTERNAL LOCATION} | u8 | @@ -11743,11 +11731,8 @@ inferType | main.rs:2271:31:2271:39 | [...] | TArray | {EXTERNAL LOCATION} | i32 | | main.rs:2271:31:2271:39 | [...] | TArray | {EXTERNAL LOCATION} | u32 | | main.rs:2271:32:2271:32 | 1 | | {EXTERNAL LOCATION} | i32 | -| main.rs:2271:32:2271:32 | 1 | | {EXTERNAL LOCATION} | u32 | | main.rs:2271:35:2271:35 | 2 | | {EXTERNAL LOCATION} | i32 | -| main.rs:2271:35:2271:35 | 2 | | {EXTERNAL LOCATION} | u32 | | main.rs:2271:38:2271:38 | 3 | | {EXTERNAL LOCATION} | i32 | -| main.rs:2271:38:2271:38 | 3 | | {EXTERNAL LOCATION} | u32 | | main.rs:2272:9:2272:25 | for ... in ... { ... } | | {EXTERNAL LOCATION} | () | | main.rs:2272:13:2272:13 | u | | {EXTERNAL LOCATION} | u32 | | main.rs:2272:18:2272:22 | vals3 | | {EXTERNAL LOCATION} | [;] | @@ -11888,7 +11873,6 @@ inferType | main.rs:2308:19:2308:25 | 0u8..10 | Idx | {EXTERNAL LOCATION} | i32 | | main.rs:2308:19:2308:25 | 0u8..10 | Idx | {EXTERNAL LOCATION} | u8 | | main.rs:2308:24:2308:25 | 10 | | {EXTERNAL LOCATION} | i32 | -| main.rs:2308:24:2308:25 | 10 | | {EXTERNAL LOCATION} | u8 | | main.rs:2308:28:2308:29 | { ... } | | {EXTERNAL LOCATION} | () | | main.rs:2309:13:2309:17 | range | | {EXTERNAL LOCATION} | Range | | main.rs:2309:13:2309:17 | range | Idx | {EXTERNAL LOCATION} | i32 | @@ -12641,11 +12625,9 @@ inferType | main.rs:2583:12:2583:12 | b | | {EXTERNAL LOCATION} | bool | | main.rs:2583:14:2586:9 | { ... } | | {EXTERNAL LOCATION} | Box | | main.rs:2583:14:2586:9 | { ... } | A | {EXTERNAL LOCATION} | Global | -| main.rs:2583:14:2586:9 | { ... } | T | main.rs:2547:5:2549:5 | dyn MyTrait | | main.rs:2583:14:2586:9 | { ... } | T | main.rs:2551:5:2552:19 | S | | main.rs:2583:14:2586:9 | { ... } | T.T | main.rs:2551:5:2552:19 | S | | main.rs:2583:14:2586:9 | { ... } | T.T.T | {EXTERNAL LOCATION} | i32 | -| main.rs:2583:14:2586:9 | { ... } | T.dyn(T) | {EXTERNAL LOCATION} | i32 | | main.rs:2584:17:2584:17 | x | | main.rs:2551:5:2552:19 | S | | main.rs:2584:17:2584:17 | x | T | main.rs:2551:5:2552:19 | S | | main.rs:2584:17:2584:17 | x | T.T | {EXTERNAL LOCATION} | i32 | @@ -12656,26 +12638,20 @@ inferType | main.rs:2584:21:2584:26 | x.m2() | T.T | {EXTERNAL LOCATION} | i32 | | main.rs:2585:13:2585:23 | ...::new(...) | | {EXTERNAL LOCATION} | Box | | main.rs:2585:13:2585:23 | ...::new(...) | A | {EXTERNAL LOCATION} | Global | -| main.rs:2585:13:2585:23 | ...::new(...) | T | main.rs:2547:5:2549:5 | dyn MyTrait | | main.rs:2585:13:2585:23 | ...::new(...) | T | main.rs:2551:5:2552:19 | S | | main.rs:2585:13:2585:23 | ...::new(...) | T.T | main.rs:2551:5:2552:19 | S | | main.rs:2585:13:2585:23 | ...::new(...) | T.T.T | {EXTERNAL LOCATION} | i32 | -| main.rs:2585:13:2585:23 | ...::new(...) | T.dyn(T) | {EXTERNAL LOCATION} | i32 | | main.rs:2585:22:2585:22 | x | | main.rs:2551:5:2552:19 | S | | main.rs:2585:22:2585:22 | x | T | main.rs:2551:5:2552:19 | S | | main.rs:2585:22:2585:22 | x | T.T | {EXTERNAL LOCATION} | i32 | | main.rs:2586:16:2588:9 | { ... } | | {EXTERNAL LOCATION} | Box | | main.rs:2586:16:2588:9 | { ... } | A | {EXTERNAL LOCATION} | Global | -| main.rs:2586:16:2588:9 | { ... } | T | main.rs:2547:5:2549:5 | dyn MyTrait | | main.rs:2586:16:2588:9 | { ... } | T | main.rs:2551:5:2552:19 | S | | main.rs:2586:16:2588:9 | { ... } | T.T | {EXTERNAL LOCATION} | i32 | -| main.rs:2586:16:2588:9 | { ... } | T.dyn(T) | {EXTERNAL LOCATION} | i32 | | main.rs:2587:13:2587:23 | ...::new(...) | | {EXTERNAL LOCATION} | Box | | main.rs:2587:13:2587:23 | ...::new(...) | A | {EXTERNAL LOCATION} | Global | -| main.rs:2587:13:2587:23 | ...::new(...) | T | main.rs:2547:5:2549:5 | dyn MyTrait | | main.rs:2587:13:2587:23 | ...::new(...) | T | main.rs:2551:5:2552:19 | S | | main.rs:2587:13:2587:23 | ...::new(...) | T.T | {EXTERNAL LOCATION} | i32 | -| main.rs:2587:13:2587:23 | ...::new(...) | T.dyn(T) | {EXTERNAL LOCATION} | i32 | | main.rs:2587:22:2587:22 | x | | main.rs:2551:5:2552:19 | S | | main.rs:2587:22:2587:22 | x | T | {EXTERNAL LOCATION} | i32 | | main.rs:2593:22:2597:5 | { ... } | | {EXTERNAL LOCATION} | () | diff --git a/rust/ql/test/library-tests/type-inference/type-inference.ql b/rust/ql/test/library-tests/type-inference/type-inference.ql index 8dcc34ad8001..374884ec4574 100644 --- a/rust/ql/test/library-tests/type-inference/type-inference.ql +++ b/rust/ql/test/library-tests/type-inference/type-inference.ql @@ -12,7 +12,7 @@ private predicate relevantNode(AstNode n) { } query predicate inferCertainType(AstNode n, TypePath path, Type t) { - t = TypeInference::CertainTypeInference::inferCertainType(n, path) and + t = TypeInference::inferTypeCertain(n, path) and t != TUnknownType() and relevantNode(n) } @@ -70,7 +70,7 @@ module TypeTest implements TestSig { ( tag = "type" or - t = TypeInference::CertainTypeInference::inferCertainType(n, path) and + t = TypeInference::inferTypeCertain(n, path) and tag = "certainType" ) and location = n.getLocation() and diff --git a/shared/typeinference/codeql/typeinference/internal/TypeInference.qll b/shared/typeinference/codeql/typeinference/internal/TypeInference.qll index c3adee01c7f8..0b10f4b1d98c 100644 --- a/shared/typeinference/codeql/typeinference/internal/TypeInference.qll +++ b/shared/typeinference/codeql/typeinference/internal/TypeInference.qll @@ -2177,9 +2177,9 @@ module Make1 Input1> { class Expr extends AstNode; /** - * A switch expression. + * A switch. */ - class SwitchExpr extends Expr { + class Switch extends AstNode { /** * Gets the expression being switched on. */ @@ -2189,13 +2189,13 @@ module Make1 Input1> { Case getCase(int index); } - /** A case in a switch expression. */ + /** A case in a switch. */ class Case extends AstNode { /** Gets a pattern being matched by this case. */ AstNode getAPattern(); /** Gets the body of this case. */ - Expr getBody(); + AstNode getBody(); } /** A ternary conditional expression. */ @@ -2336,25 +2336,41 @@ module Make1 Input1> { Type inferCallTypeOut(AstNode n, TypePosition pos, TypePath path); /** - * Holds if the types of `n1` at `path1` and `n2` at `path2` are certainly equal. + * Holds if `n1` having certain type `t` at `path1` implies that `n2` has + * certain type `t` at `path2`, but not necessarily the other way around. */ - predicate certainTypeEqualityInput(AstNode n1, TypePath path1, AstNode n2, TypePath path2); + default predicate inferStepCertain(AstNode n1, TypePath path1, AstNode n2, TypePath path2) { + none() + } + + /** + * Holds if `n1` having certain type `t` at `path1` implies that `n2` has + * certain type `t` at `path2`, and vice versa. + */ + default predicate inferStepSymmetricCertain( + AstNode n1, TypePath path1, AstNode n2, TypePath path2 + ) { + none() + } - /** Gets the inferred certain type of `n` at `path`. */ - Type inferCertainTypeInput(AstNode n, TypePath path); + /** + * Gets the inferred certain type of `n` at `path`. + * + * This predicate will be included directly in the exposed `inferTypeCertain` predicate. + */ + default Type inferTypeCertainInput(AstNode n, TypePath path) { none() } /** - * Holds if the types of `n1` at `path1` and `n2` at `path2` are possibly equal, - * and type information should be allowed to flow in both directions between them. + * Holds if `n1` having type `t` at `path1` implies that `n2` has type `t` at `path2`, + * but not necessarily the other way around. */ - predicate typeEqualityInput(AstNode n1, TypePath path1, AstNode n2, TypePath path2); + predicate inferStep(AstNode n1, TypePath path1, AstNode n2, TypePath path2); /** - * Holds if the type tree of `n1` at `path1` should be equal to the type tree - * of `n2` at `path2`, but type information should only propagate from `n1` to - * `n2`. + * Holds if `n1` having type `t` at `path1` implies that `n2` has type `t` at `path2`, + * and vice versa. */ - predicate typeEqualityAsymmetricInput(AstNode n1, TypePath path1, AstNode n2, TypePath path2); + predicate inferStepSymmetric(AstNode n1, TypePath path1, AstNode n2, TypePath path2); /** * Holds if `child` is a child of `parent` and the type of `parent` at `prefix` can be @@ -2362,9 +2378,15 @@ module Make1 Input1> { * * When `child` is unique, we also allow type information to flow from `parent` to `child`. */ - predicate parentChildType(AstNode parent, AstNode child, TypePath prefix); + default predicate inferLubStep(AstNode n1, TypePath path1, AstNode n2, TypePath path2) { + none() + } - /** Gets the inferred type of `n` at `path`. */ + /** + * Gets the inferred type of `n` at `path`. + * + * This predicate will be included directly in the exposed `inferType` predicate. + */ Type inferTypeInput(AstNode n, TypePath path); } @@ -2372,14 +2394,16 @@ module Make1 Input1> { private import Input3 /** Provides logic for inferring certain type information. */ - module CertainTypeInference { + private module Certain { /** Gets the type of `n`, which has an explicit type annotation. */ pragma[nomagic] Type inferAnnotatedType(AstNode n, TypePath path) { result = getTypeAnnotation(n).getTypeAt(path) } - predicate certainTypeEquality(AstNode n1, TypePath path1, AstNode n2, TypePath path2) { + private predicate stepSymmetricCertain( + AstNode n1, TypePath path1, AstNode n2, TypePath path2 + ) { path1.isEmpty() and path2.isEmpty() and ( @@ -2394,26 +2418,30 @@ module Make1 Input1> { n1 = n2.(ParenExpr).getExpr() ) or - certainTypeEqualityInput(n1, path1, n2, path2) + inferStepSymmetricCertain(n1, path1, n2, path2) + } + + predicate stepCertain(AstNode n1, TypePath path1, AstNode n2, TypePath path2) { + stepSymmetricCertain(n1, path1, n2, path2) + or + stepSymmetricCertain(n2, path2, n1, path1) + or + inferStepCertain(n1, path1, n2, path2) } pragma[nomagic] - private Type inferCertainTypeEquality(AstNode n, TypePath path) { + private Type inferTypeFromStepCertain(AstNode n, TypePath path) { exists(TypePath prefix1, AstNode n2, TypePath prefix2, TypePath suffix | - result = inferCertainType(n2, prefix2.appendInverse(suffix)) and - path = prefix1.append(suffix) - | - certainTypeEquality(n, prefix1, n2, prefix2) - or - certainTypeEquality(n2, prefix2, n, prefix1) + result = inferTypeCertain(n2, prefix2.appendInverse(suffix)) and + path = prefix1.append(suffix) and + stepCertain(n2, prefix2, n, prefix1) ) } private Type inferLogicalOperationType(AstNode n, TypePath path) { ( exists(LogicalAndExpr lae | n = [lae, lae.getLeftOperand(), lae.getRightOperand()]) or - exists(LogicalOrExpr loe | n = [loe, loe.getLeftOperand(), loe.getRightOperand()]) //or - // exists(LogicalNotExpr lne | n = [lne, lne.getOperand()]) + exists(LogicalOrExpr loe | n = [loe, loe.getLeftOperand(), loe.getRightOperand()]) ) and result instanceof BoolType and path.isEmpty() @@ -2450,13 +2478,13 @@ module Make1 Input1> { /** Gets the inferred certain type of `n` at `path`. */ cached - Type inferCertainType(AstNode n, TypePath path) { + Type inferTypeCertain(AstNode n, TypePath path) { CachedStage::ref() and result = inferAnnotatedType(n, path) or - result = inferCertainTypeEquality(n, path) + result = inferTypeFromStepCertain(n, path) or - result = inferCertainTypeInput(n, path) + result = inferTypeCertainInput(n, path) or result = inferLogicalOperationType(n, path) or @@ -2473,7 +2501,7 @@ module Make1 Input1> { pragma[nomagic] private predicate infersCertainTypeAt(AstNode n, TypePath prefix, TypeParameter tp) { exists(TypePath path | - exists(inferCertainType(n, path)) and + exists(inferTypeCertain(n, path)) and path.isSnoc(prefix, tp) ) } @@ -2483,7 +2511,7 @@ module Make1 Input1> { */ pragma[nomagic] predicate hasInferredCertainType(AstNode n, TypePath path) { - exists(inferCertainType(n, path)) + exists(inferTypeCertain(n, path)) } /** @@ -2493,7 +2521,7 @@ module Make1 Input1> { bindingset[n, prefix, path, t] pragma[inline_late] predicate certainTypeConflict(AstNode n, TypePath prefix, TypePath path, Type t) { - inferCertainType(n, path) != t + inferTypeCertain(n, path) != t or // If we infer that `n` has _some_ type at `T1.T2....Tn`, and we also // know that `n` certainly has type `certainType` at `T1.T2...Ti`, `0 <= i < n`, @@ -2504,15 +2532,27 @@ module Make1 Input1> { exists(TypePath suffix, TypeParameter tp, Type certainType | path = prefix.appendInverse(suffix) and tp = suffix.getHead() and - inferCertainType(n, prefix) = certainType and + inferTypeCertain(n, prefix) = certainType and not certainType.getATypeParameter() = tp ) } } - private predicate typeEquality(AstNode n1, TypePath path1, AstNode n2, TypePath path2) { - CertainTypeInference::certainTypeEquality(n1, path1, n2, path2) + predicate inferTypeCertain = Certain::inferTypeCertain/2; + + private predicate lubStep(AstNode n1, TypePath path1, AstNode n2, TypePath path2) { + path1.isEmpty() and + path2.isEmpty() and + ( + n1 = n2.(Switch).getCase(_).getBody() + or + n2 = any(ConditionalExpr ce | n1 = [ce.getThen(), ce.getElse()]) + ) or + inferLubStep(n1, path1, n2, path2) + } + + private predicate stepSymmetric(AstNode n1, TypePath path1, AstNode n2, TypePath path2) { path1.isEmpty() and path2.isEmpty() and ( @@ -2525,50 +2565,76 @@ module Make1 Input1> { let.getLeftOperand() = n1 and let.getRightOperand() = n2 ) + or + exists(Switch switch | + n1 = switch.getExpr() and + n2 = switch.getCase(_).getAPattern() + ) ) or - typeEqualityInput(n1, path1, n2, path2) + inferStepSymmetric(n1, path1, n2, path2) + // or + // n2 = unique(AstNode child | parentChildType(n1, child, path1) | child) and + // path2.isEmpty() + } + + // private predicate lubCoercion(AstNode parent, AstNode child, TypePath prefix) { + // parentChildType(parent, child, prefix) and + // strictcount(AstNode child0 | parentChildType(parent, child0, prefix) | child0) > 1 + // or + // inferStep(child, TypePath::nil(), parent, prefix) + // } + private predicate step(AstNode n1, TypePath path1, AstNode n2, TypePath path2) { + // lubCoercion(n2, n1, path2) and + // path1.isEmpty() + // or + // exists(AstNode mid, TypePath pathMid, TypePath suffix | + // typeEquality(n1, pathMid, mid, path2) or + // typeEquality(mid, path2, n1, pathMid) + // | + // lubCoercion(mid, n2, suffix) and + // not lubCoercion(mid, n1, _) and + // path1 = pathMid.append(suffix) + // ) + // or + inferStep(n1, path1, n2, path2) or - n2 = unique(AstNode child | parentChildType(n1, child, path1) | child) and - path2.isEmpty() - } - - private predicate lubCoercion(AstNode parent, AstNode child, TypePath prefix) { - parentChildType(parent, child, prefix) and - strictcount(AstNode child0 | parentChildType(parent, child0, prefix) | child0) > 1 + stepSymmetric(n1, path1, n2, path2) or - typeEqualityAsymmetricInput(child, TypePath::nil(), parent, prefix) + stepSymmetric(n2, path2, n1, path1) + or + Certain::stepCertain(n1, path1, n2, path2) + or + lubStep(n1, path1, n2, path2) + or + n2 = unique(AstNode n | lubStep(n, _, n1, _) | n) and + lubStep(n2, path2, n1, path1) } - private predicate typeEqualityAsymmetric( - AstNode n1, TypePath path1, AstNode n2, TypePath path2 - ) { - lubCoercion(n2, n1, path2) and - path1.isEmpty() - or - exists(AstNode mid, TypePath pathMid, TypePath suffix | - typeEquality(n1, pathMid, mid, path2) or - typeEquality(mid, path2, n1, pathMid) - | - lubCoercion(mid, n2, suffix) and - not lubCoercion(mid, n1, _) and - path1 = pathMid.append(suffix) + pragma[nomagic] + private Type inferTypeFromStep(AstNode n, TypePath path) { + exists(TypePath prefix1, AstNode n2, TypePath prefix2, TypePath suffix | + result = inferType(n2, prefix2.appendInverse(suffix)) and + path = prefix1.append(suffix) and + step(n2, prefix2, n, prefix1) ) - or - typeEqualityAsymmetricInput(n1, path1, n2, path2) } pragma[nomagic] - private Type inferTypeEquality(AstNode n, TypePath path) { + private Type inferTypeFromReverseLubStepCand(AstNode n, TypePath path, TypePath prefix) { exists(TypePath prefix1, AstNode n2, TypePath prefix2, TypePath suffix | result = inferType(n2, prefix2.appendInverse(suffix)) and - path = prefix1.append(suffix) - | - typeEquality(n, prefix1, n2, prefix2) - or - typeEquality(n2, prefix2, n, prefix1) - or - typeEqualityAsymmetric(n2, prefix2, n, prefix1) + path = prefix1.append(suffix) and + lubStep(n, prefix1, n2, prefix2) and + prefix = path.getAPrefix() + ) + } + + pragma[nomagic] + private Type inferTypeFromReverseLub(AstNode n, TypePath path) { + exists(TypePath prefix | + result = inferTypeFromReverseLubStepCand(n, path, prefix) and + hasUnknownTypeAt(n, prefix) ) } @@ -2578,18 +2644,20 @@ module Make1 Input1> { cached Type inferType(AstNode n, TypePath path) { CachedStage::ref() and - result = CertainTypeInference::inferCertainType(n, path) + result = inferTypeCertain(n, path) or // Don't propagate type information into a node which conflicts with certain // type information. forall(TypePath prefix | - CertainTypeInference::hasInferredCertainType(n, prefix) and + Certain::hasInferredCertainType(n, prefix) and prefix.isPrefixOf(path) | - not CertainTypeInference::certainTypeConflict(n, prefix, path, result) + not Certain::certainTypeConflict(n, prefix, path, result) ) and ( - result = inferTypeEquality(n, path) + result = inferTypeFromStep(n, path) + or + result = inferTypeFromReverseLub(n, path) or result = CheckContextTyping::check(n, path) or @@ -2646,7 +2714,7 @@ module Make1 Input1> { } pragma[nomagic] - private predicate hasUnknownTypeAt(AstNode n, TypePath path) { + predicate hasUnknownTypeAt(AstNode n, TypePath path) { inferType(n, path) instanceof UnknownType } @@ -2699,7 +2767,7 @@ module Make1 Input1> { /** Reverse references to the predicates that reference `ref()`. */ cached predicate revRef() { - (exists(CertainTypeInference::inferCertainType(_, _)) implies any()) + (exists(inferTypeCertain(_, _)) implies any()) or (exists(inferType(_, _)) implies any()) or From 0d2c6c277a447bc09f1ed97aa5dc10b2626bd856 Mon Sep 17 00:00:00 2001 From: Tom Hvitved Date: Thu, 7 May 2026 11:59:02 +0200 Subject: [PATCH 10/18] wip10 --- .../internal/typeinference/TypeInference.qll | 25 ++--- .../typeinference/internal/TypeInference.qll | 98 ++++++++++++++----- 2 files changed, 80 insertions(+), 43 deletions(-) diff --git a/rust/ql/lib/codeql/rust/internal/typeinference/TypeInference.qll b/rust/ql/lib/codeql/rust/internal/typeinference/TypeInference.qll index 02b98f9990a3..ac9adc1f96ab 100644 --- a/rust/ql/lib/codeql/rust/internal/typeinference/TypeInference.qll +++ b/rust/ql/lib/codeql/rust/internal/typeinference/TypeInference.qll @@ -916,7 +916,7 @@ private predicate bodyReturns(Expr body, Expr e) { } pragma[nomagic] -private Type inferUnknownTypeFromAnnotationCand(AstNode n, TypePath path, TypePath prefix) { +private Type inferTypeFromAnnotationTopDown(AstNode n, TypePath path) { // 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) | @@ -925,17 +925,12 @@ private Type inferUnknownTypeFromAnnotationCand(AstNode n, TypePath path, TypePa tm = any(ClosureExpr ce | n = ce.getBody()).getRetType().getTypeRepr() or tm = getReturnTypeMention(any(Function f | n = f.getBody())) - ) and - prefix = path.getAPrefix() -} - -private Type inferUnknownTypeFromAnnotation(AstNode n, TypePath path) { - exists(TypePath prefix | - result = inferUnknownTypeFromAnnotationCand(n, path, prefix) and - hasUnknownTypeAt(n, prefix) ) } +private predicate inferUnknownTypeFromAnnotation = + TopDownTyping::inferType/2; + pragma[nomagic] private TupleType inferTupleRootType(AstNode n) { // `typeEquality` handles the non-root cases @@ -2819,7 +2814,7 @@ private Type inferFunctionCallType0( call.hasUnknownTypeAt(derefChainBorrow, pos, path0) and result = TUnknownType() or - result = inferCallTypeOut(call, pos, n, derefChainBorrow, path0) + result = inferCallTypeOut(call, derefChainBorrow, pos, n, path0) ) | if @@ -3630,11 +3625,10 @@ private Type inferForLoopExprType(AstNode n, TypePath path) { } pragma[nomagic] -private Type inferClosureExprBodyTypeCand(AstNode n, TypePath path, TypePath prefix) { +private Type inferClosureExprBodyTypeTopDown(AstNode n, TypePath path) { exists(ClosureExpr ce | n = ce.getClosureBody() and - result = inferType(ce, closureReturnPath().appendInverse(path)) and - prefix = path.getAPrefix() + result = inferType(ce, closureReturnPath().appendInverse(path)) ) } @@ -3661,10 +3655,7 @@ private Type inferClosureExprType(AstNode n, TypePath path) { ) ) or - exists(TypePath prefix | - result = inferClosureExprBodyTypeCand(n, path, prefix) and - hasUnknownTypeAt(n, prefix) - ) + result = TopDownTyping::inferType(n, path) } pragma[nomagic] diff --git a/shared/typeinference/codeql/typeinference/internal/TypeInference.qll b/shared/typeinference/codeql/typeinference/internal/TypeInference.qll index 0b10f4b1d98c..b8a1e1a806ec 100644 --- a/shared/typeinference/codeql/typeinference/internal/TypeInference.qll +++ b/shared/typeinference/codeql/typeinference/internal/TypeInference.qll @@ -147,7 +147,7 @@ signature module InputSig1 { /** * A special pseudo type used to represent cases where the actual type needs - * to be inferred from the context. For example, in + * to be inferred from the context in a top-down manner. For example, in * * ```rust * let x = Vec::new(); @@ -2146,13 +2146,14 @@ module Make1 Input1> { */ signature module InputSig3 { /** - * References to cached predicates that should be included to the cached - * stage of type inference. Such predicates should reference `CachedStage::ref`. + * A predicate used to reference cached predicates that should be included to the + * cached stage of type inference. Such predicates should themselves reference + * `CachedStage::ref`. */ default predicate cachedStageRevRef() { none() } /** - * Point this predicate to the `inferType` predicate in the output of this module. + * Point this predicate to the `inferType` predicate from the output of this module. * * Needed to be able to refer to `inferType` in default signature implementations. */ @@ -2278,7 +2279,8 @@ module Make1 Input1> { } /** - * A position where a callable can have a declared type. + * A position where a callable can have a declared type and a call can have + * an inferred type. */ class TypePosition { /** Holds if this position represents the return type of a callable. */ @@ -2288,7 +2290,14 @@ module Make1 Input1> { string toString(); } - /** A context needed to resolve calls. */ + /** + * A context needed to resolve calls. + * + * For example, in Rust, we need an additional context to represent the + * candidate receiver type when resolving method calls. + * + * When not used, simply instantiate this class with `Unit`. + */ bindingset[this] class CallResolutionContext { /** Gets a textual representation of this context. */ @@ -2298,11 +2307,18 @@ module Make1 Input1> { /** A callable. */ class Callable { + /** Gets the type parameter at position `ppos` of this callable, if any. */ TypeParameter getTypeParameter(TypeParameterPosition ppos); + /** + * Gets an additional type parameter constraint for the given type parameter, + * which applies to this callable. For example, in Rust, a function can apply + * additional constraints on type parameters belonging to the `impl` block + * that the function is defined in. + */ TypeMention getAdditionalTypeParameterConstraint(TypeParameter tp); - /* Gets the declared type of this callable at `path` for position `pos`. */ + /** Gets the declared type of this callable at `path` for position `pos`. */ Type getDeclaredType(TypePosition pos, TypePath path); /** Gets a textual representation of this callable. */ @@ -2312,19 +2328,31 @@ module Make1 Input1> { Location getLocation(); } + /** A call expression. */ class Call extends Expr { + /** Gets the explicit type argument at position `apos` and `path` for this call, if any. */ Type getTypeArgument(TypeArgumentPosition apos, TypePath path); + /** Gets the AST node corresponding to the position `pos` of this call. */ AstNode getNodeAt(TypePosition pos); - /** Gets the target of this call. */ + /** + * Gets the target of this call, to be used when inferring certain types. + */ Callable getTargetCertain(); - /** Gets the target of this call. */ + /** Gets the target of this call in the given context. */ Callable getTarget(CallResolutionContext ctx); } - /** Gets the inferred type `call` at `path` for position `pos` in context `ctx`. */ + /** + * Gets the inferred type of `call` at `path` and position `pos` in context `ctx`. + * + * By default, this is the inferred type of the node at the given position, but + * in for example Rust, the inferred type of the receiver of a method call needs + * to take the call context into account, in order to use the correct candidate + * receiver type. + */ bindingset[ctx] default Type inferCallTypeIn( Call call, CallResolutionContext ctx, TypePosition pos, TypePath path @@ -2556,9 +2584,9 @@ module Make1 Input1> { path1.isEmpty() and path2.isEmpty() and ( - exists(Assignment a | - a.getLeftOperand() = n1 and - a.getRightOperand() = n2 + exists(AssignExpr ae | + ae.getLeftOperand() = n1 and + ae.getRightOperand() = n2 ) or exists(LetDeclaration let | @@ -2621,22 +2649,16 @@ module Make1 Input1> { } pragma[nomagic] - private Type inferTypeFromReverseLubStepCand(AstNode n, TypePath path, TypePath prefix) { + private Type inferTypeFromLubStepTopDown(AstNode n, TypePath path) { exists(TypePath prefix1, AstNode n2, TypePath prefix2, TypePath suffix | result = inferType(n2, prefix2.appendInverse(suffix)) and path = prefix1.append(suffix) and - lubStep(n, prefix1, n2, prefix2) and - prefix = path.getAPrefix() + lubStep(n, prefix1, n2, prefix2) ) } - pragma[nomagic] - private Type inferTypeFromReverseLub(AstNode n, TypePath path) { - exists(TypePath prefix | - result = inferTypeFromReverseLubStepCand(n, path, prefix) and - hasUnknownTypeAt(n, prefix) - ) - } + private predicate inferTypeFromReverseLub = + TopDownTyping::inferType/2; /** * Gets the inferred type of `n` at `path`. @@ -2705,22 +2727,46 @@ module Make1 Input1> { private module CallMatching = MatchingWithEnvironment; - pragma[nomagic] Type inferCallTypeOut( - Call call, TypePosition pos, AstNode n, CallResolutionContext ctx, TypePath path + Call call, CallResolutionContext ctx, TypePosition pos, AstNode n, TypePath path ) { n = call.getNodeAt(pos) and result = CallMatching::inferAccessType(call, ctx, pos, path) } pragma[nomagic] - predicate hasUnknownTypeAt(AstNode n, TypePath path) { + private predicate hasUnknownTypeAt(AstNode n, TypePath path) { inferType(n, path) instanceof UnknownType } pragma[nomagic] private predicate hasUnknownType(AstNode n) { hasUnknownTypeAt(n, _) } + signature Type inferTypeTopDownSig(AstNode n, TypePath path); + + /** + * Given a predicate `inferTypeTopDown` for inferring the type of an AST node `n` + * top-down from a context, this module exposes the predicate `inferType`, which + * restricts type information to only flow top-down into `n` when `n` has an + * explicit unknown type. + */ + module TopDownTyping { + pragma[nomagic] + private Type inferTypeTopDown(AstNode n, TypePath prefix, TypePath path) { + result = inferTypeTopDown(n, path) and + hasUnknownType(n) and + prefix = path.getAPrefix() + } + + pragma[nomagic] + Type inferType(AstNode n, TypePath path) { + exists(TypePath prefix | + result = inferTypeTopDown(n, prefix, path) and + hasUnknownTypeAt(n, prefix) + ) + } + } + signature Type inferCallTypeSig(AstNode n, TypePosition pos, TypePath path); /** From cd73d22b31acc1f35e8ed5462417e6ada28f55af Mon Sep 17 00:00:00 2001 From: Tom Hvitved Date: Thu, 7 May 2026 15:18:43 +0200 Subject: [PATCH 11/18] wip11 --- .../internal/typeinference/TypeInference.qll | 159 ++++++++---------- .../PathResolutionConsistency.expected | 2 + .../type-inference/type-inference.expected | 5 - .../typeinference/internal/TypeInference.qll | 116 +++++++++---- 4 files changed, 155 insertions(+), 127 deletions(-) create mode 100644 rust/ql/test/library-tests/dataflow/models/CONSISTENCY/PathResolutionConsistency.expected diff --git a/rust/ql/lib/codeql/rust/internal/typeinference/TypeInference.qll b/rust/ql/lib/codeql/rust/internal/typeinference/TypeInference.qll index ac9adc1f96ab..910e988d9be4 100644 --- a/rust/ql/lib/codeql/rust/internal/typeinference/TypeInference.qll +++ b/rust/ql/lib/codeql/rust/internal/typeinference/TypeInference.qll @@ -415,15 +415,30 @@ private module Input3 implements InputSig3 { } bindingset[derefChainBorrow] - Type inferCallTypeIn(Call call, string derefChainBorrow, FunctionPosition pos, TypePath path) { + Type inferCallTypeBottomUp(Call call, string derefChainBorrow, FunctionPosition pos, TypePath path) { result = call.(FunctionCallMatchingInput::Access).getInferredType(derefChainBorrow, pos, path) } - Type inferCallTypeOut(AstNode n, TypePosition pos, TypePath path) { - result = inferFunctionCallTypeNonSelf(n, pos, path) + Type inferCallReturnType(AstNode n, TypePath path) { + exists(Call call, TypePath path0 | + result = inferCallReturnType(call, _, n, path0) and + if + // index expression `x[i]` desugars to `*x.index(i)`, so we must account for + // the implicit deref + call instanceof IndexExpr + then path0.isCons(getRefTypeParameter(_), path) + else path = path0 + ) + } + + Type inferCallArgumentTypeTopDown(AstNode n, TypePath path) { + exists(FunctionCallMatchingInput::Access call, FunctionPosition pos | + result = inferCallArgumentTypeTopDown(call, pos, n, _, _, path) and + not call.(AssocFunctionResolution::AssocFunctionCall).hasReceiverAtPos(pos) + ) or exists(FunctionCallMatchingInput::Access a | - result = inferFunctionCallTypeSelf(a, n, DerefChain::nil(), path) and + result = inferFunctionCallSelfArgumentTypeTopDown(a, n, DerefChain::nil(), path) and if a.(AssocFunctionResolution::AssocFunctionCall).hasReceiver() then not path.isEmpty() else any() @@ -459,7 +474,7 @@ private module Input3 implements InputSig3 { ) } - Type inferTypeCertainInput(AstNode n, TypePath path) { + Type inferTypeCertainSpecific(AstNode n, TypePath path) { result = inferFunctionBodyType(n, path) or result = inferLiteralType(n, path, true) @@ -580,36 +595,31 @@ private module Input3 implements InputSig3 { prefix1.isEmpty() } - /** - * Holds if `child` is a child of `parent`, and the Rust compiler applies [least - * upper bound (LUB) coercion][1] to infer the type of `parent` from the type of - * `child`. - * - * In this case, we want type information to only flow from `child` to `parent`, - * to avoid (a) either having to model LUB coercions, or (b) risk combinatorial - * explosion in inferred types. - * - * [1]: https://doc.rust-lang.org/reference/type-coercions.html#r-coerce.least-upper-bound - */ - predicate inferLubStep(AstNode child, TypePath path1, AstNode parent, TypePath prefix) { + predicate inferLubStep(AstNode n1, TypePath path1, AstNode n2, TypePath path2) { path1.isEmpty() and ( - parent = any(ArrayListExpr ale | child = ale.getAnExpr()) and - prefix = TypePath::singleton(getArrayTypeParameter()) + n2 = any(ArrayListExpr ale | n1 = ale.getAnExpr()) and + path2 = TypePath::singleton(getArrayTypeParameter()) or - bodyReturns(parent, child) and - prefix.isEmpty() + bodyReturns(n2, n1) and + path2.isEmpty() or exists(Struct s | - child = [parent.(RangeExpr).getStart(), parent.(RangeExpr).getEnd()] and - prefix = + n1 = [n2.(RangeExpr).getStart(), n2.(RangeExpr).getEnd()] and + path2 = TypePath::singleton(TTypeParamTypeParameter(s.getGenericParamList().getATypeParam())) and - s = getRangeType(parent) + s = getRangeType(n2) ) ) } - Type inferTypeInput(AstNode n, TypePath path) { + Type inferTypeTopDown(AstNode n, TypePath path) { + result = inferTypeFromAnnotationTopDown(n, path) + or + result = inferClosureExprBodyTypeTopDown(n, path) + } + + Type inferTypeSpecific(AstNode n, TypePath path) { result = inferAssignmentOperationType(n, path) or result = inferConstructionType(n, path) @@ -634,7 +644,7 @@ private module Input3 implements InputSig3 { or result = inferDeconstructionPatType(n, path) or - result = inferUnknownTypeFromAnnotation(n, path) + result = inferUnknownType(n, path) } } @@ -928,9 +938,6 @@ private Type inferTypeFromAnnotationTopDown(AstNode n, TypePath path) { ) } -private predicate inferUnknownTypeFromAnnotation = - TopDownTyping::inferType/2; - pragma[nomagic] private TupleType inferTupleRootType(AstNode n) { // `typeEquality` handles the non-root cases @@ -2802,36 +2809,13 @@ private module FunctionCallMatchingInput implements MatchingWithEnvironmentInput } pragma[nomagic] -private Type inferFunctionCallType0( +private Type inferCallArgumentTypeTopDown( FunctionCallMatchingInput::Access call, FunctionPosition pos, AstNode n, DerefChain derefChain, BorrowKind borrow, TypePath path ) { - exists(TypePath path0 | - exists(string derefChainBorrow | - FunctionCallMatchingInput::decodeDerefChainBorrow(derefChainBorrow, derefChain, borrow) - | - n = call.getNodeAt(pos) and - call.hasUnknownTypeAt(derefChainBorrow, pos, path0) and - result = TUnknownType() - or - result = inferCallTypeOut(call, derefChainBorrow, pos, n, path0) - ) - | - if - // index expression `x[i]` desugars to `*x.index(i)`, so we must account for - // the implicit deref - pos.isReturn() and - call instanceof IndexExpr - then path0.isCons(getRefTypeParameter(_), path) - else path = path0 - ) -} - -pragma[nomagic] -private Type inferFunctionCallTypeNonSelf(AstNode n, FunctionPosition pos, TypePath path) { - exists(FunctionCallMatchingInput::Access call | - result = inferFunctionCallType0(call, pos, n, _, _, path) and - not call.(AssocFunctionResolution::AssocFunctionCall).hasReceiverAtPos(pos) + exists(string derefChainBorrow | + FunctionCallMatchingInput::decodeDerefChainBorrow(derefChainBorrow, derefChain, borrow) and + result = inferCallArgumentTypeTopDown(call, derefChainBorrow, pos, n, path) ) } @@ -2843,12 +2827,12 @@ private Type inferFunctionCallTypeNonSelf(AstNode n, FunctionPosition pos, TypeP * empty, at which point the inferred type can be applied back to `n`. */ pragma[nomagic] -private Type inferFunctionCallTypeSelf( +private Type inferFunctionCallSelfArgumentTypeTopDown( FunctionCallMatchingInput::Access call, AstNode n, DerefChain derefChain, TypePath path ) { exists(FunctionPosition pos, BorrowKind borrow, TypePath path0 | call.(AssocFunctionResolution::AssocFunctionCall).hasReceiverAtPos(pos) and - result = inferFunctionCallType0(call, pos, n, derefChain, borrow, path0) + result = inferCallArgumentTypeTopDown(call, pos, n, derefChain, borrow, path0) | borrow.isNoBorrow() and path = path0 @@ -2865,7 +2849,7 @@ private Type inferFunctionCallTypeSelf( DerefChain derefChain0, Type t0, TypePath path0, DerefImplItemNode impl, Type selfParamType, TypePath selfPath | - t0 = inferFunctionCallTypeSelf(call, n, derefChain0, path0) and + t0 = inferFunctionCallSelfArgumentTypeTopDown(call, n, derefChain0, path0) and derefChain0.isCons(impl, derefChain) and selfParamType = impl.resolveSelfTypeAt(selfPath) | @@ -3041,17 +3025,37 @@ private module ConstructionMatching = Matching; pragma[nomagic] private Type inferConstructionTypePreCheck(AstNode n, FunctionPosition pos, TypePath path) { - exists(ConstructionMatchingInput::Access a | n = a.getNodeAt(pos) | + exists(ConstructionMatchingInput::Access a | + n = a.getNodeAt(pos) and result = ConstructionMatching::inferAccessType(a, pos, path) - or - a.hasUnknownTypeAt(pos, path) and - result = TUnknownType() ) } private predicate inferConstructionType = CheckContextTyping::check/2; +pragma[nomagic] +private Type inferUnknownType(AstNode n, TypePath path) { + result = TUnknownType() and + ( + exists(FunctionCallMatchingInput::Access call, FunctionPosition pos | + n = call.getNodeAt(pos) and + call.hasUnknownTypeAt(_, pos, path) + ) + or + exists(ConstructionMatchingInput::Access a, FunctionPosition pos | + n = a.getNodeAt(pos) and + a.hasUnknownTypeAt(pos, path) + ) + or + exists(Param p | + not p.hasTypeRepr() and + n = p.getPat() and + path.isEmpty() + ) + ) +} + /** * A matching configuration for resolving types of operations like `a + b`. */ @@ -3633,29 +3637,14 @@ private Type inferClosureExprBodyTypeTopDown(AstNode n, TypePath path) { } pragma[nomagic] -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 - exists(TypePath path0 | - result = ce.getRetType().getTypeRepr().(TypeMention).getTypeAt(path0) and - path = closureReturnPath().append(path0) - ) - ) - or - exists(Param p | - p = ce.getAParam() and - not p.hasTypeRepr() and - n = p.getPat() and - result = TUnknownType() and - path.isEmpty() - ) - ) +private Type inferClosureExprType(ClosureExpr ce, TypePath path) { + path = TypePath::singleton(TDynTraitTypeParameter(_, any(FnTrait t).getTypeParam())) and + result.(TupleType).getArity() = ce.getNumberOfParams() or - result = TopDownTyping::inferType(n, path) + exists(TypePath suffix | + result = ce.getRetType().getTypeRepr().(TypeMention).getTypeAt(suffix) and + path = closureReturnPath().append(suffix) + ) } pragma[nomagic] diff --git a/rust/ql/test/library-tests/dataflow/models/CONSISTENCY/PathResolutionConsistency.expected b/rust/ql/test/library-tests/dataflow/models/CONSISTENCY/PathResolutionConsistency.expected new file mode 100644 index 000000000000..cfad81d2796a --- /dev/null +++ b/rust/ql/test/library-tests/dataflow/models/CONSISTENCY/PathResolutionConsistency.expected @@ -0,0 +1,2 @@ +multipleResolvedTargets +| main.rs:218:20:218:25 | ... != ... | diff --git a/rust/ql/test/library-tests/type-inference/type-inference.expected b/rust/ql/test/library-tests/type-inference/type-inference.expected index 9075fc17f3eb..94b98d92f7da 100644 --- a/rust/ql/test/library-tests/type-inference/type-inference.expected +++ b/rust/ql/test/library-tests/type-inference/type-inference.expected @@ -10250,7 +10250,6 @@ inferType | main.rs:1412:17:1412:20 | self | TRef.TSlice | main.rs:1410:14:1410:23 | T | | main.rs:1412:17:1412:27 | self.get(...) | | {EXTERNAL LOCATION} | Option | | main.rs:1412:17:1412:27 | self.get(...) | T | {EXTERNAL LOCATION} | & | -| main.rs:1412:17:1412:27 | self.get(...) | T.TRef | main.rs:1410:14:1410:23 | T | | main.rs:1412:17:1412:36 | ... .unwrap() | | {EXTERNAL LOCATION} | & | | main.rs:1412:17:1412:36 | ... .unwrap() | TRef | main.rs:1410:14:1410:23 | T | | main.rs:1412:26:1412:26 | 0 | | {EXTERNAL LOCATION} | i32 | @@ -11591,8 +11590,6 @@ inferType | main.rs:2221:18:2221:21 | true | | {EXTERNAL LOCATION} | bool | | main.rs:2223:9:2223:15 | S(...) | | main.rs:2107:5:2107:19 | S | | main.rs:2223:9:2223:15 | S(...) | T | {EXTERNAL LOCATION} | i64 | -| main.rs:2223:9:2223:15 | S(...) | T | main.rs:2107:5:2107:19 | S | -| main.rs:2223:9:2223:15 | S(...) | T.T | {EXTERNAL LOCATION} | i64 | | main.rs:2223:9:2223:31 | ... .my_add(...) | | main.rs:2107:5:2107:19 | S | | main.rs:2223:9:2223:31 | ... .my_add(...) | T | {EXTERNAL LOCATION} | i64 | | main.rs:2223:9:2223:31 | ... .my_add(...) | T | main.rs:2107:5:2107:19 | S | @@ -11611,8 +11608,6 @@ inferType | main.rs:2224:24:2224:27 | 3i64 | | {EXTERNAL LOCATION} | i64 | | main.rs:2225:9:2225:15 | S(...) | | main.rs:2107:5:2107:19 | S | | main.rs:2225:9:2225:15 | S(...) | T | {EXTERNAL LOCATION} | i64 | -| main.rs:2225:9:2225:15 | S(...) | T | {EXTERNAL LOCATION} | & | -| main.rs:2225:9:2225:15 | S(...) | T.TRef | {EXTERNAL LOCATION} | i64 | | main.rs:2225:9:2225:29 | ... .my_add(...) | | main.rs:2107:5:2107:19 | S | | main.rs:2225:9:2225:29 | ... .my_add(...) | T | {EXTERNAL LOCATION} | i64 | | main.rs:2225:11:2225:14 | 1i64 | | {EXTERNAL LOCATION} | i64 | diff --git a/shared/typeinference/codeql/typeinference/internal/TypeInference.qll b/shared/typeinference/codeql/typeinference/internal/TypeInference.qll index b8a1e1a806ec..c36f63c75fc5 100644 --- a/shared/typeinference/codeql/typeinference/internal/TypeInference.qll +++ b/shared/typeinference/codeql/typeinference/internal/TypeInference.qll @@ -2352,16 +2352,41 @@ module Make1 Input1> { * in for example Rust, the inferred type of the receiver of a method call needs * to take the call context into account, in order to use the correct candidate * receiver type. + * + * The type information provided by this predicate is used to derive type information + * about the call via the call target, such as the return type. */ bindingset[ctx] - default Type inferCallTypeIn( + default Type inferCallTypeBottomUp( Call call, CallResolutionContext ctx, TypePosition pos, TypePath path ) { result = inferType(call.getNodeAt(pos), path) and exists(ctx) } - Type inferCallTypeOut(AstNode n, TypePosition pos, TypePath path); + /** + * Gets the inferred return type of `call` at `path`. + * + * When no post-processing is needed, simply implement this predicate as + * `result = inferCallReturnType(_, _, n, path)`. + */ + Type inferCallReturnType(AstNode n, TypePath path); + + /** + * Gets the top-down inferred type of `call` at `path` and argument position + * `pos`. + * + * This predicate is used to propagate type information from the call target + * into call arguments, for example when an implicitly typed lambda is passed + * as an argument. + * + * Type information is only propagated into arguments with an explicitly unknown + * type. + * + * When no call-context based post-processing is needed, simply implement this + * predicate as `result = inferCallArgumentTypeTopDown(_, _, _, n, path)`. + */ + Type inferCallArgumentTypeTopDown(AstNode n, TypePath path); /** * Holds if `n1` having certain type `t` at `path1` implies that `n2` has @@ -2386,7 +2411,7 @@ module Make1 Input1> { * * This predicate will be included directly in the exposed `inferTypeCertain` predicate. */ - default Type inferTypeCertainInput(AstNode n, TypePath path) { none() } + default Type inferTypeCertainSpecific(AstNode n, TypePath path) { none() } /** * Holds if `n1` having type `t` at `path1` implies that `n2` has type `t` at `path2`, @@ -2401,21 +2426,41 @@ module Make1 Input1> { predicate inferStepSymmetric(AstNode n1, TypePath path1, AstNode n2, TypePath path2); /** - * Holds if `child` is a child of `parent` and the type of `parent` at `prefix` can be - * inferred from the type of `child`. + * Holds if `n1` having type `t` at `path1` implies that `n2` has a type `lub` at + * `path2`, where `lub` is a least-upper-bound of the types of all the nodes that + * have lub steps into `n2`. * - * When `child` is unique, we also allow type information to flow from `parent` to `child`. + * For example, for a ternary conditional expression, there are lub steps from each + * of the branches into the conditional expression itself. + * + * We don't actually model the least-upper-bound computation, instead we interpret + * `inferLubStep(n1, path1, n2, path2)` as + * + * - `inferStep(n1, path1, n2, path2)`, that is type information flows directly into + * the lub, and + * - `inferStep(n2, path2, n1, path1)`, provided that `n1` is unique, that is, type + * type information flows from the lub back into the unique input `n1`, and + * - type information is allowed to flow from the lub into any of its inputs, provided + * that they have an explicitly unknown type. */ default predicate inferLubStep(AstNode n1, TypePath path1, AstNode n2, TypePath path2) { none() } + /** + * Gets the top-down inferred type of `n` at `path`. + * + * Type information is only propagated into nodes with an explicitly unknown + * type. + */ + default Type inferTypeTopDown(AstNode n, TypePath path) { none() } + /** * Gets the inferred type of `n` at `path`. * * This predicate will be included directly in the exposed `inferType` predicate. */ - Type inferTypeInput(AstNode n, TypePath path); + Type inferTypeSpecific(AstNode n, TypePath path); } module Make3 { @@ -2512,7 +2557,7 @@ module Make1 Input1> { or result = inferTypeFromStepCertain(n, path) or - result = inferTypeCertainInput(n, path) + result = inferTypeCertainSpecific(n, path) or result = inferLogicalOperationType(n, path) or @@ -2601,30 +2646,9 @@ module Make1 Input1> { ) or inferStepSymmetric(n1, path1, n2, path2) - // or - // n2 = unique(AstNode child | parentChildType(n1, child, path1) | child) and - // path2.isEmpty() } - // private predicate lubCoercion(AstNode parent, AstNode child, TypePath prefix) { - // parentChildType(parent, child, prefix) and - // strictcount(AstNode child0 | parentChildType(parent, child0, prefix) | child0) > 1 - // or - // inferStep(child, TypePath::nil(), parent, prefix) - // } private predicate step(AstNode n1, TypePath path1, AstNode n2, TypePath path2) { - // lubCoercion(n2, n1, path2) and - // path1.isEmpty() - // or - // exists(AstNode mid, TypePath pathMid, TypePath suffix | - // typeEquality(n1, pathMid, mid, path2) or - // typeEquality(mid, path2, n1, pathMid) - // | - // lubCoercion(mid, n2, suffix) and - // not lubCoercion(mid, n1, _) and - // path1 = pathMid.append(suffix) - // ) - // or inferStep(n1, path1, n2, path2) or stepSymmetric(n1, path1, n2, path2) @@ -2681,9 +2705,13 @@ module Make1 Input1> { or result = inferTypeFromReverseLub(n, path) or - result = CheckContextTyping::check(n, path) + result = inferCallReturnType(n, path) or - result = inferTypeInput(n, path) + result = TopDownTyping::inferType(n, path) + or + result = TopDownTyping::inferType(n, path) + or + result = inferTypeSpecific(n, path) ) } @@ -2720,20 +2748,34 @@ module Make1 Input1> { class Access extends CallFinal { bindingset[e] Type getInferredType(AccessEnvironment e, AccessPosition apos, TypePath path) { - result = inferCallTypeIn(this, e, apos, path) + result = inferCallTypeBottomUp(this, e, apos, path) } } } private module CallMatching = MatchingWithEnvironment; - Type inferCallTypeOut( + private Type inferCallTypeOut( Call call, CallResolutionContext ctx, TypePosition pos, AstNode n, TypePath path ) { n = call.getNodeAt(pos) and result = CallMatching::inferAccessType(call, ctx, pos, path) } + Type inferCallReturnType(Call call, CallResolutionContext ctx, AstNode n, TypePath path) { + exists(TypePosition pos | + result = inferCallTypeOut(call, ctx, pos, n, path) and + pos.isReturn() + ) + } + + Type inferCallArgumentTypeTopDown( + Call call, CallResolutionContext ctx, TypePosition pos, AstNode n, TypePath path + ) { + result = inferCallTypeOut(call, ctx, pos, n, path) and + not pos.isReturn() + } + pragma[nomagic] private predicate hasUnknownTypeAt(AstNode n, TypePath path) { inferType(n, path) instanceof UnknownType @@ -2742,18 +2784,18 @@ module Make1 Input1> { pragma[nomagic] private predicate hasUnknownType(AstNode n) { hasUnknownTypeAt(n, _) } - signature Type inferTypeTopDownSig(AstNode n, TypePath path); + private signature Type inferTypeTopDownSig(AstNode n, TypePath path); /** - * Given a predicate `inferTypeTopDown` for inferring the type of an AST node `n` + * Given a predicate `infer` for inferring the type of an AST node `n` * top-down from a context, this module exposes the predicate `inferType`, which * restricts type information to only flow top-down into `n` when `n` has an * explicit unknown type. */ - module TopDownTyping { + private module TopDownTyping { pragma[nomagic] private Type inferTypeTopDown(AstNode n, TypePath prefix, TypePath path) { - result = inferTypeTopDown(n, path) and + result = infer(n, path) and hasUnknownType(n) and prefix = path.getAPrefix() } From 7095cba0a2dfa332b9341415281765e83d8aef11 Mon Sep 17 00:00:00 2001 From: Tom Hvitved Date: Thu, 7 May 2026 19:58:20 +0200 Subject: [PATCH 12/18] wip12 --- .../internal/typeinference/TypeInference.qll | 93 ++++++++----------- .../typeinference/internal/TypeInference.qll | 52 ++--------- .../type-inference/type-inference.expected | 11 --- 3 files changed, 51 insertions(+), 105 deletions(-) diff --git a/rust/ql/lib/codeql/rust/internal/typeinference/TypeInference.qll b/rust/ql/lib/codeql/rust/internal/typeinference/TypeInference.qll index 910e988d9be4..d06f9d8aa71d 100644 --- a/rust/ql/lib/codeql/rust/internal/typeinference/TypeInference.qll +++ b/rust/ql/lib/codeql/rust/internal/typeinference/TypeInference.qll @@ -14,7 +14,6 @@ private import FunctionType private import FunctionOverloading as FunctionOverloading private import BlanketImplementation as BlanketImplementation private import codeql.rust.elements.internal.VariableImpl::Impl as VariableImpl -private import codeql.rust.internal.CachedStages private import codeql.typeinference.internal.TypeInference private import codeql.rust.frameworks.stdlib.Stdlib private import codeql.rust.frameworks.stdlib.Builtins as Builtins @@ -394,13 +393,7 @@ private module Input3 implements InputSig3 { } } - class Call extends Expr instanceof FunctionCallMatchingInput::Access { - Type getTypeArgument(TypeArgumentPosition apos, TypePath path) { - result = super.getTypeArgument(apos, path) - } - - AstNode getNodeAt(TypePosition pos) { result = super.getNodeAt(pos) } - + class Call extends FunctionCallMatchingInput::Access { /** Gets the target of this call. */ Callable getTargetCertain() { exists(ImplOrTraitItemNodeOption i, FunctionDeclaration f, Path p | @@ -421,7 +414,7 @@ private module Input3 implements InputSig3 { Type inferCallReturnType(AstNode n, TypePath path) { exists(Call call, TypePath path0 | - result = inferCallReturnType(call, _, n, path0) and + result = M3::inferCallReturnType(call, _, n, path0) and if // index expression `x[i]` desugars to `*x.index(i)`, so we must account for // the implicit deref @@ -598,11 +591,15 @@ private module Input3 implements InputSig3 { predicate inferLubStep(AstNode n1, TypePath path1, AstNode n2, TypePath path2) { path1.isEmpty() and ( - n2 = any(ArrayListExpr ale | n1 = ale.getAnExpr()) and + n1 = n2.(ArrayListExpr).getAnExpr() and path2 = TypePath::singleton(getArrayTypeParameter()) or - bodyReturns(n2, n1) and - path2.isEmpty() + exists(ReturnExpr re, Rust::Callable c | + n1 = re.getExpr() and + c = re.getEnclosingCallable() and + n2 = c.getBody() and + path2.isEmpty() + ) or exists(Struct s | n1 = [n2.(RangeExpr).getStart(), n2.(RangeExpr).getEnd()] and @@ -617,14 +614,22 @@ private module Input3 implements InputSig3 { result = inferTypeFromAnnotationTopDown(n, path) or result = inferClosureExprBodyTypeTopDown(n, path) + or + exists(FunctionPosition pos | not pos.isReturn() | + result = inferConstructionType(n, pos, path) + or + result = inferOperationType(n, pos, path) + ) } Type inferTypeSpecific(AstNode n, TypePath path) { result = inferAssignmentOperationType(n, path) or - result = inferConstructionType(n, path) - or - result = inferOperationType(n, path) + exists(FunctionPosition pos | pos.isReturn() | + result = inferConstructionType(n, pos, path) + or + result = inferOperationType(n, pos, path) + ) or result = inferFieldExprType(n, path) or @@ -650,7 +655,12 @@ private module Input3 implements InputSig3 { private module M3 = Make3; -import M3 +// import M3 +predicate inferType = M3::inferType/1; + +predicate inferType = M3::inferType/2; + +predicate inferTypeCertain = M3::inferTypeCertain/2; module Consistency { import M2::Consistency @@ -917,14 +927,6 @@ private Struct getRangeType(RangeExpr re) { result instanceof RangeToInclusiveStruct } -private predicate bodyReturns(Expr body, Expr e) { - exists(ReturnExpr re, Callable c | - e = re.getExpr() and - c = re.getEnclosingCallable() and - body = c.getBody() - ) -} - pragma[nomagic] private Type inferTypeFromAnnotationTopDown(AstNode n, TypePath path) { // Normally, these are coercion sites, but in case a type is unknown we @@ -1082,7 +1084,7 @@ private module ContextTyping { * context in which the call appears, for example a call like * `Default::default()`. */ - abstract class ContextTypedCallCand extends AstNode { + abstract class ContextTypedCallCand extends Expr { abstract Type getTypeArgument(TypeArgumentPosition apos, TypePath path); predicate hasTypeArgument(TypeArgumentPosition apos) { exists(this.getTypeArgument(apos, _)) } @@ -2653,7 +2655,9 @@ private module FunctionCallMatchingInput implements MatchingWithEnvironmentInput ) } - abstract class Access extends ContextTyping::ContextTypedCallCand { + final class Access = AccessImpl; + + abstract private class AccessImpl extends ContextTyping::ContextTypedCallCand { abstract AstNode getNodeAt(FunctionPosition pos); bindingset[derefChainBorrow] @@ -2668,7 +2672,7 @@ private module FunctionCallMatchingInput implements MatchingWithEnvironmentInput abstract predicate hasUnknownTypeAt(string derefChainBorrow, FunctionPosition pos, TypePath path); } - private class AssocFunctionCallAccess extends Access instanceof AssocFunctionResolution::AssocFunctionCall + private class AssocFunctionCallAccess extends AccessImpl instanceof AssocFunctionResolution::AssocFunctionCall { AssocFunctionCallAccess() { // handled in the `OperationMatchingInput` module @@ -2755,7 +2759,7 @@ private module FunctionCallMatchingInput implements MatchingWithEnvironmentInput } } - private class NonAssocFunctionCallAccess extends Access instanceof NonAssocCallExpr, + private class NonAssocFunctionCallAccess extends AccessImpl instanceof NonAssocCallExpr, CallExprImpl::CallExprCall { pragma[nomagic] @@ -2815,7 +2819,7 @@ private Type inferCallArgumentTypeTopDown( ) { exists(string derefChainBorrow | FunctionCallMatchingInput::decodeDerefChainBorrow(derefChainBorrow, derefChain, borrow) and - result = inferCallArgumentTypeTopDown(call, derefChainBorrow, pos, n, path) + result = M3::inferCallArgumentTypeTopDown(call, derefChainBorrow, pos, n, path) ) } @@ -3024,16 +3028,13 @@ private module ConstructionMatchingInput implements MatchingInputSig { private module ConstructionMatching = Matching; pragma[nomagic] -private Type inferConstructionTypePreCheck(AstNode n, FunctionPosition pos, TypePath path) { +private Type inferConstructionType(AstNode n, FunctionPosition pos, TypePath path) { exists(ConstructionMatchingInput::Access a | n = a.getNodeAt(pos) and result = ConstructionMatching::inferAccessType(a, pos, path) ) } -private predicate inferConstructionType = - CheckContextTyping::check/2; - pragma[nomagic] private Type inferUnknownType(AstNode n, TypePath path) { result = TUnknownType() and @@ -3119,7 +3120,7 @@ private module OperationMatchingInput implements MatchingInputSig { private module OperationMatching = Matching; pragma[nomagic] -private Type inferOperationTypePreCheck(AstNode n, FunctionPosition pos, TypePath path) { +private Type inferOperationType(AstNode n, FunctionPosition pos, TypePath path) { exists(OperationMatchingInput::Access a | n = a.getNodeAt(pos) and result = OperationMatching::inferAccessType(a, pos, path) and @@ -3127,8 +3128,6 @@ private Type inferOperationTypePreCheck(AstNode n, FunctionPosition pos, TypePat ) } -private predicate inferOperationType = CheckContextTyping::check/2; - pragma[nomagic] private Type getFieldExprLookupType(FieldExpr fe, string name, DerefChain derefChain) { exists(TypePath path | @@ -3153,6 +3152,7 @@ private Type getFieldExprLookupType(FieldExpr fe, string name, DerefChain derefC */ cached StructField resolveStructFieldExpr(FieldExpr fe, DerefChain derefChain) { + M3::CachedStage::ref() and exists(string name, DataType ty | ty = getFieldExprLookupType(fe, pragma[only_bind_into](name), derefChain) | @@ -3174,6 +3174,7 @@ private Type getTupleFieldExprLookupType(FieldExpr fe, int pos, DerefChain deref */ cached TupleField resolveTupleFieldExpr(FieldExpr fe, DerefChain derefChain) { + M3::CachedStage::ref() and exists(int i | result = getTupleFieldExprLookupType(fe, pragma[only_bind_into](i), derefChain) @@ -3664,7 +3665,7 @@ private Type inferCastExprType(CastExpr ce, TypePath path) { /** Holds if `n` is implicitly dereferenced and/or borrowed. */ cached predicate implicitDerefChainBorrow(Expr e, DerefChain derefChain, boolean borrow) { - CachedStage::ref() and + M3::CachedStage::ref() and exists(BorrowKind bk | any(AssocFunctionResolution::AssocFunctionCall afc) .argumentHasImplicitDerefChainBorrow(e, derefChain, bk) and @@ -3691,6 +3692,7 @@ predicate implicitDerefChainBorrow(Expr e, DerefChain derefChain, boolean borrow */ cached Addressable resolveCallTarget(InvocationExpr call, boolean dispatch) { + M3::CachedStage::ref() and dispatch = false and result = call.(NonAssocCallExpr).resolveCallTargetViaPathResolution() or @@ -3711,7 +3713,7 @@ private module Debug { exists(string filepath, int startline, int startcolumn, int endline, int endcolumn | result.getLocation().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) and filepath.matches("%/main.rs") and - startline = 1102 + startline = 103 ) } @@ -3737,24 +3739,11 @@ private module Debug { t = self.getTypeAt(path) } - // predicate debugInferFunctionCallType(AstNode n, TypePath path, Type t) { - // n = getRelevantLocatable() and - // t = inferFunctionCallType(n, path) - // } - predicate debugInferConstructionType(AstNode n, TypePath path, Type t) { - n = getRelevantLocatable() and - t = inferConstructionType(n, path) - } - predicate debugTypeMention(TypeMention tm, TypePath path, Type type) { tm = getRelevantLocatable() and tm.getTypeAt(path) = type } - // Type debugInferAnnotatedType(AstNode n, TypePath path) { - // n = getRelevantLocatable() and - // result = inferAnnotatedType(n, path) - // } pragma[nomagic] private int countTypesAtPath(AstNode n, TypePath path, Type t) { t = inferType(n, path) and @@ -3803,7 +3792,7 @@ private module Debug { c = max(countTypePaths(_, _, _)) } - Type debuginferTypeCertain(AstNode n, TypePath path) { + Type debugInferTypeCertain(AstNode n, TypePath path) { n = getRelevantLocatable() and result = inferTypeCertain(n, path) } diff --git a/shared/typeinference/codeql/typeinference/internal/TypeInference.qll b/shared/typeinference/codeql/typeinference/internal/TypeInference.qll index c36f63c75fc5..0f646ab3552c 100644 --- a/shared/typeinference/codeql/typeinference/internal/TypeInference.qll +++ b/shared/typeinference/codeql/typeinference/internal/TypeInference.qll @@ -2521,7 +2521,7 @@ module Make1 Input1> { } pragma[nomagic] - private Type getCertainCallExprType(Call call, TypePath path) { + private Type getCertainCallExprReturnType(Call call, TypePath path) { exists(TypePosition ret | ret.isReturn() and forex(Callable target | target = call.getTargetCertain() | @@ -2531,8 +2531,8 @@ module Make1 Input1> { } pragma[nomagic] - private Type inferCertainCallExprType(Call call, TypePath path) { - exists(Type ty, TypePath prefix | ty = getCertainCallExprType(call, prefix) | + private Type inferCertainCallExprReturnType(Call call, TypePath path) { + exists(Type ty, TypePath prefix | ty = getCertainCallExprReturnType(call, prefix) | exists( Callable target, TypePath suffix, TypeParameterPosition tppos, TypeArgumentPosition tapos @@ -2561,7 +2561,7 @@ module Make1 Input1> { or result = inferLogicalOperationType(n, path) or - result = inferCertainCallExprType(n, path) + result = inferCertainCallExprReturnType(n, path) or infersCertainTypeAt(n, path, result.getATypeParameter()) } @@ -2681,9 +2681,6 @@ module Make1 Input1> { ) } - private predicate inferTypeFromReverseLub = - TopDownTyping::inferType/2; - /** * Gets the inferred type of `n` at `path`. */ @@ -2703,7 +2700,7 @@ module Make1 Input1> { ( result = inferTypeFromStep(n, path) or - result = inferTypeFromReverseLub(n, path) + result = TopDownTyping::inferType(n, path) or result = inferCallReturnType(n, path) or @@ -2755,7 +2752,7 @@ module Make1 Input1> { private module CallMatching = MatchingWithEnvironment; - private Type inferCallTypeOut( + private Type inferCallType( Call call, CallResolutionContext ctx, TypePosition pos, AstNode n, TypePath path ) { n = call.getNodeAt(pos) and @@ -2764,7 +2761,7 @@ module Make1 Input1> { Type inferCallReturnType(Call call, CallResolutionContext ctx, AstNode n, TypePath path) { exists(TypePosition pos | - result = inferCallTypeOut(call, ctx, pos, n, path) and + result = inferCallType(call, ctx, pos, n, path) and pos.isReturn() ) } @@ -2772,8 +2769,9 @@ module Make1 Input1> { Type inferCallArgumentTypeTopDown( Call call, CallResolutionContext ctx, TypePosition pos, AstNode n, TypePath path ) { - result = inferCallTypeOut(call, ctx, pos, n, path) and - not pos.isReturn() + result = inferCallType(call, ctx, pos, n, path) and + not pos.isReturn() and + hasUnknownType(n) } pragma[nomagic] @@ -2809,36 +2807,6 @@ module Make1 Input1> { } } - signature Type inferCallTypeSig(AstNode n, TypePosition pos, TypePath path); - - /** - * Given a predicate `inferCallType` for inferring the type of a call at a given - * position, this module exposes the predicate `check`, which wraps the input - * predicate and checks that types are only propagated into arguments when they - * are context-typed. - */ - module CheckContextTyping { - pragma[nomagic] - private Type inferCallNonReturnType(AstNode n, TypePath prefix, TypePath path) { - exists(TypePosition pos | - result = inferCallType(n, pos, path) and - hasUnknownType(n) and - not pos.isReturn() and - prefix = path.getAPrefix() - ) - } - - pragma[nomagic] - Type check(AstNode n, TypePath path) { - result = inferCallType(n, any(TypePosition pos | pos.isReturn()), path) - or - exists(TypePath prefix | - result = inferCallNonReturnType(n, prefix, path) and - hasUnknownTypeAt(n, prefix) - ) - } - } - /** * Gets the inferred root type of `n`, if any. */ diff --git a/swift/ql/test/library-tests/type-inference/type-inference.expected b/swift/ql/test/library-tests/type-inference/type-inference.expected index 79a3599aa1f3..e69de29bb2d1 100644 --- a/swift/ql/test/library-tests/type-inference/type-inference.expected +++ b/swift/ql/test/library-tests/type-inference/type-inference.expected @@ -1,11 +0,0 @@ -| context.swift:17:10:17:10 | C.init() | Unexpected result: target=init() | -| context.swift:17:10:17:12 | call to C.init() | Unexpected result: target=init() | -| context.swift:17:14:18:1 | // $ type=C\n | Missing result: type=C | -| context.swift:25:11:25:11 | A.init() | Unexpected result: target=init() | -| context.swift:25:11:25:13 | call to A.init() | Unexpected result: target=init() | -| context.swift:26:19:26:19 | D.init() | Unexpected result: target=init() | -| context.swift:26:19:26:21 | call to D.init() | Unexpected result: target=init() | -| context.swift:26:25:26:25 | B.init() | Unexpected result: target=init() | -| context.swift:26:25:26:27 | call to B.init() | Unexpected result: target=init() | -| file://:0:0:0:0 | A.init() | Unexpected result: target=init() | -| file://:0:0:0:0 | call to A.init() | Unexpected result: target=init() | From 3a1b7613507ea4c0d76d95553c04e5ea28dfe437 Mon Sep 17 00:00:00 2001 From: Tom Hvitved Date: Fri, 8 May 2026 10:22:37 +0200 Subject: [PATCH 13/18] wip13 --- rust/ql/lib/codeql/rust/internal/CachedStages.qll | 4 ++-- .../lib/codeql/rust/internal/typeinference/TypeInference.qll | 3 +++ 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/rust/ql/lib/codeql/rust/internal/CachedStages.qll b/rust/ql/lib/codeql/rust/internal/CachedStages.qll index a92770ed2384..f76006ddcbc7 100644 --- a/rust/ql/lib/codeql/rust/internal/CachedStages.qll +++ b/rust/ql/lib/codeql/rust/internal/CachedStages.qll @@ -147,9 +147,9 @@ module Stages { predicate backref() { 1 = 1 or - exists(Type t) + (exists(Type t) implies any()) or - exists(inferType(_)) + (exists(inferType(_)) implies any()) } } diff --git a/rust/ql/lib/codeql/rust/internal/typeinference/TypeInference.qll b/rust/ql/lib/codeql/rust/internal/typeinference/TypeInference.qll index d06f9d8aa71d..1285256c1fb5 100644 --- a/rust/ql/lib/codeql/rust/internal/typeinference/TypeInference.qll +++ b/rust/ql/lib/codeql/rust/internal/typeinference/TypeInference.qll @@ -10,6 +10,7 @@ private import TypeAbstraction as TA private import Type as T private import TypeMention private import codeql.rust.internal.typeinference.DerefChain +private import codeql.rust.internal.CachedStages private import FunctionType private import FunctionOverloading as FunctionOverloading private import BlanketImplementation as BlanketImplementation @@ -276,6 +277,8 @@ private module Input3 implements InputSig3 { private import rust as Rust predicate cachedStageRevRef() { + Stages::TypeInferenceStage::ref() + or (implicitDerefChainBorrow(_, _, _) implies any()) or (exists(resolveCallTarget(_, _)) implies any()) From cb99d5cb609479fda7d7fce904198d01340f60a8 Mon Sep 17 00:00:00 2001 From: Tom Hvitved Date: Fri, 8 May 2026 14:58:57 +0200 Subject: [PATCH 14/18] Swift: QL proto-type implementation of type inference --- .../typeinference/internal/TypeInference.qll | 4 + .../swift/elements/internal/LocationImpl.qll | 69 + .../lib/codeql/swift/typeinference/Type.qll | 250 ++ .../swift/typeinference/TypeAbstraction.qll | 25 + .../swift/typeinference/TypeInference.qll | 3343 +++++++++++++++++ .../TypeInferenceConsistency.qll | 53 + .../swift/typeinference/TypeMention.qll | 138 + swift/ql/lib/qlpack.yml | 1 + .../library-tests/type-inference/Common.qll | 51 + .../type-inference/generics.swift | 36 + .../library-tests/type-inference/lub.swift | 11 + .../type-inference/type-inference-ql.expected | 1813 +++++++++ .../type-inference/type-inference-ql.ql | 29 + .../type-inference/type-inference.ql | 46 +- 14 files changed, 5824 insertions(+), 45 deletions(-) create mode 100644 swift/ql/lib/codeql/swift/typeinference/Type.qll create mode 100644 swift/ql/lib/codeql/swift/typeinference/TypeAbstraction.qll create mode 100644 swift/ql/lib/codeql/swift/typeinference/TypeInference.qll create mode 100644 swift/ql/lib/codeql/swift/typeinference/TypeInferenceConsistency.qll create mode 100644 swift/ql/lib/codeql/swift/typeinference/TypeMention.qll create mode 100644 swift/ql/test/library-tests/type-inference/Common.qll create mode 100644 swift/ql/test/library-tests/type-inference/lub.swift create mode 100644 swift/ql/test/library-tests/type-inference/type-inference-ql.expected create mode 100644 swift/ql/test/library-tests/type-inference/type-inference-ql.ql diff --git a/shared/typeinference/codeql/typeinference/internal/TypeInference.qll b/shared/typeinference/codeql/typeinference/internal/TypeInference.qll index 0f646ab3552c..1027854f9365 100644 --- a/shared/typeinference/codeql/typeinference/internal/TypeInference.qll +++ b/shared/typeinference/codeql/typeinference/internal/TypeInference.qll @@ -2282,11 +2282,13 @@ module Make1 Input1> { * A position where a callable can have a declared type and a call can have * an inferred type. */ + bindingset[this] class TypePosition { /** Holds if this position represents the return type of a callable. */ predicate isReturn(); /** Gets a textual representation of this position. */ + bindingset[this] string toString(); } @@ -2717,6 +2719,8 @@ module Make1 Input1> { class AccessPosition = DeclarationPosition; + bindingset[apos] + bindingset[dpos] predicate accessDeclarationPositionMatch(AccessPosition apos, DeclarationPosition dpos) { apos = dpos } diff --git a/swift/ql/lib/codeql/swift/elements/internal/LocationImpl.qll b/swift/ql/lib/codeql/swift/elements/internal/LocationImpl.qll index f607065caea4..452b5f7fa996 100644 --- a/swift/ql/lib/codeql/swift/elements/internal/LocationImpl.qll +++ b/swift/ql/lib/codeql/swift/elements/internal/LocationImpl.qll @@ -5,6 +5,75 @@ module Impl { * A location of a program element. */ class Location extends Generated::Location { + /** Holds if this location starts before location `that`. */ + pragma[inline] + predicate startsBefore(Location that) { + exists(string f, int sl1, int sc1, int sl2, int sc2 | + this.hasLocationInfo(f, sl1, sc1, _, _) and + that.hasLocationInfo(f, sl2, sc2, _, _) + | + sl1 < sl2 + or + sl1 = sl2 and sc1 <= sc2 + ) + } + + /** Holds if this location starts strictly before location `that`. */ + pragma[inline] + predicate startsStrictlyBefore(Location that) { + exists(string f, int sl1, int sc1, int sl2, int sc2 | + this.hasLocationInfo(f, sl1, sc1, _, _) and + that.hasLocationInfo(f, sl2, sc2, _, _) + | + sl1 < sl2 + or + sl1 = sl2 and sc1 < sc2 + ) + } + + /** Holds if this location ends after location `that`. */ + pragma[inline] + predicate endsAfter(Location that) { + exists(string f, int el1, int ec1, int el2, int ec2 | + this.hasLocationInfo(f, _, _, el1, ec1) and + that.hasLocationInfo(f, _, _, el2, ec2) + | + el1 > el2 + or + el1 = el2 and ec1 >= ec2 + ) + } + + /** Holds if this location ends strictly after location `that`. */ + pragma[inline] + predicate endsStrictlyAfter(Location that) { + exists(string f, int el1, int ec1, int el2, int ec2 | + this.hasLocationInfo(f, _, _, el1, ec1) and + that.hasLocationInfo(f, _, _, el2, ec2) + | + el1 > el2 + or + el1 = el2 and ec1 > ec2 + ) + } + + /** + * Holds if this location contains location `that`, meaning that it starts + * before and ends after it. + */ + bindingset[this, that] + pragma[inline_late] + predicate contains(Location that) { this.startsBefore(that) and this.endsAfter(that) } + + /** + * Holds if this location strictlycontains location `that`, meaning that it starts + * strictly before and ends strictly after it. + */ + pragma[inline] + predicate strictlyContains(Location that) { + this.startsStrictlyBefore(that) and this.endsStrictlyAfter(that) + } + /** * Holds if this location is described by `path`, `startLine`, `startColumn`, `endLine` and `endColumn`. */ diff --git a/swift/ql/lib/codeql/swift/typeinference/Type.qll b/swift/ql/lib/codeql/swift/typeinference/Type.qll new file mode 100644 index 000000000000..b08fd0c5bd04 --- /dev/null +++ b/swift/ql/lib/codeql/swift/typeinference/Type.qll @@ -0,0 +1,250 @@ +/** Provides classes representing types without type arguments. */ + +import swift +private import swift as Swift +private import codeql.swift.generated.Raw +private import codeql.swift.generated.Synth + +cached +newtype TType = + TTypeDeclType(TypeDecl decl) { + not decl instanceof AssociatedTypeDecl and + // handled in `TSelfTypeParameter` + not decl = any(GenericTypeParamDecl g | decl.getName() = "Self") + } or + TTupleType(int arity) { arity = [0 .. any(Swift::TupleType tt).getNumberOfTypes()] } or + TAssociatedTypeTypeParameter(ProtocolDecl protocol, AssociatedTypeDecl associatedType) { + associatedType.getDeclaringDecl().(ProtocolDecl).getABaseTypeDecl*() = protocol + } or + TSelfTypeParameter(ProtocolDecl protocol) or + TTupleTypeTypeParameter(int arity, int index) { + arity = [0 .. any(Swift::TupleType tt).getNumberOfTypes()] and + index = [0 .. arity - 1] + } or + TUnknownType() + +/** + * A type without type arguments. + * + * Note that this type includes things that, strictly speaking, are not Rust + * types, such as traits and implementation blocks. + */ +abstract class Type extends TType { + /** + * Gets the `i`th positional type parameter of this type, if any. + * + * This excludes synthetic type parameters, such as associated types in traits. + */ + abstract TypeParameter getPositionalTypeParameter(int i); + + /** Gets the default type for the `i`th type parameter, if any. */ + TypeRepr getTypeParameterDefault(int i) { none() } + + /** + * Gets a type parameter of this type. + * + * This includes both positional type parameters and synthetic type parameters, + * such as associated types in traits. + */ + TypeParameter getATypeParameter() { result = this.getPositionalTypeParameter(_) } + + /** Gets a textual representation of this type. */ + abstract string toString(); + + /** Gets the location of this type. */ + abstract Location getLocation(); +} + +class TypeDeclType extends Type, TTypeDeclType { + TypeDecl decl; + + TypeDeclType() { this = TTypeDeclType(decl) } + + /** Gets the type item that this data type represents. */ + TypeDecl getDecl() { result = decl } + + override TypeParameter getPositionalTypeParameter(int i) { + result = TTypeDeclType(decl.(GenericTypeDecl).getGenericTypeParam(i)) + } + + override TypeParameter getATypeParameter() { + result = super.getATypeParameter() + or + result = TAssociatedTypeTypeParameter(decl, _) + } + + override TypeRepr getTypeParameterDefault(int i) { + // todo: it appears Swift does support this + none() + } + + override string toString() { result = decl.getName() } + + override Location getLocation() { result = decl.getLocation() } +} + +class StringType extends TypeDeclType { + StringType() { decl.getName() = "String" } +} + +class IntType extends TypeDeclType { + IntType() { decl.getName() = "Int" } +} + +class TupleType extends Type, TTupleType { + int arity; + + TupleType() { this = TTupleType(arity) } + + override TypeParameter getPositionalTypeParameter(int i) { + result = TTupleTypeTypeParameter(arity, i) + } + + override string toString() { result = "(T_" + arity + ")" } + + override Location getLocation() { result.hasLocationInfo("", 0, 0, 0, 0) } +} + +class VoidType extends TupleType { + VoidType() { arity = 0 } +} + +/** A type parameter. */ +abstract class TypeParameter extends Type { + override TypeParameter getPositionalTypeParameter(int i) { none() } +} + +class GenericTypeParamDeclTypeParameter extends TypeParameter, TypeDeclType { + override GenericTypeParamDecl decl; + + override TypeParameter getPositionalTypeParameter(int i) { + result = TypeParameter.super.getPositionalTypeParameter(i) + } +} + +/** + * A type parameter corresponding to an associated type in a protocol. + * + * We treat associated type declarations in protocols as type parameters. E.g., a + * protocol such as + * + * ```swift + * protocol Protocol { + * associatedtype AssociatedType + * // ... + * } + * ``` + * + * is treated as if it was + * + * ```swift + * protocol Protocol { + * // ... + * } + * ``` + * + * Furthermore, associated types of a super protocol induce a corresponding type + * parameter in any subprotocols. E.g., if we have a protocol `SubProtocol: AProtocol` then + * `SubProtocol` also has a type parameter for the associated type + * `AssociatedType`. + */ +class AssociatedTypeTypeParameter extends TypeParameter, TAssociatedTypeTypeParameter { + ProtocolDecl protocol; + AssociatedTypeDecl associatedType; + + AssociatedTypeTypeParameter() { this = TAssociatedTypeTypeParameter(protocol, associatedType) } + + AssociatedTypeDecl getAssociatedType() { result = associatedType } + + /** Gets the protocol that contains this associated type declaration. */ + ProtocolDecl getProtocol() { result = protocol } + + /** + * Holds if this associated type type parameter corresponds directly its + * protocol, that is, it is not inherited from a superprotocol. + */ + predicate isDirect() { protocol = associatedType.getDeclaringDecl() } + + override string toString() { + exists(string fromString, ProtocolDecl protocol2 | + result = associatedType.getName() + "[" + protocol.getName() + fromString + "]" and + protocol2 = associatedType.getDeclaringDecl() and + if protocol = protocol2 + then fromString = "" + else fromString = " (inherited from " + protocol2.getName() + ")" + ) + } + + override Location getLocation() { result = associatedType.getLocation() } + + override TypeParameter getPositionalTypeParameter(int i) { + // todo: it appears that associated types can also have type parameters + result = TypeParameter.super.getPositionalTypeParameter(i) + } +} + +/** + * The implicit `Self` type parameter of a protocol, that refers to the + * implementing type of the protocol. + */ +class SelfTypeParameter extends TypeParameter, TSelfTypeParameter { + private ProtocolDecl protocol; + + SelfTypeParameter() { this = TSelfTypeParameter(protocol) } + + ProtocolDecl getProtocol() { result = protocol } + + override string toString() { result = "Self [" + protocol.getName() + "]" } + + override Location getLocation() { result = protocol.getLocation() } +} + +class TupleTypeTypeParameter extends TypeParameter, TTupleTypeTypeParameter { + int arity; + int index; + + TupleTypeTypeParameter() { this = TTupleTypeTypeParameter(arity, index) } + + override string toString() { result = "element " + index + " of tuple of arity " + arity } + + override Location getLocation() { result.hasLocationInfo("", 0, 0, 0, 0) } +} + +/** + * A special pseudo type used to indicate that the actual type may have to be + * inferred by propagating type information back into call arguments. + * + * For example, in + * + * ```rust + * let x = Default::default(); + * foo(x); + * ``` + * + * `Default::default()` is assigned this type, which allows us to infer the actual + * type from the type of `foo`'s first parameter. + * + * Unknown types are not restricted to root types, for example in a call like + * `Vec::new()` we assign this type at the type path corresponding to the type + * parameter of `Vec`. + * + * Unknown types are used to restrict when type information is allowed to flow + * into call arguments (including method call receivers), in order to avoid + * combinatorial explosions. + */ +class UnknownType extends Type, TUnknownType { + override TypeParameter getPositionalTypeParameter(int i) { none() } + + override string toString() { result = "(context typed)" } + + override Location getLocation() { result.hasLocationInfo("", 0, 0, 0, 0) } +} + +private class RawTypeParameter = + @generic_type_param_decl or @associated_type_decl or @protocol_decl; + +private predicate id(RawTypeParameter x, RawTypeParameter y) { x = y } + +private predicate idOfRaw(RawTypeParameter x, int y) = equivalenceRelation(id/2)(x, y) + +int idOfTypeParameterAstNode(AstNode node) { idOfRaw(Synth::convertAstNodeToRaw(node), result) } diff --git a/swift/ql/lib/codeql/swift/typeinference/TypeAbstraction.qll b/swift/ql/lib/codeql/swift/typeinference/TypeAbstraction.qll new file mode 100644 index 000000000000..8d2216bc9328 --- /dev/null +++ b/swift/ql/lib/codeql/swift/typeinference/TypeAbstraction.qll @@ -0,0 +1,25 @@ +private import Type + +/** + * A type abstraction. I.e., a place in the program where type variables may + * be introduced. + * + * Example: + * ```rust + * impl Foo { } + * // ^^^^^^ a type abstraction + * ``` + */ +abstract class TypeAbstraction extends AstNode { + abstract TypeParameter getATypeParameter(); +} + +final class GenericContextTypeAbstraction extends TypeAbstraction, GenericContext { + override TypeParameter getATypeParameter() { + result.(GenericTypeParamDeclTypeParameter).getDecl() = this.getAGenericTypeParam() + or + result.(SelfTypeParameter).getProtocol() = this + or + result.(AssociatedTypeTypeParameter).getProtocol() = this + } +} diff --git a/swift/ql/lib/codeql/swift/typeinference/TypeInference.qll b/swift/ql/lib/codeql/swift/typeinference/TypeInference.qll new file mode 100644 index 000000000000..5c61476bf37b --- /dev/null +++ b/swift/ql/lib/codeql/swift/typeinference/TypeInference.qll @@ -0,0 +1,3343 @@ +/** Provides functionality for inferring types. */ + +private import codeql.util.Boolean +private import codeql.util.Option +private import codeql.util.Void +private import Type +private import TypeAbstraction +private import TypeAbstraction as TA +private import Type +private import Type as T +private import TypeMention +private import codeql.typeinference.internal.TypeInference + +class Type = T::Type; + +private module Input1 implements InputSig1 { + private import codeql.swift.generated.Raw + private import codeql.swift.generated.Synth + + class Type = T::Type; + + class UnknownType = T::UnknownType; + + class TypeParameter = T::TypeParameter; + + class TypeAbstraction = TA::TypeAbstraction; + + class TypeArgumentPosition = Void; // Swift does not support explicit type arguments + + class TypeParameterPosition = int; + + bindingset[apos] + bindingset[ppos] + predicate typeArgumentParameterPositionMatch(TypeArgumentPosition apos, TypeParameterPosition ppos) { + none() + } + + int getTypeParameterId(TypeParameter tp) { + tp = + rank[result](TypeParameter tp0, int kind, int id1, int id2 | + kind = 1 and + id1 = idOfTypeParameterAstNode(tp0.(GenericTypeParamDeclTypeParameter).getDecl()) and + id2 = 0 + or + kind = 2 and + id1 = idOfTypeParameterAstNode(tp0.(AssociatedTypeTypeParameter).getProtocol()) and + id2 = idOfTypeParameterAstNode(tp0.(AssociatedTypeTypeParameter).getAssociatedType()) + or + kind = 3 and + id1 = idOfTypeParameterAstNode(tp0.(SelfTypeParameter).getProtocol()) and + id2 = 0 + or + kind = 4 and + tp0 = TTupleTypeTypeParameter(id1, id2) + | + tp0 order by kind, id1, id2 + ) + } +} + +private import Input1 + +private module M1 = Make1; + +import M1 + +predicate getTypePathLimit = Input1::getTypePathLimit/0; + +predicate getTypeParameterId = Input1::getTypeParameterId/1; + +class TypePath = M1::TypePath; + +module TypePath = M1::TypePath; + +private module Input2 implements InputSig2 { + TypeMention getABaseTypeMention(Type t) { none() } // todo: check that `conditionSatisfiesConstraint` can be used + + TypeMention getATypeParameterConstraint(TypeParameter tp) { + result.(GenericContextMention).getContext() = tp.(SelfTypeParameter).getProtocol() + or + // todo: type parameter constraints + none() + } + + /** + * Use the constraint mechanism in the shared type inference library to + * support traits. In Rust `constraint` is always a trait. + * + * See the documentation of `conditionSatisfiesConstraint` in the shared type + * inference module for more information. + */ + predicate conditionSatisfiesConstraint( + TypeAbstraction abs, TypeMention condition, TypeMention constraint, boolean transitive + ) { + condition.(GenericContextMention).getContext() = + any(GenericContext ctx | + abs = ctx and + constraint.(TypeDeclBaseTypeMention).getDecl() = ctx + ) and + transitive = true + // // `impl` blocks implementing traits + // transitive = false and + // exists(Impl impl | + // abs = impl and + // condition = impl.getSelfTy() and + // constraint = impl.getTrait() + // ) + // or + // transitive = true and + // ( + // // super protocols + // exists(ProtocolDecl protocol | + // abs = protocol and + // condition = protocol and + // constraint =protocol.getABaseTypeDecl() trait.getATypeBound().getTypeRepr() + // ) + // or + // // trait bounds on type parameters + // exists(TypeParam param | + // abs = param.getATypeBound() and + // condition = param and + // constraint = abs.(TypeBound).getTypeRepr() + // ) + // or + // // the implicit `Self` type parameter satisfies the trait + // exists(SelfTypeParameterMention self | + // abs = self and + // condition = self and + // constraint = self.getTrait() + // ) + // or + // exists(ImplTraitTypeRepr impl | + // abs = impl and + // condition = impl and + // constraint = impl.getTypeBoundList().getABound().getTypeRepr() + // ) + // or + // // a `dyn Trait` type implements `Trait`. See the comment on + // // `DynTypeBoundListMention` for further details. + // exists(DynTraitTypeRepr object | + // abs = object and + // condition = object.getTypeBoundList() and + // constraint = object.getTrait() + // ) + // ) + } + + predicate typeParameterIsFunctionallyDetermined(TypeParameter tp) { + tp instanceof AssociatedTypeTypeParameter + } + + predicate typeAbstractionHasAmbiguousConstraintAt( + TypeAbstraction abs, Type constraint, TypePath path + ) { + none() // todo + } +} + +private import Input2 + +private module M2 = Make2; + +import M2 + +private module Input3 implements InputSig3 { + private import swift as Swift + + predicate cachedStageRevRef() { none() } + + predicate inferType = M3::inferType/2; + + class BoolType extends TypeDeclType { + BoolType() { this.getDecl() = any(Swift::BoolType b).(AnyGenericType).getDeclaration() } // check + } + + class AstNode = Swift::AstNode; + + TypeMention getTypeAnnotation(AstNode n) { + result.(ParamDeclTypeMention).getDecl() = n + // exists(LetStmt let | + // n = let.getPat() and + // result = let.getTypeRepr() + // ) + // or + // result = n.(SelfParam).getTypeRepr() + // or + // exists(Param p | + // n = p.getPat() and + // result = p.getTypeRepr() + // ) + // or + // result = n.(ShorthandSelfParameterMention) + } + + class Expr = Swift::Expr; + + class Switch extends AstNode { + Switch() { none() } + + Expr getExpr() { none() } + + Case getCase(int index) { none() } + } + + class Case extends AstNode { + Case() { none() } + + AstNode getAPattern() { none() } + + AstNode getBody() { none() } + } + + class ConditionalExpr extends IfExpr { + Expr getThen() { result = super.getThenExpr() } + + Expr getElse() { result = super.getElseExpr() } + } + + abstract class BinaryExpr extends Expr { + abstract Expr getLeftOperand(); + + abstract Expr getRightOperand(); + } + + class LogicalAndExpr extends BinaryExpr instanceof Swift::LogicalAndExpr { + override Expr getLeftOperand() { result = Swift::LogicalAndExpr.super.getLeftOperand() } + + override Expr getRightOperand() { result = Swift::LogicalAndExpr.super.getRightOperand() } + } + + class LogicalOrExpr extends BinaryExpr instanceof Swift::LogicalOrExpr { + override Expr getLeftOperand() { result = Swift::LogicalOrExpr.super.getLeftOperand() } + + override Expr getRightOperand() { result = Swift::LogicalOrExpr.super.getRightOperand() } + } + + abstract class Assignment extends BinaryExpr { } + + class AssignExpr extends Assignment instanceof Swift::AssignExpr { + override Expr getLeftOperand() { result = Swift::AssignExpr.super.getDest() } + + override Expr getRightOperand() { result = Swift::AssignExpr.super.getSource() } + } + + class ParenExpr extends Swift::ParenExpr { + Expr getExpr() { result = super.getSubExpr() } + } + + class Variable extends Swift::VarDecl { + AstNode getDefiningNode() { result = this } + + Expr getAnAccess() { result = super.getAnAccess() } + } + + abstract class LetDeclaration extends AstNode { + abstract predicate isCoercionSite(); + + abstract AstNode getLeftOperand(); + + abstract AstNode getRightOperand(); + } + + private class PatternBindingDeclLetDeclaration extends LetDeclaration, PatternBindingDecl { + override predicate isCoercionSite() { none() } // todo + + override AstNode getLeftOperand() { result = this.getPattern(0) } + + override AstNode getRightOperand() { result = this.getInit(0) } + } + + class CallResolutionContext = Unit; + + class TypePosition extends int { + bindingset[this] + TypePosition() { exists(this) } + + predicate isSelf() { this = -1 } + + predicate isReturn() { this = -2 } + } + + class Callable extends Function { + TypeParameter getTypeParameter(int i) { + result.(GenericTypeParamDeclTypeParameter).getDecl() = this.getGenericTypeParam(i) + } + + TypeMention getAdditionalTypeParameterConstraint(TypeParameter tp) { none() } + + Type getDeclaredType(TypePosition pos, TypePath path) { + exists(ParamDeclTypeMention param | result = param.getTypeAt(path) | + pos.isSelf() and + param.getDecl() = this.getSelfParam() + or + param.getDecl() = this.getParam(pos) + ) + or + exists(CallableReturnTypeMention ret | + this = ret.getCallable() and + pos.isReturn() and + result = ret.getTypeAt(path) + ) + } + } + + class Call extends CallExpr { + Type getTypeArgument(TypeArgumentPosition apos, TypePath path) { none() } + + AstNode getNodeAt(TypePosition pos) { + result = this.getQualifier() and + pos.isSelf() + or + result = this.getArgument(pos).getExpr() + or + pos.isReturn() and + result = this + } + + /** Gets the target of this call. */ + Callable getTargetCertain() { none() } + + Callable getTarget(CallResolutionContext ctx) { + // todo: implement in QL + result = this.getStaticTarget() and + exists(ctx) + } + } + + Type inferCallReturnType(AstNode n, TypePath path) { + result = M3::inferCallReturnType(_, _, n, path) + } + + Type inferCallArgumentTypeTopDown(AstNode n, TypePath path) { + result = M3::inferCallArgumentTypeTopDown(_, _, _, n, path) + } + + predicate inferStepSymmetricCertain(AstNode n1, TypePath prefix1, AstNode n2, TypePath prefix2) { + none() + // n1 = + // any(IdentPat ip | + // n2 = ip.getName() and + // prefix1.isEmpty() and + // if ip.isRef() + // then + // exists(boolean isMutable | if ip.isMut() then isMutable = true else isMutable = false | + // prefix2 = TypePath::singleton(getRefTypeParameter(isMutable)) + // ) + // 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() + // ) + } + + Type inferTypeCertainSpecific(AstNode n, TypePath path) { + none() + // result = inferFunctionBodyType(n, path) + // or + // result = inferLiteralType(n, path, true) + // or + // result = inferRefPatType(n) and + // path.isEmpty() + // or + // result = inferRefExprType(n) and + // path.isEmpty() + // or + // result = inferCertainStructExprType(n, path) + // or + // result = inferCertainStructPatType(n, path) + // or + // result = inferRangeExprType(n) and + // path.isEmpty() + // or + // result = inferTupleRootType(n) and + // path.isEmpty() + // or + // result = inferBlockExprType(n, path) + // or + // result = inferArrayExprType(n) and + // path.isEmpty() + // or + // result = inferCastExprType(n, path) + // or + // exprHasUnitType(n) and + // path.isEmpty() and + // result instanceof UnitType + // or + // isPanicMacroCall(n) and + // path.isEmpty() and + // result instanceof NeverType + // or + // n instanceof ClosureExpr and + // path.isEmpty() and + // result = closureRootType() + } + + predicate inferStepSymmetric(AstNode n1, TypePath prefix1, AstNode n2, TypePath prefix2) { + none() + // prefix1.isEmpty() and + // prefix2.isEmpty() and + // ( + // n1 = n2.(OrPat).getAPat() + // or + // n1 = n2.(ParenPat).getPat() + // or + // n1 = n2.(LiteralPat).getLiteral() + // or + // exists(BreakExpr break | + // break.getExpr() = n1 and + // break.getTarget() = n2.(LoopExpr) + // ) + // or + // n1 = n2.(MacroExpr).getMacroCall().getMacroCallExpansion() and + // not isPanicMacroCall(n2) + // or + // n1 = n2.(MacroPat).getMacroCall().getMacroCallExpansion() + // ) + // or + // n2 = + // any(RefExpr re | + // n1 = re.getExpr() and + // prefix1.isEmpty() and + // prefix2 = TypePath::singleton(inferRefExprType(re).getPositionalTypeParameter(0)) + // ) + // or + // n2 = + // any(RefPat rp | + // n1 = rp.getPat() and + // prefix1.isEmpty() and + // exists(boolean isMutable | if rp.isMut() then isMutable = true else isMutable = false | + // prefix2 = TypePath::singleton(getRefTypeParameter(isMutable)) + // ) + // ) + // or + // exists(int i, int arity | + // prefix1.isEmpty() and + // prefix2 = TypePath::singleton(getTupleTypeParameter(arity, i)) + // | + // arity = n2.(TupleExpr).getNumberOfFields() and + // n1 = n2.(TupleExpr).getField(i) + // or + // arity = n2.(TuplePat).getTupleArity() and + // n1 = n2.(TuplePat).getField(i) + // ) + // or + // exists(BlockExpr be | + // n1 = be and + // n2 = be.getStmtList().getTailExpr() and + // if be.isAsync() + // then + // prefix1 = TypePath::singleton(getDynFutureOutputTypeParameter()) and + // prefix2.isEmpty() + // else ( + // prefix1.isEmpty() and + // prefix2.isEmpty() + // ) + // ) + // or + // // an array repeat expression (`[1; 3]`) has the type of the repeat operand + // n1.(ArrayRepeatExpr).getRepeatOperand() = n2 and + // prefix1 = TypePath::singleton(getArrayTypeParameter()) and + // prefix2.isEmpty() + } + + predicate inferStep(AstNode n1, TypePath path1, AstNode n2, TypePath path2) { + path1.isEmpty() and + path2.isEmpty() and + exists(Variable v | + n1 = v.getParentInitializer() and + n2 = v.getDefiningNode() + ) + // // When `n2` is `*n1` propagate type information from a raw pointer type + // // parameter at `n1`. The other direction is handled in + // // `inferDereferencedExprPtrType`. + // n1 = n2.(DerefExpr).getExpr() and + // prefix1 = TypePath::singleton(getPtrTypeParameter()) and + // prefix2.isEmpty() + // or + // n2 = any(ClosureExpr ce | not ce.hasRetType() and ce.getClosureBody() = n1) and + // prefix2 = closureReturnPath() and + // prefix1.isEmpty() + } + + predicate inferLubStep(AstNode n1, TypePath path1, AstNode n2, TypePath path2) { + none() + // path1.isEmpty() and + // ( + // n1 = n2.(ArrayListExpr).getAnExpr() and + // path2 = TypePath::singleton(getArrayTypeParameter()) + // or + // exists(ReturnExpr re, Rust::Callable c | + // n1 = re.getExpr() and + // c = re.getEnclosingCallable() and + // n2 = c.getBody() and + // path2.isEmpty() + // ) + // or + // exists(Struct s | + // n1 = [n2.(RangeExpr).getStart(), n2.(RangeExpr).getEnd()] and + // path2 = + // TypePath::singleton(TTypeParamTypeParameter(s.getGenericParamList().getATypeParam())) and + // s = getRangeType(n2) + // ) + // ) + } + + Type inferTypeTopDown(AstNode n, TypePath path) { + none() + // result = inferTypeFromAnnotationTopDown(n, path) + // or + // result = inferClosureExprBodyTypeTopDown(n, path) + // or + // exists(FunctionPosition pos | not pos.isReturn() | + // result = inferConstructionType(n, pos, path) + // or + // result = inferOperationType(n, pos, path) + // ) + } + + pragma[nomagic] + private Type inferLiteralType(AstNode n, TypePath path) { + path.isEmpty() and + ( + n instanceof StringLiteralExpr and + result instanceof StringType + or + n instanceof IntegerLiteralExpr and + result instanceof IntType + ) + } + + Type inferTypeSpecific(AstNode n, TypePath path) { + result = inferLiteralType(n, path) + // result = inferAssignmentOperationType(n, path) + // or + // exists(FunctionPosition pos | pos.isReturn() | + // result = inferConstructionType(n, pos, path) + // or + // result = inferOperationType(n, pos, path) + // ) + // or + // result = inferFieldExprType(n, path) + // or + // result = inferTryExprType(n, path) + // or + // result = inferLiteralType(n, path, false) + // or + // result = inferAwaitExprType(n, path) + // or + // result = inferDereferencedExprPtrType(n, path) + // or + // result = inferForLoopExprType(n, path) + // or + // result = inferClosureExprType(n, path) + // or + // result = inferArgList(n, path) + // or + // result = inferDeconstructionPatType(n, path) + // or + // result = inferUnknownType(n, path) + } +} + +private module M3 = Make3; + +predicate inferType = M3::inferType/1; + +predicate inferType = M3::inferType/2; + +predicate inferTypeCertain = M3::inferTypeCertain/2; + +module Consistency { + import M2::Consistency +} +// /** A function without a `self` parameter. */ +// private class NonMethodFunction extends Function { +// NonMethodFunction() { not this.hasSelfParam() } +// } +// private module ImplOrTraitItemNodeOption = Option; +// private class ImplOrTraitItemNodeOption = ImplOrTraitItemNodeOption::Option; +// private class FunctionDeclaration extends Function { +// private ImplOrTraitItemNodeOption parent; +// FunctionDeclaration() { +// not this = any(ImplOrTraitItemNode i).getAnAssocItem() and parent.isNone() +// or +// this = parent.asSome().getASuccessor(_) +// } +// /** Holds if this function is associated with `i`. */ +// predicate isAssoc(ImplOrTraitItemNode i) { i = parent.asSome() } +// /** Holds if this is a free function. */ +// predicate isFree() { parent.isNone() } +// /** Holds if this function is valid for `i`. */ +// predicate isFor(ImplOrTraitItemNodeOption i) { i = parent } +// /** +// * Holds if this function is valid for `i`. If `i` is a trait or `impl` block then +// * this function must be declared directly inside `i`. +// */ +// predicate isDirectlyFor(ImplOrTraitItemNodeOption i) { +// i.isNone() and +// this.isFree() +// or +// this = i.asSome().getAnAssocItem() +// } +// TypeParam getTypeParam(ImplOrTraitItemNodeOption i) { +// i = parent and +// result = [this.getGenericParamList().getATypeParam(), i.asSome().getTypeParam(_)] +// } +// TypeParameter getTypeParameter(ImplOrTraitItemNodeOption i, TypeParameterPosition ppos) { +// typeParamMatchPosition(this.getTypeParam(i), result, ppos) +// or +// // For every `TypeParam` of this function, any associated types accessed on +// // the type parameter are also type parameters. +// ppos.isImplicit() and +// result.(TypeParamAssociatedTypeTypeParameter).getTypeParam() = this.getTypeParam(i) +// or +// i = parent and +// ( +// ppos.isImplicit() and result = TSelfTypeParameter(i.asSome()) +// or +// ppos.isImplicit() and result.(AssociatedTypeTypeParameter).getTrait() = i.asSome() +// or +// ppos.isImplicit() and this = result.(ImplTraitTypeTypeParameter).getFunction() +// ) +// } +// pragma[nomagic] +// Type getParameterType(ImplOrTraitItemNodeOption i, FunctionPosition pos, TypePath path) { +// i = parent and +// ( +// not pos.isReturn() and +// result = getAssocFunctionTypeAt(this, i.asSome(), pos, path) +// or +// i.isNone() and +// result = this.getParam(pos.asPosition()).getTypeRepr().(TypeMention).getTypeAt(path) +// ) +// } +// private Type resolveRetType(ImplOrTraitItemNodeOption i, TypePath path) { +// i = parent and +// ( +// result = +// getAssocFunctionTypeAt(this, i.asSome(), any(FunctionPosition ret | ret.isReturn()), path) +// or +// i.isNone() and +// result = getReturnTypeMention(this).getTypeAt(path) +// ) +// } +// Type getReturnType(ImplOrTraitItemNodeOption i, TypePath path) { +// if this.isAsync() +// then +// i = parent and +// path.isEmpty() and +// result = getFutureTraitType() +// or +// exists(TypePath suffix | +// result = this.resolveRetType(i, suffix) and +// path = TypePath::cons(getDynFutureOutputTypeParameter(), suffix) +// ) +// else result = this.resolveRetType(i, path) +// } +// string toStringExt(ImplOrTraitItemNode i) { +// i = parent.asSome() and +// if this = i.getAnAssocItem() +// then result = this.toString() +// else +// result = this + " [" + [i.(Impl).getSelfTy().toString(), i.(Trait).getName().toString()] + "]" +// } +// } +// private class AssocFunctionDeclaration extends FunctionDeclaration { +// AssocFunctionDeclaration() { this.isAssoc(_) } +// } +// pragma[nomagic] +// private TypePath getPathToImplSelfTypeParam(TypeParam tp) { +// exists(ImplItemNode impl | +// tp = impl.getTypeParam(_) and +// TTypeParamTypeParameter(tp) = impl.(Impl).getSelfTy().(TypeMention).getTypeAt(result) +// ) +// } +// pragma[nomagic] +// private Type getCallExprTypeArgument(CallExpr ce, TypeArgumentPosition apos, TypePath path) { +// exists(Path p, ItemNode resolved, TypeParam tp | +// p = CallExprImpl::getFunctionPath(ce) and +// resolved = resolvePath(p) and +// apos.asTypeParam() = tp +// | +// // For type parameters of the function we must resolve their +// // instantiation from the path. For instance, for `fn bar(a: A) -> A` +// // and the path `bar`, we must resolve `A` to `i64`. +// exists(int i | +// tp = resolved.getTypeParam(pragma[only_bind_into](i)) and +// result = getPathTypeArgument(p, pragma[only_bind_into](i)).getTypeAt(path) +// ) +// or +// // For type parameters of the `impl` block we must resolve their +// // instantiation from the path. For instance, for `impl for Foo` +// // and the path `Foo::bar` we must resolve `A` to `i64`. +// exists(ImplItemNode impl, TypePath pathToTp | +// resolved = impl.getASuccessor(_) and +// tp = impl.getTypeParam(_) and +// pathToTp = getPathToImplSelfTypeParam(tp) and +// result = p.getQualifier().(TypeMention).getTypeAt(pathToTp.appendInverse(path)) +// ) +// ) +// or +// // Handle constructions that use `Self(...)` syntax +// exists(Path p, TypePath path0 | +// p = CallExprImpl::getFunctionPath(ce) and +// result = p.(TypeMention).getTypeAt(path0) and +// path0.isCons(TTypeParamTypeParameter(apos.asTypeParam()), path) +// ) +// } +// pragma[nomagic] +// private Type inferFunctionBodyType(AstNode n, TypePath path) { +// exists(Function f | +// n = f.getFunctionBody() and +// result = getReturnTypeMention(f).getTypeAt(path) and +// not exists(ImplTraitReturnType i | i.getFunction() = f | +// result = i or result = i.getATypeParameter() +// ) +// ) +// } +// /** +// * Holds if `me` is a call to the `panic!` macro. +// * +// * `panic!` needs special treatment, because it expands to a block expression +// * that looks like it should have type `()` instead of the correct `!` type. +// */ +// pragma[nomagic] +// 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))) +// } +// private Type inferCertainStructExprType(StructExpr se, TypePath path) { +// result = se.getPath().(TypeMention).getTypeAt(path) +// } +// private Type inferCertainStructPatType(StructPat sp, TypePath path) { +// result = sp.getPath().(TypeMention).getTypeAt(path) +// } +// private Type inferAssignmentOperationType(AstNode n, TypePath path) { +// n instanceof AssignmentOperation and +// path.isEmpty() and +// result instanceof UnitType +// } +// pragma[nomagic] +// private Struct getRangeType(RangeExpr re) { +// re instanceof RangeFromExpr and +// result instanceof RangeFromStruct +// or +// re instanceof RangeToExpr and +// result instanceof RangeToStruct +// or +// re instanceof RangeFullExpr and +// result instanceof RangeFullStruct +// or +// re instanceof RangeFromToExpr and +// result instanceof RangeStruct +// or +// re instanceof RangeInclusiveExpr and +// result instanceof RangeInclusiveStruct +// or +// re instanceof RangeToInclusiveExpr and +// result instanceof RangeToInclusiveStruct +// } +// pragma[nomagic] +// private Type inferTypeFromAnnotationTopDown(AstNode n, TypePath path) { +// // 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())) +// ) +// } +// pragma[nomagic] +// private TupleType inferTupleRootType(AstNode n) { +// // `typeEquality` handles the non-root cases +// result.getArity() = [n.(TupleExpr).getNumberOfFields(), n.(TuplePat).getTupleArity()] +// } +// pragma[nomagic] +// private Path getCallExprPathQualifier(CallExpr ce) { +// result = CallExprImpl::getFunctionPath(ce).getQualifier() +// } +// /** +// * Gets the type qualifier of function call `ce`, if any. +// * +// * For example, the type qualifier of `Foo::::default()` is `Foo::`, +// * but only when `Foo` is not a trait. The type qualifier of `::baz()` +// * is `Foo`. +// * +// * `isDefaultTypeArg` indicates whether the returned type is a default type +// * argument, for example in `Vec::new()` the default type for the type parameter +// * `A` of `Vec` is `Global`. +// */ +// pragma[nomagic] +// private Type getCallExprTypeQualifier(CallExpr ce, TypePath path, boolean isDefaultTypeArg) { +// exists(Path p, TypeMention tm | +// p = getCallExprPathQualifier(ce) and +// tm = [p.(AstNode), p.getSegment().getTypeRepr()] +// | +// result = tm.getTypeAt(path) and +// not resolvePath(tm) instanceof Trait and +// isDefaultTypeArg = false +// or +// exists(TypeParameter tp, TypePath suffix | +// result = +// tm.(NonAliasPathTypeMention).getDefaultTypeForTypeParameterInNonAnnotationAt(tp, suffix) and +// path = TypePath::cons(tp, suffix) and +// isDefaultTypeArg = true +// ) +// ) +// } +// /** +// * Gets the trait qualifier of function call `ce`, if any. +// * +// * For example, the trait qualifier of `Default::::default()` is `Default`. +// */ +// pragma[nomagic] +// private Trait getCallExprTraitQualifier(CallExpr ce) { +// exists(PathExt qualifierPath | +// qualifierPath = getCallExprPathQualifier(ce) and +// result = resolvePath(qualifierPath) and +// // When the qualifier is `Self` and resolves to a trait, it's inside a +// // trait method's default implementation. This is not a dispatch whose +// // target is inferred from the type of the receiver, but should always +// // resolve to the function in the trait block as path resolution does. +// not qualifierPath.isUnqualified("Self") +// ) +// } +// pragma[nomagic] +// private predicate nonAssocFunction(ItemNode i) { not i instanceof AssocFunctionDeclaration } +// /** +// * A call expression that can only resolve to something that is not an associated +// * function, and hence does not need type inference for resolution. +// */ +// private class NonAssocCallExpr extends CallExpr { +// NonAssocCallExpr() { +// forex(ItemNode i | i = CallExprImpl::getResolvedFunction(this) | nonAssocFunction(i)) +// } +// /** +// * Gets the target of this call, which can be resolved using only path resolution. +// */ +// ItemNode resolveCallTargetViaPathResolution() { result = CallExprImpl::getResolvedFunction(this) } +// pragma[nomagic] +// Type getTypeArgument(TypeArgumentPosition apos, TypePath path) { +// result = getCallExprTypeArgument(this, apos, path) +// } +// AstNode getNodeAt(FunctionPosition pos) { +// result = this.getSyntacticArgument(pos.asArgumentPosition()) +// or +// result = this and pos.isReturn() +// } +// pragma[nomagic] +// Type getInferredType(FunctionPosition pos, TypePath path) { +// pos.isTypeQualifier() and +// result = getCallExprTypeQualifier(this, path, false) +// or +// result = inferType(this.getNodeAt(pos), path) +// } +// } +// /** +// * Provides functionality related to context-based typing of calls. +// */ +// private module ContextTyping { +// /** +// * Holds if `f` mentions type parameter `tp` at some non-return position, +// * possibly via a constraint on another mentioned type parameter. +// */ +// pragma[nomagic] +// private predicate assocFunctionMentionsTypeParameterAtNonRetPos( +// ImplOrTraitItemNode i, Function f, TypeParameter tp +// ) { +// exists(FunctionPosition nonRetPos | +// not nonRetPos.isReturn() and +// not nonRetPos.isTypeQualifier() and +// tp = getAssocFunctionTypeAt(f, i, nonRetPos, _) +// ) +// or +// exists(TypeParameter mid | +// assocFunctionMentionsTypeParameterAtNonRetPos(i, f, mid) and +// tp = getATypeParameterConstraint(mid).getTypeAt(_) +// ) +// } +// /** +// * Holds if the return type of the function `f` inside `i` at `path` is type +// * parameter `tp`, and `tp` does not appear in the type of any parameter of +// * `f`. +// * +// * In this case, the context in which `f` is called may be needed to infer +// * the instantiation of `tp`. +// * +// * This covers functions like `Default::default` and `Vec::new`. +// */ +// pragma[nomagic] +// private predicate assocFunctionReturnContextTypedAt( +// ImplOrTraitItemNode i, Function f, FunctionPosition pos, TypePath path, TypeParameter tp +// ) { +// pos.isReturn() and +// tp = getAssocFunctionTypeAt(f, i, pos, path) and +// not assocFunctionMentionsTypeParameterAtNonRetPos(i, f, tp) +// } +// /** +// * A call where the type of the result may have to be inferred from the +// * context in which the call appears, for example a call like +// * `Default::default()`. +// */ +// abstract class ContextTypedCallCand extends Expr { +// abstract Type getTypeArgument(TypeArgumentPosition apos, TypePath path); +// predicate hasTypeArgument(TypeArgumentPosition apos) { exists(this.getTypeArgument(apos, _)) } +// /** +// * Holds if this call resolves to `target` inside `i`, and the return type +// * at `pos` and `path` may have to be inferred from the context. +// */ +// bindingset[this, i, target] +// predicate hasUnknownTypeAt( +// ImplOrTraitItemNode i, Function target, FunctionPosition pos, TypePath path +// ) { +// exists(TypeParameter tp | +// assocFunctionReturnContextTypedAt(i, target, pos, path, tp) and +// // check that no explicit type arguments have been supplied for `tp` +// not exists(TypeArgumentPosition tapos | this.hasTypeArgument(tapos) | +// exists(int j | +// j = tapos.asMethodTypeArgumentPosition() and +// tp = TTypeParamTypeParameter(target.getGenericParamList().getTypeParam(j)) +// ) +// or +// TTypeParamTypeParameter(tapos.asTypeParam()) = tp +// ) and +// not ( +// tp instanceof TSelfTypeParameter and +// exists(getCallExprTypeQualifier(this, _, _)) +// ) +// ) +// } +// } +// } +// /** +// * Holds if the type path `path` pointing to `type` is stripped of any leading +// * complex root type allowed for `self` parameters, such as `&`, `Box`, `Rc`, +// * `Arc`, and `Pin`. +// * +// * We strip away the complex root type for performance reasons only, which will +// * allow us to construct a much smaller set of candidate call targets (otherwise, +// * for example _a lot_ of methods have a `self` parameter with a `&` root type). +// */ +// bindingset[path, type] +// private predicate isComplexRootStripped(TypePath path, Type type) { +// ( +// path.isEmpty() and +// not validSelfType(type) +// or +// exists(TypeParameter tp | +// complexSelfRoot(_, tp) and +// path = TypePath::singleton(tp) and +// exists(type) +// ) +// ) and +// type != TNeverType() +// } +// private newtype TBorrowKind = +// TNoBorrowKind() or +// TSomeBorrowKind(Boolean isMutable) +// private class BorrowKind extends TBorrowKind { +// predicate isNoBorrow() { this = TNoBorrowKind() } +// predicate isSharedBorrow() { this = TSomeBorrowKind(false) } +// predicate isMutableBorrow() { this = TSomeBorrowKind(true) } +// RefType getRefType() { +// exists(boolean isMutable | +// this = TSomeBorrowKind(isMutable) and +// result = getRefType(isMutable) +// ) +// } +// string toString() { +// this.isNoBorrow() and +// result = "" +// or +// this.isMutableBorrow() and +// result = "&mut" +// or +// this.isSharedBorrow() and +// result = "&" +// } +// } +// /** +// * Provides logic for resolving calls to associated functions. +// * +// * When resolving a method call, a list of [candidate receiver types][1] is constructed +// * +// * > by repeatedly dereferencing the receiver expression's type, adding each type +// * > encountered to the list, then finally attempting an unsized coercion at the end, +// * > and adding the result type if that is successful. +// * > +// * > Then, for each candidate `T`, add `&T` and `&mut T` to the list immediately after `T`. +// * +// * We do not currently model unsized coercions, and we do not yet model the `Deref` trait, +// * instead we limit dereferencing to standard dereferencing and the fact that `String` +// * dereferences to `str`. +// * +// * Instead of constructing the full list of candidate receiver types +// * +// * ``` +// * T1, &T1, &mut T1, ..., Tn, &Tn, &mut Tn +// * ``` +// * +// * we recursively compute a set of candidates, only adding a new candidate receiver type +// * to the set when we can rule out that the method cannot be found for the current +// * candidate: +// * +// * ```text +// * forall method: +// * not current_candidate matches method +// * ``` +// * +// * Care must be taken to ensure that the `not current_candidate matches method` check is +// * monotonic, which we achieve using the monotonic `isNotInstantiationOf` predicate. +// * +// * [1]: https://doc.rust-lang.org/reference/expressions/method-call-expr.html#r-expr.method.candidate-receivers +// */ +// private module AssocFunctionResolution { +// /** +// * Holds if function `f` with the name `name` and the arity `arity` exists in +// * `i`, and the type at function-call adjusted position `pos` is `t`. +// */ +// pragma[nomagic] +// private predicate assocFunctionInfo( +// Function f, string name, int arity, ImplOrTraitItemNode i, FunctionPosition pos, +// AssocFunctionType t +// ) { +// f = i.getASuccessor(name) and +// arity = f.getNumberOfParamsInclSelf() and +// t.appliesTo(f, i, pos) +// } +// /** +// * Holds if the non-method trait function `f` mentions the implicit `Self` type +// * parameter at position `pos`. +// */ +// pragma[nomagic] +// private predicate traitSelfTypeParameterOccurrence( +// TraitItemNode trait, NonMethodFunction f, FunctionPosition pos +// ) { +// FunctionOverloading::traitTypeParameterOccurrence(trait, f, _, pos, _, TSelfTypeParameter(trait)) +// } +// /** +// * Holds if the non-method function `f` implements a trait function that mentions +// * the implicit `Self` type parameter at position `pos`. +// */ +// pragma[nomagic] +// private predicate traitImplSelfTypeParameterOccurrence( +// ImplItemNode impl, NonMethodFunction f, FunctionPosition pos +// ) { +// exists(NonMethodFunction traitFunction | +// f = impl.getAnAssocItem() and +// f.implements(traitFunction) and +// traitSelfTypeParameterOccurrence(_, traitFunction, pos) +// ) +// } +// private module TypeOption = Option; +// private class TypeOption = TypeOption::Option; +// /** +// * Holds if function `f` with the name `name` and the arity `arity` exists in +// * `i`, and the type at function-call adjusted position `selfPos` is `selfType`. +// * +// * `selfPos` is a position relevant for call resolution: either a position +// * corresponding to the `self` parameter of `f` (if present); a type qualifier +// * position; or a position where the implicit `Self` type parameter of some trait +// * is mentioned in some non-method function `f_trait`, and either `f = f_trait` +// * or `f` implements `f_trait`. +// * +// * `strippedTypePath` points to the type `strippedType` inside `selfType`, which +// * is the (possibly complex-stripped) root type of `selfType`. For example, if +// * `f` has a `&self` parameter, then `strippedTypePath` is `getRefSharedTypeParameter()` +// * and `strippedType` is the type inside the reference. +// * +// * `implType` is the type being implemented by `i` (`None` when `i` is a trait). +// * +// * `trait` is the trait being implemented by `i` or `i` itself (`None` when `i` is inherent). +// * +// * `isMethod` indicates whether `f` is a method. +// */ +// pragma[nomagic] +// private predicate assocFunctionInfo( +// Function f, string name, int arity, FunctionPosition selfPos, ImplOrTraitItemNode i, +// AssocFunctionType selfType, TypePath strippedTypePath, Type strippedType, TypeOption implType, +// TypeOption trait, boolean isMethod +// ) { +// assocFunctionInfo(f, name, arity, i, selfPos, selfType) and +// strippedType = selfType.getTypeAt(strippedTypePath) and +// ( +// isComplexRootStripped(strippedTypePath, strippedType) +// or +// selfPos.isTypeQualifier() and strippedTypePath.isEmpty() +// ) and +// ( +// f instanceof Method and +// selfPos.asPosition() = 0 +// or +// selfPos.isTypeQualifier() +// or +// traitSelfTypeParameterOccurrence(i, f, selfPos) +// or +// traitImplSelfTypeParameterOccurrence(i, f, selfPos) +// ) and +// ( +// implType.asSome() = resolveImplSelfTypeAt(i, TypePath::nil()) +// or +// i instanceof Trait and +// implType.isNone() +// ) and +// ( +// trait.asSome() = +// [ +// TTrait(i).(Type), +// TTrait(i.(ImplItemNode).resolveTraitTy()).(Type) +// ] +// or +// i.(Impl).isInherent() and trait.isNone() +// ) and +// if f instanceof Method then isMethod = true else isMethod = false +// } +// /** +// * Holds if function `f` with the name `name` and the arity `arity` exists in +// * blanket (like) implementation `impl`, and the type at function-call adjusted +// * position `selfPos` is `selfType`. +// * +// * `selfPos` is a position relevant for call resolution: either a position +// * corresponding to the `self` parameter of `f` (if present); a type qualifier +// * position; or a position where the implicit `Self` type parameter of some trait +// * is mentioned in some non-method function `f_trait`, and `f` implements `f_trait`. +// * +// * `blanketPath` points to the type `blanketTypeParam` inside `selfType`, which +// * is the type parameter used in the blanket implementation. +// * +// * `implType` is the type being implemented by `i`. +// * +// * `trait` is the trait being implemented by `i`. +// * +// * `isMethod` indicates whether `f` is a method. +// */ +// pragma[nomagic] +// private predicate assocFunctionInfoBlanketLike( +// Function f, string name, int arity, ImplItemNode impl, TypeOption implType, TypeOption trait, +// FunctionPosition selfPos, AssocFunctionType selfType, TypePath blanketPath, +// TypeParam blanketTypeParam, boolean isMethod +// ) { +// exists(TypePath blanketSelfPath | +// assocFunctionInfo(f, name, arity, selfPos, impl, selfType, _, _, implType, trait, isMethod) and +// TTypeParamTypeParameter(blanketTypeParam) = selfType.getTypeAt(blanketPath) and +// blanketPath = any(string s) + blanketSelfPath and +// BlanketImplementation::isBlanketLike(impl, blanketSelfPath, blanketTypeParam) +// ) +// } +// pragma[nomagic] +// private predicate assocFunctionTraitInfo(string name, int arity, Trait trait) { +// exists(ImplItemNode i | +// assocFunctionInfo(_, name, arity, i, _, _) and +// trait = i.resolveTraitTy() +// ) +// or +// assocFunctionInfo(_, name, arity, trait, _, _) +// } +// pragma[nomagic] +// private predicate assocFunctionCallTraitCandidate(Element afc, Trait trait) { +// afc = +// any(AssocFunctionCall afc0 | +// exists(string name, int arity | +// afc0.hasNameAndArity(name, arity) and +// assocFunctionTraitInfo(name, arity, trait) and +// // we only need to check visibility of traits that are not mentioned explicitly +// not afc0.hasATrait() +// ) +// ) +// } +// private module AssocFunctionTraitIsVisible = TraitIsVisible; +// bindingset[afc, impl] +// pragma[inline_late] +// private predicate callVisibleImplTraitCandidate(AssocFunctionCall afc, ImplItemNode impl) { +// AssocFunctionTraitIsVisible::traitIsVisible(afc, impl.resolveTraitTy()) +// } +// /** +// * Checks that the explicit type qualifier of a call (if any), `typeQualifier`, +// * matches the type being implemented by the target, `implType`. +// */ +// bindingset[implType] +// private predicate callTypeQualifierCheck(TypeOption implType, TypeOption typeQualifier) { +// typeQualifier = [implType, TypeOption::none_()] +// } +// /** +// * Checks that the explicit trait qualifier of a call (if any), `traitQualifier`, +// * matches the trait being implemented by the target (or in which the target is defined), +// * `trait`, and that when a receiver is present in the call, the target is a method. +// */ +// bindingset[trait, isMethod] +// pragma[inline_late] +// private predicate callTraitQualifierAndReceiverCheck( +// TypeOption trait, Boolean isMethod, TypeOption traitQualifier, boolean hasReceiver +// ) { +// traitQualifier = [trait, TypeOption::none_()] and +// hasReceiver = [isMethod, false] +// } +// bindingset[implType, trait, isMethod] +// private predicate callCheck( +// TypeOption implType, TypeOption trait, Boolean isMethod, TypeOption typeQualifier, +// TypeOption traitQualifier, boolean hasReceiver +// ) { +// callTypeQualifierCheck(implType, typeQualifier) and +// callTraitQualifierAndReceiverCheck(trait, isMethod, traitQualifier, hasReceiver) +// } +// pragma[nomagic] +// private predicate assocFunctionInfoNonBlanketLikeCheck( +// Function f, string name, int arity, FunctionPosition selfPos, ImplOrTraitItemNode i, +// AssocFunctionType selfType, TypePath strippedTypePath, Type strippedType, +// TypeOption typeQualifier, TypeOption traitQualifier, boolean hasReceiver +// ) { +// exists(TypeOption implType, TypeOption trait, boolean isMethod | +// assocFunctionInfo(f, name, arity, selfPos, i, selfType, strippedTypePath, strippedType, +// implType, trait, isMethod) and +// not BlanketImplementation::isBlanketLike(i, _, _) and +// callCheck(implType, trait, isMethod, typeQualifier, traitQualifier, hasReceiver) +// ) +// } +// pragma[nomagic] +// private predicate assocFunctionInfoNonBlanketLikeTypeParamCheck( +// Function f, string name, int arity, FunctionPosition selfPos, ImplOrTraitItemNode i, +// AssocFunctionType selfType, TypePath strippedTypePath, TypeOption typeQualifier, +// TypeOption traitQualifier, boolean hasReceiver +// ) { +// assocFunctionInfoNonBlanketLikeCheck(f, name, arity, selfPos, i, selfType, strippedTypePath, +// TTypeParamTypeParameter(_), typeQualifier, traitQualifier, hasReceiver) +// } +// /** +// * Holds if call `afc` may target function `f` in `i` with type `selfType` at +// * function-call adjusted position `selfPos`. +// * +// * `strippedTypePath` points to the type `strippedType` inside `selfType`, +// * which is the (possibly complex-stripped) root type of `selfType`. +// */ +// bindingset[afc, strippedTypePath, strippedType] +// pragma[inline_late] +// private predicate nonBlanketLikeCandidate( +// AssocFunctionCall afc, Function f, FunctionPosition selfPos, ImplOrTraitItemNode i, +// AssocFunctionType selfType, TypePath strippedTypePath, Type strippedType +// ) { +// exists( +// string name, int arity, TypeOption typeQualifier, TypeOption traitQualifier, +// boolean hasReceiver +// | +// afc.hasSyntacticInfo(name, arity, typeQualifier, traitQualifier, hasReceiver) and +// if not afc.hasATrait() and i.(Impl).hasTrait() +// then callVisibleImplTraitCandidate(afc, i) +// else any() +// | +// assocFunctionInfoNonBlanketLikeCheck(f, name, arity, selfPos, i, selfType, strippedTypePath, +// strippedType, typeQualifier, traitQualifier, hasReceiver) +// or +// assocFunctionInfoNonBlanketLikeTypeParamCheck(f, name, arity, selfPos, i, selfType, +// strippedTypePath, typeQualifier, traitQualifier, hasReceiver) +// ) +// } +// bindingset[name, arity, typeQualifier, traitQualifier, hasReceiver] +// pragma[inline_late] +// private predicate assocFunctionInfoBlanketLikeCheck( +// Function f, string name, int arity, FunctionPosition selfPos, ImplItemNode impl, +// AssocFunctionType selfType, TypePath blanketPath, TypeParam blanketTypeParam, +// TypeOption typeQualifier, TypeOption traitQualifier, boolean hasReceiver +// ) { +// exists(TypeOption implType, TypeOption trait, boolean isMethod | +// assocFunctionInfoBlanketLike(f, name, arity, impl, implType, trait, selfPos, selfType, +// blanketPath, blanketTypeParam, isMethod) and +// callTraitQualifierAndReceiverCheck(trait, isMethod, traitQualifier, hasReceiver) and +// if impl.isBlanketImplementation() +// then any() +// else callTypeQualifierCheck(implType, typeQualifier) +// ) +// } +// /** +// * Holds if call `afc` may target function `f` in blanket (like) implementation +// * `impl` with type `selfType` at function-call adjusted position `selfPos`. +// * +// * `blanketPath` points to the type `blanketTypeParam` inside `selfType`, which +// * is the type parameter used in the blanket implementation. +// */ +// bindingset[afc] +// pragma[inline_late] +// private predicate blanketLikeCandidate( +// AssocFunctionCall afc, Function f, FunctionPosition selfPos, ImplItemNode impl, +// AssocFunctionType self, TypePath blanketPath, TypeParam blanketTypeParam +// ) { +// exists( +// string name, int arity, TypeOption typeQualifier, TypeOption traitQualifier, +// boolean hasReceiver +// | +// afc.hasSyntacticInfo(name, arity, typeQualifier, traitQualifier, hasReceiver) and +// assocFunctionInfoBlanketLikeCheck(f, name, arity, selfPos, impl, self, blanketPath, +// blanketTypeParam, typeQualifier, traitQualifier, hasReceiver) +// | +// if not afc.hasATrait() then callVisibleImplTraitCandidate(afc, impl) else any() +// ) +// } +// /** +// * A (potential) call to an associated function. +// * +// * This is either: +// * +// * 1. `MethodCallExprAssocFunctionCall`: a method call, `x.m()`; +// * 2. `IndexExprAssocFunctionCall`: an index expression, `x[i]`, which is [syntactic sugar][1] +// * for `*x.index(i)`; +// * 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. +// * +// * Note also that only case 3 is a _potential_ call; in all other cases, we are guaranteed that +// * the target is an associated function (in fact, a method). +// * +// * [1]: https://doc.rust-lang.org/std/ops/trait.Index.html +// */ +// abstract class AssocFunctionCall extends Expr { +// /** +// * Holds if this call targets a function named `name` with `arity` parameters +// * (including `self`). +// */ +// pragma[nomagic] +// abstract predicate hasNameAndArity(string name, int arity); +// abstract AstNode getNonReturnNodeAt(FunctionPosition pos); +// AstNode getNodeAt(FunctionPosition pos) { +// result = this.getNonReturnNodeAt(pos) +// or +// result = this and pos.isReturn() +// } +// /** Holds if this call has a receiver and hence must target a method. */ +// abstract predicate hasReceiver(); +// abstract predicate supportsAutoDerefAndBorrow(); +// /** Gets the trait targeted by this call, if any. */ +// abstract Trait getTrait(); +// /** Holds if this call targets a trait. */ +// predicate hasTrait() { exists(this.getTrait()) } +// Trait getATrait() { +// result = this.getTrait() +// or +// result = getALookupTrait(this, getCallExprTypeQualifier(this, TypePath::nil(), _)) +// } +// predicate hasATrait() { exists(this.getATrait()) } +// private Type getNonTypeParameterTypeQualifier() { +// result = getCallExprTypeQualifier(this, TypePath::nil(), _) and +// not result instanceof TypeParameter +// } +// /** +// * Holds if this call has the given purely syntactic information, that is, +// * information that does not rely on type inference. +// */ +// pragma[nomagic] +// predicate hasSyntacticInfo( +// string name, int arity, TypeOption typeQualifier, TypeOption traitQualifier, +// boolean hasReceiver +// ) { +// this.hasNameAndArity(name, arity) and +// (if this.hasReceiver() then hasReceiver = true else hasReceiver = false) and +// ( +// typeQualifier.asSome() = this.getNonTypeParameterTypeQualifier() +// or +// not exists(this.getNonTypeParameterTypeQualifier()) and +// typeQualifier.isNone() +// ) and +// ( +// traitQualifier.asSome() = TTrait(this.getATrait()) +// or +// not this.hasATrait() and +// traitQualifier.isNone() +// ) +// } +// Type getTypeAt(FunctionPosition pos, TypePath path) { +// result = inferType(this.getNodeAt(pos), path) +// } +// /** +// * Holds if `selfPos` is a potentially relevant function-call adjusted position +// * for resolving this call. +// * +// * Only holds when we don't know for sure that the target is a method (in those +// * cases we rely on the receiver only). +// */ +// pragma[nomagic] +// private predicate isRelevantSelfPos(FunctionPosition selfPos) { +// not this.hasReceiver() and +// exists(TypePath strippedTypePath, Type strippedType | +// strippedType = substituteLookupTraits(this, this.getTypeAt(selfPos, strippedTypePath)) and +// strippedType != TNeverType() and +// strippedType != TUnknownType() +// | +// nonBlanketLikeCandidate(this, _, selfPos, _, _, strippedTypePath, strippedType) +// or +// blanketLikeCandidate(this, _, selfPos, _, _, strippedTypePath, _) +// ) +// } +// predicate hasReceiverAtPos(FunctionPosition pos) { this.hasReceiver() and pos.asPosition() = 0 } +// pragma[nomagic] +// private predicate hasIncompatibleArgsTarget( +// ImplOrTraitItemNode i, FunctionPosition selfPos, DerefChain derefChain, BorrowKind borrow, +// AssocFunctionType selfType +// ) { +// SelfArgIsInstantiationOf::argIsInstantiationOf(this, i, selfPos, derefChain, borrow, selfType) and +// OverloadedCallArgsAreInstantiationsOf::argsAreNotInstantiationsOf(this, i) +// } +// /** +// * Holds if the function inside `i` with matching name and arity can be ruled +// * out as a target of this call, because the candidate receiver type represented +// * by `derefChain` and `borrow` is incompatible with the type at function-call +// * adjusted position `selfPos`. +// * +// * The types are incompatible because they disagree on a concrete type somewhere +// * inside `root`. +// */ +// pragma[nomagic] +// predicate hasIncompatibleTarget( +// ImplOrTraitItemNode i, FunctionPosition selfPos, DerefChain derefChain, BorrowKind borrow, +// Type root +// ) { +// exists(AssocFunctionType selfType | root = selfType.getTypeAt(TypePath::nil()) | +// this.hasIncompatibleArgsTarget(i, selfPos, derefChain, borrow, selfType) +// or +// SelfArgIsInstantiationOf::argIsNotInstantiationOf(this, i, selfPos, derefChain, borrow, +// selfType) +// ) +// } +// /** +// * Holds if the function inside blanket-like implementation `impl` with matching name +// * and arity can be ruled out as a target of this call, either because the candidate +// * receiver type represented by `derefChain` and `borrow` is incompatible with the type +// * at function-call adjusted position `selfPos`, or because the blanket constraint +// * is not satisfied. +// */ +// pragma[nomagic] +// predicate hasIncompatibleBlanketLikeTarget( +// ImplItemNode impl, FunctionPosition selfPos, DerefChain derefChain, BorrowKind borrow +// ) { +// SelfArgIsNotInstantiationOfBlanketLike::argIsNotInstantiationOf(MkAssocFunctionCallCand(this, +// selfPos, derefChain, borrow), impl, _, _) +// or +// ArgSatisfiesBlanketLikeConstraint::dissatisfiesBlanketConstraint(MkAssocFunctionCallCand(this, +// selfPos, derefChain, borrow), impl) +// } +// pragma[nomagic] +// private predicate hasNoInherentTargetCheck( +// FunctionPosition selfPos, DerefChain derefChain, BorrowKind borrow +// ) { +// MkAssocFunctionCallCand(this, selfPos, derefChain, borrow) +// .(AssocFunctionCallCand) +// .hasNoInherentTargetCheck() +// } +// pragma[nomagic] +// private predicate hasNoInherentTargetTypeQualifierCheck() { +// exists(FunctionPosition typeQualifierPos | +// typeQualifierPos.isTypeQualifier() and +// this.hasNoInherentTargetCheck(typeQualifierPos, DerefChain::nil(), TNoBorrowKind()) +// ) +// } +// pragma[nomagic] +// predicate hasNoInherentTarget(FunctionPosition selfPos, DerefChain derefChain, BorrowKind borrow) { +// this.hasNoInherentTargetCheck(selfPos, derefChain, borrow) and +// if exists(this.getNonTypeParameterTypeQualifier()) and not selfPos.isTypeQualifier() +// then +// // If this call is of the form `Foo::bar(x)` and we are resolving with respect to the type +// // of `x`, then we additionally need to check that the type qualifier does not give rise +// // to an inherent target +// this.hasNoInherentTargetTypeQualifierCheck() +// else any() +// } +// /** +// * Same as `getSelfTypeAt`, but excludes pseudo types `!` and `unknown`. +// */ +// pragma[nomagic] +// Type getANonPseudoSelfTypeAt( +// FunctionPosition selfPos, DerefChain derefChain, BorrowKind borrow, TypePath path +// ) { +// result = this.getSelfTypeAt(selfPos, derefChain, borrow, path) and +// result != TNeverType() and +// result != TUnknownType() +// } +// pragma[nomagic] +// Type getComplexStrippedSelfType( +// FunctionPosition selfPos, DerefChain derefChain, BorrowKind borrow, TypePath strippedTypePath +// ) { +// result = this.getANonPseudoSelfTypeAt(selfPos, derefChain, borrow, strippedTypePath) and +// ( +// isComplexRootStripped(strippedTypePath, result) +// or +// selfPos.isTypeQualifier() and strippedTypePath.isEmpty() +// ) +// } +// /** +// * Holds if the candidate receiver type represented by `derefChain` and `borrow` +// * does not have a matching call target at function-call adjusted position `selfPos`. +// */ +// predicate hasNoCompatibleTarget( +// FunctionPosition selfPos, DerefChain derefChain, BorrowKind borrow +// ) { +// NoCompatibleTarget::hasNoCompatibleTarget(this, selfPos, derefChain, borrow) +// } +// /** +// * Holds if the candidate receiver type represented by `derefChain` and `borrow` +// * does not have a matching non-blanket call target at function-call adjusted +// * position `selfPos`. +// */ +// predicate hasNoCompatibleNonBlanketTarget( +// FunctionPosition selfPos, DerefChain derefChain, BorrowKind borrow +// ) { +// NoCompatibleTarget::hasNoCompatibleNonBlanketTarget(this, selfPos, derefChain, borrow) +// } +// /** +// * Same as `getSelfTypeAt`, but without borrows. +// */ +// pragma[nomagic] +// Type getSelfTypeAtNoBorrow(FunctionPosition selfPos, DerefChain derefChain, TypePath path) { +// result = this.getTypeAt(selfPos, path) and +// derefChain.isEmpty() and +// ( +// this.hasReceiverAtPos(selfPos) +// or +// selfPos.isTypeQualifier() +// or +// this.isRelevantSelfPos(selfPos) +// ) +// or +// exists(DerefImplItemNode impl, DerefChain suffix | +// result = +// ImplicitDeref::getDereferencedCandidateReceiverType(this, selfPos, impl, suffix, path) and +// derefChain = DerefChain::cons(impl, suffix) +// ) +// } +// /** +// * Holds if this call may have an implicit borrow of kind `borrow` at +// * function-call adjusted position `selfPos` with the given `derefChain`. +// */ +// pragma[nomagic] +// predicate hasImplicitBorrowCand( +// FunctionPosition selfPos, DerefChain derefChain, BorrowKind borrow +// ) { +// exists(BorrowKind prev | this.hasNoCompatibleTarget(selfPos, derefChain, prev) | +// // first try shared borrow +// prev.isNoBorrow() and +// borrow.isSharedBorrow() +// or +// // then try mutable borrow +// prev.isSharedBorrow() and +// borrow.isMutableBorrow() +// ) +// } +// /** +// * Gets the type of this call at function-call adjusted position `selfPos` and +// * type path `path`. +// * +// * In case this call supports auto-dereferencing and borrowing and `selfPos` is +// * position 0 (corresponding to the receiver), the result is a +// * [candidate receiver type][1]: +// * +// * The type is obtained by repeatedly dereferencing the receiver expression's type, +// * as long as the method cannot be resolved in an earlier candidate type, and possibly +// * applying a borrow at the end. +// * +// * The parameter `derefChain` encodes the sequence of dereferences, and `borrows` indicates +// * whether a borrow has been applied. +// * +// * [1]: https://doc.rust-lang.org/reference/expressions/method-call-expr.html#r-expr.method.candidate-receivers +// */ +// pragma[nomagic] +// Type getSelfTypeAt( +// FunctionPosition selfPos, DerefChain derefChain, BorrowKind borrow, TypePath path +// ) { +// result = this.getSelfTypeAtNoBorrow(selfPos, derefChain, path) and +// borrow.isNoBorrow() +// or +// exists(RefType rt | +// this.hasImplicitBorrowCand(selfPos, derefChain, borrow) and +// rt = borrow.getRefType() +// | +// path.isEmpty() and +// result = rt +// or +// exists(TypePath suffix | +// result = this.getSelfTypeAtNoBorrow(selfPos, derefChain, suffix) and +// path = TypePath::cons(rt.getPositionalTypeParameter(0), suffix) +// ) +// ) +// } +// /** +// * Gets a function that this call resolves to after having applied a sequence of +// * dereferences and possibly a borrow on the receiver type at `selfPos`, encoded +// * in `derefChain` and `borrow`. +// */ +// pragma[nomagic] +// AssocFunctionDeclaration resolveCallTarget( +// ImplOrTraitItemNode i, FunctionPosition selfPos, DerefChain derefChain, BorrowKind borrow +// ) { +// exists(AssocFunctionCallCand afcc | +// afcc = MkAssocFunctionCallCand(this, selfPos, derefChain, borrow) and +// result = afcc.resolveCallTarget(i) +// ) +// } +// /** +// * Holds if the argument `arg` of this call has been implicitly dereferenced +// * and borrowed according to `derefChain` and `borrow`, in order to be able to +// * resolve the call target. +// */ +// predicate argumentHasImplicitDerefChainBorrow(Expr arg, DerefChain derefChain, BorrowKind borrow) { +// exists(FunctionPosition selfAdj | +// this.hasReceiverAtPos(selfAdj) and +// exists(this.resolveCallTarget(_, selfAdj, derefChain, borrow)) and +// arg = this.getNodeAt(selfAdj) and +// not (derefChain.isEmpty() and borrow.isNoBorrow()) +// ) +// } +// } +// private class MethodCallExprAssocFunctionCall extends AssocFunctionCall instanceof MethodCallExpr { +// override predicate hasNameAndArity(string name, int arity) { +// name = super.getIdentifier().getText() and +// arity = super.getNumberOfSyntacticArguments() +// } +// override predicate hasReceiver() { any() } +// override Expr getNonReturnNodeAt(FunctionPosition pos) { +// result = super.getReceiver() and +// pos.asPosition() = 0 +// or +// result = super.getPositionalArgument(pos.asPosition() - 1) +// } +// override predicate supportsAutoDerefAndBorrow() { any() } +// override Trait getTrait() { none() } +// } +// private class IndexExprAssocFunctionCall extends AssocFunctionCall, IndexExpr { +// private predicate isInMutableContext() { +// // todo: does not handle all cases yet +// VariableImpl::assignmentOperationDescendant(_, this) +// } +// override predicate hasNameAndArity(string name, int arity) { +// (if this.isInMutableContext() then name = "index_mut" else name = "index") and +// arity = 2 +// } +// override predicate hasReceiver() { any() } +// override Expr getNonReturnNodeAt(FunctionPosition pos) { +// pos.asPosition() = 0 and +// result = this.getBase() +// or +// pos.asPosition() = 1 and +// result = this.getIndex() +// } +// override predicate supportsAutoDerefAndBorrow() { any() } +// override Trait getTrait() { +// if this.isInMutableContext() +// then result instanceof IndexMutTrait +// else result instanceof IndexTrait +// } +// } +// 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`) +// forall(ItemNode i | i = CallExprImpl::getResolvedFunction(this) | +// i instanceof AssocFunctionDeclaration +// ) +// } +// override predicate hasNameAndArity(string name, int arity) { +// name = CallExprImpl::getFunctionPath(this).getText() and +// arity = this.getNumberOfSyntacticArguments() +// } +// override predicate hasReceiver() { none() } +// override Expr getNonReturnNodeAt(FunctionPosition pos) { +// result = this.getSyntacticPositionalArgument(pos.asPosition()) +// } +// override Type getTypeAt(FunctionPosition pos, TypePath path) { +// result = super.getTypeAt(pos, path) +// or +// pos.isTypeQualifier() and +// result = getCallExprTypeQualifier(this, path, _) +// } +// override predicate supportsAutoDerefAndBorrow() { none() } +// override Trait getTrait() { result = getCallExprTraitQualifier(this) } +// } +// final class OperationAssocFunctionCall extends AssocFunctionCall, Operation { +// override predicate hasNameAndArity(string name, int arity) { +// this.isOverloaded(_, name, _) and +// arity = this.getNumberOfOperands() +// } +// override predicate hasReceiver() { any() } +// override Expr getNonReturnNodeAt(FunctionPosition pos) { +// result = this.getOperand(pos.asPosition()) +// } +// private predicate implicitBorrowAt(FunctionPosition pos, boolean isMutable) { +// exists(int borrows | this.isOverloaded(_, _, borrows) | +// pos.asPosition() = 0 and +// borrows >= 1 and +// if this instanceof CompoundAssignmentExpr then isMutable = true else isMutable = false +// or +// pos.asPosition() = 1 and +// borrows = 2 and +// isMutable = false +// ) +// } +// override Type getTypeAt(FunctionPosition pos, TypePath path) { +// exists(boolean isMutable, RefType rt | +// this.implicitBorrowAt(pos, isMutable) and +// rt = getRefType(isMutable) +// | +// result = rt and +// path.isEmpty() +// or +// exists(TypePath path0 | +// result = inferType(this.getNodeAt(pos), path0) and +// path = TypePath::cons(rt.getPositionalTypeParameter(0), path0) +// ) +// ) +// or +// not this.implicitBorrowAt(pos, _) and +// result = inferType(this.getNodeAt(pos), path) +// } +// override predicate argumentHasImplicitDerefChainBorrow( +// Expr arg, DerefChain derefChain, BorrowKind borrow +// ) { +// exists(FunctionPosition pos, boolean isMutable | +// this.implicitBorrowAt(pos, isMutable) and +// arg = this.getNodeAt(pos) and +// derefChain = DerefChain::nil() and +// borrow = TSomeBorrowKind(isMutable) +// ) +// } +// override predicate supportsAutoDerefAndBorrow() { none() } +// 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 } +// } +// /** +// * Provides logic for efficiently checking that there are no compatible call +// * targets for a given candidate receiver type. +// * +// * For calls with non-blanket target candidates, we need to check: +// * +// * ```text +// * forall types `t` where `t` is a lookup type for the given candidate receiver type: +// * forall non-blanket candidates `c` matching `t`: +// * check that `c` is not a compatible target +// * ``` +// * +// * Instead of implementing the above using `forall`, we apply the standard trick +// * of using ranked recursion. +// */ +// private module NoCompatibleTarget { +// private import codeql.rust.elements.internal.generated.Raw +// private import codeql.rust.elements.internal.generated.Synth +// private class RawImplOrTrait = @impl or @trait; +// private predicate id(RawImplOrTrait x, RawImplOrTrait y) { x = y } +// private predicate idOfRaw(RawImplOrTrait x, int y) = equivalenceRelation(id/2)(x, y) +// private int idOfImplOrTraitItemNode(ImplOrTraitItemNode i) { +// idOfRaw(Synth::convertAstNodeToRaw(i), result) +// } +// /** +// * Holds if `t` is the `n`th lookup type for the candidate receiver type +// * represented by `derefChain` and `borrow` at function-call adjusted position +// * `selfPos` of `afc`. +// * +// * There are no compatible non-blanket-like candidates for lookup types `0` to `n - 1`. +// */ +// pragma[nomagic] +// private predicate noCompatibleNonBlanketLikeTargetCandNthLookupType( +// AssocFunctionCall afc, FunctionPosition selfPos, DerefChain derefChain, BorrowKind borrow, +// TypePath strippedTypePath, Type strippedType, int n, Type t +// ) { +// ( +// ( +// ( +// afc.supportsAutoDerefAndBorrow() and +// afc.hasReceiverAtPos(selfPos) +// or +// // needed for the `hasNoCompatibleNonBlanketTarget` check in +// // `ArgSatisfiesBlanketLikeConstraintInput::hasBlanketCandidate` +// exists(ImplItemNode i | +// derefChain.isEmpty() and +// blanketLikeCandidate(afc, _, selfPos, i, _, _, _) and +// i.isBlanketImplementation() +// ) +// ) and +// borrow.isNoBorrow() +// or +// afc.hasImplicitBorrowCand(selfPos, derefChain, borrow) +// ) and +// strippedType = afc.getComplexStrippedSelfType(selfPos, derefChain, borrow, strippedTypePath) and +// n = 0 +// or +// hasNoCompatibleNonBlanketLikeTargetForNthLookupType(afc, selfPos, derefChain, borrow, +// strippedTypePath, strippedType, n - 1) +// ) and +// t = getNthLookupType(afc, strippedType, n) +// } +// pragma[nomagic] +// private ImplOrTraitItemNode getKthNonBlanketLikeCandidateForNthLookupType( +// AssocFunctionCall afc, FunctionPosition selfPos, DerefChain derefChain, BorrowKind borrow, +// TypePath strippedTypePath, Type strippedType, int n, Type t, int k +// ) { +// noCompatibleNonBlanketLikeTargetCandNthLookupType(afc, selfPos, derefChain, borrow, +// strippedTypePath, strippedType, n, t) and +// result = +// rank[k + 1](ImplOrTraitItemNode i, int id | +// nonBlanketLikeCandidate(afc, _, selfPos, i, _, strippedTypePath, t) and +// id = idOfImplOrTraitItemNode(i) +// | +// i order by id +// ) +// } +// pragma[nomagic] +// private int getLastNonBlanketLikeCandidateForNthLookupType( +// AssocFunctionCall afc, FunctionPosition selfPos, DerefChain derefChain, BorrowKind borrow, +// TypePath strippedTypePath, Type strippedType, int n +// ) { +// exists(Type t | +// noCompatibleNonBlanketLikeTargetCandNthLookupType(afc, selfPos, derefChain, borrow, +// strippedTypePath, strippedType, n, t) and +// result = +// count(ImplOrTraitItemNode i | +// nonBlanketLikeCandidate(afc, _, selfPos, i, _, strippedTypePath, t) +// ) - 1 +// ) +// } +// pragma[nomagic] +// private predicate hasNoCompatibleNonBlanketLikeTargetForNthLookupTypeToIndex( +// AssocFunctionCall afc, FunctionPosition selfPos, DerefChain derefChain, BorrowKind borrow, +// TypePath strippedTypePath, Type strippedType, int n, int k +// ) { +// exists(Type t | +// noCompatibleNonBlanketLikeTargetCandNthLookupType(afc, selfPos, derefChain, borrow, +// strippedTypePath, strippedType, n, t) +// | +// k = -1 +// or +// hasNoCompatibleNonBlanketLikeTargetForNthLookupTypeToIndex(afc, selfPos, derefChain, borrow, +// strippedTypePath, strippedType, n, k - 1) and +// exists(ImplOrTraitItemNode i | +// i = +// getKthNonBlanketLikeCandidateForNthLookupType(afc, selfPos, derefChain, borrow, +// strippedTypePath, strippedType, n, t, k) and +// afc.hasIncompatibleTarget(i, selfPos, derefChain, borrow, t) +// ) +// ) +// } +// pragma[nomagic] +// private predicate hasNoCompatibleNonBlanketLikeTargetForNthLookupType( +// AssocFunctionCall afc, FunctionPosition selfPos, DerefChain derefChain, BorrowKind borrow, +// TypePath strippedTypePath, Type strippedType, int n +// ) { +// exists(int last | +// last = +// getLastNonBlanketLikeCandidateForNthLookupType(afc, selfPos, derefChain, borrow, +// strippedTypePath, strippedType, n) and +// hasNoCompatibleNonBlanketLikeTargetForNthLookupTypeToIndex(afc, selfPos, derefChain, borrow, +// strippedTypePath, strippedType, n, last) +// ) +// } +// pragma[nomagic] +// private predicate hasNoCompatibleNonBlanketLikeTarget( +// AssocFunctionCall afc, FunctionPosition selfPos, DerefChain derefChain, BorrowKind borrow +// ) { +// exists(Type strippedType | +// hasNoCompatibleNonBlanketLikeTargetForNthLookupType(afc, selfPos, derefChain, borrow, _, +// strippedType, getLastLookupTypeIndex(afc, strippedType)) +// ) +// } +// pragma[nomagic] +// predicate hasNoCompatibleTarget( +// AssocFunctionCall afc, FunctionPosition selfPos, DerefChain derefChain, BorrowKind borrow +// ) { +// hasNoCompatibleNonBlanketLikeTarget(afc, selfPos, derefChain, borrow) and +// // todo: replace with ranked recursion if needed +// forall(ImplItemNode i | blanketLikeCandidate(afc, _, selfPos, i, _, _, _) | +// afc.hasIncompatibleBlanketLikeTarget(i, selfPos, derefChain, borrow) +// ) +// } +// pragma[nomagic] +// predicate hasNoCompatibleNonBlanketTarget( +// AssocFunctionCall afc, FunctionPosition selfPos, DerefChain derefChain, BorrowKind borrow +// ) { +// hasNoCompatibleNonBlanketLikeTarget(afc, selfPos, derefChain, borrow) and +// // todo: replace with ranked recursion if needed +// forall(ImplItemNode i | +// blanketLikeCandidate(afc, _, selfPos, i, _, _, _) and +// not i.isBlanketImplementation() +// | +// afc.hasIncompatibleBlanketLikeTarget(i, selfPos, derefChain, borrow) +// ) +// } +// } +// pragma[nomagic] +// private AssocFunctionDeclaration getAssocFunctionSuccessor( +// ImplOrTraitItemNode i, string name, int arity +// ) { +// result = i.getASuccessor(name) and +// arity = result.getNumberOfParamsInclSelf() +// } +// private newtype TAssocFunctionCallCand = +// MkAssocFunctionCallCand( +// AssocFunctionCall afc, FunctionPosition selfPos, DerefChain derefChain, BorrowKind borrow +// ) { +// exists(afc.getANonPseudoSelfTypeAt(selfPos, derefChain, borrow, _)) +// } +// /** A call with a dereference chain and a potential borrow at a given position. */ +// final private class AssocFunctionCallCand extends MkAssocFunctionCallCand { +// AssocFunctionCall afc_; +// FunctionPosition selfPos_; +// DerefChain derefChain; +// BorrowKind borrow; +// AssocFunctionCallCand() { this = MkAssocFunctionCallCand(afc_, selfPos_, derefChain, borrow) } +// AssocFunctionCall getAssocFunctionCall() { result = afc_ } +// ItemNode getEnclosingItemNode() { result.getADescendant() = afc_ } +// Type getTypeAt(TypePath path) { +// result = +// substituteLookupTraits(afc_, +// afc_.getANonPseudoSelfTypeAt(selfPos_, derefChain, borrow, path)) +// } +// pragma[nomagic] +// predicate hasNoCompatibleNonBlanketTarget() { +// afc_.hasNoCompatibleNonBlanketTarget(selfPos_, derefChain, borrow) +// } +// pragma[nomagic] +// predicate hasSignature( +// AssocFunctionCall afc, FunctionPosition selfPos, TypePath strippedTypePath, Type strippedType, +// string name, int arity +// ) { +// strippedType = this.getTypeAt(strippedTypePath) and +// ( +// isComplexRootStripped(strippedTypePath, strippedType) +// or +// selfPos_.isTypeQualifier() and strippedTypePath.isEmpty() +// ) and +// afc = afc_ and +// afc.hasNameAndArity(name, arity) and +// selfPos = selfPos_ +// } +// /** +// * Holds if the inherent function inside `impl` with matching name and arity can be +// * ruled out as a candidate for this call. +// */ +// pragma[nomagic] +// private predicate hasIncompatibleInherentTarget(Impl impl) { +// SelfArgIsNotInstantiationOfInherent::argIsNotInstantiationOf(this, impl, _, _) +// } +// pragma[nomagic] +// predicate hasNoInherentTargetCheck() { +// exists( +// TypePath strippedTypePath, Type strippedType, string name, int arity, +// TypeOption typeQualifier, TypeOption traitQualifier, boolean hasReceiver, +// boolean targetMustBeMethod +// | +// // Calls to inherent functions are always of the form `x.m(...)` or `Foo::bar(...)`, +// // where `Foo` is a type. In case `bar` is a method, we can use both the type qualifier +// // and the type of the first argument to rule out candidates +// selfPos_.isTypeQualifier() and targetMustBeMethod = false +// or +// selfPos_.asPosition() = 0 and targetMustBeMethod = true +// | +// afc_.hasSyntacticInfo(name, arity, typeQualifier, traitQualifier, hasReceiver) and +// (if hasReceiver = true then targetMustBeMethod = true else any()) and +// this.hasSignature(_, selfPos_, strippedTypePath, strippedType, name, arity) and +// forall(Impl i | +// i.isInherent() and +// ( +// assocFunctionInfoNonBlanketLikeCheck(_, name, arity, selfPos_, i, _, strippedTypePath, +// strippedType, typeQualifier, traitQualifier, targetMustBeMethod) +// or +// assocFunctionInfoNonBlanketLikeTypeParamCheck(_, name, arity, selfPos_, i, _, +// strippedTypePath, typeQualifier, traitQualifier, targetMustBeMethod) +// ) +// | +// this.hasIncompatibleInherentTarget(i) +// ) +// ) +// } +// /** +// * Holds if this function call has no inherent target, i.e., it does not +// * resolve to a function in an `impl` block for the type of the receiver. +// */ +// pragma[nomagic] +// predicate hasNoInherentTarget() { +// afc_.hasTrait() +// or +// afc_.hasNoInherentTarget(selfPos_, derefChain, borrow) +// } +// pragma[nomagic] +// private predicate selfArgIsInstantiationOf(ImplOrTraitItemNode i, string name, int arity) { +// SelfArgIsInstantiationOf::argIsInstantiationOf(this, i, _) and +// afc_.hasNameAndArity(name, arity) +// } +// pragma[nomagic] +// AssocFunctionDeclaration resolveCallTargetCand(ImplOrTraitItemNode i) { +// exists(string name, int arity | +// this.selfArgIsInstantiationOf(i, name, arity) and +// result = getAssocFunctionSuccessor(i, name, arity) +// ) +// } +// /** Gets the associated function targeted by this call, if any. */ +// pragma[nomagic] +// AssocFunctionDeclaration resolveCallTarget(ImplOrTraitItemNode i) { +// result = this.resolveCallTargetCand(i) and +// not FunctionOverloading::functionResolutionDependsOnArgument(i, result, _, _) +// or +// OverloadedCallArgsAreInstantiationsOf::argsAreInstantiationsOf(this, i, result) +// } +// string toString() { +// result = afc_ + " at " + selfPos_ + " [" + derefChain.toString() + "; " + borrow + "]" +// } +// Location getLocation() { result = afc_.getLocation() } +// } +// /** +// * Provides logic for resolving implicit `Deref::deref` calls. +// */ +// private module ImplicitDeref { +// private newtype TCallDerefCand = +// MkCallDerefCand(AssocFunctionCall afc, FunctionPosition selfPos, DerefChain derefChain) { +// afc.supportsAutoDerefAndBorrow() and +// afc.hasReceiverAtPos(selfPos) and +// afc.hasNoCompatibleTarget(selfPos, derefChain, TSomeBorrowKind(true)) and +// exists(afc.getSelfTypeAtNoBorrow(selfPos, derefChain, TypePath::nil())) +// } +// /** A call with a dereference chain. */ +// private class CallDerefCand extends MkCallDerefCand { +// AssocFunctionCall afc; +// FunctionPosition selfPos; +// DerefChain derefChain; +// CallDerefCand() { this = MkCallDerefCand(afc, selfPos, derefChain) } +// Type getTypeAt(TypePath path) { +// result = substituteLookupTraits(afc, afc.getSelfTypeAtNoBorrow(selfPos, derefChain, path)) and +// result != TNeverType() and +// result != TUnknownType() +// } +// string toString() { result = afc + " [" + derefChain.toString() + "]" } +// Location getLocation() { result = afc.getLocation() } +// } +// private module CallSatisfiesDerefConstraintInput implements SatisfiesTypeInputSig +// { +// pragma[nomagic] +// predicate relevantConstraint(CallDerefCand mc, Type constraint) { +// exists(mc) and +// constraint.(TraitType).getTrait() instanceof DerefTrait +// } +// } +// private module CallSatisfiesDerefConstraint = +// SatisfiesType; +// pragma[nomagic] +// private AssociatedTypeTypeParameter getDerefTargetTypeParameter() { +// result.getTypeAlias() = any(DerefTrait ft).getTargetType() +// } +// /** +// * Gets the type of the receiver of `afc` at `path` after applying the implicit +// * dereference inside `impl`, following the existing dereference chain `derefChain`. +// */ +// pragma[nomagic] +// Type getDereferencedCandidateReceiverType( +// AssocFunctionCall afc, FunctionPosition selfPos, DerefImplItemNode impl, +// DerefChain derefChain, TypePath path +// ) { +// exists(CallDerefCand cdc, TypePath exprPath | +// cdc = MkCallDerefCand(afc, selfPos, derefChain) and +// CallSatisfiesDerefConstraint::satisfiesConstraintThrough(cdc, impl, _, exprPath, result) and +// exprPath.isCons(getDerefTargetTypeParameter(), path) +// ) +// } +// } +// private module ArgSatisfiesBlanketLikeConstraintInput implements +// BlanketImplementation::SatisfiesBlanketConstraintInputSig +// { +// pragma[nomagic] +// predicate hasBlanketCandidate( +// AssocFunctionCallCand afcc, ImplItemNode impl, TypePath blanketPath, +// TypeParam blanketTypeParam +// ) { +// exists(AssocFunctionCall afc, FunctionPosition selfPos, BorrowKind borrow | +// afcc = MkAssocFunctionCallCand(afc, selfPos, _, borrow) and +// blanketLikeCandidate(afc, _, selfPos, impl, _, blanketPath, blanketTypeParam) and +// // Only apply blanket implementations when no other implementations are possible; +// // this is to account for codebases that use the (unstable) specialization feature +// // (https://rust-lang.github.io/rfcs/1210-impl-specialization.html), as well as +// // cases where our blanket implementation filtering is not precise enough. +// if impl.isBlanketImplementation() then afcc.hasNoCompatibleNonBlanketTarget() else any() +// ) +// } +// } +// private module ArgSatisfiesBlanketLikeConstraint = +// BlanketImplementation::SatisfiesBlanketConstraint; +// /** +// * A configuration for matching the type of an argument against the type of +// * a function at a function-call adjusted position relevant for dispatch +// * (such as a `self` parameter). +// */ +// private module SelfArgIsInstantiationOfInput implements +// IsInstantiationOfInputSig +// { +// pragma[nomagic] +// additional predicate potentialInstantiationOf0( +// AssocFunctionCallCand afcc, ImplOrTraitItemNode i, AssocFunctionType selfType +// ) { +// exists( +// AssocFunctionCall afc, FunctionPosition selfPos, Function f, TypePath strippedTypePath, +// Type strippedType +// | +// afcc.hasSignature(afc, selfPos, strippedTypePath, strippedType, _, _) +// | +// nonBlanketLikeCandidate(afc, f, selfPos, i, selfType, strippedTypePath, strippedType) +// or +// blanketLikeCandidate(afc, f, selfPos, i, selfType, _, _) and +// ArgSatisfiesBlanketLikeConstraint::satisfiesBlanketConstraint(afcc, i) +// ) +// } +// pragma[nomagic] +// predicate potentialInstantiationOf( +// AssocFunctionCallCand afcc, TypeAbstraction abs, AssocFunctionType constraint +// ) { +// potentialInstantiationOf0(afcc, abs, constraint) and +// if abs.(Impl).hasTrait() +// then +// // inherent functions take precedence over trait functions, so only allow +// // trait functions when there are no matching inherent functions +// afcc.hasNoInherentTarget() +// else any() +// } +// predicate relevantConstraint(AssocFunctionType constraint) { +// assocFunctionInfo(_, _, _, _, _, constraint, _, _, _, _, _) +// } +// } +// private module SelfArgIsInstantiationOf { +// import ArgIsInstantiationOf +// pragma[nomagic] +// predicate argIsNotInstantiationOf( +// AssocFunctionCall afc, ImplOrTraitItemNode i, FunctionPosition selfPos, DerefChain derefChain, +// BorrowKind borrow, AssocFunctionType selfType +// ) { +// exists(TypePath path | +// argIsNotInstantiationOf(MkAssocFunctionCallCand(afc, selfPos, derefChain, borrow), i, +// selfType, path) and +// not path.isEmpty() +// ) +// } +// pragma[nomagic] +// predicate argIsInstantiationOf( +// AssocFunctionCall afc, ImplOrTraitItemNode i, FunctionPosition selfPos, DerefChain derefChain, +// BorrowKind borrow, AssocFunctionType selfType +// ) { +// argIsInstantiationOf(MkAssocFunctionCallCand(afc, selfPos, derefChain, borrow), i, selfType) +// } +// } +// /** +// * A configuration for anti-matching the type of an argument against the type of +// * a function at a function-call adjusted position relevant for dispatch +// * (such as a `self` parameter) in a blanket (like) implementation. +// */ +// private module SelfArgIsNotInstantiationOfBlanketLikeInput implements +// IsInstantiationOfInputSig +// { +// pragma[nomagic] +// predicate potentialInstantiationOf( +// AssocFunctionCallCand afcc, TypeAbstraction abs, AssocFunctionType constraint +// ) { +// exists(AssocFunctionCall afc, FunctionPosition selfPos | +// afcc = MkAssocFunctionCallCand(afc, selfPos, _, _) and +// blanketLikeCandidate(afc, _, selfPos, abs, constraint, _, _) and +// if abs.(Impl).hasTrait() +// then +// // inherent functions take precedence over trait functions, so only allow +// // trait functions when there are no matching inherent functions +// afcc.hasNoInherentTarget() +// else any() +// ) +// } +// } +// private module SelfArgIsNotInstantiationOfBlanketLike = +// ArgIsInstantiationOf; +// /** +// * A configuration for anti-matching the type of an argument against the type of +// * a function at a function-call adjusted position relevant for dispatch (such as +// * a `self` parameter) in an inherent function. +// */ +// private module SelfArgIsNotInstantiationOfInherentInput implements +// IsInstantiationOfInputSig +// { +// pragma[nomagic] +// predicate potentialInstantiationOf( +// AssocFunctionCallCand afcc, TypeAbstraction abs, AssocFunctionType constraint +// ) { +// SelfArgIsInstantiationOfInput::potentialInstantiationOf0(afcc, abs, constraint) and +// abs.(Impl).isInherent() and +// exists(AssocFunctionCall afc, FunctionPosition selfPos | +// afcc = MkAssocFunctionCallCand(afc, selfPos, _, _) +// | +// selfPos.isTypeQualifier() or +// afc.hasReceiverAtPos(selfPos) +// ) +// } +// } +// private module SelfArgIsNotInstantiationOfInherent = +// ArgIsInstantiationOf; +// /** +// * A configuration for matching the types of positional arguments against the +// * types of parameters, when needed to disambiguate the call. +// */ +// private module OverloadedCallArgsAreInstantiationsOfInput implements +// ArgsAreInstantiationsOfInputSig +// { +// predicate toCheck(ImplOrTraitItemNode i, Function f, TypeParameter traitTp, FunctionPosition pos) { +// FunctionOverloading::functionResolutionDependsOnArgument(i, f, traitTp, pos) +// } +// class Call extends AssocFunctionCallCand { +// Type getArgType(FunctionPosition pos, TypePath path) { +// result = this.getAssocFunctionCall().getTypeAt(pos, path) +// } +// predicate hasTargetCand(ImplOrTraitItemNode i, Function f) { +// f = this.resolveCallTargetCand(i) +// } +// } +// } +// private module OverloadedCallArgsAreInstantiationsOf { +// import ArgsAreInstantiationsOf +// pragma[nomagic] +// predicate argsAreNotInstantiationsOf(AssocFunctionCall afc, ImplOrTraitItemNode i) { +// argsAreNotInstantiationsOf(MkAssocFunctionCallCand(afc, _, _, _), i, _) +// } +// } +// } +// /** +// * A matching configuration for resolving types of function call expressions +// * like `foo.bar(baz)` and `Foo::bar(baz)`. +// */ +// private module FunctionCallMatchingInput implements MatchingWithEnvironmentInputSig { +// import FunctionPositionMatchingInput +// private newtype TDeclaration = +// TFunctionDeclaration(ImplOrTraitItemNodeOption i, FunctionDeclaration f) { f.isFor(i) } +// final class Declaration extends TFunctionDeclaration { +// ImplOrTraitItemNodeOption i; +// FunctionDeclaration f; +// Declaration() { this = TFunctionDeclaration(i, f) } +// FunctionDeclaration getFunction() { result = f } +// predicate isFunction(ImplOrTraitItemNodeOption i_, Function f_) { +// i_ = i and +// f_ = f +// } +// predicate isAssocFunction(ImplOrTraitItemNode i_, Function f_) { +// i_ = i.asSome() and +// f_ = f +// } +// TypeParameter getTypeParameter(TypeParameterPosition ppos) { +// result = f.getTypeParameter(i, ppos) +// } +// Type getDeclaredType(FunctionPosition pos, TypePath path) { +// result = f.getParameterType(i, pos, path) +// or +// pos.isReturn() and +// result = f.getReturnType(i, path) +// } +// string toString() { +// i.isNone() and result = f.toString() +// or +// result = f.toStringExt(i.asSome()) +// } +// Location getLocation() { result = f.getLocation() } +// } +// pragma[nomagic] +// private TypeMention getAdditionalTypeParameterConstraint(TypeParameter tp, Declaration decl) { +// result = +// tp.(TypeParamTypeParameter) +// .getTypeParam() +// .getAdditionalTypeBound(decl.getFunction(), _) +// .getTypeRepr() +// } +// bindingset[decl] +// TypeMention getATypeParameterConstraint(TypeParameter tp, Declaration decl) { +// result = Input2::getATypeParameterConstraint(tp) and +// exists(decl) +// or +// result = getAdditionalTypeParameterConstraint(tp, decl) +// } +// class AccessEnvironment = string; +// bindingset[derefChain, borrow] +// private AccessEnvironment encodeDerefChainBorrow(DerefChain derefChain, BorrowKind borrow) { +// result = derefChain + ";" + borrow +// } +// bindingset[derefChainBorrow] +// additional predicate decodeDerefChainBorrow( +// string derefChainBorrow, DerefChain derefChain, BorrowKind borrow +// ) { +// exists(int i | +// i = derefChainBorrow.indexOf(";") and +// derefChain = derefChainBorrow.prefix(i) and +// borrow.toString() = derefChainBorrow.suffix(i + 1) +// ) +// } +// private string noDerefChainBorrow() { +// exists(DerefChain derefChain, BorrowKind borrow | +// derefChain.isEmpty() and +// borrow.isNoBorrow() and +// result = encodeDerefChainBorrow(derefChain, borrow) +// ) +// } +// final class Access = AccessImpl; +// abstract private class AccessImpl extends ContextTyping::ContextTypedCallCand { +// abstract AstNode getNodeAt(FunctionPosition pos); +// bindingset[derefChainBorrow] +// abstract Type getInferredType(string derefChainBorrow, FunctionPosition pos, TypePath path); +// abstract Declaration getTarget(string derefChainBorrow); +// /** +// * Holds if the return type of this call at `path` may have to be inferred +// * from the context. +// */ +// abstract predicate hasUnknownTypeAt(string derefChainBorrow, FunctionPosition pos, TypePath path); +// } +// private class AssocFunctionCallAccess extends AccessImpl instanceof AssocFunctionResolution::AssocFunctionCall +// { +// AssocFunctionCallAccess() { +// // handled in the `OperationMatchingInput` module +// not this instanceof Operation +// } +// pragma[nomagic] +// override Type getTypeArgument(TypeArgumentPosition apos, TypePath path) { +// result = +// this.(MethodCallExpr) +// .getGenericArgList() +// .getTypeArg(apos.asMethodTypeArgumentPosition()) +// .(TypeMention) +// .getTypeAt(path) +// or +// result = getCallExprTypeArgument(this, apos, path) +// } +// override AstNode getNodeAt(FunctionPosition pos) { +// result = AssocFunctionResolution::AssocFunctionCall.super.getNodeAt(pos) +// } +// pragma[nomagic] +// private Type getInferredSelfType(FunctionPosition pos, string derefChainBorrow, TypePath path) { +// exists(DerefChain derefChain, BorrowKind borrow | +// result = super.getSelfTypeAt(pos, derefChain, borrow, path) and +// derefChainBorrow = encodeDerefChainBorrow(derefChain, borrow) and +// super.hasReceiverAtPos(pos) +// ) +// } +// pragma[nomagic] +// private Type getInferredNonSelfType(FunctionPosition pos, TypePath path) { +// if +// // index expression `x[i]` desugars to `*x.index(i)`, so we must account for +// // the implicit deref +// pos.isReturn() and +// this instanceof IndexExpr +// then +// path.isEmpty() and +// result instanceof RefType +// or +// exists(TypePath suffix | +// result = super.getTypeAt(pos, suffix) and +// path = TypePath::cons(getRefTypeParameter(_), suffix) +// ) +// else ( +// not super.hasReceiverAtPos(pos) and +// result = super.getTypeAt(pos, path) +// ) +// } +// bindingset[derefChainBorrow] +// override Type getInferredType(string derefChainBorrow, FunctionPosition pos, TypePath path) { +// result = this.getInferredSelfType(pos, derefChainBorrow, path) +// or +// result = this.getInferredNonSelfType(pos, path) +// } +// private AssocFunctionDeclaration getTarget(ImplOrTraitItemNode i, string derefChainBorrow) { +// exists(DerefChain derefChain, BorrowKind borrow | +// derefChainBorrow = encodeDerefChainBorrow(derefChain, borrow) and +// result = super.resolveCallTarget(i, _, derefChain, borrow) // mutual recursion; resolving method calls requires resolving types and vice versa +// ) +// } +// override Declaration getTarget(string derefChainBorrow) { +// exists(ImplOrTraitItemNode i | result.isAssocFunction(i, this.getTarget(i, derefChainBorrow))) +// } +// pragma[nomagic] +// override predicate hasUnknownTypeAt(string derefChainBorrow, FunctionPosition pos, TypePath path) { +// exists(ImplOrTraitItemNode i | +// this.hasUnknownTypeAt(i, this.getTarget(i, derefChainBorrow), pos, path) +// ) +// or +// derefChainBorrow = noDerefChainBorrow() and +// forex(ImplOrTraitItemNode i, Function f | +// f = CallExprImpl::getResolvedFunction(this) and +// f = i.getAnAssocItem() +// | +// this.hasUnknownTypeAt(i, f, pos, path) +// ) +// } +// } +// private class NonAssocFunctionCallAccess extends AccessImpl instanceof NonAssocCallExpr, +// CallExprImpl::CallExprCall +// { +// pragma[nomagic] +// override Type getTypeArgument(TypeArgumentPosition apos, TypePath path) { +// result = NonAssocCallExpr.super.getTypeArgument(apos, path) +// } +// override AstNode getNodeAt(FunctionPosition pos) { +// result = NonAssocCallExpr.super.getNodeAt(pos) +// } +// pragma[nomagic] +// private Type getInferredType(FunctionPosition pos, TypePath path) { +// result = super.getInferredType(pos, path) +// } +// bindingset[derefChainBorrow] +// override Type getInferredType(string derefChainBorrow, FunctionPosition pos, TypePath path) { +// exists(derefChainBorrow) and +// result = this.getInferredType(pos, path) +// } +// pragma[nomagic] +// private Declaration getTarget() { +// result = +// TFunctionDeclaration(ImplOrTraitItemNodeOption::none_(), +// super.resolveCallTargetViaPathResolution()) +// } +// override Declaration getTarget(string derefChainBorrow) { +// result = this.getTarget() and +// derefChainBorrow = noDerefChainBorrow() +// } +// pragma[nomagic] +// override predicate hasUnknownTypeAt(string derefChainBorrow, FunctionPosition pos, TypePath path) { +// derefChainBorrow = noDerefChainBorrow() and +// exists(FunctionDeclaration f, TypeParameter tp | +// f = super.resolveCallTargetViaPathResolution() and +// pos.isReturn() and +// tp = f.getReturnType(_, path) and +// not tp = f.getParameterType(_, _, _) and +// // check that no explicit type arguments have been supplied for `tp` +// not exists(TypeArgumentPosition tapos | +// this.hasTypeArgument(tapos) and +// TTypeParamTypeParameter(tapos.asTypeParam()) = tp +// ) +// ) +// } +// } +// } +// pragma[nomagic] +// private Type inferCallArgumentTypeTopDown( +// FunctionCallMatchingInput::Access call, FunctionPosition pos, AstNode n, DerefChain derefChain, +// BorrowKind borrow, TypePath path +// ) { +// exists(string derefChainBorrow | +// FunctionCallMatchingInput::decodeDerefChainBorrow(derefChainBorrow, derefChain, borrow) and +// result = M3::inferCallArgumentTypeTopDown(call, derefChainBorrow, pos, n, path) +// ) +// } +// /** +// * Gets the type of `n` at `path` after applying `derefChain`, where `n` is the +// * `self` argument of a method call. +// * +// * The predicate recursively pops the head of `derefChain` until it becomes +// * empty, at which point the inferred type can be applied back to `n`. +// */ +// pragma[nomagic] +// private Type inferFunctionCallSelfArgumentTypeTopDown( +// FunctionCallMatchingInput::Access call, AstNode n, DerefChain derefChain, TypePath path +// ) { +// exists(FunctionPosition pos, BorrowKind borrow, TypePath path0 | +// call.(AssocFunctionResolution::AssocFunctionCall).hasReceiverAtPos(pos) and +// result = inferCallArgumentTypeTopDown(call, pos, n, derefChain, borrow, path0) +// | +// borrow.isNoBorrow() and +// path = path0 +// or +// // adjust for implicit borrow +// exists(TypePath prefix | +// prefix = TypePath::singleton(borrow.getRefType().getPositionalTypeParameter(0)) and +// path0 = prefix.appendInverse(path) +// ) +// ) +// or +// // adjust for implicit deref +// exists( +// DerefChain derefChain0, Type t0, TypePath path0, DerefImplItemNode impl, Type selfParamType, +// TypePath selfPath +// | +// t0 = inferFunctionCallSelfArgumentTypeTopDown(call, n, derefChain0, path0) and +// derefChain0.isCons(impl, derefChain) and +// selfParamType = impl.resolveSelfTypeAt(selfPath) +// | +// result = selfParamType and +// path = selfPath and +// not result instanceof TypeParameter +// or +// exists(TypePath pathToTypeParam, TypePath suffix | +// impl.targetHasTypeParameterAt(pathToTypeParam, selfParamType) and +// path0 = pathToTypeParam.appendInverse(suffix) and +// result = t0 and +// path = selfPath.append(suffix) +// ) +// ) +// } +// abstract private class Constructor extends Addressable { +// final TypeParameter getTypeParameter(TypeParameterPosition ppos) { +// typeParamMatchPosition(this.getTypeItem().getGenericParamList().getATypeParam(), result, ppos) +// } +// abstract TypeItem getTypeItem(); +// abstract TypeRepr getParameterTypeRepr(int pos); +// Type getReturnType(TypePath path) { +// result = TDataType(this.getTypeItem()) and +// path.isEmpty() +// or +// result = TTypeParamTypeParameter(this.getTypeItem().getGenericParamList().getATypeParam()) and +// path = TypePath::singleton(result) +// } +// Type getDeclaredType(FunctionPosition pos, TypePath path) { +// result = this.getParameterType(pos.asPosition(), path) +// or +// pos.isReturn() and +// result = this.getReturnType(path) +// } +// Type getParameterType(int pos, TypePath path) { +// result = this.getParameterTypeRepr(pos).(TypeMention).getTypeAt(path) +// } +// } +// private class StructConstructor extends Constructor instanceof Struct { +// override TypeItem getTypeItem() { result = this } +// override TypeRepr getParameterTypeRepr(int i) { +// result = [super.getTupleField(i).getTypeRepr(), super.getNthStructField(i).getTypeRepr()] +// } +// } +// private class VariantConstructor extends Constructor instanceof Variant { +// override TypeItem getTypeItem() { result = super.getEnum() } +// override TypeRepr getParameterTypeRepr(int i) { +// result = [super.getTupleField(i).getTypeRepr(), super.getNthStructField(i).getTypeRepr()] +// } +// } +// /** +// * A matching configuration for resolving types of constructions of enums and +// * structs, such as `Result::Ok(42)`, `Foo { bar: 1 }` and `None`. +// */ +// private module ConstructionMatchingInput implements MatchingInputSig { +// import FunctionPositionMatchingInput +// class Declaration = Constructor; +// abstract class Access extends AstNode { +// abstract Type getInferredType(FunctionPosition pos, TypePath path); +// abstract Declaration getTarget(); +// abstract AstNode getNodeAt(AccessPosition apos); +// abstract Type getTypeArgument(TypeArgumentPosition apos, TypePath path); +// /** +// * Holds if the return type of this construction expression at `path` may +// * have to be inferred from the context. For example in `Result::Ok(42)` the +// * error type has to be inferred from the context. +// */ +// pragma[nomagic] +// predicate hasUnknownTypeAt(FunctionPosition pos, TypePath path) { +// exists(Declaration d, TypeParameter tp | +// d = this.getTarget() and +// pos.isReturn() and +// tp = d.getReturnType(path) and +// not exists(FunctionPosition pos2 | not pos2.isReturn() and tp = d.getDeclaredType(pos2, _)) and +// // check that no explicit type arguments have been supplied for `tp` +// not exists(TypeArgumentPosition tapos | +// exists(this.getTypeArgument(tapos, _)) and +// TTypeParamTypeParameter(tapos.asTypeParam()) = tp +// ) +// ) +// } +// } +// private class NonAssocCallAccess extends Access, NonAssocCallExpr, +// ContextTyping::ContextTypedCallCand +// { +// NonAssocCallAccess() { +// this instanceof CallExprImpl::TupleStructExpr or +// this instanceof CallExprImpl::TupleVariantExpr +// } +// override Type getTypeArgument(TypeArgumentPosition apos, TypePath path) { +// result = NonAssocCallExpr.super.getTypeArgument(apos, path) +// } +// override AstNode getNodeAt(AccessPosition apos) { +// result = NonAssocCallExpr.super.getNodeAt(apos) +// } +// override Type getInferredType(FunctionPosition pos, TypePath path) { +// result = NonAssocCallExpr.super.getInferredType(pos, path) +// } +// override Declaration getTarget() { result = this.resolveCallTargetViaPathResolution() } +// } +// abstract private class StructAccess extends Access instanceof PathAstNode { +// pragma[nomagic] +// override Type getInferredType(AccessPosition apos, TypePath path) { +// result = inferType(this.getNodeAt(apos), path) +// } +// pragma[nomagic] +// override Declaration getTarget() { result = resolvePath(super.getPath()) } +// pragma[nomagic] +// override Type getTypeArgument(TypeArgumentPosition apos, TypePath path) { +// // Handle constructions that use `Self {...}` syntax +// exists(TypeMention tm, TypePath path0 | +// tm = super.getPath() and +// result = tm.getTypeAt(path0) and +// path0.isCons(TTypeParamTypeParameter(apos.asTypeParam()), path) +// ) +// } +// } +// private class StructExprAccess extends StructAccess, StructExpr { +// override Type getTypeArgument(TypeArgumentPosition apos, TypePath path) { +// result = super.getTypeArgument(apos, path) +// or +// exists(TypePath suffix | +// suffix.isCons(TTypeParamTypeParameter(apos.asTypeParam()), path) and +// result = inferTypeCertain(this, suffix) +// ) +// } +// override AstNode getNodeAt(AccessPosition apos) { +// result = +// this.getFieldExpr(pragma[only_bind_into](this.getNthStructField(apos.asPosition()) +// .getName() +// .getText())).getExpr() +// or +// result = this and apos.isReturn() +// } +// } +// /** A potential nullary struct/variant construction such as `None`. */ +// private class PathExprAccess extends StructAccess, PathExpr { +// PathExprAccess() { not exists(CallExpr ce | this = ce.getFunction()) } +// override AstNode getNodeAt(AccessPosition apos) { result = this and apos.isReturn() } +// } +// } +// private module ConstructionMatching = Matching; +// pragma[nomagic] +// private Type inferConstructionType(AstNode n, FunctionPosition pos, TypePath path) { +// exists(ConstructionMatchingInput::Access a | +// n = a.getNodeAt(pos) and +// result = ConstructionMatching::inferAccessType(a, pos, path) +// ) +// } +// pragma[nomagic] +// private Type inferUnknownType(AstNode n, TypePath path) { +// result = TUnknownType() and +// ( +// exists(FunctionCallMatchingInput::Access call, FunctionPosition pos | +// n = call.getNodeAt(pos) and +// call.hasUnknownTypeAt(_, pos, path) +// ) +// or +// exists(ConstructionMatchingInput::Access a, FunctionPosition pos | +// n = a.getNodeAt(pos) and +// a.hasUnknownTypeAt(pos, path) +// ) +// or +// exists(Param p | +// not p.hasTypeRepr() and +// n = p.getPat() and +// path.isEmpty() +// ) +// ) +// } +// /** +// * A matching configuration for resolving types of operations like `a + b`. +// */ +// private module OperationMatchingInput implements MatchingInputSig { +// private import codeql.rust.elements.internal.OperationImpl::Impl as OperationImpl +// import FunctionPositionMatchingInput +// class Declaration extends FunctionCallMatchingInput::Declaration { +// private Method getSelfOrImpl() { +// result = f +// or +// f.implements(result) +// } +// pragma[nomagic] +// private predicate borrowsAt(FunctionPosition pos) { +// exists(TraitItemNode t, string path, string method | +// this.getSelfOrImpl() = t.getAssocItem(method) and +// path = t.getCanonicalPath(_) and +// exists(int borrows | OperationImpl::isOverloaded(_, _, path, method, borrows) | +// pos.asPosition() = 0 and borrows >= 1 +// or +// pos.asPosition() = 1 and +// borrows >= 2 +// ) +// ) +// } +// pragma[nomagic] +// private predicate derefsReturn() { this.getSelfOrImpl() = any(DerefTrait t).getDerefFunction() } +// Type getDeclaredType(FunctionPosition pos, TypePath path) { +// exists(TypePath path0 | +// result = super.getDeclaredType(pos, path0) and +// if +// this.borrowsAt(pos) +// or +// pos.isReturn() and this.derefsReturn() +// then path0.isCons(getRefTypeParameter(_), path) +// else path0 = path +// ) +// } +// } +// class Access extends AssocFunctionResolution::OperationAssocFunctionCall { +// Type getTypeArgument(TypeArgumentPosition apos, TypePath path) { none() } +// pragma[nomagic] +// Type getInferredType(FunctionPosition pos, TypePath path) { +// result = inferType(this.getNodeAt(pos), path) +// } +// Declaration getTarget() { +// exists(ImplOrTraitItemNode i | +// result.isAssocFunction(i, this.resolveCallTarget(i, _, _, _)) // mutual recursion +// ) +// } +// } +// } +// private module OperationMatching = Matching; +// pragma[nomagic] +// private Type inferOperationType(AstNode n, FunctionPosition pos, TypePath path) { +// exists(OperationMatchingInput::Access a | +// n = a.getNodeAt(pos) and +// result = OperationMatching::inferAccessType(a, pos, path) and +// if pos.asPosition() = 0 then not path.isEmpty() else any() +// ) +// } +// pragma[nomagic] +// private Type getFieldExprLookupType(FieldExpr fe, string name, DerefChain derefChain) { +// exists(TypePath path | +// result = inferType(fe.getContainer(), path) and +// name = fe.getIdentifier().getText() and +// isComplexRootStripped(path, result) +// | +// // TODO: Support full derefence chains as for method calls +// path.isEmpty() and +// derefChain = DerefChain::nil() +// or +// exists(DerefImplItemNode impl, TypeParamTypeParameter tp | +// tp = impl.getFirstSelfTypeParameter() and +// path.getHead() = tp and +// derefChain = DerefChain::singleton(impl) +// ) +// ) +// } +// /** +// * Gets the struct field that the field expression `fe` resolves to, if any. +// */ +// cached +// StructField resolveStructFieldExpr(FieldExpr fe, DerefChain derefChain) { +// M3::CachedStage::ref() and +// exists(string name, DataType ty | +// ty = getFieldExprLookupType(fe, pragma[only_bind_into](name), derefChain) +// | +// result = ty.(StructType).getTypeItem().getStructField(pragma[only_bind_into](name)) or +// result = ty.(UnionType).getTypeItem().getStructField(pragma[only_bind_into](name)) +// ) +// } +// pragma[nomagic] +// private Type getTupleFieldExprLookupType(FieldExpr fe, int pos, DerefChain derefChain) { +// exists(string name | +// result = getFieldExprLookupType(fe, name, derefChain) and +// pos = name.toInt() +// ) +// } +// /** +// * Gets the tuple field that the field expression `fe` resolves to, if any. +// */ +// cached +// TupleField resolveTupleFieldExpr(FieldExpr fe, DerefChain derefChain) { +// M3::CachedStage::ref() and +// exists(int i | +// result = +// getTupleFieldExprLookupType(fe, pragma[only_bind_into](i), derefChain) +// .(StructType) +// .getTypeItem() +// .getTupleField(pragma[only_bind_into](i)) +// ) +// } +// /** +// * A matching configuration for resolving types of field expressions like `x.field`. +// */ +// private module FieldExprMatchingInput implements MatchingInputSig { +// private newtype TDeclarationPosition = +// TSelfDeclarationPosition() or +// TFieldPos() +// class DeclarationPosition extends TDeclarationPosition { +// predicate isSelf() { this = TSelfDeclarationPosition() } +// predicate isField() { this = TFieldPos() } +// string toString() { +// this.isSelf() and +// result = "self" +// or +// this.isField() and +// result = "(field)" +// } +// } +// private newtype TDeclaration = +// TStructFieldDecl(StructField sf) or +// TTupleFieldDecl(TupleField tf) +// abstract class Declaration extends TDeclaration { +// TypeParameter getTypeParameter(TypeParameterPosition ppos) { none() } +// abstract Type getDeclaredType(DeclarationPosition dpos, TypePath path); +// abstract string toString(); +// abstract Location getLocation(); +// } +// abstract private class StructOrTupleFieldDecl extends Declaration { +// abstract AstNode getAstNode(); +// abstract TypeRepr getTypeRepr(); +// override Type getDeclaredType(DeclarationPosition dpos, TypePath path) { +// dpos.isSelf() and +// // no case for variants as those can only be destructured using pattern matching +// exists(Struct s | this.getAstNode() = [s.getStructField(_).(AstNode), s.getTupleField(_)] | +// result = TDataType(s) and +// path.isEmpty() +// or +// result = TTypeParamTypeParameter(s.getGenericParamList().getATypeParam()) and +// path = TypePath::singleton(result) +// ) +// or +// dpos.isField() and +// result = this.getTypeRepr().(TypeMention).getTypeAt(path) +// } +// override string toString() { result = this.getAstNode().toString() } +// override Location getLocation() { result = this.getAstNode().getLocation() } +// } +// private class StructFieldDecl extends StructOrTupleFieldDecl, TStructFieldDecl { +// private StructField sf; +// StructFieldDecl() { this = TStructFieldDecl(sf) } +// override AstNode getAstNode() { result = sf } +// override TypeRepr getTypeRepr() { result = sf.getTypeRepr() } +// } +// private class TupleFieldDecl extends StructOrTupleFieldDecl, TTupleFieldDecl { +// private TupleField tf; +// TupleFieldDecl() { this = TTupleFieldDecl(tf) } +// override AstNode getAstNode() { result = tf } +// override TypeRepr getTypeRepr() { result = tf.getTypeRepr() } +// } +// class AccessPosition = DeclarationPosition; +// class Access extends FieldExpr { +// Type getTypeArgument(TypeArgumentPosition apos, TypePath path) { none() } +// AstNode getNodeAt(AccessPosition apos) { +// result = this.getContainer() and +// apos.isSelf() +// or +// result = this and +// apos.isField() +// } +// Type getInferredType(AccessPosition apos, TypePath path) { +// exists(TypePath path0 | result = inferType(this.getNodeAt(apos), path0) | +// if apos.isSelf() +// then +// // adjust for implicit deref +// path0.isCons(getRefTypeParameter(_), path) +// or +// not path0.isCons(getRefTypeParameter(_), _) and +// not (result instanceof RefType and path0.isEmpty()) and +// path = path0 +// else path = path0 +// ) +// } +// Declaration getTarget() { +// // mutual recursion; resolving fields requires resolving types and vice versa +// result = +// [ +// TStructFieldDecl(resolveStructFieldExpr(this, _)).(TDeclaration), +// TTupleFieldDecl(resolveTupleFieldExpr(this, _)) +// ] +// } +// } +// predicate accessDeclarationPositionMatch(AccessPosition apos, DeclarationPosition dpos) { +// apos = dpos +// } +// } +// private module FieldExprMatching = Matching; +// /** +// * Gets the type of `n` at `path`, where `n` is either a field expression or +// * the receiver of field expression call. +// */ +// pragma[nomagic] +// private Type inferFieldExprType(AstNode n, TypePath path) { +// exists( +// FieldExprMatchingInput::Access a, FieldExprMatchingInput::AccessPosition apos, TypePath path0 +// | +// n = a.getNodeAt(apos) and +// result = FieldExprMatching::inferAccessType(a, apos, path0) +// | +// if apos.isSelf() +// then +// exists(Type receiverType | receiverType = inferType(n) | +// if receiverType instanceof RefType +// then +// // adjust for implicit deref +// not path0.isCons(getRefTypeParameter(_), _) and +// not (path0.isEmpty() and result instanceof RefType) and +// path = TypePath::cons(getRefTypeParameter(_), path0) +// else path = path0 +// ) +// else path = path0 +// ) +// } +// /** Gets the root type of the reference expression `ref`. */ +// pragma[nomagic] +// private Type inferRefExprType(RefExpr ref) { +// if ref.isRaw() +// then +// ref.isMut() and result instanceof PtrMutType +// or +// ref.isConst() and result instanceof PtrConstType +// else +// if ref.isMut() +// then result instanceof RefMutType +// else result instanceof RefSharedType +// } +// /** Gets the root type of the reference node `ref`. */ +// pragma[nomagic] +// private Type inferRefPatType(AstNode ref) { +// exists(boolean isMut | +// ref = +// any(IdentPat ip | +// ip.isRef() and +// if ip.isMut() then isMut = true else isMut = false +// ).getName() +// or +// ref = any(RefPat rp | if rp.isMut() then isMut = true else isMut = false) +// | +// result = getRefType(isMut) +// ) +// } +// pragma[nomagic] +// private Type inferTryExprType(TryExpr te, TypePath path) { +// exists(TypeParam tp, TypePath path0 | +// result = inferType(te.getExpr(), path0) and +// path0.isCons(TTypeParamTypeParameter(tp), path) +// | +// tp = any(ResultEnum r).getGenericParamList().getGenericParam(0) +// or +// tp = any(OptionEnum o).getGenericParamList().getGenericParam(0) +// ) +// } +// pragma[nomagic] +// private StructType getStrStruct() { result = TDataType(any(Builtins::Str s)) } +// pragma[nomagic] +// private Type inferLiteralType(LiteralExpr le, TypePath path, boolean certain) { +// path.isEmpty() and +// exists(Builtins::BuiltinType t | result = TDataType(t) | +// le instanceof CharLiteralExpr and +// t instanceof Builtins::Char and +// certain = true +// or +// le = +// any(NumberLiteralExpr ne | +// t.getName() = ne.getSuffix() and +// certain = true +// or +// // When a number literal has no suffix, the type may depend on the context. +// // For simplicity, we assume either `i32` or `f64`. +// not exists(ne.getSuffix()) and +// certain = false and +// ( +// ne instanceof IntegerLiteralExpr and +// t instanceof Builtins::I32 +// or +// ne instanceof FloatLiteralExpr and +// t instanceof Builtins::F64 +// ) +// ) +// or +// le instanceof BooleanLiteralExpr and +// t instanceof Builtins::Bool and +// certain = true +// ) +// or +// le instanceof StringLiteralExpr and +// ( +// path.isEmpty() and result instanceof RefSharedType +// or +// path = TypePath::singleton(getRefTypeParameter(false)) and +// result = getStrStruct() +// ) and +// certain = true +// } +// pragma[nomagic] +// private DynTraitType getFutureTraitType() { result.getTrait() instanceof FutureTrait } +// pragma[nomagic] +// private AssociatedTypeTypeParameter getFutureOutputTypeParameter() { +// result = getAssociatedTypeTypeParameter(any(FutureTrait ft).getOutputType()) +// } +// pragma[nomagic] +// private DynTraitTypeParameter getDynFutureOutputTypeParameter() { +// result.getTraitTypeParameter() = getFutureOutputTypeParameter() +// } +// pragma[nomagic] +// predicate isUnitBlockExpr(BlockExpr be) { +// not be.getStmtList().hasTailExpr() and +// not be = any(Callable c).getBody() and +// not be.hasLabel() +// } +// pragma[nomagic] +// private Type inferBlockExprType(BlockExpr be, TypePath path) { +// // `typeEquality` handles the non-root case +// if be instanceof AsyncBlockExpr +// then ( +// path.isEmpty() and +// result = getFutureTraitType() +// or +// isUnitBlockExpr(be) and +// path = TypePath::singleton(getDynFutureOutputTypeParameter()) and +// result instanceof UnitType +// ) else ( +// isUnitBlockExpr(be) and +// path.isEmpty() and +// result instanceof UnitType +// ) +// } +// pragma[nomagic] +// private predicate exprHasUnitType(Expr e) { +// e = any(IfExpr ie | not ie.hasElse()) +// or +// e instanceof WhileExpr +// or +// e instanceof ForExpr +// } +// final private class AwaitTarget extends Expr { +// AwaitTarget() { this = any(AwaitExpr ae).getExpr() } +// Type getTypeAt(TypePath path) { result = inferType(this, path) } +// } +// private module AwaitSatisfiesTypeInput implements SatisfiesTypeInputSig { +// pragma[nomagic] +// predicate relevantConstraint(AwaitTarget term, Type constraint) { +// exists(term) and +// constraint.(TraitType).getTrait() instanceof FutureTrait +// } +// } +// private module AwaitSatisfiesType = SatisfiesType; +// pragma[nomagic] +// private Type inferAwaitExprType(AstNode n, TypePath path) { +// exists(TypePath exprPath | +// AwaitSatisfiesType::satisfiesConstraint(n.(AwaitExpr).getExpr(), _, exprPath, result) and +// exprPath.isCons(getFutureOutputTypeParameter(), path) +// ) +// } +// /** +// * Gets the root type of the array expression `ae`. +// */ +// pragma[nomagic] +// private Type inferArrayExprType(ArrayExpr ae) { exists(ae) and result instanceof ArrayType } +// /** +// * Gets the root type of the range expression `re`. +// */ +// pragma[nomagic] +// private Type inferRangeExprType(RangeExpr re) { result = TDataType(getRangeType(re)) } +// pragma[nomagic] +// private Type getInferredDerefType(DerefExpr de, TypePath path) { result = inferType(de, path) } +// pragma[nomagic] +// private PtrType getInferredDerefExprPtrType(DerefExpr de) { result = inferType(de.getExpr()) } +// /** +// * Gets the inferred type of `n` at `path` when `n` occurs in a dereference +// * expression `*n` and when `n` is known to have a raw pointer type. +// * +// * The other direction is handled in `typeEqualityAsymmetric`. +// */ +// private Type inferDereferencedExprPtrType(AstNode n, TypePath path) { +// exists(DerefExpr de, PtrType type, TypePath suffix | +// de.getExpr() = n and +// type = getInferredDerefExprPtrType(de) and +// result = getInferredDerefType(de, suffix) and +// path = TypePath::cons(type.getPositionalTypeParameter(0), suffix) +// ) +// } +// /** +// * A matching configuration for resolving types of deconstruction patterns like +// * `let Foo { bar } = ...` or `let Some(x) = ...`. +// */ +// private module DeconstructionPatMatchingInput implements MatchingInputSig { +// import FunctionPositionMatchingInput +// class Declaration = ConstructionMatchingInput::Declaration; +// class Access extends Pat instanceof PathAstNode { +// Access() { this instanceof TupleStructPat or this instanceof StructPat } +// Type getTypeArgument(TypeArgumentPosition apos, TypePath path) { none() } +// AstNode getNodeAt(AccessPosition apos) { +// this = +// any(StructPat sp | +// result = +// sp.getPatField(pragma[only_bind_into](sp.getNthStructField(apos.asPosition()) +// .getName() +// .getText())).getPat() +// ) +// or +// result = this.(TupleStructPat).getField(apos.asPosition()) +// or +// result = this and +// apos.isReturn() +// } +// Type getInferredType(AccessPosition apos, TypePath path) { +// result = inferType(this.getNodeAt(apos), path) +// or +// // The struct/enum type is supplied explicitly as a type qualifier, e.g. +// // `let Foo::::Variant { ... } = ...` or +// // `let Option::::Some(x) = ...`. +// apos.isReturn() and +// result = super.getPath().(TypeMention).getTypeAt(path) +// } +// Declaration getTarget() { result = resolvePath(super.getPath()) } +// } +// } +// private module DeconstructionPatMatching = Matching; +// /** +// * Gets the type of `n` at `path`, where `n` is a pattern for a constructor, +// * either a struct pattern or a tuple-struct pattern. +// */ +// pragma[nomagic] +// private Type inferDeconstructionPatType(AstNode n, TypePath path) { +// exists(DeconstructionPatMatchingInput::Access a, FunctionPosition apos | +// n = a.getNodeAt(apos) and +// result = DeconstructionPatMatching::inferAccessType(a, apos, path) +// ) +// } +// final private class ForIterableExpr extends Expr { +// ForIterableExpr() { this = any(ForExpr fe).getIterable() } +// Type getTypeAt(TypePath path) { result = inferType(this, path) } +// } +// private module ForIterableSatisfiesTypeInput implements SatisfiesTypeInputSig { +// predicate relevantConstraint(ForIterableExpr term, Type constraint) { +// exists(term) and +// exists(Trait t | t = constraint.(TraitType).getTrait() | +// // TODO: Remove the line below once we can handle the `impl IntoIterator for I` implementation +// t instanceof IteratorTrait or +// t instanceof IntoIteratorTrait +// ) +// } +// } +// pragma[nomagic] +// private AssociatedTypeTypeParameter getIteratorItemTypeParameter() { +// result = getAssociatedTypeTypeParameter(any(IteratorTrait t).getItemType()) +// } +// pragma[nomagic] +// private AssociatedTypeTypeParameter getIntoIteratorItemTypeParameter() { +// result = getAssociatedTypeTypeParameter(any(IntoIteratorTrait t).getItemType()) +// } +// private module ForIterableSatisfiesType = +// SatisfiesType; +// 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 +// ForIterableSatisfiesType::satisfiesConstraint(fe.getIterable(), _, exprPath, result) and +// exprPath.isCons(tp, path) +// | +// tp = getIntoIteratorItemTypeParameter() +// or +// // TODO: Remove once we can handle the `impl IntoIterator for I` implementation +// tp = getIteratorItemTypeParameter() and +// inferType(fe.getIterable()) != getArrayTypeParameter() +// ) +// } +// pragma[nomagic] +// private Type inferClosureExprBodyTypeTopDown(AstNode n, TypePath path) { +// exists(ClosureExpr ce | +// n = ce.getClosureBody() and +// result = inferType(ce, closureReturnPath().appendInverse(path)) +// ) +// } +// pragma[nomagic] +// private Type inferClosureExprType(ClosureExpr ce, TypePath path) { +// path = TypePath::singleton(TDynTraitTypeParameter(_, any(FnTrait t).getTypeParam())) and +// result.(TupleType).getArity() = ce.getNumberOfParams() +// or +// exists(TypePath suffix | +// result = ce.getRetType().getTypeRepr().(TypeMention).getTypeAt(suffix) and +// path = closureReturnPath().append(suffix) +// ) +// } +// pragma[nomagic] +// private TupleType inferArgList(ArgList args, TypePath path) { +// exists(CallExprImpl::DynamicCallExpr dce | +// args = dce.getArgList() and +// result.getArity() = dce.getNumberOfSyntacticArguments() and +// path.isEmpty() +// ) +// } +// pragma[nomagic] +// private Type inferCastExprType(CastExpr ce, TypePath path) { +// result = ce.getTypeRepr().(TypeMention).getTypeAt(path) +// } +// /** Holds if `n` is implicitly dereferenced and/or borrowed. */ +// cached +// predicate implicitDerefChainBorrow(Expr e, DerefChain derefChain, boolean borrow) { +// M3::CachedStage::ref() and +// exists(BorrowKind bk | +// any(AssocFunctionResolution::AssocFunctionCall afc) +// .argumentHasImplicitDerefChainBorrow(e, derefChain, bk) and +// if bk.isNoBorrow() then borrow = false else borrow = true +// ) +// or +// e = +// any(FieldExpr fe | +// exists(resolveStructFieldExpr(fe, derefChain)) +// or +// exists(resolveTupleFieldExpr(fe, derefChain)) +// ).getContainer() and +// not derefChain.isEmpty() and +// borrow = false +// } +// /** +// * Gets an item (function or tuple struct/variant) that `call` resolves to, if +// * any. +// * +// * The parameter `dispatch` is `true` if and only if the resolved target is a +// * trait item because a precise target could not be determined from the +// * types (for instance in the presence of generics or `dyn` types) +// */ +// cached +// Addressable resolveCallTarget(InvocationExpr call, boolean dispatch) { +// M3::CachedStage::ref() and +// dispatch = false and +// result = call.(NonAssocCallExpr).resolveCallTargetViaPathResolution() +// or +// exists(ImplOrTraitItemNode i | +// i instanceof TraitItemNode and dispatch = true +// or +// i instanceof ImplItemNode and dispatch = false +// | +// result = call.(AssocFunctionResolution::AssocFunctionCall).resolveCallTarget(i, _, _, _) and +// not call instanceof CallExprImpl::DynamicCallExpr and +// not i instanceof Builtins::BuiltinImpl +// ) +// } +// /** Provides predicates for debugging the type inference implementation. */ +// private module Debug { +// Locatable getRelevantLocatable() { +// exists(string filepath, int startline, int startcolumn, int endline, int endcolumn | +// result.getLocation().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) and +// filepath.matches("%/main.rs") and +// startline = 103 +// ) +// } +// Type debugInferType(AstNode n, TypePath path) { +// n = getRelevantLocatable() and +// result = inferType(n, path) +// } +// Addressable debugResolveCallTarget(InvocationExpr c, boolean dispatch) { +// c = getRelevantLocatable() and +// result = resolveCallTarget(c, dispatch) +// } +// predicate debugConditionSatisfiesConstraint( +// TypeAbstraction abs, TypeMention condition, TypeMention constraint, boolean transitive +// ) { +// abs = getRelevantLocatable() and +// Input2::conditionSatisfiesConstraint(abs, condition, constraint, transitive) +// } +// predicate debugInferShorthandSelfType(ShorthandSelfParameterMention self, TypePath path, Type t) { +// self = getRelevantLocatable() and +// t = self.getTypeAt(path) +// } +// predicate debugTypeMention(TypeMention tm, TypePath path, Type type) { +// tm = getRelevantLocatable() and +// tm.getTypeAt(path) = type +// } +// pragma[nomagic] +// private int countTypesAtPath(AstNode n, TypePath path, Type t) { +// t = inferType(n, path) and +// result = strictcount(Type t0 | t0 = inferType(n, path)) +// } +// pragma[nomagic] +// private predicate atLimit(AstNode n) { +// exists(TypePath path0 | exists(inferType(n, path0)) and path0.length() >= getTypePathLimit()) +// } +// Type debugInferTypeForNodeAtLimit(AstNode n, TypePath path) { +// result = inferType(n, path) and +// atLimit(n) +// } +// predicate countTypesForNodeAtLimit(AstNode n, int c) { +// n = getRelevantLocatable() and +// c = strictcount(Type t, TypePath path | t = debugInferTypeForNodeAtLimit(n, path)) +// } +// predicate maxTypes(AstNode n, TypePath path, Type t, int c) { +// c = countTypesAtPath(n, path, t) and +// c = max(countTypesAtPath(_, _, _)) +// } +// pragma[nomagic] +// private predicate typePathLength(AstNode n, TypePath path, Type t, int len) { +// t = inferType(n, path) and +// len = path.length() +// } +// predicate maxTypePath(AstNode n, TypePath path, Type t, int len) { +// typePathLength(n, path, t, len) and +// len = max(int i | typePathLength(_, _, _, i)) +// } +// pragma[nomagic] +// private int countTypePaths(AstNode n, TypePath path, Type t) { +// t = inferType(n, path) and +// result = strictcount(TypePath path0, Type t0 | t0 = inferType(n, path0)) +// } +// predicate maxTypePaths(AstNode n, TypePath path, Type t, int c) { +// c = countTypePaths(n, path, t) and +// c = max(countTypePaths(_, _, _)) +// } +// Type debugInferTypeCertain(AstNode n, TypePath path) { +// n = getRelevantLocatable() and +// result = inferTypeCertain(n, path) +// } +// Type debugInferCertainNonUniqueType(AstNode n, TypePath path) { +// n = getRelevantLocatable() and +// Consistency::nonUniqueCertainType(n, path, result) +// } +// } diff --git a/swift/ql/lib/codeql/swift/typeinference/TypeInferenceConsistency.qll b/swift/ql/lib/codeql/swift/typeinference/TypeInferenceConsistency.qll new file mode 100644 index 000000000000..96e0bea2f189 --- /dev/null +++ b/swift/ql/lib/codeql/swift/typeinference/TypeInferenceConsistency.qll @@ -0,0 +1,53 @@ +/** + * Provides classes for recognizing type inference inconsistencies. + */ + +private import rust +private import Type +private import TypeMention +private import TypeInference +private import TypeInference::Consistency as Consistency +import TypeInference::Consistency + +query predicate illFormedTypeMention(TypeMention tm) { + // NOTE: We do not use `illFormedTypeMention` from the shared library as it is + // instantiated with `PreTypeMention` and we are interested in inconsistencies + // for `TypeMention`. + not exists(tm.getTypeAt(TypePath::nil())) and + exists(tm.getLocation()) and + // avoid overlap with `PathTypeMention` + not tm instanceof PathTypeReprMention and + // known limitation for type mentions that would mention an escaping type parameter + not tm = + any(PathTypeMention ptm | + exists(ptm.resolvePathTypeAt(TypePath::nil())) and + not exists(ptm.getType()) + or + ptm.(NonAliasPathTypeMention).getResolved() instanceof TypeAlias + ) and + // Only include inconsistencies in the source, as we otherwise get + // inconsistencies from library code in every project. + tm.fromSource() +} + +query predicate nonUniqueCertainType(AstNode n, TypePath path) { + Consistency::nonUniqueCertainType(n, path, _) and + n.fromSource() // Only include inconsistencies in the source. +} + +int getTypeInferenceInconsistencyCounts(string type) { + type = "Missing type parameter ID" and + result = count(TypeParameter tp | missingTypeParameterId(tp) | tp) + or + type = "Non-functional type parameter ID" and + result = count(TypeParameter tp | nonFunctionalTypeParameterId(tp) | tp) + or + type = "Non-injective type parameter ID" and + result = count(TypeParameter tp | nonInjectiveTypeParameterId(tp, _) | tp) + or + type = "Ill-formed type mention" and + result = count(TypeMention tm | illFormedTypeMention(tm) | tm) + or + type = "Non-unique certain type information" and + result = count(AstNode n, TypePath path | nonUniqueCertainType(n, path) | n) +} diff --git a/swift/ql/lib/codeql/swift/typeinference/TypeMention.qll b/swift/ql/lib/codeql/swift/typeinference/TypeMention.qll new file mode 100644 index 000000000000..06b29cae23da --- /dev/null +++ b/swift/ql/lib/codeql/swift/typeinference/TypeMention.qll @@ -0,0 +1,138 @@ +/** Provides classes for representing type mentions, used in type inference. */ + +private import swift as Swift +private import Type +private import TypeAbstraction +private import TypeInference + +private Type getTypeAt(Swift::Type t, TypePath path) { + exists(Swift::Type u | u = t.getUnderlyingType() | + path = "" and + ( + result.(TypeDeclType).getDecl() = u.(AnyGenericType).getDeclaration() + or + result.(TypeDeclType).getDecl().getDeclaredInterfaceType() = + u.(PrimaryArchetypeType).getInterfaceType() + or + result.(TypeDeclType).getDecl().(GenericTypeParamDecl).getDeclaredInterfaceType() = u + or + result = TTupleType(u.(Swift::TupleType).getNumberOfTypes()) + ) + or + exists(BoundGenericType b, GenericTypeDecl decl | + b = u and + decl = b.getDeclaration() + | + exists(TypePath suffix, int i, GenericTypeParamDeclTypeParameter tp | + result = getTypeAt(b.getArgType(i), suffix) and + tp.getDecl() = decl.getGenericTypeParam(i) and + path = TypePath::singleton(tp).append(suffix) + ) + ) + or + exists(Swift::TupleType tt | tt = u | + exists(TypePath suffix, int i, TupleTypeTypeParameter tp | + result = getTypeAt(tt.getType(i), suffix) and + tp = TTupleTypeTypeParameter(tt.getNumberOfTypes(), i) and + path = TypePath::singleton(tp).append(suffix) + ) + ) + ) +} + +newtype TTypeMention = + TGenericContextMention(GenericContext context) or + TTypeDeclBaseTypeMention(TypeDecl decl, int i) { exists(decl.getInheritedType(i)) } or + TParamDeclTypeMention(ParamDecl decl) or + TCallableReturnTypeMention(Callable c) + +/** An AST node that may mention a type. */ +abstract private class TypeMentionImpl extends TTypeMention { + /** Gets the type at `path` that this type mention resolves to, if any. */ + pragma[nomagic] + abstract Type getTypeAt(TypePath path); + + /** Gets the root type that this type mention resolves to, if any. */ + pragma[nomagic] + final Type getType() { result = this.getTypeAt(TypePath::nil()) } + + abstract string toString(); + + abstract Location getLocation(); +} + +final class TypeMention = TypeMentionImpl; + +class GenericContextMention extends TypeMentionImpl, TGenericContextMention { + GenericContext context; + + GenericContextMention() { this = TGenericContextMention(context) } + + GenericContext getContext() { result = context } + + override Type getTypeAt(TypePath path) { + path.isEmpty() and + result = TTypeDeclType(context) + or + exists(TypeDeclType t | + context = t.getDecl() and + result = t.getATypeParameter() and + path = TypePath::singleton(result) + ) + } + + override string toString() { result = context.toString() } + + override Location getLocation() { result = context.(Locatable).getLocation() } +} + +class TypeDeclBaseTypeMention extends TypeMentionImpl, TTypeDeclBaseTypeMention { + TypeDecl decl; + int index; + + TypeDeclBaseTypeMention() { this = TTypeDeclBaseTypeMention(decl, index) } + + TypeDecl getDecl() { result = decl } + + int getIndex() { result = index } + + override Type getTypeAt(TypePath path) { result = getTypeAt(decl.getInheritedType(index), path) } + + override string toString() { result = decl.getName() + " [base type " + index + "]" } + + override Location getLocation() { result = decl.getLocation() } +} + +class ParamDeclTypeMention extends TypeMentionImpl, TParamDeclTypeMention { + ParamDecl decl; + + ParamDeclTypeMention() { this = TParamDeclTypeMention(decl) } + + ParamDecl getDecl() { result = decl } + + override Type getTypeAt(TypePath path) { result = getTypeAt(decl.getType(), path) } + + override string toString() { result = decl.toString() + " [parameter type]" } + + override Location getLocation() { result = decl.getLocation() } +} + +class CallableReturnTypeMention extends TypeMentionImpl, TCallableReturnTypeMention { + Callable c; + + CallableReturnTypeMention() { this = TCallableReturnTypeMention(c) } + + Callable getCallable() { result = c } + + override Type getTypeAt(TypePath path) { + exists(Swift::Type res, Swift::Type t | + res = c.(ValueDecl).getInterfaceType().(AnyFunctionType).getResult() and + t = [res.(FunctionType).getResult(), res.(Swift::TupleType)] and + result = getTypeAt(t, path) + ) + } + + override string toString() { result = c.toString() + " [return type]" } + + override Location getLocation() { result = c.getLocation() } +} diff --git a/swift/ql/lib/qlpack.yml b/swift/ql/lib/qlpack.yml index 921af77ca70c..89ec5f0c2de8 100644 --- a/swift/ql/lib/qlpack.yml +++ b/swift/ql/lib/qlpack.yml @@ -13,5 +13,6 @@ dependencies: codeql/mad: ${workspace} codeql/ssa: ${workspace} codeql/tutorial: ${workspace} + codeql/typeinference: ${workspace} codeql/util: ${workspace} warnOnImplicitThis: true diff --git a/swift/ql/test/library-tests/type-inference/Common.qll b/swift/ql/test/library-tests/type-inference/Common.qll new file mode 100644 index 000000000000..a194c50d818b --- /dev/null +++ b/swift/ql/test/library-tests/type-inference/Common.qll @@ -0,0 +1,51 @@ +private import swift +private import TestUtils as TestUtils +import utils.test.InlineExpectationsTest + +predicate toBeTested(Locatable e) { + TestUtils::toBeTested(e) and + exists(e.getLocation().getFile().getRelativePath()) +} + +pragma[nomagic] +private predicate declHasPotentialCommentAt(Decl d, string path, int line) { + d.getLocation().hasLocationInfo(path, line + 1, _, _, _) +} + +pragma[nomagic] +private SingleLineComment getPrecedingComment(Decl d) { + exists(string path, int line | + declHasPotentialCommentAt(d, path, line) and + result.getLocation().hasLocationInfo(path, line, _, _, _) + ) +} + +module ResolveTest implements TestSig { + string getARelevantTag() { result = "target" } + + private predicate declHasName(Decl c, string value) { + exists(string s | + s = getPrecedingComment(c).getText() and + value = s.substring(3, s.length() - 1) + ) + or + not exists(getPrecedingComment(c)) and + value = [c.(EnumElementDecl).getName(), c.(Function).getName()] + } + + predicate hasActualResult(Location location, string element, string tag, string value) { + exists(AstNode source, Decl target | + location = source.getLocation() and + element = source.toString() and + target = + [ + source.(CallExpr).getStaticTarget().(Decl), source.(MethodLookupExpr).getMember(), + source.(EnumElementPattern).getElement() + ] and + declHasName(target, value) and + tag = "target" and + toBeTested(source) and + toBeTested(target) + ) + } +} diff --git a/swift/ql/test/library-tests/type-inference/generics.swift b/swift/ql/test/library-tests/type-inference/generics.swift index cdc3c8a28772..3509d3a8349c 100644 --- a/swift/ql/test/library-tests/type-inference/generics.swift +++ b/swift/ql/test/library-tests/type-inference/generics.swift @@ -141,3 +141,39 @@ func testConstrainedGeneric() { let v = w.get() // $ type=v:Int target=Wrapper.get let z = w.callFoo() // $ type=z:Int target=Wrapper.callFoo } + +// --- Generics and inheritance --- + +class Base { + var value1 : T1 + var value2 : T2 + + // Base.init + init(_ v1: T1, _ v2: T2) { + self.value1 = v1 // $ type=.value1:T1 + self.value2 = v2 // $ type=.value2:T2 + } + + // Base.getValue1 + func getValue1() -> T1 { + return value1 // $ type=.value1:T1 + } + + // Base.getValue2 + func getValue2() -> T2 { + return value2 // $ type=.value2:T2 + } +} + +class Derived : Base { + // Derived.init + init(_ v1: T1, _ v2: T2) { + super.init(v2, v1) // $ type=v2:T2 type=v1:T1 target=Base.init + } +} + +func testDerived() { + let d = Derived(1, "x") // $ type=d@Derived:Int type=d@Derived:String target=Derived.init + let v1 = d.getValue1() // $ type=v1:String target=Base.getValue1 + let v2 = d.getValue2() // $ type=v2:Int target=Base.getValue2 +} \ No newline at end of file diff --git a/swift/ql/test/library-tests/type-inference/lub.swift b/swift/ql/test/library-tests/type-inference/lub.swift new file mode 100644 index 000000000000..288148b9bd55 --- /dev/null +++ b/swift/ql/test/library-tests/type-inference/lub.swift @@ -0,0 +1,11 @@ +class A {} + +class B : A {} + +class C : A {} + +func foo() { + let b = B() // $ type=b:B target=init() + let c = C() // $ type=c:C target=init() + let x = 2 > 3 ? b : c // $ type=x:A +} \ No newline at end of file diff --git a/swift/ql/test/library-tests/type-inference/type-inference-ql.expected b/swift/ql/test/library-tests/type-inference/type-inference-ql.expected new file mode 100644 index 000000000000..bf1808f359e0 --- /dev/null +++ b/swift/ql/test/library-tests/type-inference/type-inference-ql.expected @@ -0,0 +1,1813 @@ +testFailures +| basics.swift:14:18:15:1 | // $ type=.myInt:Int\n | Missing result: type=.myInt:Int | +| basics.swift:21:22:22:1 | // $ type=super:C target=C.init\n | Missing result: type=super:C | +| basics.swift:40:18:41:1 | // $ type=.value:T\n | Missing result: type=.value:T | +| basics.swift:47:22:48:1 | // $ type=super@Generic:Int target=Generic.init\n | Missing result: type=super@Generic:Int | +| basics.swift:56:25:57:1 | // $ type=y:Int target=Generic.getValue\n | Missing result: type=y:Int | +| basics.swift:64:22:65:1 | // $ type=.myInt:Int\n | Missing result: type=.myInt:Int | +| classes.swift:74:25:75:1 | // $ type=.value:Int\n | Missing result: type=.value:Int | +| classes.swift:106:18:107:1 | // $ type=.value:Int\n | Missing result: type=.value:Int | +| classes.swift:146:20:147:1 | // $ type=.celsius:Double\n | Missing result: type=.celsius:Double | +| classes.swift:151:39:152:1 | // $ type=.celsius:Double\n | Missing result: type=.celsius:Double | +| classes.swift:223:18:224:1 | // $ type=.count:Int\n | Missing result: type=.count:Int | +| generics.swift:14:24:15:1 | // $ type=i:Int target=identity\n | Missing result: type=i:Int | +| generics.swift:15:29:16:1 | // $ type=s:String target=identity\n | Missing result: type=s:String | +| generics.swift:33:18:34:1 | // $ type=.first:A\n | Missing result: type=.first:A | +| generics.swift:38:19:39:1 | // $ type=.second:B\n | Missing result: type=.second:B | +| generics.swift:60:16:61:1 | // $ type=v:T\n | Missing result: type=v:T | +| generics.swift:68:30:69:1 | // $ type=r@Result:Int target=Result.success\n | Missing result: type=r@Result:Int | +| generics.swift:71:39:72:1 | // $ type=r2@Result:Int target=Result.success\n | Missing result: type=r2@Result:Int | +| generics.swift:83:50:84:1 | // $ target=applyTransform type=result:Int\n | Missing result: type=result:Int | +| generics.swift:84:56:85:1 | // $ target=applyTransform type=strings:String\n | Missing result: type=strings:String | +| generics.swift:109:18:110:1 | // $ type=.inner:T\n | Missing result: type=.inner:T | +| generics.swift:114:26:115:1 | // $ type=x:T target=MyProtocol.foo\n | Missing result: type=x:T | +| generics.swift:120:26:121:1 | // $ type=x:MyType target=MyProtocol.bar\n | Missing result: type=x:MyType | +| generics.swift:159:19:160:1 | // $ type=.value1:T1\n | Missing result: type=.value1:T1 | +| generics.swift:164:19:165:1 | // $ type=.value2:T2\n | Missing result: type=.value2:T2 | +| generics.swift:177:26:178:1 | // $ type=v1:String target=Base.getValue1\n | Missing result: type=v1:String | +| generics.swift:178:26:179:1 | // $ type=v2:Int target=Base.getValue2\n | Missing result: type=v2:Int | +| key_paths.swift:35:30:36:1 | // $ type=xVal:Double\n | Missing result: type=xVal:Double | +| key_paths.swift:36:30:37:1 | // $ type=yVal:Double\n | Missing result: type=yVal:Double | +| key_paths.swift:48:40:49:1 | // $ type=startX:Double\n | Missing result: type=startX:Double | +| key_paths.swift:49:36:50:1 | // $ type=endY:Double\n | Missing result: type=endY:Double | +| key_paths.swift:56:33:57:1 | // $ type=val:Int\n | Missing result: type=val:Int | +| key_paths.swift:88:38:89:1 | // $ type=city:String?\n | Missing result: type=city:String? | +| key_paths.swift:114:23:115:1 | // $ type=name:String\n | Missing result: type=name:String | +| key_paths.swift:116:28:117:1 | // $ type=salary:Int\n | Missing result: type=salary:Int | +| key_paths.swift:133:26:134:1 | // $ type=v:Int\n | Missing result: type=v:Int | +| key_paths.swift:142:30:143:1 | // $ type=newX:Double\n | Missing result: type=newX:Double | +| key_paths.swift:155:37:156:1 | // $ type=val:Double\n | Missing result: type=val:Double | +| key_paths.swift:165:23:166:1 | // $ type=name:String\n | Missing result: type=name:String | +| key_paths.swift:167:28:168:1 | // $ type=salary:Int\n | Missing result: type=salary:Int | +| key_paths.swift:196:33:197:1 | // $ type=col:String\n | Missing result: type=col:String | +| key_paths.swift:197:34:198:1 | // $ type=rad:Double\n | Missing result: type=rad:Double | +| key_paths.swift:206:35:207:1 | // $ type=first:Int\n | Missing result: type=first:Int | +| key_paths.swift:207:36:208:1 | // $ type=second:String\n | Missing result: type=second:String | +| key_paths.swift:215:37:216:1 | // $ type=first:Int\n | Missing result: type=first:Int | +| key_paths.swift:219:34:220:1 | // $ type=val:Int?\n | Missing result: type=val:Int? | +| lub.swift:10:25:11:1 | // $ type=x:A\n | Missing result: type=x:A | +| overload_resolution.swift:162:29:163:1 | // $ type=r1:String target=freeOverload(_:Int)\n | Missing result: type=r1:String | +| overload_resolution.swift:163:31:164:1 | // $ type=r2:String target=freeOverload(_:String)\n | Missing result: type=r2:String | +| overload_resolution.swift:164:30:165:1 | // $ type=r3:String target=freeOverload(_:Double)\n | Missing result: type=r3:String | +| overload_resolution.swift:194:18:195:1 | // $ type=.value:String\n | Missing result: type=.value:String | +| overload_resolution.swift:379:30:380:1 | // $ type=r1:Int target=constrainedId(_:Numeric2)\n | Missing result: type=r1:Int | +| overload_resolution.swift:380:35:381:1 | // $ type=r2:String target=constrainedId(_:Equatable)\n | Missing result: type=r2:String | +| overload_resolution.swift:395:17:396:1 | // $ type=.item:T\n | Missing result: type=.item:T | +| protocols.swift:18:38:19:1 | // $ type=.radius:Double\n | Missing result: type=.radius:Double | +| protocols.swift:34:27:35:1 | // $ type=.width:Double\n | Missing result: type=.width:Double | +| protocols.swift:106:17:107:1 | // $ type=.name:String\n | Missing result: type=.name:String | +| protocols.swift:111:21:112:1 | // $ type=.entityId:Int\n | Missing result: type=.entityId:Int | +| type_constraints.swift:14:24:15:1 | // $ type=m1:Int target=minOf(_:_:)\n | Missing result: type=m1:Int | +| type_constraints.swift:15:28:16:1 | // $ type=m2:String target=minOf(_:_:)\n | Missing result: type=m2:String | +| type_constraints.swift:16:24:17:1 | // $ type=m3:Int target=maxOf(_:_:)\n | Missing result: type=m3:Int | +| type_constraints.swift:17:28:18:1 | // $ type=m4:String target=maxOf(_:_:)\n | Missing result: type=m4:String | +| type_constraints.swift:44:16:45:1 | // $ type=.tag:String\n | Missing result: type=.tag:String | +| type_constraints.swift:49:21:50:1 | // $ type=.priority:Int\n | Missing result: type=.priority:Int | +| type_constraints.swift:65:29:66:1 | // $ type=s:String target=showAndSort(_:)\n | Missing result: type=s:String | +| type_constraints.swift:66:43:67:1 | // $ type=eq:Bool target=showSortAndCompare(_:_:)\n | Missing result: type=eq:Bool | +| type_constraints.swift:149:35:150:1 | // $ type=r1:Int target=extractFirst(from:Int)\n | Missing result: type=r1:Int | +| type_constraints.swift:150:35:151:1 | // $ type=r2:String target=extractFirst(from:String)\n | Missing result: type=r2:String | +| type_constraints.swift:211:33:212:1 | // $ type=d1:String target=describeVehicle(_:)\n | Missing result: type=d1:String | +| type_constraints.swift:212:35:213:1 | // $ type=d2:String target=describeVehicle(_:)\n | Missing result: type=d2:String | +| type_constraints.swift:259:28:260:1 | // $ type=tot:Int target=Accumulator.total\n | Missing result: type=tot:Int | +| type_constraints.swift:282:35:283:1 | // $ type=r1:Int target=Transformer.transform\n | Missing result: type=r1:Int | +| type_constraints.swift:283:41:284:1 | // $ type=r2:String target=Transformer.transform\n | Missing result: type=r2:String | +| type_constraints.swift:297:38:298:1 | // $ type=r1:Int target=clamp(_:min:max:)\n | Missing result: type=r1:Int | +| type_constraints.swift:298:43:299:1 | // $ type=r2:Double target=clamp(_:min:max:)\n | Missing result: type=r2:Double | +| type_constraints.swift:299:43:300:1 | // $ type=r3:String target=clamp(_:min:max:)\n | Missing result: type=r3:String | +inferType +| basics.swift:1:5:1:5 | topLevelDecl | | file://:0:0:0:0 | Int | +| basics.swift:1:5:1:20 | ... as ... | | file://:0:0:0:0 | Int | +| basics.swift:1:26:1:26 | 0 | | file://:0:0:0:0 | Int | +| basics.swift:2:1:2:1 | 0 | | file://:0:0:0:0 | Int | +| basics.swift:3:1:3:1 | topLevelDecl | | file://:0:0:0:0 | Int | +| basics.swift:3:16:3:16 | 1 | | file://:0:0:0:0 | Int | +| basics.swift:5:7:5:7 | self | | basics.swift:5:1:16:1 | C | +| basics.swift:6:7:6:7 | self | | basics.swift:5:1:16:1 | C | +| basics.swift:6:7:6:7 | self | | basics.swift:5:1:16:1 | C | +| basics.swift:6:7:6:7 | self | | basics.swift:5:1:16:1 | C | +| basics.swift:6:7:6:7 | value | | file://:0:0:0:0 | Int | +| basics.swift:8:3:8:3 | self | | basics.swift:5:1:16:1 | C | +| basics.swift:8:8:8:11 | n | | file://:0:0:0:0 | Int | +| basics.swift:9:5:9:5 | .myInt | | file://:0:0:0:0 | Int | +| basics.swift:9:5:9:5 | self | | basics.swift:5:1:16:1 | C | +| basics.swift:9:13:9:13 | n | | file://:0:0:0:0 | Int | +| basics.swift:13:8:13:8 | self | | basics.swift:5:1:16:1 | C | +| basics.swift:14:12:14:12 | self | | basics.swift:5:1:16:1 | C | +| basics.swift:18:7:18:7 | basics.Derived | | file://:0:0:0:0 | String | +| basics.swift:18:7:18:7 | self | | basics.swift:18:1:29:1 | Derived | +| basics.swift:18:19:18:19 | self | | basics.swift:18:1:29:1 | Derived | +| basics.swift:20:3:20:3 | self | | basics.swift:18:1:29:1 | Derived | +| basics.swift:21:5:21:20 | call to C.init(n:) | | basics.swift:5:1:16:1 | C | +| basics.swift:21:19:21:19 | 0 | | file://:0:0:0:0 | Int | +| basics.swift:25:8:25:8 | self | | basics.swift:18:1:29:1 | Derived | +| basics.swift:26:9:26:9 | x | | file://:0:0:0:0 | Int | +| basics.swift:26:9:26:9 | x | | file://:0:0:0:0 | Int | +| basics.swift:26:13:26:13 | self | | basics.swift:18:1:29:1 | Derived | +| basics.swift:26:13:26:22 | call to getMyInt() | | file://:0:0:0:0 | Int | +| basics.swift:27:12:27:12 | x | | file://:0:0:0:0 | Int | +| basics.swift:31:7:31:7 | self | | basics.swift:31:1:42:1 | Generic | +| basics.swift:31:7:31:7 | self | T | basics.swift:31:15:31:15 | T | +| basics.swift:32:7:32:7 | self | | basics.swift:31:1:42:1 | Generic | +| basics.swift:32:7:32:7 | self | | basics.swift:31:1:42:1 | Generic | +| basics.swift:32:7:32:7 | self | | basics.swift:31:1:42:1 | Generic | +| basics.swift:32:7:32:7 | self | T | basics.swift:31:15:31:15 | T | +| basics.swift:32:7:32:7 | self | T | basics.swift:31:15:31:15 | T | +| basics.swift:32:7:32:7 | self | T | basics.swift:31:15:31:15 | T | +| basics.swift:32:7:32:7 | value | | basics.swift:31:15:31:15 | T | +| basics.swift:34:3:34:3 | self | | basics.swift:31:1:42:1 | Generic | +| basics.swift:34:3:34:3 | self | T | basics.swift:31:15:31:15 | T | +| basics.swift:34:8:34:11 | v | | basics.swift:31:15:31:15 | T | +| basics.swift:35:5:35:5 | .value | | basics.swift:31:15:31:15 | T | +| basics.swift:35:5:35:5 | self | | basics.swift:31:1:42:1 | Generic | +| basics.swift:35:5:35:5 | self | T | basics.swift:31:15:31:15 | T | +| basics.swift:35:13:35:13 | v | | basics.swift:31:15:31:15 | T | +| basics.swift:39:8:39:8 | self | | basics.swift:31:1:42:1 | Generic | +| basics.swift:39:8:39:8 | self | T | basics.swift:31:15:31:15 | T | +| basics.swift:40:12:40:12 | self | | basics.swift:31:1:42:1 | Generic | +| basics.swift:40:12:40:12 | self | T | basics.swift:31:15:31:15 | T | +| basics.swift:44:7:44:7 | basics.GenericDerived | | file://:0:0:0:0 | String | +| basics.swift:44:7:44:7 | self | | basics.swift:44:1:49:1 | GenericDerived | +| basics.swift:44:37:44:37 | self | | basics.swift:44:1:49:1 | GenericDerived | +| basics.swift:46:3:46:3 | self | | basics.swift:44:1:49:1 | GenericDerived | +| basics.swift:47:5:47:20 | call to Generic.init(v:) | | basics.swift:31:1:42:1 | Generic | +| basics.swift:47:5:47:20 | call to Generic.init(v:) | T | file://:0:0:0:0 | Int | +| basics.swift:47:19:47:19 | 0 | | file://:0:0:0:0 | Int | +| basics.swift:52:7:52:7 | g | | basics.swift:31:1:42:1 | Generic | +| basics.swift:52:7:52:7 | g | | basics.swift:31:1:42:1 | Generic | +| basics.swift:52:7:52:7 | g | T | file://:0:0:0:0 | Int | +| basics.swift:52:7:52:7 | g | T | file://:0:0:0:0 | Int | +| basics.swift:52:11:52:24 | call to Generic.init(v:) | | basics.swift:31:1:42:1 | Generic | +| basics.swift:52:11:52:24 | call to Generic.init(v:) | T | file://:0:0:0:0 | Int | +| basics.swift:52:22:52:22 | 42 | | file://:0:0:0:0 | Int | +| basics.swift:53:7:53:7 | x | | file://:0:0:0:0 | Int | +| basics.swift:53:7:53:7 | x | | file://:0:0:0:0 | Int | +| basics.swift:53:11:53:11 | g | | basics.swift:31:1:42:1 | Generic | +| basics.swift:53:11:53:11 | g | T | file://:0:0:0:0 | Int | +| basics.swift:53:11:53:22 | call to getValue() | | file://:0:0:0:0 | Int | +| basics.swift:55:7:55:7 | gd | | basics.swift:44:1:49:1 | GenericDerived | +| basics.swift:55:7:55:7 | gd | | basics.swift:44:1:49:1 | GenericDerived | +| basics.swift:55:12:55:27 | call to GenericDerived.init() | | basics.swift:44:1:49:1 | GenericDerived | +| basics.swift:56:11:56:11 | gd | | basics.swift:44:1:49:1 | GenericDerived | +| basics.swift:63:8:63:8 | self | | basics.swift:5:1:16:1 | C | +| basics.swift:64:12:64:12 | self | | basics.swift:5:1:16:1 | C | +| basics.swift:64:20:64:20 | 2 | | file://:0:0:0:0 | Int | +| basics.swift:69:7:69:7 | obj | | basics.swift:5:1:16:1 | C | +| basics.swift:69:7:69:7 | obj | | basics.swift:5:1:16:1 | C | +| basics.swift:69:13:69:20 | call to C.init(n:) | | basics.swift:5:1:16:1 | C | +| basics.swift:69:18:69:18 | 10 | | file://:0:0:0:0 | Int | +| basics.swift:70:7:70:7 | d | | file://:0:0:0:0 | Int | +| basics.swift:70:7:70:7 | d | | file://:0:0:0:0 | Int | +| basics.swift:70:11:70:11 | obj | | basics.swift:5:1:16:1 | C | +| basics.swift:70:11:70:23 | call to doubled() | | file://:0:0:0:0 | Int | +| classes.swift:3:7:3:7 | self | | classes.swift:3:1:13:1 | MathUtils | +| classes.swift:3:7:3:7 | self | | classes.swift:3:1:13:1 | MathUtils | +| classes.swift:5:22:5:25 | x | | file://:0:0:0:0 | Int | +| classes.swift:6:12:6:12 | x | | file://:0:0:0:0 | Int | +| classes.swift:6:16:6:16 | x | | file://:0:0:0:0 | Int | +| classes.swift:10:19:10:22 | x | | file://:0:0:0:0 | Int | +| classes.swift:11:12:11:12 | x | | file://:0:0:0:0 | Int | +| classes.swift:11:16:11:16 | x | | file://:0:0:0:0 | Int | +| classes.swift:11:20:11:20 | x | | file://:0:0:0:0 | Int | +| classes.swift:16:7:16:7 | s | | file://:0:0:0:0 | Int | +| classes.swift:16:7:16:7 | s | | file://:0:0:0:0 | Int | +| classes.swift:16:11:16:32 | call to square(x:) | | file://:0:0:0:0 | Int | +| classes.swift:16:31:16:31 | 4 | | file://:0:0:0:0 | Int | +| classes.swift:17:7:17:7 | cu | | file://:0:0:0:0 | Int | +| classes.swift:17:7:17:7 | cu | | file://:0:0:0:0 | Int | +| classes.swift:17:12:17:31 | call to cube(x:) | | file://:0:0:0:0 | Int | +| classes.swift:17:30:17:30 | 3 | | file://:0:0:0:0 | Int | +| classes.swift:22:7:22:7 | self | | classes.swift:22:1:32:1 | Overloaded | +| classes.swift:22:7:22:7 | self | | classes.swift:22:1:32:1 | Overloaded | +| classes.swift:24:8:24:8 | self | | classes.swift:22:1:32:1 | Overloaded | +| classes.swift:24:16:24:21 | x | | file://:0:0:0:0 | Int | +| classes.swift:25:12:25:12 | x | | file://:0:0:0:0 | Int | +| classes.swift:25:16:25:16 | 1 | | file://:0:0:0:0 | Int | +| classes.swift:29:8:29:8 | self | | classes.swift:22:1:32:1 | Overloaded | +| classes.swift:29:16:29:21 | s | | file://:0:0:0:0 | String | +| classes.swift:30:12:30:12 | s | | file://:0:0:0:0 | String | +| classes.swift:35:7:35:7 | o | | classes.swift:22:1:32:1 | Overloaded | +| classes.swift:35:7:35:7 | o | | classes.swift:22:1:32:1 | Overloaded | +| classes.swift:35:11:35:22 | call to Overloaded.init() | | classes.swift:22:1:32:1 | Overloaded | +| classes.swift:36:7:36:7 | r1 | | file://:0:0:0:0 | Int | +| classes.swift:36:7:36:7 | r1 | | file://:0:0:0:0 | Int | +| classes.swift:36:12:36:12 | o | | classes.swift:22:1:32:1 | Overloaded | +| classes.swift:36:12:36:24 | call to process(_:) | | file://:0:0:0:0 | Int | +| classes.swift:36:22:36:22 | 42 | | file://:0:0:0:0 | Int | +| classes.swift:37:7:37:7 | r2 | | file://:0:0:0:0 | String | +| classes.swift:37:7:37:7 | r2 | | file://:0:0:0:0 | String | +| classes.swift:37:12:37:12 | o | | classes.swift:22:1:32:1 | Overloaded | +| classes.swift:37:12:37:29 | call to process(_:) | | file://:0:0:0:0 | String | +| classes.swift:37:22:37:22 | hello | | file://:0:0:0:0 | String | +| classes.swift:43:7:43:7 | self | | classes.swift:42:1:54:1 | Matrix | +| classes.swift:43:7:43:7 | self | | classes.swift:42:1:54:1 | Matrix | +| classes.swift:43:7:43:7 | self | | classes.swift:42:1:54:1 | Matrix | +| classes.swift:46:3:46:3 | self | | classes.swift:42:1:54:1 | Matrix | +| classes.swift:47:5:47:5 | self | | classes.swift:42:1:54:1 | Matrix | +| classes.swift:51:8:51:8 | self | | classes.swift:42:1:54:1 | Matrix | +| classes.swift:52:12:52:12 | self | | classes.swift:42:1:54:1 | Matrix | +| classes.swift:57:7:57:7 | m | | classes.swift:42:1:54:1 | Matrix | +| classes.swift:57:7:57:7 | m | | classes.swift:42:1:54:1 | Matrix | +| classes.swift:57:11:57:40 | call to Matrix.init(data:) | | classes.swift:42:1:54:1 | Matrix | +| classes.swift:57:26:57:26 | 1 | | file://:0:0:0:0 | Int | +| classes.swift:57:29:57:29 | 2 | | file://:0:0:0:0 | Int | +| classes.swift:57:34:57:34 | 3 | | file://:0:0:0:0 | Int | +| classes.swift:57:37:57:37 | 4 | | file://:0:0:0:0 | Int | +| classes.swift:58:7:58:7 | rc | | file://:0:0:0:0 | Int | +| classes.swift:58:7:58:7 | rc | | file://:0:0:0:0 | Int | +| classes.swift:58:12:58:12 | m | | classes.swift:42:1:54:1 | Matrix | +| classes.swift:58:12:58:23 | call to rowCount() | | file://:0:0:0:0 | Int | +| classes.swift:63:7:63:7 | self | | classes.swift:63:1:77:1 | Outer | +| classes.swift:63:7:63:7 | self | | classes.swift:63:1:77:1 | Outer | +| classes.swift:64:9:64:9 | self | | classes.swift:64:3:76:3 | Inner | +| classes.swift:65:9:65:9 | self | | classes.swift:64:3:76:3 | Inner | +| classes.swift:65:9:65:9 | self | | classes.swift:64:3:76:3 | Inner | +| classes.swift:65:9:65:9 | self | | classes.swift:64:3:76:3 | Inner | +| classes.swift:65:9:65:9 | value | | file://:0:0:0:0 | Int | +| classes.swift:68:5:68:5 | self | | classes.swift:64:3:76:3 | Inner | +| classes.swift:68:10:68:17 | value | | file://:0:0:0:0 | Int | +| classes.swift:69:7:69:7 | self | | classes.swift:64:3:76:3 | Inner | +| classes.swift:69:7:69:12 | .value | | file://:0:0:0:0 | Int | +| classes.swift:69:20:69:20 | value | | file://:0:0:0:0 | Int | +| classes.swift:73:10:73:10 | self | | classes.swift:64:3:76:3 | Inner | +| classes.swift:74:14:74:14 | self | | classes.swift:64:3:76:3 | Inner | +| classes.swift:80:7:80:7 | inner | | classes.swift:64:3:76:3 | Inner | +| classes.swift:80:7:80:7 | inner | | classes.swift:64:3:76:3 | Inner | +| classes.swift:80:15:80:36 | call to Outer.Inner.init(value:) | | classes.swift:64:3:76:3 | Inner | +| classes.swift:80:34:80:34 | 99 | | file://:0:0:0:0 | Int | +| classes.swift:81:7:81:7 | v | | file://:0:0:0:0 | Int | +| classes.swift:81:7:81:7 | v | | file://:0:0:0:0 | Int | +| classes.swift:81:11:81:11 | inner | | classes.swift:64:3:76:3 | Inner | +| classes.swift:81:11:81:26 | call to getValue() | | file://:0:0:0:0 | Int | +| classes.swift:86:7:86:7 | self | | classes.swift:86:1:108:1 | Builder | +| classes.swift:87:7:87:7 | self | | classes.swift:86:1:108:1 | Builder | +| classes.swift:87:7:87:7 | self | | classes.swift:86:1:108:1 | Builder | +| classes.swift:87:7:87:7 | self | | classes.swift:86:1:108:1 | Builder | +| classes.swift:87:7:87:7 | value | | file://:0:0:0:0 | Int | +| classes.swift:87:7:87:7 | value | | file://:0:0:0:0 | Int | +| classes.swift:87:7:87:15 | ... as ... | | file://:0:0:0:0 | Int | +| classes.swift:87:21:87:21 | 0 | | file://:0:0:0:0 | Int | +| classes.swift:90:3:90:3 | self | | classes.swift:86:1:108:1 | Builder | +| classes.swift:93:8:93:8 | self | | classes.swift:86:1:108:1 | Builder | +| classes.swift:93:12:93:17 | v | | file://:0:0:0:0 | Int | +| classes.swift:94:5:94:5 | .value | | file://:0:0:0:0 | Int | +| classes.swift:94:5:94:5 | self | | classes.swift:86:1:108:1 | Builder | +| classes.swift:94:13:94:13 | v | | file://:0:0:0:0 | Int | +| classes.swift:95:12:95:12 | self | | classes.swift:86:1:108:1 | Builder | +| classes.swift:99:8:99:8 | self | | classes.swift:86:1:108:1 | Builder | +| classes.swift:99:12:99:17 | v | | file://:0:0:0:0 | Int | +| classes.swift:100:5:100:5 | self | | classes.swift:86:1:108:1 | Builder | +| classes.swift:100:14:100:14 | v | | file://:0:0:0:0 | Int | +| classes.swift:101:12:101:12 | self | | classes.swift:86:1:108:1 | Builder | +| classes.swift:105:8:105:8 | self | | classes.swift:86:1:108:1 | Builder | +| classes.swift:106:12:106:12 | self | | classes.swift:86:1:108:1 | Builder | +| classes.swift:111:7:111:7 | b | | classes.swift:86:1:108:1 | Builder | +| classes.swift:111:7:111:7 | b | | classes.swift:86:1:108:1 | Builder | +| classes.swift:111:11:111:19 | call to Builder.init() | | classes.swift:86:1:108:1 | Builder | +| classes.swift:112:7:112:7 | result | | file://:0:0:0:0 | Int | +| classes.swift:112:7:112:7 | result | | file://:0:0:0:0 | Int | +| classes.swift:112:16:112:16 | b | | classes.swift:86:1:108:1 | Builder | +| classes.swift:112:16:112:24 | call to set(_:) | | classes.swift:86:1:108:1 | Builder | +| classes.swift:112:16:112:31 | call to add(_:) | | classes.swift:86:1:108:1 | Builder | +| classes.swift:112:16:112:39 | call to build() | | file://:0:0:0:0 | Int | +| classes.swift:112:22:112:22 | 10 | | file://:0:0:0:0 | Int | +| classes.swift:112:30:112:30 | 5 | | file://:0:0:0:0 | Int | +| classes.swift:117:7:117:7 | self | | classes.swift:117:1:125:1 | Config | +| classes.swift:119:3:119:3 | self | | classes.swift:117:1:125:1 | Config | +| classes.swift:122:8:122:8 | self | | classes.swift:117:1:125:1 | Config | +| classes.swift:122:14:122:29 | retries | | file://:0:0:0:0 | Int | +| classes.swift:122:32:122:50 | timeout | | file://:0:0:0:0 | Double | +| classes.swift:123:12:123:12 | retries | | file://:0:0:0:0 | Int | +| classes.swift:128:7:128:7 | cfg | | classes.swift:117:1:125:1 | Config | +| classes.swift:128:7:128:7 | cfg | | classes.swift:117:1:125:1 | Config | +| classes.swift:128:13:128:20 | call to Config.init() | | classes.swift:117:1:125:1 | Config | +| classes.swift:129:7:129:7 | r1 | | file://:0:0:0:0 | Int | +| classes.swift:129:7:129:7 | r1 | | file://:0:0:0:0 | Int | +| classes.swift:129:12:129:12 | cfg | | classes.swift:117:1:125:1 | Config | +| classes.swift:129:12:129:22 | call to setup(retries:timeout:) | | file://:0:0:0:0 | Int | +| classes.swift:130:7:130:7 | r2 | | file://:0:0:0:0 | Int | +| classes.swift:130:7:130:7 | r2 | | file://:0:0:0:0 | Int | +| classes.swift:130:12:130:12 | cfg | | classes.swift:117:1:125:1 | Config | +| classes.swift:130:12:130:32 | call to setup(retries:timeout:) | | file://:0:0:0:0 | Int | +| classes.swift:130:31:130:31 | 5 | | file://:0:0:0:0 | Int | +| classes.swift:131:7:131:7 | r3 | | file://:0:0:0:0 | Int | +| classes.swift:131:7:131:7 | r3 | | file://:0:0:0:0 | Int | +| classes.swift:131:12:131:12 | cfg | | classes.swift:117:1:125:1 | Config | +| classes.swift:131:12:131:47 | call to setup(retries:timeout:) | | file://:0:0:0:0 | Int | +| classes.swift:131:31:131:31 | 2 | | file://:0:0:0:0 | Int | +| classes.swift:136:7:136:7 | self | | classes.swift:136:1:153:1 | Temperature | +| classes.swift:137:7:137:7 | self | | classes.swift:136:1:153:1 | Temperature | +| classes.swift:137:7:137:7 | self | | classes.swift:136:1:153:1 | Temperature | +| classes.swift:137:7:137:7 | self | | classes.swift:136:1:153:1 | Temperature | +| classes.swift:137:7:137:7 | value | | file://:0:0:0:0 | Double | +| classes.swift:140:3:140:3 | self | | classes.swift:136:1:153:1 | Temperature | +| classes.swift:140:8:140:17 | celsius | | file://:0:0:0:0 | Double | +| classes.swift:141:5:141:5 | self | | classes.swift:136:1:153:1 | Temperature | +| classes.swift:141:5:141:10 | .celsius | | file://:0:0:0:0 | Double | +| classes.swift:141:20:141:20 | celsius | | file://:0:0:0:0 | Double | +| classes.swift:145:8:145:8 | self | | classes.swift:136:1:153:1 | Temperature | +| classes.swift:146:12:146:12 | self | | classes.swift:136:1:153:1 | Temperature | +| classes.swift:150:8:150:8 | self | | classes.swift:136:1:153:1 | Temperature | +| classes.swift:151:12:151:12 | self | | classes.swift:136:1:153:1 | Temperature | +| classes.swift:156:7:156:7 | t | | classes.swift:136:1:153:1 | Temperature | +| classes.swift:156:7:156:7 | t | | classes.swift:136:1:153:1 | Temperature | +| classes.swift:156:11:156:37 | call to Temperature.init(celsius:) | | classes.swift:136:1:153:1 | Temperature | +| classes.swift:157:7:157:7 | c | | file://:0:0:0:0 | Double | +| classes.swift:157:7:157:7 | c | | file://:0:0:0:0 | Double | +| classes.swift:157:11:157:11 | t | | classes.swift:136:1:153:1 | Temperature | +| classes.swift:157:11:157:23 | call to toCelsius() | | file://:0:0:0:0 | Double | +| classes.swift:158:7:158:7 | f | | file://:0:0:0:0 | Double | +| classes.swift:158:7:158:7 | f | | file://:0:0:0:0 | Double | +| classes.swift:158:11:158:11 | t | | classes.swift:136:1:153:1 | Temperature | +| classes.swift:158:11:158:26 | call to toFahrenheit() | | file://:0:0:0:0 | Double | +| classes.swift:163:7:163:7 | self | | classes.swift:163:1:171:1 | Animal | +| classes.swift:165:3:165:3 | self | | classes.swift:163:1:171:1 | Animal | +| classes.swift:168:8:168:8 | self | | classes.swift:163:1:171:1 | Animal | +| classes.swift:169:12:169:12 | ... | | file://:0:0:0:0 | String | +| classes.swift:173:7:173:7 | self | | classes.swift:173:1:188:1 | Dog | +| classes.swift:175:12:175:12 | self | | classes.swift:173:1:188:1 | Dog | +| classes.swift:176:5:176:16 | call to Animal.init() | | classes.swift:163:1:171:1 | Animal | +| classes.swift:180:17:180:17 | self | | classes.swift:173:1:188:1 | Dog | +| classes.swift:181:12:181:12 | Woof | | file://:0:0:0:0 | String | +| classes.swift:185:8:185:8 | self | | classes.swift:173:1:188:1 | Dog | +| classes.swift:186:12:186:12 | ball | | file://:0:0:0:0 | String | +| classes.swift:190:7:190:7 | self | | classes.swift:190:1:200:1 | Cat | +| classes.swift:192:12:192:12 | self | | classes.swift:190:1:200:1 | Cat | +| classes.swift:193:5:193:16 | call to Animal.init() | | classes.swift:163:1:171:1 | Animal | +| classes.swift:197:17:197:17 | self | | classes.swift:190:1:200:1 | Cat | +| classes.swift:198:12:198:12 | Meow | | file://:0:0:0:0 | String | +| classes.swift:203:7:203:7 | d | | classes.swift:173:1:188:1 | Dog | +| classes.swift:203:7:203:7 | d | | classes.swift:173:1:188:1 | Dog | +| classes.swift:203:11:203:15 | call to Dog.init() | | classes.swift:173:1:188:1 | Dog | +| classes.swift:204:7:204:7 | ds | | file://:0:0:0:0 | String | +| classes.swift:204:7:204:7 | ds | | file://:0:0:0:0 | String | +| classes.swift:204:12:204:12 | d | | classes.swift:173:1:188:1 | Dog | +| classes.swift:204:12:204:20 | call to speak() | | file://:0:0:0:0 | String | +| classes.swift:205:7:205:7 | df | | file://:0:0:0:0 | String | +| classes.swift:205:7:205:7 | df | | file://:0:0:0:0 | String | +| classes.swift:205:12:205:12 | d | | classes.swift:173:1:188:1 | Dog | +| classes.swift:205:12:205:20 | call to fetch() | | file://:0:0:0:0 | String | +| classes.swift:207:7:207:7 | ct | | classes.swift:190:1:200:1 | Cat | +| classes.swift:207:7:207:7 | ct | | classes.swift:190:1:200:1 | Cat | +| classes.swift:207:12:207:16 | call to Cat.init() | | classes.swift:190:1:200:1 | Cat | +| classes.swift:208:7:208:7 | cs | | file://:0:0:0:0 | String | +| classes.swift:208:7:208:7 | cs | | file://:0:0:0:0 | String | +| classes.swift:208:12:208:12 | ct | | classes.swift:190:1:200:1 | Cat | +| classes.swift:208:12:208:21 | call to speak() | | file://:0:0:0:0 | String | +| classes.swift:213:8:213:8 | count | | file://:0:0:0:0 | Int | +| classes.swift:213:8:213:8 | self | | classes.swift:213:1:225:1 | Counter | +| classes.swift:213:8:213:8 | self | | classes.swift:213:1:225:1 | Counter | +| classes.swift:214:7:214:7 | count | | file://:0:0:0:0 | Int | +| classes.swift:214:7:214:7 | self | | classes.swift:213:1:225:1 | Counter | +| classes.swift:214:7:214:7 | self | | classes.swift:213:1:225:1 | Counter | +| classes.swift:214:7:214:7 | self | | classes.swift:213:1:225:1 | Counter | +| classes.swift:214:7:214:7 | value | | file://:0:0:0:0 | Int | +| classes.swift:214:7:214:15 | ... as ... | | file://:0:0:0:0 | Int | +| classes.swift:214:21:214:21 | 0 | | file://:0:0:0:0 | Int | +| classes.swift:217:17:217:17 | self | | classes.swift:213:1:225:1 | Counter | +| classes.swift:218:5:218:5 | self | | classes.swift:213:1:225:1 | Counter | +| classes.swift:218:14:218:14 | 1 | | file://:0:0:0:0 | Int | +| classes.swift:222:8:222:8 | self | | classes.swift:213:1:225:1 | Counter | +| classes.swift:223:12:223:12 | self | | classes.swift:213:1:225:1 | Counter | +| classes.swift:228:7:228:7 | ctr | | classes.swift:213:1:225:1 | Counter | +| classes.swift:228:7:228:7 | ctr | | classes.swift:213:1:225:1 | Counter | +| classes.swift:228:13:228:21 | call to Counter.init() | | classes.swift:213:1:225:1 | Counter | +| classes.swift:229:3:229:3 | ctr | | classes.swift:213:1:225:1 | Counter | +| classes.swift:229:3:229:17 | call to increment() | | file://:0:0:0:0 | (T_0) | +| classes.swift:230:7:230:7 | val | | file://:0:0:0:0 | Int | +| classes.swift:230:7:230:7 | val | | file://:0:0:0:0 | Int | +| classes.swift:230:13:230:13 | ctr | | classes.swift:213:1:225:1 | Counter | +| classes.swift:230:13:230:26 | call to getCount() | | file://:0:0:0:0 | Int | +| generics.swift:4:18:4:23 | x | | generics.swift:4:15:4:15 | T | +| generics.swift:5:10:5:10 | x | | generics.swift:4:15:4:15 | T | +| generics.swift:9:21:9:26 | a | | generics.swift:9:15:9:15 | A | +| generics.swift:9:29:9:34 | b | | generics.swift:9:18:9:18 | B | +| generics.swift:10:11:10:11 | a | | generics.swift:9:15:9:15 | A | +| generics.swift:10:14:10:14 | b | | generics.swift:9:18:9:18 | B | +| generics.swift:14:20:14:20 | 42 | | file://:0:0:0:0 | Int | +| generics.swift:15:20:15:20 | hello | | file://:0:0:0:0 | String | +| generics.swift:16:7:16:7 | p | | file://:0:0:0:0 | (T_2) | +| generics.swift:16:7:16:7 | p | | file://:0:0:0:0 | (T_2) | +| generics.swift:16:7:16:7 | p | element 0 of tuple of arity 2 | file://:0:0:0:0 | Int | +| generics.swift:16:7:16:7 | p | element 0 of tuple of arity 2 | file://:0:0:0:0 | Int | +| generics.swift:16:7:16:7 | p | element 1 of tuple of arity 2 | file://:0:0:0:0 | String | +| generics.swift:16:7:16:7 | p | element 1 of tuple of arity 2 | file://:0:0:0:0 | String | +| generics.swift:16:11:16:28 | call to makePair(_:_:) | | file://:0:0:0:0 | (T_2) | +| generics.swift:16:11:16:28 | call to makePair(_:_:) | element 0 of tuple of arity 2 | file://:0:0:0:0 | Int | +| generics.swift:16:11:16:28 | call to makePair(_:_:) | element 1 of tuple of arity 2 | file://:0:0:0:0 | String | +| generics.swift:16:20:16:20 | 1 | | file://:0:0:0:0 | Int | +| generics.swift:16:23:16:23 | two | | file://:0:0:0:0 | String | +| generics.swift:22:7:22:7 | self | | generics.swift:21:1:40:1 | Pair | +| generics.swift:22:7:22:7 | self | | generics.swift:21:1:40:1 | Pair | +| generics.swift:22:7:22:7 | self | | generics.swift:21:1:40:1 | Pair | +| generics.swift:22:7:22:7 | self | A | generics.swift:21:13:21:13 | A | +| generics.swift:22:7:22:7 | self | A | generics.swift:21:13:21:13 | A | +| generics.swift:22:7:22:7 | self | A | generics.swift:21:13:21:13 | A | +| generics.swift:22:7:22:7 | self | B | generics.swift:21:16:21:16 | B | +| generics.swift:22:7:22:7 | self | B | generics.swift:21:16:21:16 | B | +| generics.swift:22:7:22:7 | self | B | generics.swift:21:16:21:16 | B | +| generics.swift:22:7:22:7 | value | | generics.swift:21:13:21:13 | A | +| generics.swift:23:7:23:7 | self | | generics.swift:21:1:40:1 | Pair | +| generics.swift:23:7:23:7 | self | | generics.swift:21:1:40:1 | Pair | +| generics.swift:23:7:23:7 | self | | generics.swift:21:1:40:1 | Pair | +| generics.swift:23:7:23:7 | self | A | generics.swift:21:13:21:13 | A | +| generics.swift:23:7:23:7 | self | A | generics.swift:21:13:21:13 | A | +| generics.swift:23:7:23:7 | self | A | generics.swift:21:13:21:13 | A | +| generics.swift:23:7:23:7 | self | B | generics.swift:21:16:21:16 | B | +| generics.swift:23:7:23:7 | self | B | generics.swift:21:16:21:16 | B | +| generics.swift:23:7:23:7 | self | B | generics.swift:21:16:21:16 | B | +| generics.swift:23:7:23:7 | value | | generics.swift:21:16:21:16 | B | +| generics.swift:26:3:26:3 | self | | generics.swift:21:1:40:1 | Pair | +| generics.swift:26:3:26:3 | self | A | generics.swift:21:13:21:13 | A | +| generics.swift:26:3:26:3 | self | B | generics.swift:21:16:21:16 | B | +| generics.swift:26:8:26:15 | first | | generics.swift:21:13:21:13 | A | +| generics.swift:26:18:26:26 | second | | generics.swift:21:16:21:16 | B | +| generics.swift:27:5:27:5 | self | | generics.swift:21:1:40:1 | Pair | +| generics.swift:27:5:27:5 | self | A | generics.swift:21:13:21:13 | A | +| generics.swift:27:5:27:5 | self | B | generics.swift:21:16:21:16 | B | +| generics.swift:27:5:27:10 | .first | | generics.swift:21:13:21:13 | A | +| generics.swift:27:18:27:18 | first | | generics.swift:21:13:21:13 | A | +| generics.swift:28:5:28:5 | self | | generics.swift:21:1:40:1 | Pair | +| generics.swift:28:5:28:5 | self | A | generics.swift:21:13:21:13 | A | +| generics.swift:28:5:28:5 | self | B | generics.swift:21:16:21:16 | B | +| generics.swift:28:5:28:10 | .second | | generics.swift:21:16:21:16 | B | +| generics.swift:28:19:28:19 | second | | generics.swift:21:16:21:16 | B | +| generics.swift:32:8:32:8 | self | | generics.swift:21:1:40:1 | Pair | +| generics.swift:32:8:32:8 | self | A | generics.swift:21:13:21:13 | A | +| generics.swift:32:8:32:8 | self | B | generics.swift:21:16:21:16 | B | +| generics.swift:33:12:33:12 | self | | generics.swift:21:1:40:1 | Pair | +| generics.swift:33:12:33:12 | self | A | generics.swift:21:13:21:13 | A | +| generics.swift:33:12:33:12 | self | B | generics.swift:21:16:21:16 | B | +| generics.swift:37:8:37:8 | self | | generics.swift:21:1:40:1 | Pair | +| generics.swift:37:8:37:8 | self | A | generics.swift:21:13:21:13 | A | +| generics.swift:37:8:37:8 | self | B | generics.swift:21:16:21:16 | B | +| generics.swift:38:12:38:12 | self | | generics.swift:21:1:40:1 | Pair | +| generics.swift:38:12:38:12 | self | A | generics.swift:21:13:21:13 | A | +| generics.swift:38:12:38:12 | self | B | generics.swift:21:16:21:16 | B | +| generics.swift:43:7:43:7 | p | | generics.swift:21:1:40:1 | Pair | +| generics.swift:43:7:43:7 | p | | generics.swift:21:1:40:1 | Pair | +| generics.swift:43:7:43:7 | p | A | file://:0:0:0:0 | Int | +| generics.swift:43:7:43:7 | p | A | file://:0:0:0:0 | Int | +| generics.swift:43:7:43:7 | p | B | file://:0:0:0:0 | String | +| generics.swift:43:7:43:7 | p | B | file://:0:0:0:0 | String | +| generics.swift:43:11:43:37 | call to Pair.init(first:second:) | | generics.swift:21:1:40:1 | Pair | +| generics.swift:43:11:43:37 | call to Pair.init(first:second:) | A | file://:0:0:0:0 | Int | +| generics.swift:43:11:43:37 | call to Pair.init(first:second:) | B | file://:0:0:0:0 | String | +| generics.swift:43:23:43:23 | 1 | | file://:0:0:0:0 | Int | +| generics.swift:43:34:43:34 | x | | file://:0:0:0:0 | String | +| generics.swift:44:7:44:7 | f | | file://:0:0:0:0 | Int | +| generics.swift:44:7:44:7 | f | | file://:0:0:0:0 | Int | +| generics.swift:44:11:44:11 | p | | generics.swift:21:1:40:1 | Pair | +| generics.swift:44:11:44:11 | p | A | file://:0:0:0:0 | Int | +| generics.swift:44:11:44:11 | p | B | file://:0:0:0:0 | String | +| generics.swift:44:11:44:22 | call to getFirst() | | file://:0:0:0:0 | Int | +| generics.swift:45:7:45:7 | sc | | file://:0:0:0:0 | String | +| generics.swift:45:7:45:7 | sc | | file://:0:0:0:0 | String | +| generics.swift:45:12:45:12 | p | | generics.swift:21:1:40:1 | Pair | +| generics.swift:45:12:45:12 | p | A | file://:0:0:0:0 | Int | +| generics.swift:45:12:45:12 | p | B | file://:0:0:0:0 | String | +| generics.swift:45:12:45:24 | call to getSecond() | | file://:0:0:0:0 | String | +| generics.swift:52:16:52:16 | _ | | generics.swift:50:13:50:13 | T | +| generics.swift:54:16:54:16 | _ | | file://:0:0:0:0 | String | +| generics.swift:57:8:57:8 | self | | generics.swift:50:1:65:1 | Result | +| generics.swift:57:8:57:8 | self | T | generics.swift:50:13:50:13 | T | +| generics.swift:58:12:58:12 | self | | generics.swift:50:1:65:1 | Result | +| generics.swift:58:12:58:12 | self | T | generics.swift:50:13:50:13 | T | +| generics.swift:68:26:68:26 | 42 | | file://:0:0:0:0 | Int | +| generics.swift:71:35:71:35 | 42 | | file://:0:0:0:0 | Int | +| generics.swift:78:27:78:36 | value | | generics.swift:78:21:78:21 | T | +| generics.swift:79:20:79:20 | value | | generics.swift:78:21:78:21 | T | +| generics.swift:83:31:83:31 | 5 | | file://:0:0:0:0 | Int | +| generics.swift:83:36:83:36 | x | | file://:0:0:0:0 | Int | +| generics.swift:83:41:83:41 | x | | file://:0:0:0:0 | Int | +| generics.swift:83:45:83:45 | 2 | | file://:0:0:0:0 | Int | +| generics.swift:84:32:84:32 | 10 | | file://:0:0:0:0 | Int | +| generics.swift:84:38:84:38 | x | | file://:0:0:0:0 | Int | +| generics.swift:84:43:84:51 | call to String.init(_:) | | file://:0:0:0:0 | String | +| generics.swift:84:50:84:50 | x | | file://:0:0:0:0 | Int | +| generics.swift:99:7:99:7 | self | | generics.swift:99:1:123:1 | Wrapper | +| generics.swift:99:7:99:7 | self | T | generics.swift:99:15:99:18 | T | +| generics.swift:100:7:100:7 | self | | generics.swift:99:1:123:1 | Wrapper | +| generics.swift:100:7:100:7 | self | | generics.swift:99:1:123:1 | Wrapper | +| generics.swift:100:7:100:7 | self | | generics.swift:99:1:123:1 | Wrapper | +| generics.swift:100:7:100:7 | self | T | generics.swift:99:15:99:18 | T | +| generics.swift:100:7:100:7 | self | T | generics.swift:99:15:99:18 | T | +| generics.swift:100:7:100:7 | self | T | generics.swift:99:15:99:18 | T | +| generics.swift:100:7:100:7 | value | | generics.swift:99:15:99:18 | T | +| generics.swift:103:3:103:3 | self | | generics.swift:99:1:123:1 | Wrapper | +| generics.swift:103:3:103:3 | self | T | generics.swift:99:15:99:18 | T | +| generics.swift:103:8:103:17 | inner | | generics.swift:99:15:99:18 | T | +| generics.swift:104:5:104:5 | self | | generics.swift:99:1:123:1 | Wrapper | +| generics.swift:104:5:104:5 | self | T | generics.swift:99:15:99:18 | T | +| generics.swift:104:5:104:10 | .inner | | generics.swift:99:15:99:18 | T | +| generics.swift:104:18:104:18 | inner | | generics.swift:99:15:99:18 | T | +| generics.swift:108:8:108:8 | self | | generics.swift:99:1:123:1 | Wrapper | +| generics.swift:108:8:108:8 | self | T | generics.swift:99:15:99:18 | T | +| generics.swift:109:12:109:12 | self | | generics.swift:99:1:123:1 | Wrapper | +| generics.swift:109:12:109:12 | self | T | generics.swift:99:15:99:18 | T | +| generics.swift:113:8:113:8 | self | | generics.swift:99:1:123:1 | Wrapper | +| generics.swift:113:8:113:8 | self | T | generics.swift:99:15:99:18 | T | +| generics.swift:114:13:114:13 | self | | generics.swift:99:1:123:1 | Wrapper | +| generics.swift:114:13:114:13 | self | T | generics.swift:99:15:99:18 | T | +| generics.swift:119:8:119:8 | self | | generics.swift:99:1:123:1 | Wrapper | +| generics.swift:119:8:119:8 | self | T | generics.swift:99:15:99:18 | T | +| generics.swift:120:13:120:13 | self | | generics.swift:99:1:123:1 | Wrapper | +| generics.swift:120:13:120:13 | self | T | generics.swift:99:15:99:18 | T | +| generics.swift:129:8:129:8 | self | | file://:0:0:0:0 | Int | +| generics.swift:130:12:130:12 | self | | file://:0:0:0:0 | Int | +| generics.swift:130:19:130:19 | 2 | | file://:0:0:0:0 | Int | +| generics.swift:134:8:134:8 | self | | file://:0:0:0:0 | Int | +| generics.swift:135:12:135:12 | number | | file://:0:0:0:0 | String | +| generics.swift:140:7:140:7 | w | | generics.swift:99:1:123:1 | Wrapper | +| generics.swift:140:7:140:7 | w | | generics.swift:99:1:123:1 | Wrapper | +| generics.swift:140:7:140:7 | w | T | file://:0:0:0:0 | Int | +| generics.swift:140:7:140:7 | w | T | file://:0:0:0:0 | Int | +| generics.swift:140:11:140:21 | call to Wrapper.init(_:) | | generics.swift:99:1:123:1 | Wrapper | +| generics.swift:140:11:140:21 | call to Wrapper.init(_:) | T | file://:0:0:0:0 | Int | +| generics.swift:140:19:140:19 | 42 | | file://:0:0:0:0 | Int | +| generics.swift:141:7:141:7 | v | | file://:0:0:0:0 | Int | +| generics.swift:141:7:141:7 | v | | file://:0:0:0:0 | Int | +| generics.swift:141:11:141:11 | w | | generics.swift:99:1:123:1 | Wrapper | +| generics.swift:141:11:141:11 | w | T | file://:0:0:0:0 | Int | +| generics.swift:141:11:141:17 | call to get() | | file://:0:0:0:0 | Int | +| generics.swift:142:7:142:7 | z | | file://:0:0:0:0 | Int | +| generics.swift:142:7:142:7 | z | | file://:0:0:0:0 | Int | +| generics.swift:142:11:142:11 | w | | generics.swift:99:1:123:1 | Wrapper | +| generics.swift:142:11:142:11 | w | T | file://:0:0:0:0 | Int | +| generics.swift:142:11:142:21 | call to callFoo() | | file://:0:0:0:0 | Int | +| generics.swift:147:7:147:7 | self | | generics.swift:147:1:166:1 | Base | +| generics.swift:147:7:147:7 | self | T1 | generics.swift:147:12:147:12 | T1 | +| generics.swift:147:7:147:7 | self | T2 | generics.swift:147:16:147:16 | T2 | +| generics.swift:148:7:148:7 | self | | generics.swift:147:1:166:1 | Base | +| generics.swift:148:7:148:7 | self | | generics.swift:147:1:166:1 | Base | +| generics.swift:148:7:148:7 | self | | generics.swift:147:1:166:1 | Base | +| generics.swift:148:7:148:7 | self | T1 | generics.swift:147:12:147:12 | T1 | +| generics.swift:148:7:148:7 | self | T1 | generics.swift:147:12:147:12 | T1 | +| generics.swift:148:7:148:7 | self | T1 | generics.swift:147:12:147:12 | T1 | +| generics.swift:148:7:148:7 | self | T2 | generics.swift:147:16:147:16 | T2 | +| generics.swift:148:7:148:7 | self | T2 | generics.swift:147:16:147:16 | T2 | +| generics.swift:148:7:148:7 | self | T2 | generics.swift:147:16:147:16 | T2 | +| generics.swift:148:7:148:7 | value | | generics.swift:147:12:147:12 | T1 | +| generics.swift:149:7:149:7 | self | | generics.swift:147:1:166:1 | Base | +| generics.swift:149:7:149:7 | self | | generics.swift:147:1:166:1 | Base | +| generics.swift:149:7:149:7 | self | | generics.swift:147:1:166:1 | Base | +| generics.swift:149:7:149:7 | self | T1 | generics.swift:147:12:147:12 | T1 | +| generics.swift:149:7:149:7 | self | T1 | generics.swift:147:12:147:12 | T1 | +| generics.swift:149:7:149:7 | self | T1 | generics.swift:147:12:147:12 | T1 | +| generics.swift:149:7:149:7 | self | T2 | generics.swift:147:16:147:16 | T2 | +| generics.swift:149:7:149:7 | self | T2 | generics.swift:147:16:147:16 | T2 | +| generics.swift:149:7:149:7 | self | T2 | generics.swift:147:16:147:16 | T2 | +| generics.swift:149:7:149:7 | value | | generics.swift:147:16:147:16 | T2 | +| generics.swift:152:3:152:3 | self | | generics.swift:147:1:166:1 | Base | +| generics.swift:152:3:152:3 | self | T1 | generics.swift:147:12:147:12 | T1 | +| generics.swift:152:3:152:3 | self | T2 | generics.swift:147:16:147:16 | T2 | +| generics.swift:152:8:152:14 | v1 | | generics.swift:147:12:147:12 | T1 | +| generics.swift:152:18:152:24 | v2 | | generics.swift:147:16:147:16 | T2 | +| generics.swift:153:5:153:5 | self | | generics.swift:147:1:166:1 | Base | +| generics.swift:153:5:153:5 | self | T1 | generics.swift:147:12:147:12 | T1 | +| generics.swift:153:5:153:5 | self | T2 | generics.swift:147:16:147:16 | T2 | +| generics.swift:153:5:153:10 | .value1 | | generics.swift:147:12:147:12 | T1 | +| generics.swift:153:19:153:19 | v1 | | generics.swift:147:12:147:12 | T1 | +| generics.swift:154:5:154:5 | self | | generics.swift:147:1:166:1 | Base | +| generics.swift:154:5:154:5 | self | T1 | generics.swift:147:12:147:12 | T1 | +| generics.swift:154:5:154:5 | self | T2 | generics.swift:147:16:147:16 | T2 | +| generics.swift:154:5:154:10 | .value2 | | generics.swift:147:16:147:16 | T2 | +| generics.swift:154:19:154:19 | v2 | | generics.swift:147:16:147:16 | T2 | +| generics.swift:158:8:158:8 | self | | generics.swift:147:1:166:1 | Base | +| generics.swift:158:8:158:8 | self | T1 | generics.swift:147:12:147:12 | T1 | +| generics.swift:158:8:158:8 | self | T2 | generics.swift:147:16:147:16 | T2 | +| generics.swift:159:12:159:12 | self | | generics.swift:147:1:166:1 | Base | +| generics.swift:159:12:159:12 | self | T1 | generics.swift:147:12:147:12 | T1 | +| generics.swift:159:12:159:12 | self | T2 | generics.swift:147:16:147:16 | T2 | +| generics.swift:163:8:163:8 | self | | generics.swift:147:1:166:1 | Base | +| generics.swift:163:8:163:8 | self | T1 | generics.swift:147:12:147:12 | T1 | +| generics.swift:163:8:163:8 | self | T2 | generics.swift:147:16:147:16 | T2 | +| generics.swift:164:12:164:12 | self | | generics.swift:147:1:166:1 | Base | +| generics.swift:164:12:164:12 | self | T1 | generics.swift:147:12:147:12 | T1 | +| generics.swift:164:12:164:12 | self | T2 | generics.swift:147:16:147:16 | T2 | +| generics.swift:168:7:168:7 | generics.Derived | | file://:0:0:0:0 | String | +| generics.swift:168:7:168:7 | self | | generics.swift:168:1:173:1 | Derived | +| generics.swift:168:7:168:7 | self | T1 | generics.swift:168:15:168:15 | T1 | +| generics.swift:168:7:168:7 | self | T2 | generics.swift:168:19:168:19 | T2 | +| generics.swift:168:38:168:38 | self | | generics.swift:168:1:173:1 | Derived | +| generics.swift:168:38:168:38 | self | T1 | generics.swift:168:15:168:15 | T1 | +| generics.swift:168:38:168:38 | self | T2 | generics.swift:168:19:168:19 | T2 | +| generics.swift:170:3:170:3 | self | | generics.swift:168:1:173:1 | Derived | +| generics.swift:170:3:170:3 | self | T1 | generics.swift:168:15:168:15 | T1 | +| generics.swift:170:3:170:3 | self | T2 | generics.swift:168:19:168:19 | T2 | +| generics.swift:170:8:170:14 | v1 | | generics.swift:168:15:168:15 | T1 | +| generics.swift:170:18:170:24 | v2 | | generics.swift:168:19:168:19 | T2 | +| generics.swift:171:5:171:22 | call to Base.init(_:_:) | | generics.swift:147:1:166:1 | Base | +| generics.swift:171:5:171:22 | call to Base.init(_:_:) | T1 | generics.swift:168:19:168:19 | T2 | +| generics.swift:171:5:171:22 | call to Base.init(_:_:) | T2 | generics.swift:168:15:168:15 | T1 | +| generics.swift:171:16:171:16 | v2 | | generics.swift:168:19:168:19 | T2 | +| generics.swift:171:20:171:20 | v1 | | generics.swift:168:15:168:15 | T1 | +| generics.swift:176:7:176:7 | d | | generics.swift:168:1:173:1 | Derived | +| generics.swift:176:7:176:7 | d | | generics.swift:168:1:173:1 | Derived | +| generics.swift:176:7:176:7 | d | T1 | file://:0:0:0:0 | Int | +| generics.swift:176:7:176:7 | d | T1 | file://:0:0:0:0 | Int | +| generics.swift:176:7:176:7 | d | T2 | file://:0:0:0:0 | String | +| generics.swift:176:7:176:7 | d | T2 | file://:0:0:0:0 | String | +| generics.swift:176:11:176:25 | call to Derived.init(_:_:) | | generics.swift:168:1:173:1 | Derived | +| generics.swift:176:11:176:25 | call to Derived.init(_:_:) | T1 | file://:0:0:0:0 | Int | +| generics.swift:176:11:176:25 | call to Derived.init(_:_:) | T2 | file://:0:0:0:0 | String | +| generics.swift:176:19:176:19 | 1 | | file://:0:0:0:0 | Int | +| generics.swift:176:22:176:22 | x | | file://:0:0:0:0 | String | +| generics.swift:177:12:177:12 | d | | generics.swift:168:1:173:1 | Derived | +| generics.swift:177:12:177:12 | d | T1 | file://:0:0:0:0 | Int | +| generics.swift:177:12:177:12 | d | T2 | file://:0:0:0:0 | String | +| generics.swift:178:12:178:12 | d | | generics.swift:168:1:173:1 | Derived | +| generics.swift:178:12:178:12 | d | T1 | file://:0:0:0:0 | Int | +| generics.swift:178:12:178:12 | d | T2 | file://:0:0:0:0 | String | +| key_paths.swift:4:7:4:7 | self | | key_paths.swift:3:1:17:1 | Point | +| key_paths.swift:4:7:4:7 | self | | key_paths.swift:3:1:17:1 | Point | +| key_paths.swift:4:7:4:7 | self | | key_paths.swift:3:1:17:1 | Point | +| key_paths.swift:4:7:4:7 | value | | file://:0:0:0:0 | Double | +| key_paths.swift:5:7:5:7 | self | | key_paths.swift:3:1:17:1 | Point | +| key_paths.swift:5:7:5:7 | self | | key_paths.swift:3:1:17:1 | Point | +| key_paths.swift:5:7:5:7 | self | | key_paths.swift:3:1:17:1 | Point | +| key_paths.swift:5:7:5:7 | value | | file://:0:0:0:0 | Double | +| key_paths.swift:8:3:8:3 | self | | key_paths.swift:3:1:17:1 | Point | +| key_paths.swift:8:8:8:11 | x | | file://:0:0:0:0 | Double | +| key_paths.swift:8:19:8:22 | y | | file://:0:0:0:0 | Double | +| key_paths.swift:9:5:9:5 | self | | key_paths.swift:3:1:17:1 | Point | +| key_paths.swift:9:5:9:10 | .x | | file://:0:0:0:0 | Double | +| key_paths.swift:9:14:9:14 | x | | file://:0:0:0:0 | Double | +| key_paths.swift:10:5:10:5 | self | | key_paths.swift:3:1:17:1 | Point | +| key_paths.swift:10:5:10:10 | .y | | file://:0:0:0:0 | Double | +| key_paths.swift:10:14:10:14 | y | | file://:0:0:0:0 | Double | +| key_paths.swift:14:8:14:8 | self | | key_paths.swift:3:1:17:1 | Point | +| key_paths.swift:15:13:15:13 | self | | key_paths.swift:3:1:17:1 | Point | +| key_paths.swift:15:17:15:17 | self | | key_paths.swift:3:1:17:1 | Point | +| key_paths.swift:15:21:15:21 | self | | key_paths.swift:3:1:17:1 | Point | +| key_paths.swift:15:25:15:25 | self | | key_paths.swift:3:1:17:1 | Point | +| key_paths.swift:20:7:20:7 | self | | key_paths.swift:19:1:28:1 | Line | +| key_paths.swift:20:7:20:7 | self | | key_paths.swift:19:1:28:1 | Line | +| key_paths.swift:20:7:20:7 | self | | key_paths.swift:19:1:28:1 | Line | +| key_paths.swift:20:7:20:7 | value | | key_paths.swift:3:1:17:1 | Point | +| key_paths.swift:21:7:21:7 | self | | key_paths.swift:19:1:28:1 | Line | +| key_paths.swift:21:7:21:7 | self | | key_paths.swift:19:1:28:1 | Line | +| key_paths.swift:21:7:21:7 | self | | key_paths.swift:19:1:28:1 | Line | +| key_paths.swift:21:7:21:7 | value | | key_paths.swift:3:1:17:1 | Point | +| key_paths.swift:24:3:24:3 | self | | key_paths.swift:19:1:28:1 | Line | +| key_paths.swift:24:8:24:15 | start | | key_paths.swift:3:1:17:1 | Point | +| key_paths.swift:24:22:24:27 | end | | key_paths.swift:3:1:17:1 | Point | +| key_paths.swift:25:5:25:5 | self | | key_paths.swift:19:1:28:1 | Line | +| key_paths.swift:25:5:25:10 | .start | | key_paths.swift:3:1:17:1 | Point | +| key_paths.swift:25:18:25:18 | start | | key_paths.swift:3:1:17:1 | Point | +| key_paths.swift:26:5:26:5 | self | | key_paths.swift:19:1:28:1 | Line | +| key_paths.swift:26:5:26:10 | .end | | key_paths.swift:3:1:17:1 | Point | +| key_paths.swift:26:16:26:16 | end | | key_paths.swift:3:1:17:1 | Point | +| key_paths.swift:34:7:34:7 | p | | key_paths.swift:3:1:17:1 | Point | +| key_paths.swift:34:7:34:7 | p | | key_paths.swift:3:1:17:1 | Point | +| key_paths.swift:34:11:34:31 | call to Point.init(x:y:) | | key_paths.swift:3:1:17:1 | Point | +| key_paths.swift:35:14:35:14 | p | | key_paths.swift:3:1:17:1 | Point | +| key_paths.swift:36:14:36:14 | p | | key_paths.swift:3:1:17:1 | Point | +| key_paths.swift:45:7:45:7 | s | | key_paths.swift:3:1:17:1 | Point | +| key_paths.swift:45:7:45:7 | s | | key_paths.swift:3:1:17:1 | Point | +| key_paths.swift:45:11:45:31 | call to Point.init(x:y:) | | key_paths.swift:3:1:17:1 | Point | +| key_paths.swift:46:7:46:7 | e | | key_paths.swift:3:1:17:1 | Point | +| key_paths.swift:46:7:46:7 | e | | key_paths.swift:3:1:17:1 | Point | +| key_paths.swift:46:11:46:31 | call to Point.init(x:y:) | | key_paths.swift:3:1:17:1 | Point | +| key_paths.swift:47:7:47:7 | line | | key_paths.swift:19:1:28:1 | Line | +| key_paths.swift:47:7:47:7 | line | | key_paths.swift:19:1:28:1 | Line | +| key_paths.swift:47:14:47:35 | call to Line.init(start:end:) | | key_paths.swift:19:1:28:1 | Line | +| key_paths.swift:47:26:47:26 | s | | key_paths.swift:3:1:17:1 | Point | +| key_paths.swift:47:34:47:34 | e | | key_paths.swift:3:1:17:1 | Point | +| key_paths.swift:48:16:48:16 | line | | key_paths.swift:19:1:28:1 | Line | +| key_paths.swift:49:14:49:14 | line | | key_paths.swift:19:1:28:1 | Line | +| key_paths.swift:56:13:56:13 | 42 | | file://:0:0:0:0 | Int | +| key_paths.swift:62:7:62:7 | self | | key_paths.swift:61:1:70:1 | Person | +| key_paths.swift:62:7:62:7 | self | | key_paths.swift:61:1:70:1 | Person | +| key_paths.swift:62:7:62:7 | self | | key_paths.swift:61:1:70:1 | Person | +| key_paths.swift:62:7:62:7 | value | | file://:0:0:0:0 | String | +| key_paths.swift:63:7:63:7 | self | | key_paths.swift:61:1:70:1 | Person | +| key_paths.swift:63:7:63:7 | self | | key_paths.swift:61:1:70:1 | Person | +| key_paths.swift:63:7:63:7 | self | | key_paths.swift:61:1:70:1 | Person | +| key_paths.swift:66:3:66:3 | self | | key_paths.swift:61:1:70:1 | Person | +| key_paths.swift:66:8:66:14 | name | | file://:0:0:0:0 | String | +| key_paths.swift:67:5:67:5 | self | | key_paths.swift:61:1:70:1 | Person | +| key_paths.swift:67:5:67:10 | .name | | file://:0:0:0:0 | String | +| key_paths.swift:67:17:67:17 | name | | file://:0:0:0:0 | String | +| key_paths.swift:68:5:68:5 | self | | key_paths.swift:61:1:70:1 | Person | +| key_paths.swift:73:7:73:7 | self | | key_paths.swift:72:1:81:1 | Address | +| key_paths.swift:73:7:73:7 | self | | key_paths.swift:72:1:81:1 | Address | +| key_paths.swift:73:7:73:7 | self | | key_paths.swift:72:1:81:1 | Address | +| key_paths.swift:73:7:73:7 | value | | file://:0:0:0:0 | String | +| key_paths.swift:74:7:74:7 | self | | key_paths.swift:72:1:81:1 | Address | +| key_paths.swift:74:7:74:7 | self | | key_paths.swift:72:1:81:1 | Address | +| key_paths.swift:74:7:74:7 | self | | key_paths.swift:72:1:81:1 | Address | +| key_paths.swift:74:7:74:7 | value | | file://:0:0:0:0 | String | +| key_paths.swift:77:3:77:3 | self | | key_paths.swift:72:1:81:1 | Address | +| key_paths.swift:77:8:77:14 | city | | file://:0:0:0:0 | String | +| key_paths.swift:77:22:77:27 | zip | | file://:0:0:0:0 | String | +| key_paths.swift:78:5:78:5 | self | | key_paths.swift:72:1:81:1 | Address | +| key_paths.swift:78:5:78:10 | .city | | file://:0:0:0:0 | String | +| key_paths.swift:78:17:78:17 | city | | file://:0:0:0:0 | String | +| key_paths.swift:79:5:79:5 | self | | key_paths.swift:72:1:81:1 | Address | +| key_paths.swift:79:5:79:10 | .zip | | file://:0:0:0:0 | String | +| key_paths.swift:79:16:79:16 | zip | | file://:0:0:0:0 | String | +| key_paths.swift:85:7:85:7 | addr | | key_paths.swift:72:1:81:1 | Address | +| key_paths.swift:85:7:85:7 | addr | | key_paths.swift:72:1:81:1 | Address | +| key_paths.swift:85:14:85:47 | call to Address.init(city:zip:) | | key_paths.swift:72:1:81:1 | Address | +| key_paths.swift:85:28:85:28 | NYC | | file://:0:0:0:0 | String | +| key_paths.swift:85:40:85:40 | 10001 | | file://:0:0:0:0 | String | +| key_paths.swift:86:7:86:7 | person | | key_paths.swift:61:1:70:1 | Person | +| key_paths.swift:86:7:86:7 | person | | key_paths.swift:61:1:70:1 | Person | +| key_paths.swift:86:16:86:51 | call to Person.init(name:address:) | | key_paths.swift:61:1:70:1 | Person | +| key_paths.swift:86:29:86:29 | Alice | | file://:0:0:0:0 | String | +| key_paths.swift:86:47:86:47 | addr | | key_paths.swift:72:1:81:1 | Address | +| key_paths.swift:88:14:88:14 | person | | key_paths.swift:61:1:70:1 | Person | +| key_paths.swift:94:7:94:7 | self | | key_paths.swift:93:1:102:1 | Employee | +| key_paths.swift:94:7:94:7 | self | | key_paths.swift:93:1:102:1 | Employee | +| key_paths.swift:94:7:94:7 | self | | key_paths.swift:93:1:102:1 | Employee | +| key_paths.swift:94:7:94:7 | value | | file://:0:0:0:0 | String | +| key_paths.swift:95:7:95:7 | self | | key_paths.swift:93:1:102:1 | Employee | +| key_paths.swift:95:7:95:7 | self | | key_paths.swift:93:1:102:1 | Employee | +| key_paths.swift:95:7:95:7 | self | | key_paths.swift:93:1:102:1 | Employee | +| key_paths.swift:95:7:95:7 | value | | file://:0:0:0:0 | Int | +| key_paths.swift:98:3:98:3 | self | | key_paths.swift:93:1:102:1 | Employee | +| key_paths.swift:98:8:98:14 | name | | file://:0:0:0:0 | String | +| key_paths.swift:98:22:98:30 | salary | | file://:0:0:0:0 | Int | +| key_paths.swift:99:5:99:5 | self | | key_paths.swift:93:1:102:1 | Employee | +| key_paths.swift:99:5:99:10 | .name | | file://:0:0:0:0 | String | +| key_paths.swift:99:17:99:17 | name | | file://:0:0:0:0 | String | +| key_paths.swift:100:5:100:5 | self | | key_paths.swift:93:1:102:1 | Employee | +| key_paths.swift:100:5:100:10 | .salary | | file://:0:0:0:0 | Int | +| key_paths.swift:100:19:100:19 | salary | | file://:0:0:0:0 | Int | +| key_paths.swift:105:39:105:60 | keyPath | | file://:0:0:0:0 | KeyPath | +| key_paths.swift:105:39:105:60 | keyPath | Root | key_paths.swift:105:19:105:19 | T | +| key_paths.swift:105:39:105:60 | keyPath | Value | key_paths.swift:105:22:105:22 | V | +| key_paths.swift:106:20:106:20 | $0 | | key_paths.swift:105:19:105:19 | T | +| key_paths.swift:106:22:106:22 | $0 | | key_paths.swift:105:19:105:19 | T | +| key_paths.swift:106:34:106:34 | keyPath | | file://:0:0:0:0 | KeyPath | +| key_paths.swift:106:34:106:34 | keyPath | Root | key_paths.swift:105:19:105:19 | T | +| key_paths.swift:106:34:106:34 | keyPath | Value | key_paths.swift:105:22:105:22 | V | +| key_paths.swift:110:7:110:7 | e1 | | key_paths.swift:93:1:102:1 | Employee | +| key_paths.swift:110:7:110:7 | e1 | | key_paths.swift:93:1:102:1 | Employee | +| key_paths.swift:110:12:110:47 | call to Employee.init(name:salary:) | | key_paths.swift:93:1:102:1 | Employee | +| key_paths.swift:110:27:110:27 | Alice | | file://:0:0:0:0 | String | +| key_paths.swift:110:44:110:44 | 100 | | file://:0:0:0:0 | Int | +| key_paths.swift:111:7:111:7 | e2 | | key_paths.swift:93:1:102:1 | Employee | +| key_paths.swift:111:7:111:7 | e2 | | key_paths.swift:93:1:102:1 | Employee | +| key_paths.swift:111:12:111:45 | call to Employee.init(name:salary:) | | key_paths.swift:93:1:102:1 | Employee | +| key_paths.swift:111:27:111:27 | Bob | | file://:0:0:0:0 | String | +| key_paths.swift:111:42:111:42 | 200 | | file://:0:0:0:0 | Int | +| key_paths.swift:112:20:112:20 | e1 | | key_paths.swift:93:1:102:1 | Employee | +| key_paths.swift:112:24:112:24 | e2 | | key_paths.swift:93:1:102:1 | Employee | +| key_paths.swift:114:20:114:20 | 0 | | file://:0:0:0:0 | Int | +| key_paths.swift:116:25:116:25 | 0 | | file://:0:0:0:0 | Int | +| key_paths.swift:121:7:121:7 | self | | key_paths.swift:121:1:128:1 | KPContainer | +| key_paths.swift:121:7:121:7 | self | T | key_paths.swift:121:19:121:19 | T | +| key_paths.swift:122:7:122:7 | self | | key_paths.swift:121:1:128:1 | KPContainer | +| key_paths.swift:122:7:122:7 | self | | key_paths.swift:121:1:128:1 | KPContainer | +| key_paths.swift:122:7:122:7 | self | | key_paths.swift:121:1:128:1 | KPContainer | +| key_paths.swift:122:7:122:7 | self | T | key_paths.swift:121:19:121:19 | T | +| key_paths.swift:122:7:122:7 | self | T | key_paths.swift:121:19:121:19 | T | +| key_paths.swift:122:7:122:7 | self | T | key_paths.swift:121:19:121:19 | T | +| key_paths.swift:122:7:122:7 | value | | key_paths.swift:121:19:121:19 | T | +| key_paths.swift:125:3:125:3 | self | | key_paths.swift:121:1:128:1 | KPContainer | +| key_paths.swift:125:3:125:3 | self | T | key_paths.swift:121:19:121:19 | T | +| key_paths.swift:125:8:125:14 | item | | key_paths.swift:121:19:121:19 | T | +| key_paths.swift:126:5:126:5 | self | | key_paths.swift:121:1:128:1 | KPContainer | +| key_paths.swift:126:5:126:5 | self | T | key_paths.swift:121:19:121:19 | T | +| key_paths.swift:126:5:126:10 | .item | | key_paths.swift:121:19:121:19 | T | +| key_paths.swift:126:17:126:17 | item | | key_paths.swift:121:19:121:19 | T | +| key_paths.swift:132:7:132:7 | c | | key_paths.swift:121:1:128:1 | KPContainer | +| key_paths.swift:132:7:132:7 | c | | key_paths.swift:121:1:128:1 | KPContainer | +| key_paths.swift:132:7:132:7 | c | T | file://:0:0:0:0 | Int | +| key_paths.swift:132:7:132:7 | c | T | file://:0:0:0:0 | Int | +| key_paths.swift:132:11:132:31 | call to KPContainer.init(item:) | | key_paths.swift:121:1:128:1 | KPContainer | +| key_paths.swift:132:11:132:31 | call to KPContainer.init(item:) | T | file://:0:0:0:0 | Int | +| key_paths.swift:132:29:132:29 | 42 | | file://:0:0:0:0 | Int | +| key_paths.swift:133:11:133:11 | c | | key_paths.swift:121:1:128:1 | KPContainer | +| key_paths.swift:133:11:133:11 | c | T | file://:0:0:0:0 | Int | +| key_paths.swift:139:7:139:7 | p | | key_paths.swift:3:1:17:1 | Point | +| key_paths.swift:139:7:139:7 | p | | key_paths.swift:3:1:17:1 | Point | +| key_paths.swift:139:11:139:31 | call to Point.init(x:y:) | | key_paths.swift:3:1:17:1 | Point | +| key_paths.swift:141:3:141:3 | p | | key_paths.swift:3:1:17:1 | Point | +| key_paths.swift:142:14:142:14 | p | | key_paths.swift:3:1:17:1 | Point | +| key_paths.swift:150:7:150:7 | kpStartX | | file://:0:0:0:0 | WritableKeyPath | +| key_paths.swift:150:7:150:7 | kpStartX | | file://:0:0:0:0 | WritableKeyPath | +| key_paths.swift:150:18:150:45 | call to appending(path:) | | file://:0:0:0:0 | WritableKeyPath | +| key_paths.swift:152:7:152:7 | s | | key_paths.swift:3:1:17:1 | Point | +| key_paths.swift:152:7:152:7 | s | | key_paths.swift:3:1:17:1 | Point | +| key_paths.swift:152:11:152:31 | call to Point.init(x:y:) | | key_paths.swift:3:1:17:1 | Point | +| key_paths.swift:153:7:153:7 | e | | key_paths.swift:3:1:17:1 | Point | +| key_paths.swift:153:7:153:7 | e | | key_paths.swift:3:1:17:1 | Point | +| key_paths.swift:153:11:153:31 | call to Point.init(x:y:) | | key_paths.swift:3:1:17:1 | Point | +| key_paths.swift:154:7:154:7 | line | | key_paths.swift:19:1:28:1 | Line | +| key_paths.swift:154:7:154:7 | line | | key_paths.swift:19:1:28:1 | Line | +| key_paths.swift:154:14:154:35 | call to Line.init(start:end:) | | key_paths.swift:19:1:28:1 | Line | +| key_paths.swift:154:26:154:26 | s | | key_paths.swift:3:1:17:1 | Point | +| key_paths.swift:154:34:154:34 | e | | key_paths.swift:3:1:17:1 | Point | +| key_paths.swift:155:13:155:13 | line | | key_paths.swift:19:1:28:1 | Line | +| key_paths.swift:155:27:155:27 | kpStartX | | file://:0:0:0:0 | WritableKeyPath | +| key_paths.swift:161:7:161:7 | e1 | | key_paths.swift:93:1:102:1 | Employee | +| key_paths.swift:161:7:161:7 | e1 | | key_paths.swift:93:1:102:1 | Employee | +| key_paths.swift:161:12:161:47 | call to Employee.init(name:salary:) | | key_paths.swift:93:1:102:1 | Employee | +| key_paths.swift:161:27:161:27 | Alice | | file://:0:0:0:0 | String | +| key_paths.swift:161:44:161:44 | 100 | | file://:0:0:0:0 | Int | +| key_paths.swift:162:7:162:7 | e2 | | key_paths.swift:93:1:102:1 | Employee | +| key_paths.swift:162:7:162:7 | e2 | | key_paths.swift:93:1:102:1 | Employee | +| key_paths.swift:162:12:162:45 | call to Employee.init(name:salary:) | | key_paths.swift:93:1:102:1 | Employee | +| key_paths.swift:162:27:162:27 | Bob | | file://:0:0:0:0 | String | +| key_paths.swift:162:42:162:42 | 200 | | file://:0:0:0:0 | Int | +| key_paths.swift:163:20:163:20 | e1 | | key_paths.swift:93:1:102:1 | Employee | +| key_paths.swift:163:24:163:24 | e2 | | key_paths.swift:93:1:102:1 | Employee | +| key_paths.swift:164:29:164:29 | $0 | | key_paths.swift:93:1:102:1 | Employee | +| key_paths.swift:165:20:165:20 | 0 | | file://:0:0:0:0 | Int | +| key_paths.swift:166:32:166:32 | $0 | | key_paths.swift:93:1:102:1 | Employee | +| key_paths.swift:167:25:167:25 | 0 | | file://:0:0:0:0 | Int | +| key_paths.swift:172:7:172:7 | self | | key_paths.swift:172:1:179:1 | Shape2 | +| key_paths.swift:173:7:173:7 | self | | key_paths.swift:172:1:179:1 | Shape2 | +| key_paths.swift:173:7:173:7 | self | | key_paths.swift:172:1:179:1 | Shape2 | +| key_paths.swift:173:7:173:7 | self | | key_paths.swift:172:1:179:1 | Shape2 | +| key_paths.swift:173:7:173:7 | value | | file://:0:0:0:0 | String | +| key_paths.swift:176:3:176:3 | self | | key_paths.swift:172:1:179:1 | Shape2 | +| key_paths.swift:176:8:176:15 | color | | file://:0:0:0:0 | String | +| key_paths.swift:177:5:177:5 | self | | key_paths.swift:172:1:179:1 | Shape2 | +| key_paths.swift:177:5:177:10 | .color | | file://:0:0:0:0 | String | +| key_paths.swift:177:18:177:18 | color | | file://:0:0:0:0 | String | +| key_paths.swift:181:7:181:7 | key_paths.Circle2 | | file://:0:0:0:0 | String | +| key_paths.swift:181:7:181:7 | self | | key_paths.swift:181:1:189:1 | Circle2 | +| key_paths.swift:181:24:181:24 | self | | key_paths.swift:181:1:189:1 | Circle2 | +| key_paths.swift:182:7:182:7 | self | | key_paths.swift:181:1:189:1 | Circle2 | +| key_paths.swift:182:7:182:7 | self | | key_paths.swift:181:1:189:1 | Circle2 | +| key_paths.swift:182:7:182:7 | self | | key_paths.swift:181:1:189:1 | Circle2 | +| key_paths.swift:182:7:182:7 | value | | file://:0:0:0:0 | Double | +| key_paths.swift:185:3:185:3 | self | | key_paths.swift:181:1:189:1 | Circle2 | +| key_paths.swift:185:8:185:15 | color | | file://:0:0:0:0 | String | +| key_paths.swift:185:23:185:31 | radius | | file://:0:0:0:0 | Double | +| key_paths.swift:186:5:186:5 | self | | key_paths.swift:181:1:189:1 | Circle2 | +| key_paths.swift:186:5:186:10 | .radius | | file://:0:0:0:0 | Double | +| key_paths.swift:186:19:186:19 | radius | | file://:0:0:0:0 | Double | +| key_paths.swift:187:5:187:28 | call to Shape2.init(color:) | | key_paths.swift:172:1:179:1 | Shape2 | +| key_paths.swift:187:23:187:23 | color | | file://:0:0:0:0 | String | +| key_paths.swift:195:7:195:7 | c | | key_paths.swift:181:1:189:1 | Circle2 | +| key_paths.swift:195:7:195:7 | c | | key_paths.swift:181:1:189:1 | Circle2 | +| key_paths.swift:195:11:195:44 | call to Circle2.init(color:radius:) | | key_paths.swift:181:1:189:1 | Circle2 | +| key_paths.swift:195:26:195:26 | red | | file://:0:0:0:0 | String | +| key_paths.swift:196:13:196:13 | c | | key_paths.swift:181:1:189:1 | Circle2 | +| key_paths.swift:197:13:197:13 | c | | key_paths.swift:181:1:189:1 | Circle2 | +| key_paths.swift:205:16:205:16 | 42 | | file://:0:0:0:0 | Int | +| key_paths.swift:205:20:205:20 | hello | | file://:0:0:0:0 | String | +| key_paths.swift:213:24:213:24 | 0 | | file://:0:0:0:0 | Int | +| key_paths.swift:214:14:214:14 | 10 | | file://:0:0:0:0 | Int | +| key_paths.swift:214:18:214:18 | 20 | | file://:0:0:0:0 | Int | +| key_paths.swift:214:22:214:22 | 30 | | file://:0:0:0:0 | Int | +| key_paths.swift:217:30:217:30 | x | | file://:0:0:0:0 | String | +| key_paths.swift:218:15:218:15 | x | | file://:0:0:0:0 | String | +| key_paths.swift:218:20:218:20 | 1 | | file://:0:0:0:0 | Int | +| key_paths.swift:218:23:218:23 | y | | file://:0:0:0:0 | String | +| key_paths.swift:218:28:218:28 | 2 | | file://:0:0:0:0 | Int | +| lub.swift:1:7:1:7 | self | | lub.swift:1:1:1:10 | A | +| lub.swift:1:7:1:7 | self | | lub.swift:1:1:1:10 | A | +| lub.swift:3:7:3:7 | self | | lub.swift:3:1:3:14 | B | +| lub.swift:3:13:3:13 | self | | lub.swift:3:1:3:14 | B | +| lub.swift:5:7:5:7 | self | | lub.swift:5:1:5:14 | C | +| lub.swift:5:13:5:13 | self | | lub.swift:5:1:5:14 | C | +| lub.swift:8:7:8:7 | b | | lub.swift:3:1:3:14 | B | +| lub.swift:8:7:8:7 | b | | lub.swift:3:1:3:14 | B | +| lub.swift:8:11:8:13 | call to B.init() | | lub.swift:3:1:3:14 | B | +| lub.swift:9:7:9:7 | c | | lub.swift:5:1:5:14 | C | +| lub.swift:9:7:9:7 | c | | lub.swift:5:1:5:14 | C | +| lub.swift:9:11:9:13 | call to C.init() | | lub.swift:5:1:5:14 | C | +| lub.swift:10:7:10:7 | x | | lub.swift:3:1:3:14 | B | +| lub.swift:10:7:10:7 | x | | lub.swift:3:1:3:14 | B | +| lub.swift:10:7:10:7 | x | | lub.swift:5:1:5:14 | C | +| lub.swift:10:7:10:7 | x | | lub.swift:5:1:5:14 | C | +| lub.swift:10:11:10:11 | 2 | | file://:0:0:0:0 | Int | +| lub.swift:10:11:10:23 | ... ? ... : ... | | lub.swift:3:1:3:14 | B | +| lub.swift:10:11:10:23 | ... ? ... : ... | | lub.swift:5:1:5:14 | C | +| lub.swift:10:15:10:15 | 3 | | file://:0:0:0:0 | Int | +| lub.swift:10:19:10:19 | b | | lub.swift:3:1:3:14 | B | +| lub.swift:10:23:10:23 | c | | lub.swift:5:1:5:14 | C | +| overload_resolution.swift:3:7:3:7 | self | | overload_resolution.swift:3:1:23:1 | OverloadByType | +| overload_resolution.swift:3:7:3:7 | self | | overload_resolution.swift:3:1:23:1 | OverloadByType | +| overload_resolution.swift:5:8:5:8 | self | | overload_resolution.swift:3:1:23:1 | OverloadByType | +| overload_resolution.swift:5:15:5:20 | x | | file://:0:0:0:0 | Int | +| overload_resolution.swift:6:12:6:12 | int | | file://:0:0:0:0 | String | +| overload_resolution.swift:10:8:10:8 | self | | overload_resolution.swift:3:1:23:1 | OverloadByType | +| overload_resolution.swift:10:15:10:20 | x | | file://:0:0:0:0 | Double | +| overload_resolution.swift:11:12:11:12 | double | | file://:0:0:0:0 | String | +| overload_resolution.swift:15:8:15:8 | self | | overload_resolution.swift:3:1:23:1 | OverloadByType | +| overload_resolution.swift:15:15:15:20 | x | | file://:0:0:0:0 | String | +| overload_resolution.swift:16:12:16:12 | string | | file://:0:0:0:0 | String | +| overload_resolution.swift:20:8:20:8 | self | | overload_resolution.swift:3:1:23:1 | OverloadByType | +| overload_resolution.swift:20:15:20:20 | x | | file://:0:0:0:0 | Bool | +| overload_resolution.swift:21:12:21:12 | bool | | file://:0:0:0:0 | String | +| overload_resolution.swift:26:7:26:7 | o | | overload_resolution.swift:3:1:23:1 | OverloadByType | +| overload_resolution.swift:26:7:26:7 | o | | overload_resolution.swift:3:1:23:1 | OverloadByType | +| overload_resolution.swift:26:11:26:26 | call to OverloadByType.init() | | overload_resolution.swift:3:1:23:1 | OverloadByType | +| overload_resolution.swift:27:7:27:7 | r1 | | file://:0:0:0:0 | String | +| overload_resolution.swift:27:7:27:7 | r1 | | file://:0:0:0:0 | String | +| overload_resolution.swift:27:12:27:12 | o | | overload_resolution.swift:3:1:23:1 | OverloadByType | +| overload_resolution.swift:27:12:27:23 | call to handle(_:) | | file://:0:0:0:0 | String | +| overload_resolution.swift:27:21:27:21 | 42 | | file://:0:0:0:0 | Int | +| overload_resolution.swift:28:7:28:7 | r2 | | file://:0:0:0:0 | String | +| overload_resolution.swift:28:7:28:7 | r2 | | file://:0:0:0:0 | String | +| overload_resolution.swift:28:12:28:12 | o | | overload_resolution.swift:3:1:23:1 | OverloadByType | +| overload_resolution.swift:28:12:28:25 | call to handle(_:) | | file://:0:0:0:0 | String | +| overload_resolution.swift:29:7:29:7 | r3 | | file://:0:0:0:0 | String | +| overload_resolution.swift:29:7:29:7 | r3 | | file://:0:0:0:0 | String | +| overload_resolution.swift:29:12:29:12 | o | | overload_resolution.swift:3:1:23:1 | OverloadByType | +| overload_resolution.swift:29:12:29:25 | call to handle(_:) | | file://:0:0:0:0 | String | +| overload_resolution.swift:29:21:29:21 | hi | | file://:0:0:0:0 | String | +| overload_resolution.swift:30:7:30:7 | r4 | | file://:0:0:0:0 | String | +| overload_resolution.swift:30:7:30:7 | r4 | | file://:0:0:0:0 | String | +| overload_resolution.swift:30:12:30:12 | o | | overload_resolution.swift:3:1:23:1 | OverloadByType | +| overload_resolution.swift:30:12:30:25 | call to handle(_:) | | file://:0:0:0:0 | String | +| overload_resolution.swift:35:7:35:7 | self | | overload_resolution.swift:35:1:55:1 | OverloadByLabel | +| overload_resolution.swift:35:7:35:7 | self | | overload_resolution.swift:35:1:55:1 | OverloadByLabel | +| overload_resolution.swift:37:8:37:8 | self | | overload_resolution.swift:35:1:55:1 | OverloadByLabel | +| overload_resolution.swift:37:18:37:25 | width | | file://:0:0:0:0 | Int | +| overload_resolution.swift:38:12:38:12 | width | | file://:0:0:0:0 | String | +| overload_resolution.swift:42:8:42:8 | self | | overload_resolution.swift:35:1:55:1 | OverloadByLabel | +| overload_resolution.swift:42:18:42:26 | height | | file://:0:0:0:0 | Int | +| overload_resolution.swift:43:12:43:12 | height | | file://:0:0:0:0 | String | +| overload_resolution.swift:47:8:47:8 | self | | overload_resolution.swift:35:1:55:1 | OverloadByLabel | +| overload_resolution.swift:47:18:47:25 | width | | file://:0:0:0:0 | Int | +| overload_resolution.swift:47:30:47:38 | height | | file://:0:0:0:0 | Int | +| overload_resolution.swift:48:12:48:12 | both | | file://:0:0:0:0 | String | +| overload_resolution.swift:52:8:52:8 | self | | overload_resolution.swift:35:1:55:1 | OverloadByLabel | +| overload_resolution.swift:52:18:52:24 | size | | file://:0:0:0:0 | Int | +| overload_resolution.swift:53:12:53:12 | size | | file://:0:0:0:0 | String | +| overload_resolution.swift:58:7:58:7 | o | | overload_resolution.swift:35:1:55:1 | OverloadByLabel | +| overload_resolution.swift:58:7:58:7 | o | | overload_resolution.swift:35:1:55:1 | OverloadByLabel | +| overload_resolution.swift:58:11:58:27 | call to OverloadByLabel.init() | | overload_resolution.swift:35:1:55:1 | OverloadByLabel | +| overload_resolution.swift:59:7:59:7 | r1 | | file://:0:0:0:0 | String | +| overload_resolution.swift:59:7:59:7 | r1 | | file://:0:0:0:0 | String | +| overload_resolution.swift:59:12:59:12 | o | | overload_resolution.swift:35:1:55:1 | OverloadByLabel | +| overload_resolution.swift:59:12:59:33 | call to configure(width:) | | file://:0:0:0:0 | String | +| overload_resolution.swift:59:31:59:31 | 10 | | file://:0:0:0:0 | Int | +| overload_resolution.swift:60:7:60:7 | r2 | | file://:0:0:0:0 | String | +| overload_resolution.swift:60:7:60:7 | r2 | | file://:0:0:0:0 | String | +| overload_resolution.swift:60:12:60:12 | o | | overload_resolution.swift:35:1:55:1 | OverloadByLabel | +| overload_resolution.swift:60:12:60:34 | call to configure(height:) | | file://:0:0:0:0 | String | +| overload_resolution.swift:60:32:60:32 | 20 | | file://:0:0:0:0 | Int | +| overload_resolution.swift:61:7:61:7 | r3 | | file://:0:0:0:0 | String | +| overload_resolution.swift:61:7:61:7 | r3 | | file://:0:0:0:0 | String | +| overload_resolution.swift:61:12:61:12 | o | | overload_resolution.swift:35:1:55:1 | OverloadByLabel | +| overload_resolution.swift:61:12:61:45 | call to configure(width:height:) | | file://:0:0:0:0 | String | +| overload_resolution.swift:61:31:61:31 | 10 | | file://:0:0:0:0 | Int | +| overload_resolution.swift:61:43:61:43 | 20 | | file://:0:0:0:0 | Int | +| overload_resolution.swift:62:7:62:7 | r4 | | file://:0:0:0:0 | String | +| overload_resolution.swift:62:7:62:7 | r4 | | file://:0:0:0:0 | String | +| overload_resolution.swift:62:12:62:12 | o | | overload_resolution.swift:35:1:55:1 | OverloadByLabel | +| overload_resolution.swift:62:12:62:32 | call to configure(size:) | | file://:0:0:0:0 | String | +| overload_resolution.swift:62:30:62:30 | 30 | | file://:0:0:0:0 | Int | +| overload_resolution.swift:67:7:67:7 | self | | overload_resolution.swift:67:1:87:1 | OverloadByArity | +| overload_resolution.swift:67:7:67:7 | self | | overload_resolution.swift:67:1:87:1 | OverloadByArity | +| overload_resolution.swift:69:8:69:8 | self | | overload_resolution.swift:67:1:87:1 | OverloadByArity | +| overload_resolution.swift:70:12:70:12 | 0 | | file://:0:0:0:0 | Int | +| overload_resolution.swift:74:8:74:8 | self | | overload_resolution.swift:67:1:87:1 | OverloadByArity | +| overload_resolution.swift:74:16:74:21 | x | | file://:0:0:0:0 | Int | +| overload_resolution.swift:75:12:75:12 | x | | file://:0:0:0:0 | Int | +| overload_resolution.swift:79:8:79:8 | self | | overload_resolution.swift:67:1:87:1 | OverloadByArity | +| overload_resolution.swift:79:16:79:21 | x | | file://:0:0:0:0 | Int | +| overload_resolution.swift:79:26:79:31 | y | | file://:0:0:0:0 | Int | +| overload_resolution.swift:80:12:80:12 | x | | file://:0:0:0:0 | Int | +| overload_resolution.swift:80:16:80:16 | y | | file://:0:0:0:0 | Int | +| overload_resolution.swift:84:8:84:8 | self | | overload_resolution.swift:67:1:87:1 | OverloadByArity | +| overload_resolution.swift:84:16:84:21 | x | | file://:0:0:0:0 | Int | +| overload_resolution.swift:84:26:84:31 | y | | file://:0:0:0:0 | Int | +| overload_resolution.swift:84:36:84:41 | z | | file://:0:0:0:0 | Int | +| overload_resolution.swift:85:12:85:12 | x | | file://:0:0:0:0 | Int | +| overload_resolution.swift:85:16:85:16 | y | | file://:0:0:0:0 | Int | +| overload_resolution.swift:85:20:85:20 | z | | file://:0:0:0:0 | Int | +| overload_resolution.swift:90:7:90:7 | o | | overload_resolution.swift:67:1:87:1 | OverloadByArity | +| overload_resolution.swift:90:7:90:7 | o | | overload_resolution.swift:67:1:87:1 | OverloadByArity | +| overload_resolution.swift:90:11:90:27 | call to OverloadByArity.init() | | overload_resolution.swift:67:1:87:1 | OverloadByArity | +| overload_resolution.swift:91:7:91:7 | r0 | | file://:0:0:0:0 | Int | +| overload_resolution.swift:91:7:91:7 | r0 | | file://:0:0:0:0 | Int | +| overload_resolution.swift:91:12:91:12 | o | | overload_resolution.swift:67:1:87:1 | OverloadByArity | +| overload_resolution.swift:91:12:91:22 | call to compute() | | file://:0:0:0:0 | Int | +| overload_resolution.swift:92:7:92:7 | r1 | | file://:0:0:0:0 | Int | +| overload_resolution.swift:92:7:92:7 | r1 | | file://:0:0:0:0 | Int | +| overload_resolution.swift:92:12:92:12 | o | | overload_resolution.swift:67:1:87:1 | OverloadByArity | +| overload_resolution.swift:92:12:92:23 | call to compute(_:) | | file://:0:0:0:0 | Int | +| overload_resolution.swift:92:22:92:22 | 1 | | file://:0:0:0:0 | Int | +| overload_resolution.swift:93:7:93:7 | r2 | | file://:0:0:0:0 | Int | +| overload_resolution.swift:93:7:93:7 | r2 | | file://:0:0:0:0 | Int | +| overload_resolution.swift:93:12:93:12 | o | | overload_resolution.swift:67:1:87:1 | OverloadByArity | +| overload_resolution.swift:93:12:93:26 | call to compute(_:_:) | | file://:0:0:0:0 | Int | +| overload_resolution.swift:93:22:93:22 | 1 | | file://:0:0:0:0 | Int | +| overload_resolution.swift:93:25:93:25 | 2 | | file://:0:0:0:0 | Int | +| overload_resolution.swift:94:7:94:7 | r3 | | file://:0:0:0:0 | Int | +| overload_resolution.swift:94:7:94:7 | r3 | | file://:0:0:0:0 | Int | +| overload_resolution.swift:94:12:94:12 | o | | overload_resolution.swift:67:1:87:1 | OverloadByArity | +| overload_resolution.swift:94:12:94:29 | call to compute(_:_:_:) | | file://:0:0:0:0 | Int | +| overload_resolution.swift:94:22:94:22 | 1 | | file://:0:0:0:0 | Int | +| overload_resolution.swift:94:25:94:25 | 2 | | file://:0:0:0:0 | Int | +| overload_resolution.swift:94:28:94:28 | 3 | | file://:0:0:0:0 | Int | +| overload_resolution.swift:99:7:99:7 | self | | overload_resolution.swift:99:1:114:1 | OverloadByReturn | +| overload_resolution.swift:99:7:99:7 | self | | overload_resolution.swift:99:1:114:1 | OverloadByReturn | +| overload_resolution.swift:101:8:101:8 | self | | overload_resolution.swift:99:1:114:1 | OverloadByReturn | +| overload_resolution.swift:102:12:102:12 | 0 | | file://:0:0:0:0 | Int | +| overload_resolution.swift:106:8:106:8 | self | | overload_resolution.swift:99:1:114:1 | OverloadByReturn | +| overload_resolution.swift:107:12:107:12 | | | file://:0:0:0:0 | String | +| overload_resolution.swift:111:8:111:8 | self | | overload_resolution.swift:99:1:114:1 | OverloadByReturn | +| overload_resolution.swift:117:7:117:7 | o | | overload_resolution.swift:99:1:114:1 | OverloadByReturn | +| overload_resolution.swift:117:7:117:7 | o | | overload_resolution.swift:99:1:114:1 | OverloadByReturn | +| overload_resolution.swift:117:11:117:28 | call to OverloadByReturn.init() | | overload_resolution.swift:99:1:114:1 | OverloadByReturn | +| overload_resolution.swift:118:7:118:7 | r1 | | file://:0:0:0:0 | Int | +| overload_resolution.swift:118:7:118:12 | ... as ... | | file://:0:0:0:0 | Int | +| overload_resolution.swift:118:18:118:18 | o | | overload_resolution.swift:99:1:114:1 | OverloadByReturn | +| overload_resolution.swift:118:18:118:27 | call to create() | | file://:0:0:0:0 | Int | +| overload_resolution.swift:119:7:119:7 | r2 | | file://:0:0:0:0 | String | +| overload_resolution.swift:119:7:119:12 | ... as ... | | file://:0:0:0:0 | String | +| overload_resolution.swift:119:21:119:21 | o | | overload_resolution.swift:99:1:114:1 | OverloadByReturn | +| overload_resolution.swift:119:21:119:30 | call to create() | | file://:0:0:0:0 | String | +| overload_resolution.swift:120:7:120:7 | r3 | | file://:0:0:0:0 | Double | +| overload_resolution.swift:120:7:120:12 | ... as ... | | file://:0:0:0:0 | Double | +| overload_resolution.swift:120:21:120:21 | o | | overload_resolution.swift:99:1:114:1 | OverloadByReturn | +| overload_resolution.swift:120:21:120:30 | call to create() | | file://:0:0:0:0 | Double | +| overload_resolution.swift:125:7:125:7 | self | | overload_resolution.swift:125:1:135:1 | OverloadGenericVsConcrete | +| overload_resolution.swift:125:7:125:7 | self | | overload_resolution.swift:125:1:135:1 | OverloadGenericVsConcrete | +| overload_resolution.swift:127:8:127:8 | self | | overload_resolution.swift:125:1:135:1 | OverloadGenericVsConcrete | +| overload_resolution.swift:127:16:127:21 | x | | file://:0:0:0:0 | Int | +| overload_resolution.swift:128:12:128:12 | concrete | | file://:0:0:0:0 | String | +| overload_resolution.swift:132:8:132:8 | self | | overload_resolution.swift:125:1:135:1 | OverloadGenericVsConcrete | +| overload_resolution.swift:132:19:132:24 | x | | overload_resolution.swift:132:16:132:16 | T | +| overload_resolution.swift:133:12:133:12 | generic | | file://:0:0:0:0 | String | +| overload_resolution.swift:138:7:138:7 | o | | overload_resolution.swift:125:1:135:1 | OverloadGenericVsConcrete | +| overload_resolution.swift:138:7:138:7 | o | | overload_resolution.swift:125:1:135:1 | OverloadGenericVsConcrete | +| overload_resolution.swift:138:11:138:37 | call to OverloadGenericVsConcrete.init() | | overload_resolution.swift:125:1:135:1 | OverloadGenericVsConcrete | +| overload_resolution.swift:139:7:139:7 | r1 | | file://:0:0:0:0 | String | +| overload_resolution.swift:139:7:139:7 | r1 | | file://:0:0:0:0 | String | +| overload_resolution.swift:139:12:139:12 | o | | overload_resolution.swift:125:1:135:1 | OverloadGenericVsConcrete | +| overload_resolution.swift:139:12:139:24 | call to process(_:) | | file://:0:0:0:0 | String | +| overload_resolution.swift:139:22:139:22 | 42 | | file://:0:0:0:0 | Int | +| overload_resolution.swift:140:7:140:7 | r2 | | file://:0:0:0:0 | String | +| overload_resolution.swift:140:7:140:7 | r2 | | file://:0:0:0:0 | String | +| overload_resolution.swift:140:12:140:12 | o | | overload_resolution.swift:125:1:135:1 | OverloadGenericVsConcrete | +| overload_resolution.swift:140:12:140:29 | call to process(_:) | | file://:0:0:0:0 | String | +| overload_resolution.swift:140:22:140:22 | hello | | file://:0:0:0:0 | String | +| overload_resolution.swift:141:7:141:7 | r3 | | file://:0:0:0:0 | String | +| overload_resolution.swift:141:7:141:7 | r3 | | file://:0:0:0:0 | String | +| overload_resolution.swift:141:12:141:12 | o | | overload_resolution.swift:125:1:135:1 | OverloadGenericVsConcrete | +| overload_resolution.swift:141:12:141:26 | call to process(_:) | | file://:0:0:0:0 | String | +| overload_resolution.swift:147:19:147:24 | x | | file://:0:0:0:0 | Int | +| overload_resolution.swift:148:10:148:10 | int | | file://:0:0:0:0 | String | +| overload_resolution.swift:152:19:152:24 | x | | file://:0:0:0:0 | String | +| overload_resolution.swift:153:10:153:10 | string | | file://:0:0:0:0 | String | +| overload_resolution.swift:157:19:157:24 | x | | file://:0:0:0:0 | Double | +| overload_resolution.swift:158:10:158:10 | double | | file://:0:0:0:0 | String | +| overload_resolution.swift:162:25:162:25 | 42 | | file://:0:0:0:0 | Int | +| overload_resolution.swift:163:25:163:25 | hi | | file://:0:0:0:0 | String | +| overload_resolution.swift:169:7:169:7 | self | | overload_resolution.swift:169:1:196:1 | MultiInit | +| overload_resolution.swift:170:7:170:7 | self | | overload_resolution.swift:169:1:196:1 | MultiInit | +| overload_resolution.swift:170:7:170:7 | self | | overload_resolution.swift:169:1:196:1 | MultiInit | +| overload_resolution.swift:170:7:170:7 | self | | overload_resolution.swift:169:1:196:1 | MultiInit | +| overload_resolution.swift:170:7:170:7 | value | | file://:0:0:0:0 | String | +| overload_resolution.swift:173:3:173:3 | self | | overload_resolution.swift:169:1:196:1 | MultiInit | +| overload_resolution.swift:174:5:174:5 | .value | | file://:0:0:0:0 | String | +| overload_resolution.swift:174:5:174:5 | self | | overload_resolution.swift:169:1:196:1 | MultiInit | +| overload_resolution.swift:174:13:174:13 | default | | file://:0:0:0:0 | String | +| overload_resolution.swift:178:3:178:3 | self | | overload_resolution.swift:169:1:196:1 | MultiInit | +| overload_resolution.swift:178:8:178:13 | int | | file://:0:0:0:0 | Int | +| overload_resolution.swift:179:5:179:5 | .value | | file://:0:0:0:0 | String | +| overload_resolution.swift:179:5:179:5 | self | | overload_resolution.swift:169:1:196:1 | MultiInit | +| overload_resolution.swift:179:13:179:13 | int | | file://:0:0:0:0 | String | +| overload_resolution.swift:183:3:183:3 | self | | overload_resolution.swift:169:1:196:1 | MultiInit | +| overload_resolution.swift:183:8:183:13 | str | | file://:0:0:0:0 | String | +| overload_resolution.swift:184:5:184:5 | .value | | file://:0:0:0:0 | String | +| overload_resolution.swift:184:5:184:5 | self | | overload_resolution.swift:169:1:196:1 | MultiInit | +| overload_resolution.swift:184:13:184:13 | str | | file://:0:0:0:0 | String | +| overload_resolution.swift:188:3:188:3 | self | | overload_resolution.swift:169:1:196:1 | MultiInit | +| overload_resolution.swift:188:8:188:11 | x | | file://:0:0:0:0 | Int | +| overload_resolution.swift:188:16:188:19 | y | | file://:0:0:0:0 | Int | +| overload_resolution.swift:189:5:189:5 | .value | | file://:0:0:0:0 | String | +| overload_resolution.swift:189:5:189:5 | self | | overload_resolution.swift:169:1:196:1 | MultiInit | +| overload_resolution.swift:189:13:189:13 | pair | | file://:0:0:0:0 | String | +| overload_resolution.swift:193:8:193:8 | self | | overload_resolution.swift:169:1:196:1 | MultiInit | +| overload_resolution.swift:194:12:194:12 | self | | overload_resolution.swift:169:1:196:1 | MultiInit | +| overload_resolution.swift:199:7:199:7 | m1 | | overload_resolution.swift:169:1:196:1 | MultiInit | +| overload_resolution.swift:199:7:199:7 | m1 | | overload_resolution.swift:169:1:196:1 | MultiInit | +| overload_resolution.swift:199:12:199:22 | call to MultiInit.init() | | overload_resolution.swift:169:1:196:1 | MultiInit | +| overload_resolution.swift:200:7:200:7 | m2 | | overload_resolution.swift:169:1:196:1 | MultiInit | +| overload_resolution.swift:200:7:200:7 | m2 | | overload_resolution.swift:169:1:196:1 | MultiInit | +| overload_resolution.swift:200:12:200:28 | call to MultiInit.init(int:) | | overload_resolution.swift:169:1:196:1 | MultiInit | +| overload_resolution.swift:200:27:200:27 | 5 | | file://:0:0:0:0 | Int | +| overload_resolution.swift:201:7:201:7 | m3 | | overload_resolution.swift:169:1:196:1 | MultiInit | +| overload_resolution.swift:201:7:201:7 | m3 | | overload_resolution.swift:169:1:196:1 | MultiInit | +| overload_resolution.swift:201:12:201:30 | call to MultiInit.init(str:) | | overload_resolution.swift:169:1:196:1 | MultiInit | +| overload_resolution.swift:201:27:201:27 | x | | file://:0:0:0:0 | String | +| overload_resolution.swift:202:7:202:7 | m4 | | overload_resolution.swift:169:1:196:1 | MultiInit | +| overload_resolution.swift:202:7:202:7 | m4 | | overload_resolution.swift:169:1:196:1 | MultiInit | +| overload_resolution.swift:202:12:202:32 | call to MultiInit.init(x:y:) | | overload_resolution.swift:169:1:196:1 | MultiInit | +| overload_resolution.swift:202:25:202:25 | 1 | | file://:0:0:0:0 | Int | +| overload_resolution.swift:202:31:202:31 | 2 | | file://:0:0:0:0 | Int | +| overload_resolution.swift:203:7:203:7 | v | | file://:0:0:0:0 | String | +| overload_resolution.swift:203:7:203:7 | v | | file://:0:0:0:0 | String | +| overload_resolution.swift:203:11:203:11 | m1 | | overload_resolution.swift:169:1:196:1 | MultiInit | +| overload_resolution.swift:203:11:203:23 | call to getValue() | | file://:0:0:0:0 | String | +| overload_resolution.swift:208:7:208:7 | self | | overload_resolution.swift:208:1:221:1 | StaticVsInstance | +| overload_resolution.swift:210:8:210:8 | self | | overload_resolution.swift:208:1:221:1 | StaticVsInstance | +| overload_resolution.swift:211:12:211:12 | instance | | file://:0:0:0:0 | String | +| overload_resolution.swift:216:12:216:12 | static | | file://:0:0:0:0 | String | +| overload_resolution.swift:220:3:220:3 | self | | overload_resolution.swift:208:1:221:1 | StaticVsInstance | +| overload_resolution.swift:224:7:224:7 | o | | overload_resolution.swift:208:1:221:1 | StaticVsInstance | +| overload_resolution.swift:224:7:224:7 | o | | overload_resolution.swift:208:1:221:1 | StaticVsInstance | +| overload_resolution.swift:224:11:224:28 | call to StaticVsInstance.init() | | overload_resolution.swift:208:1:221:1 | StaticVsInstance | +| overload_resolution.swift:225:7:225:7 | r1 | | file://:0:0:0:0 | String | +| overload_resolution.swift:225:7:225:7 | r1 | | file://:0:0:0:0 | String | +| overload_resolution.swift:225:12:225:12 | o | | overload_resolution.swift:208:1:221:1 | StaticVsInstance | +| overload_resolution.swift:225:12:225:21 | call to action() | | file://:0:0:0:0 | String | +| overload_resolution.swift:226:7:226:7 | r2 | | file://:0:0:0:0 | String | +| overload_resolution.swift:226:7:226:7 | r2 | | file://:0:0:0:0 | String | +| overload_resolution.swift:226:12:226:36 | call to action() | | file://:0:0:0:0 | String | +| overload_resolution.swift:239:12:239:12 | default | | file://:0:0:0:0 | String | +| overload_resolution.swift:244:12:244:12 | extra | | file://:0:0:0:0 | String | +| overload_resolution.swift:248:7:248:7 | self | | overload_resolution.swift:248:1:256:1 | DescribableImpl | +| overload_resolution.swift:250:3:250:3 | self | | overload_resolution.swift:248:1:256:1 | DescribableImpl | +| overload_resolution.swift:253:8:253:8 | self | | overload_resolution.swift:248:1:256:1 | DescribableImpl | +| overload_resolution.swift:254:12:254:12 | concrete | | file://:0:0:0:0 | String | +| overload_resolution.swift:259:7:259:7 | d | | overload_resolution.swift:248:1:256:1 | DescribableImpl | +| overload_resolution.swift:259:7:259:7 | d | | overload_resolution.swift:248:1:256:1 | DescribableImpl | +| overload_resolution.swift:259:11:259:27 | call to DescribableImpl.init() | | overload_resolution.swift:248:1:256:1 | DescribableImpl | +| overload_resolution.swift:260:7:260:7 | r1 | | file://:0:0:0:0 | String | +| overload_resolution.swift:260:7:260:7 | r1 | | file://:0:0:0:0 | String | +| overload_resolution.swift:260:12:260:12 | d | | overload_resolution.swift:248:1:256:1 | DescribableImpl | +| overload_resolution.swift:260:12:260:23 | call to describe() | | file://:0:0:0:0 | String | +| overload_resolution.swift:261:7:261:7 | r2 | | file://:0:0:0:0 | String | +| overload_resolution.swift:261:7:261:7 | r2 | | file://:0:0:0:0 | String | +| overload_resolution.swift:261:12:261:12 | d | | overload_resolution.swift:248:1:256:1 | DescribableImpl | +| overload_resolution.swift:261:12:261:20 | call to extra() | | file://:0:0:0:0 | String | +| overload_resolution.swift:266:7:266:7 | self | | overload_resolution.swift:266:1:279:1 | Base | +| overload_resolution.swift:268:3:268:3 | self | | overload_resolution.swift:266:1:279:1 | Base | +| overload_resolution.swift:271:8:271:8 | self | | overload_resolution.swift:266:1:279:1 | Base | +| overload_resolution.swift:272:12:272:12 | base | | file://:0:0:0:0 | String | +| overload_resolution.swift:276:8:276:8 | self | | overload_resolution.swift:266:1:279:1 | Base | +| overload_resolution.swift:277:12:277:12 | baseOnly | | file://:0:0:0:0 | String | +| overload_resolution.swift:281:7:281:7 | self | | overload_resolution.swift:281:1:296:1 | Sub | +| overload_resolution.swift:283:12:283:12 | self | | overload_resolution.swift:281:1:296:1 | Sub | +| overload_resolution.swift:284:5:284:16 | call to Base.init() | | overload_resolution.swift:266:1:279:1 | Base | +| overload_resolution.swift:288:17:288:17 | self | | overload_resolution.swift:281:1:296:1 | Sub | +| overload_resolution.swift:289:12:289:12 | sub | | file://:0:0:0:0 | String | +| overload_resolution.swift:293:8:293:8 | self | | overload_resolution.swift:281:1:296:1 | Sub | +| overload_resolution.swift:294:12:294:12 | subOnly | | file://:0:0:0:0 | String | +| overload_resolution.swift:298:7:298:7 | self | | overload_resolution.swift:298:1:308:1 | SubSub | +| overload_resolution.swift:300:12:300:12 | self | | overload_resolution.swift:298:1:308:1 | SubSub | +| overload_resolution.swift:301:5:301:16 | call to Sub.init() | | overload_resolution.swift:281:1:296:1 | Sub | +| overload_resolution.swift:305:17:305:17 | self | | overload_resolution.swift:298:1:308:1 | SubSub | +| overload_resolution.swift:306:12:306:12 | subsub | | file://:0:0:0:0 | String | +| overload_resolution.swift:311:7:311:7 | b | | overload_resolution.swift:266:1:279:1 | Base | +| overload_resolution.swift:311:7:311:7 | b | | overload_resolution.swift:266:1:279:1 | Base | +| overload_resolution.swift:311:11:311:16 | call to Base.init() | | overload_resolution.swift:266:1:279:1 | Base | +| overload_resolution.swift:312:7:312:7 | rb | | file://:0:0:0:0 | String | +| overload_resolution.swift:312:7:312:7 | rb | | file://:0:0:0:0 | String | +| overload_resolution.swift:312:12:312:12 | b | | overload_resolution.swift:266:1:279:1 | Base | +| overload_resolution.swift:312:12:312:21 | call to action() | | file://:0:0:0:0 | String | +| overload_resolution.swift:314:7:314:7 | s | | overload_resolution.swift:281:1:296:1 | Sub | +| overload_resolution.swift:314:7:314:7 | s | | overload_resolution.swift:281:1:296:1 | Sub | +| overload_resolution.swift:314:11:314:15 | call to Sub.init() | | overload_resolution.swift:281:1:296:1 | Sub | +| overload_resolution.swift:315:7:315:7 | rs | | file://:0:0:0:0 | String | +| overload_resolution.swift:315:7:315:7 | rs | | file://:0:0:0:0 | String | +| overload_resolution.swift:315:12:315:12 | s | | overload_resolution.swift:281:1:296:1 | Sub | +| overload_resolution.swift:315:12:315:21 | call to action() | | file://:0:0:0:0 | String | +| overload_resolution.swift:316:7:316:7 | rbo | | file://:0:0:0:0 | String | +| overload_resolution.swift:316:7:316:7 | rbo | | file://:0:0:0:0 | String | +| overload_resolution.swift:316:13:316:13 | s | | overload_resolution.swift:281:1:296:1 | Sub | +| overload_resolution.swift:316:13:316:24 | call to baseOnly() | | file://:0:0:0:0 | String | +| overload_resolution.swift:317:7:317:7 | rso | | file://:0:0:0:0 | String | +| overload_resolution.swift:317:7:317:7 | rso | | file://:0:0:0:0 | String | +| overload_resolution.swift:317:13:317:13 | s | | overload_resolution.swift:281:1:296:1 | Sub | +| overload_resolution.swift:317:13:317:23 | call to subOnly() | | file://:0:0:0:0 | String | +| overload_resolution.swift:319:7:319:7 | ss | | overload_resolution.swift:298:1:308:1 | SubSub | +| overload_resolution.swift:319:7:319:7 | ss | | overload_resolution.swift:298:1:308:1 | SubSub | +| overload_resolution.swift:319:12:319:19 | call to SubSub.init() | | overload_resolution.swift:298:1:308:1 | SubSub | +| overload_resolution.swift:320:7:320:7 | rss | | file://:0:0:0:0 | String | +| overload_resolution.swift:320:7:320:7 | rss | | file://:0:0:0:0 | String | +| overload_resolution.swift:320:13:320:13 | ss | | overload_resolution.swift:298:1:308:1 | SubSub | +| overload_resolution.swift:320:13:320:23 | call to action() | | file://:0:0:0:0 | String | +| overload_resolution.swift:321:7:321:7 | rbo2 | | file://:0:0:0:0 | String | +| overload_resolution.swift:321:7:321:7 | rbo2 | | file://:0:0:0:0 | String | +| overload_resolution.swift:321:14:321:14 | ss | | overload_resolution.swift:298:1:308:1 | SubSub | +| overload_resolution.swift:321:14:321:26 | call to baseOnly() | | file://:0:0:0:0 | String | +| overload_resolution.swift:322:7:322:7 | rso2 | | file://:0:0:0:0 | String | +| overload_resolution.swift:322:7:322:7 | rso2 | | file://:0:0:0:0 | String | +| overload_resolution.swift:322:14:322:14 | ss | | overload_resolution.swift:298:1:308:1 | SubSub | +| overload_resolution.swift:322:14:322:25 | call to subOnly() | | file://:0:0:0:0 | String | +| overload_resolution.swift:327:7:327:7 | self | | overload_resolution.swift:327:1:342:1 | LabelVariants | +| overload_resolution.swift:327:7:327:7 | self | | overload_resolution.swift:327:1:342:1 | LabelVariants | +| overload_resolution.swift:329:8:329:8 | self | | overload_resolution.swift:327:1:342:1 | LabelVariants | +| overload_resolution.swift:329:13:329:24 | target | | file://:0:0:0:0 | String | +| overload_resolution.swift:330:12:330:12 | target | | file://:0:0:0:0 | String | +| overload_resolution.swift:334:8:334:8 | self | | overload_resolution.swift:327:1:342:1 | LabelVariants | +| overload_resolution.swift:334:13:334:26 | source | | file://:0:0:0:0 | String | +| overload_resolution.swift:335:12:335:12 | source | | file://:0:0:0:0 | String | +| overload_resolution.swift:339:8:339:8 | self | | overload_resolution.swift:327:1:342:1 | LabelVariants | +| overload_resolution.swift:339:13:339:24 | target | | file://:0:0:0:0 | String | +| overload_resolution.swift:339:32:339:45 | source | | file://:0:0:0:0 | String | +| overload_resolution.swift:340:12:340:12 | target | | file://:0:0:0:0 | String | +| overload_resolution.swift:340:21:340:21 | source | | file://:0:0:0:0 | String | +| overload_resolution.swift:345:7:345:7 | o | | overload_resolution.swift:327:1:342:1 | LabelVariants | +| overload_resolution.swift:345:7:345:7 | o | | overload_resolution.swift:327:1:342:1 | LabelVariants | +| overload_resolution.swift:345:11:345:25 | call to LabelVariants.init() | | overload_resolution.swift:327:1:342:1 | LabelVariants | +| overload_resolution.swift:346:7:346:7 | r1 | | file://:0:0:0:0 | String | +| overload_resolution.swift:346:7:346:7 | r1 | | file://:0:0:0:0 | String | +| overload_resolution.swift:346:12:346:12 | o | | overload_resolution.swift:327:1:342:1 | LabelVariants | +| overload_resolution.swift:346:12:346:26 | call to send(to:) | | file://:0:0:0:0 | String | +| overload_resolution.swift:346:23:346:23 | x | | file://:0:0:0:0 | String | +| overload_resolution.swift:347:7:347:7 | r2 | | file://:0:0:0:0 | String | +| overload_resolution.swift:347:7:347:7 | r2 | | file://:0:0:0:0 | String | +| overload_resolution.swift:347:12:347:12 | o | | overload_resolution.swift:327:1:342:1 | LabelVariants | +| overload_resolution.swift:347:12:347:28 | call to send(from:) | | file://:0:0:0:0 | String | +| overload_resolution.swift:347:25:347:25 | y | | file://:0:0:0:0 | String | +| overload_resolution.swift:348:7:348:7 | r3 | | file://:0:0:0:0 | String | +| overload_resolution.swift:348:7:348:7 | r3 | | file://:0:0:0:0 | String | +| overload_resolution.swift:348:12:348:12 | o | | overload_resolution.swift:327:1:342:1 | LabelVariants | +| overload_resolution.swift:348:12:348:37 | call to send(to:from:) | | file://:0:0:0:0 | String | +| overload_resolution.swift:348:23:348:23 | x | | file://:0:0:0:0 | String | +| overload_resolution.swift:348:34:348:34 | y | | file://:0:0:0:0 | String | +| overload_resolution.swift:360:38:360:38 | 0 | | file://:0:0:0:0 | Int | +| overload_resolution.swift:369:33:369:38 | x | | overload_resolution.swift:369:20:369:23 | T | +| overload_resolution.swift:370:10:370:10 | x | | overload_resolution.swift:369:20:369:23 | T | +| overload_resolution.swift:374:34:374:39 | x | | overload_resolution.swift:374:20:374:23 | T | +| overload_resolution.swift:375:10:375:10 | x | | overload_resolution.swift:374:20:374:23 | T | +| overload_resolution.swift:379:26:379:26 | 42 | | file://:0:0:0:0 | Int | +| overload_resolution.swift:380:26:380:26 | hello | | file://:0:0:0:0 | String | +| overload_resolution.swift:385:7:385:7 | self | | overload_resolution.swift:385:1:402:1 | Box | +| overload_resolution.swift:385:7:385:7 | self | T | overload_resolution.swift:385:11:385:11 | T | +| overload_resolution.swift:386:7:386:7 | self | | overload_resolution.swift:385:1:402:1 | Box | +| overload_resolution.swift:386:7:386:7 | self | | overload_resolution.swift:385:1:402:1 | Box | +| overload_resolution.swift:386:7:386:7 | self | | overload_resolution.swift:385:1:402:1 | Box | +| overload_resolution.swift:386:7:386:7 | self | T | overload_resolution.swift:385:11:385:11 | T | +| overload_resolution.swift:386:7:386:7 | self | T | overload_resolution.swift:385:11:385:11 | T | +| overload_resolution.swift:386:7:386:7 | self | T | overload_resolution.swift:385:11:385:11 | T | +| overload_resolution.swift:386:7:386:7 | value | | overload_resolution.swift:385:11:385:11 | T | +| overload_resolution.swift:389:3:389:3 | self | | overload_resolution.swift:385:1:402:1 | Box | +| overload_resolution.swift:389:3:389:3 | self | T | overload_resolution.swift:385:11:385:11 | T | +| overload_resolution.swift:389:8:389:16 | item | | overload_resolution.swift:385:11:385:11 | T | +| overload_resolution.swift:390:5:390:5 | self | | overload_resolution.swift:385:1:402:1 | Box | +| overload_resolution.swift:390:5:390:5 | self | T | overload_resolution.swift:385:11:385:11 | T | +| overload_resolution.swift:390:5:390:10 | .item | | overload_resolution.swift:385:11:385:11 | T | +| overload_resolution.swift:390:17:390:17 | item | | overload_resolution.swift:385:11:385:11 | T | +| overload_resolution.swift:394:8:394:8 | self | | overload_resolution.swift:385:1:402:1 | Box | +| overload_resolution.swift:394:8:394:8 | self | T | overload_resolution.swift:385:11:385:11 | T | +| overload_resolution.swift:395:12:395:12 | self | | overload_resolution.swift:385:1:402:1 | Box | +| overload_resolution.swift:395:12:395:12 | self | T | overload_resolution.swift:385:11:385:11 | T | +| overload_resolution.swift:399:8:399:8 | self | | overload_resolution.swift:385:1:402:1 | Box | +| overload_resolution.swift:399:8:399:8 | self | T | overload_resolution.swift:385:11:385:11 | T | +| overload_resolution.swift:399:16:399:27 | newItem | | overload_resolution.swift:385:11:385:11 | T | +| overload_resolution.swift:400:5:400:5 | .item | | overload_resolution.swift:385:11:385:11 | T | +| overload_resolution.swift:400:5:400:5 | self | | overload_resolution.swift:385:1:402:1 | Box | +| overload_resolution.swift:400:5:400:5 | self | T | overload_resolution.swift:385:11:385:11 | T | +| overload_resolution.swift:400:12:400:12 | newItem | | overload_resolution.swift:385:11:385:11 | T | +| overload_resolution.swift:405:7:405:7 | intBox | | overload_resolution.swift:385:1:402:1 | Box | +| overload_resolution.swift:405:7:405:7 | intBox | | overload_resolution.swift:385:1:402:1 | Box | +| overload_resolution.swift:405:7:405:7 | intBox | T | file://:0:0:0:0 | Int | +| overload_resolution.swift:405:7:405:7 | intBox | T | file://:0:0:0:0 | Int | +| overload_resolution.swift:405:16:405:22 | call to Box.init(_:) | | overload_resolution.swift:385:1:402:1 | Box | +| overload_resolution.swift:405:16:405:22 | call to Box.init(_:) | T | file://:0:0:0:0 | Int | +| overload_resolution.swift:405:20:405:20 | 10 | | file://:0:0:0:0 | Int | +| overload_resolution.swift:406:7:406:7 | strBox | | overload_resolution.swift:385:1:402:1 | Box | +| overload_resolution.swift:406:7:406:7 | strBox | | overload_resolution.swift:385:1:402:1 | Box | +| overload_resolution.swift:406:7:406:7 | strBox | T | file://:0:0:0:0 | String | +| overload_resolution.swift:406:7:406:7 | strBox | T | file://:0:0:0:0 | String | +| overload_resolution.swift:406:16:406:24 | call to Box.init(_:) | | overload_resolution.swift:385:1:402:1 | Box | +| overload_resolution.swift:406:16:406:24 | call to Box.init(_:) | T | file://:0:0:0:0 | String | +| overload_resolution.swift:406:20:406:20 | hi | | file://:0:0:0:0 | String | +| overload_resolution.swift:407:7:407:7 | v1 | | file://:0:0:0:0 | Int | +| overload_resolution.swift:407:7:407:7 | v1 | | file://:0:0:0:0 | Int | +| overload_resolution.swift:407:12:407:12 | intBox | | overload_resolution.swift:385:1:402:1 | Box | +| overload_resolution.swift:407:12:407:12 | intBox | T | file://:0:0:0:0 | Int | +| overload_resolution.swift:407:12:407:23 | call to get() | | file://:0:0:0:0 | Int | +| overload_resolution.swift:408:7:408:7 | v2 | | file://:0:0:0:0 | String | +| overload_resolution.swift:408:7:408:7 | v2 | | file://:0:0:0:0 | String | +| overload_resolution.swift:408:12:408:12 | strBox | | overload_resolution.swift:385:1:402:1 | Box | +| overload_resolution.swift:408:12:408:12 | strBox | T | file://:0:0:0:0 | String | +| overload_resolution.swift:408:12:408:23 | call to get() | | file://:0:0:0:0 | String | +| overload_resolution.swift:409:3:409:3 | intBox | | overload_resolution.swift:385:1:402:1 | Box | +| overload_resolution.swift:409:3:409:3 | intBox | T | file://:0:0:0:0 | Int | +| overload_resolution.swift:409:3:409:20 | call to replace(_:) | | file://:0:0:0:0 | (T_0) | +| overload_resolution.swift:409:18:409:18 | 20 | | file://:0:0:0:0 | Int | +| overload_resolution.swift:410:3:410:3 | strBox | | overload_resolution.swift:385:1:402:1 | Box | +| overload_resolution.swift:410:3:410:3 | strBox | T | file://:0:0:0:0 | String | +| overload_resolution.swift:410:3:410:23 | call to replace(_:) | | file://:0:0:0:0 | (T_0) | +| overload_resolution.swift:410:18:410:18 | bye | | file://:0:0:0:0 | String | +| overload_resolution.swift:415:7:415:7 | self | | overload_resolution.swift:415:1:434:1 | Widget | +| overload_resolution.swift:416:7:416:7 | self | | overload_resolution.swift:415:1:434:1 | Widget | +| overload_resolution.swift:416:7:416:7 | self | | overload_resolution.swift:415:1:434:1 | Widget | +| overload_resolution.swift:416:7:416:7 | self | | overload_resolution.swift:415:1:434:1 | Widget | +| overload_resolution.swift:416:7:416:7 | value | | file://:0:0:0:0 | String | +| overload_resolution.swift:417:7:417:7 | self | | overload_resolution.swift:415:1:434:1 | Widget | +| overload_resolution.swift:417:7:417:7 | self | | overload_resolution.swift:415:1:434:1 | Widget | +| overload_resolution.swift:417:7:417:7 | self | | overload_resolution.swift:415:1:434:1 | Widget | +| overload_resolution.swift:417:7:417:7 | value | | file://:0:0:0:0 | Int | +| overload_resolution.swift:420:3:420:3 | self | | overload_resolution.swift:415:1:434:1 | Widget | +| overload_resolution.swift:420:8:420:14 | name | | file://:0:0:0:0 | String | +| overload_resolution.swift:420:22:420:28 | size | | file://:0:0:0:0 | Int | +| overload_resolution.swift:421:5:421:5 | self | | overload_resolution.swift:415:1:434:1 | Widget | +| overload_resolution.swift:421:5:421:10 | .name | | file://:0:0:0:0 | String | +| overload_resolution.swift:421:17:421:17 | name | | file://:0:0:0:0 | String | +| overload_resolution.swift:422:5:422:5 | self | | overload_resolution.swift:415:1:434:1 | Widget | +| overload_resolution.swift:422:5:422:10 | .size | | file://:0:0:0:0 | Int | +| overload_resolution.swift:422:17:422:17 | size | | file://:0:0:0:0 | Int | +| overload_resolution.swift:426:15:426:15 | self | | overload_resolution.swift:415:1:434:1 | Widget | +| overload_resolution.swift:426:20:426:26 | name | | file://:0:0:0:0 | String | +| overload_resolution.swift:427:5:427:5 | self | | overload_resolution.swift:415:1:434:1 | Widget | +| overload_resolution.swift:427:5:427:34 | call to Widget.init(name:size:) | | overload_resolution.swift:415:1:434:1 | Widget | +| overload_resolution.swift:427:21:427:21 | name | | file://:0:0:0:0 | String | +| overload_resolution.swift:427:33:427:33 | 1 | | file://:0:0:0:0 | Int | +| overload_resolution.swift:431:15:431:15 | self | | overload_resolution.swift:415:1:434:1 | Widget | +| overload_resolution.swift:431:20:431:26 | size | | file://:0:0:0:0 | Int | +| overload_resolution.swift:432:5:432:5 | self | | overload_resolution.swift:415:1:434:1 | Widget | +| overload_resolution.swift:432:5:432:42 | call to Widget.init(name:size:) | | overload_resolution.swift:415:1:434:1 | Widget | +| overload_resolution.swift:432:21:432:21 | default | | file://:0:0:0:0 | String | +| overload_resolution.swift:432:38:432:38 | size | | file://:0:0:0:0 | Int | +| overload_resolution.swift:437:7:437:7 | w1 | | overload_resolution.swift:415:1:434:1 | Widget | +| overload_resolution.swift:437:7:437:7 | w1 | | overload_resolution.swift:415:1:434:1 | Widget | +| overload_resolution.swift:437:12:437:37 | call to Widget.init(name:size:) | | overload_resolution.swift:415:1:434:1 | Widget | +| overload_resolution.swift:437:25:437:25 | a | | file://:0:0:0:0 | String | +| overload_resolution.swift:437:36:437:36 | 5 | | file://:0:0:0:0 | Int | +| overload_resolution.swift:438:7:438:7 | w2 | | overload_resolution.swift:415:1:434:1 | Widget | +| overload_resolution.swift:438:7:438:7 | w2 | | overload_resolution.swift:415:1:434:1 | Widget | +| overload_resolution.swift:438:12:438:28 | call to Widget.init(name:) | | overload_resolution.swift:415:1:434:1 | Widget | +| overload_resolution.swift:438:25:438:25 | b | | file://:0:0:0:0 | String | +| overload_resolution.swift:439:7:439:7 | w3 | | overload_resolution.swift:415:1:434:1 | Widget | +| overload_resolution.swift:439:7:439:7 | w3 | | overload_resolution.swift:415:1:434:1 | Widget | +| overload_resolution.swift:439:12:439:27 | call to Widget.init(size:) | | overload_resolution.swift:415:1:434:1 | Widget | +| overload_resolution.swift:439:25:439:25 | 10 | | file://:0:0:0:0 | Int | +| overload_resolution.swift:444:7:444:7 | self | | overload_resolution.swift:444:1:462:1 | Processor | +| overload_resolution.swift:446:3:446:3 | self | | overload_resolution.swift:444:1:462:1 | Processor | +| overload_resolution.swift:449:8:449:8 | self | | overload_resolution.swift:444:1:462:1 | Processor | +| overload_resolution.swift:450:14:450:14 | 0 | | file://:0:0:0:0 | Int | +| overload_resolution.swift:454:8:454:8 | self | | overload_resolution.swift:444:1:462:1 | Processor | +| overload_resolution.swift:455:14:455:14 | | | file://:0:0:0:0 | String | +| overload_resolution.swift:459:8:459:8 | self | | overload_resolution.swift:444:1:462:1 | Processor | +| overload_resolution.swift:460:14:460:14 | 0 | | file://:0:0:0:0 | Int | +| overload_resolution.swift:460:17:460:17 | 0 | | file://:0:0:0:0 | Int | +| overload_resolution.swift:465:7:465:7 | p | | overload_resolution.swift:444:1:462:1 | Processor | +| overload_resolution.swift:465:7:465:7 | p | | overload_resolution.swift:444:1:462:1 | Processor | +| overload_resolution.swift:465:11:465:21 | call to Processor.init() | | overload_resolution.swift:444:1:462:1 | Processor | +| overload_resolution.swift:466:7:466:7 | r1 | | file://:0:0:0:0 | Int | +| overload_resolution.swift:466:7:466:7 | r1 | | file://:0:0:0:0 | Int | +| overload_resolution.swift:466:12:466:12 | p | | overload_resolution.swift:444:1:462:1 | Processor | +| overload_resolution.swift:466:12:466:34 | call to apply(_:) | | file://:0:0:0:0 | Int | +| overload_resolution.swift:466:22:466:22 | x | | file://:0:0:0:0 | Int | +| overload_resolution.swift:466:27:466:27 | x | | file://:0:0:0:0 | Int | +| overload_resolution.swift:466:31:466:31 | 1 | | file://:0:0:0:0 | Int | +| overload_resolution.swift:467:7:467:7 | r2 | | file://:0:0:0:0 | String | +| overload_resolution.swift:467:7:467:7 | r2 | | file://:0:0:0:0 | String | +| overload_resolution.swift:467:12:467:12 | p | | overload_resolution.swift:444:1:462:1 | Processor | +| overload_resolution.swift:467:12:467:36 | call to apply(_:) | | file://:0:0:0:0 | String | +| overload_resolution.swift:467:22:467:22 | s | | file://:0:0:0:0 | String | +| overload_resolution.swift:467:27:467:27 | s | | file://:0:0:0:0 | String | +| overload_resolution.swift:467:31:467:31 | ! | | file://:0:0:0:0 | String | +| overload_resolution.swift:468:7:468:7 | r3 | | file://:0:0:0:0 | Int | +| overload_resolution.swift:468:7:468:7 | r3 | | file://:0:0:0:0 | Int | +| overload_resolution.swift:468:12:468:12 | p | | overload_resolution.swift:444:1:462:1 | Processor | +| overload_resolution.swift:468:12:468:37 | call to apply(_:) | | file://:0:0:0:0 | Int | +| overload_resolution.swift:468:22:468:22 | x | | file://:0:0:0:0 | Int | +| overload_resolution.swift:468:25:468:25 | y | | file://:0:0:0:0 | Int | +| overload_resolution.swift:468:30:468:30 | x | | file://:0:0:0:0 | Int | +| overload_resolution.swift:468:34:468:34 | y | | file://:0:0:0:0 | Int | +| overload_resolution.swift:473:7:473:7 | self | | overload_resolution.swift:473:1:489:1 | MultiSubscript | +| overload_resolution.swift:474:7:474:7 | self | | overload_resolution.swift:473:1:489:1 | MultiSubscript | +| overload_resolution.swift:474:7:474:7 | self | | overload_resolution.swift:473:1:489:1 | MultiSubscript | +| overload_resolution.swift:474:7:474:7 | self | | overload_resolution.swift:473:1:489:1 | MultiSubscript | +| overload_resolution.swift:474:23:474:23 | 1 | | file://:0:0:0:0 | Int | +| overload_resolution.swift:474:26:474:26 | 2 | | file://:0:0:0:0 | Int | +| overload_resolution.swift:474:29:474:29 | 3 | | file://:0:0:0:0 | Int | +| overload_resolution.swift:475:7:475:7 | self | | overload_resolution.swift:473:1:489:1 | MultiSubscript | +| overload_resolution.swift:475:7:475:7 | self | | overload_resolution.swift:473:1:489:1 | MultiSubscript | +| overload_resolution.swift:475:7:475:7 | self | | overload_resolution.swift:473:1:489:1 | MultiSubscript | +| overload_resolution.swift:475:31:475:31 | a | | file://:0:0:0:0 | String | +| overload_resolution.swift:475:36:475:36 | 1 | | file://:0:0:0:0 | Int | +| overload_resolution.swift:478:3:478:3 | self | | overload_resolution.swift:473:1:489:1 | MultiSubscript | +| overload_resolution.swift:481:8:481:8 | self | | overload_resolution.swift:473:1:489:1 | MultiSubscript | +| overload_resolution.swift:481:12:481:21 | index | | file://:0:0:0:0 | Int | +| overload_resolution.swift:482:12:482:12 | self | | overload_resolution.swift:473:1:489:1 | MultiSubscript | +| overload_resolution.swift:482:17:482:17 | index | | file://:0:0:0:0 | Int | +| overload_resolution.swift:486:8:486:8 | self | | overload_resolution.swift:473:1:489:1 | MultiSubscript | +| overload_resolution.swift:486:12:486:19 | key | | file://:0:0:0:0 | String | +| overload_resolution.swift:487:12:487:12 | self | | overload_resolution.swift:473:1:489:1 | MultiSubscript | +| overload_resolution.swift:487:17:487:17 | key | | file://:0:0:0:0 | String | +| overload_resolution.swift:487:25:487:25 | 0 | | file://:0:0:0:0 | Int | +| overload_resolution.swift:492:7:492:7 | ms | | overload_resolution.swift:473:1:489:1 | MultiSubscript | +| overload_resolution.swift:492:7:492:7 | ms | | overload_resolution.swift:473:1:489:1 | MultiSubscript | +| overload_resolution.swift:492:12:492:27 | call to MultiSubscript.init() | | overload_resolution.swift:473:1:489:1 | MultiSubscript | +| overload_resolution.swift:493:7:493:7 | r1 | | file://:0:0:0:0 | Int | +| overload_resolution.swift:493:7:493:7 | r1 | | file://:0:0:0:0 | Int | +| overload_resolution.swift:493:12:493:12 | ms | | overload_resolution.swift:473:1:489:1 | MultiSubscript | +| overload_resolution.swift:493:12:493:20 | call to get(_:) | | file://:0:0:0:0 | Int | +| overload_resolution.swift:493:19:493:19 | 0 | | file://:0:0:0:0 | Int | +| overload_resolution.swift:494:7:494:7 | r2 | | file://:0:0:0:0 | Int | +| overload_resolution.swift:494:7:494:7 | r2 | | file://:0:0:0:0 | Int | +| overload_resolution.swift:494:12:494:12 | ms | | overload_resolution.swift:473:1:489:1 | MultiSubscript | +| overload_resolution.swift:494:12:494:22 | call to get(_:) | | file://:0:0:0:0 | Int | +| overload_resolution.swift:494:19:494:19 | a | | file://:0:0:0:0 | String | +| protocols.swift:9:7:9:7 | self | | protocols.swift:8:1:20:1 | Circle | +| protocols.swift:9:7:9:7 | self | | protocols.swift:8:1:20:1 | Circle | +| protocols.swift:9:7:9:7 | self | | protocols.swift:8:1:20:1 | Circle | +| protocols.swift:9:7:9:7 | value | | file://:0:0:0:0 | Double | +| protocols.swift:12:3:12:3 | self | | protocols.swift:8:1:20:1 | Circle | +| protocols.swift:12:8:12:16 | radius | | file://:0:0:0:0 | Double | +| protocols.swift:13:5:13:5 | self | | protocols.swift:8:1:20:1 | Circle | +| protocols.swift:13:5:13:10 | .radius | | file://:0:0:0:0 | Double | +| protocols.swift:13:19:13:19 | radius | | file://:0:0:0:0 | Double | +| protocols.swift:17:8:17:8 | self | | protocols.swift:8:1:20:1 | Circle | +| protocols.swift:18:22:18:22 | self | | protocols.swift:8:1:20:1 | Circle | +| protocols.swift:18:31:18:31 | self | | protocols.swift:8:1:20:1 | Circle | +| protocols.swift:23:7:23:7 | self | | protocols.swift:22:1:36:1 | Rectangle | +| protocols.swift:23:7:23:7 | self | | protocols.swift:22:1:36:1 | Rectangle | +| protocols.swift:23:7:23:7 | self | | protocols.swift:22:1:36:1 | Rectangle | +| protocols.swift:23:7:23:7 | value | | file://:0:0:0:0 | Double | +| protocols.swift:24:7:24:7 | self | | protocols.swift:22:1:36:1 | Rectangle | +| protocols.swift:24:7:24:7 | self | | protocols.swift:22:1:36:1 | Rectangle | +| protocols.swift:24:7:24:7 | self | | protocols.swift:22:1:36:1 | Rectangle | +| protocols.swift:24:7:24:7 | value | | file://:0:0:0:0 | Double | +| protocols.swift:27:3:27:3 | self | | protocols.swift:22:1:36:1 | Rectangle | +| protocols.swift:27:8:27:15 | width | | file://:0:0:0:0 | Double | +| protocols.swift:27:23:27:31 | height | | file://:0:0:0:0 | Double | +| protocols.swift:28:5:28:5 | self | | protocols.swift:22:1:36:1 | Rectangle | +| protocols.swift:28:5:28:10 | .width | | file://:0:0:0:0 | Double | +| protocols.swift:28:18:28:18 | width | | file://:0:0:0:0 | Double | +| protocols.swift:29:5:29:5 | self | | protocols.swift:22:1:36:1 | Rectangle | +| protocols.swift:29:5:29:10 | .height | | file://:0:0:0:0 | Double | +| protocols.swift:29:19:29:19 | height | | file://:0:0:0:0 | Double | +| protocols.swift:33:8:33:8 | self | | protocols.swift:22:1:36:1 | Rectangle | +| protocols.swift:34:12:34:12 | self | | protocols.swift:22:1:36:1 | Rectangle | +| protocols.swift:34:20:34:20 | self | | protocols.swift:22:1:36:1 | Rectangle | +| protocols.swift:39:7:39:7 | c | | protocols.swift:8:1:20:1 | Circle | +| protocols.swift:39:7:39:7 | c | | protocols.swift:8:1:20:1 | Circle | +| protocols.swift:39:11:39:29 | call to Circle.init(radius:) | | protocols.swift:8:1:20:1 | Circle | +| protocols.swift:40:7:40:7 | a1 | | file://:0:0:0:0 | Double | +| protocols.swift:40:7:40:7 | a1 | | file://:0:0:0:0 | Double | +| protocols.swift:40:12:40:12 | c | | protocols.swift:8:1:20:1 | Circle | +| protocols.swift:40:12:40:19 | call to area() | | file://:0:0:0:0 | Double | +| protocols.swift:42:7:42:7 | r | | protocols.swift:22:1:36:1 | Rectangle | +| protocols.swift:42:7:42:7 | r | | protocols.swift:22:1:36:1 | Rectangle | +| protocols.swift:42:11:42:44 | call to Rectangle.init(width:height:) | | protocols.swift:22:1:36:1 | Rectangle | +| protocols.swift:43:7:43:7 | a2 | | file://:0:0:0:0 | Double | +| protocols.swift:43:7:43:7 | a2 | | file://:0:0:0:0 | Double | +| protocols.swift:43:12:43:12 | r | | protocols.swift:22:1:36:1 | Rectangle | +| protocols.swift:43:12:43:19 | call to area() | | file://:0:0:0:0 | Double | +| protocols.swift:58:7:58:7 | self | | protocols.swift:56:1:74:1 | IntContainer | +| protocols.swift:58:7:58:7 | self | | protocols.swift:56:1:74:1 | IntContainer | +| protocols.swift:58:7:58:7 | self | | protocols.swift:56:1:74:1 | IntContainer | +| protocols.swift:61:3:61:3 | self | | protocols.swift:56:1:74:1 | IntContainer | +| protocols.swift:62:5:62:5 | self | | protocols.swift:56:1:74:1 | IntContainer | +| protocols.swift:66:8:66:8 | self | | protocols.swift:56:1:74:1 | IntContainer | +| protocols.swift:67:12:67:12 | self | | protocols.swift:56:1:74:1 | IntContainer | +| protocols.swift:67:18:67:18 | 0 | | file://:0:0:0:0 | Int | +| protocols.swift:71:8:71:8 | self | | protocols.swift:56:1:74:1 | IntContainer | +| protocols.swift:72:12:72:12 | self | | protocols.swift:56:1:74:1 | IntContainer | +| protocols.swift:77:7:77:7 | ic | | protocols.swift:56:1:74:1 | IntContainer | +| protocols.swift:77:7:77:7 | ic | | protocols.swift:56:1:74:1 | IntContainer | +| protocols.swift:77:12:77:41 | call to IntContainer.init(items:) | | protocols.swift:56:1:74:1 | IntContainer | +| protocols.swift:77:33:77:33 | 1 | | file://:0:0:0:0 | Int | +| protocols.swift:77:36:77:36 | 2 | | file://:0:0:0:0 | Int | +| protocols.swift:77:39:77:39 | 3 | | file://:0:0:0:0 | Int | +| protocols.swift:78:7:78:7 | item | | file://:0:0:0:0 | Int | +| protocols.swift:78:7:78:7 | item | | file://:0:0:0:0 | Int | +| protocols.swift:78:14:78:14 | ic | | protocols.swift:56:1:74:1 | IntContainer | +| protocols.swift:78:14:78:25 | call to getItem() | | file://:0:0:0:0 | Int | +| protocols.swift:79:7:79:7 | cnt | | file://:0:0:0:0 | Int | +| protocols.swift:79:7:79:7 | cnt | | file://:0:0:0:0 | Int | +| protocols.swift:79:13:79:13 | ic | | protocols.swift:56:1:74:1 | IntContainer | +| protocols.swift:79:13:79:22 | call to count() | | file://:0:0:0:0 | Int | +| protocols.swift:94:7:94:7 | self | | protocols.swift:94:1:113:1 | Entity | +| protocols.swift:95:7:95:7 | self | | protocols.swift:94:1:113:1 | Entity | +| protocols.swift:95:7:95:7 | self | | protocols.swift:94:1:113:1 | Entity | +| protocols.swift:95:7:95:7 | self | | protocols.swift:94:1:113:1 | Entity | +| protocols.swift:95:7:95:7 | value | | file://:0:0:0:0 | String | +| protocols.swift:96:7:96:7 | self | | protocols.swift:94:1:113:1 | Entity | +| protocols.swift:96:7:96:7 | self | | protocols.swift:94:1:113:1 | Entity | +| protocols.swift:96:7:96:7 | self | | protocols.swift:94:1:113:1 | Entity | +| protocols.swift:96:7:96:7 | value | | file://:0:0:0:0 | Int | +| protocols.swift:99:3:99:3 | self | | protocols.swift:94:1:113:1 | Entity | +| protocols.swift:99:8:99:14 | name | | file://:0:0:0:0 | String | +| protocols.swift:99:22:99:32 | entityId | | file://:0:0:0:0 | Int | +| protocols.swift:100:5:100:5 | self | | protocols.swift:94:1:113:1 | Entity | +| protocols.swift:100:5:100:10 | .name | | file://:0:0:0:0 | String | +| protocols.swift:100:17:100:17 | name | | file://:0:0:0:0 | String | +| protocols.swift:101:5:101:5 | self | | protocols.swift:94:1:113:1 | Entity | +| protocols.swift:101:5:101:10 | .entityId | | file://:0:0:0:0 | Int | +| protocols.swift:101:21:101:21 | entityId | | file://:0:0:0:0 | Int | +| protocols.swift:105:8:105:8 | self | | protocols.swift:94:1:113:1 | Entity | +| protocols.swift:106:12:106:12 | self | | protocols.swift:94:1:113:1 | Entity | +| protocols.swift:110:8:110:8 | self | | protocols.swift:94:1:113:1 | Entity | +| protocols.swift:111:12:111:12 | self | | protocols.swift:94:1:113:1 | Entity | +| protocols.swift:116:7:116:7 | e | | protocols.swift:94:1:113:1 | Entity | +| protocols.swift:116:7:116:7 | e | | protocols.swift:94:1:113:1 | Entity | +| protocols.swift:116:11:116:44 | call to Entity.init(name:entityId:) | | protocols.swift:94:1:113:1 | Entity | +| protocols.swift:116:24:116:24 | test | | file://:0:0:0:0 | String | +| protocols.swift:116:42:116:42 | 42 | | file://:0:0:0:0 | Int | +| protocols.swift:117:7:117:7 | d | | file://:0:0:0:0 | String | +| protocols.swift:117:7:117:7 | d | | file://:0:0:0:0 | String | +| protocols.swift:117:11:117:11 | e | | protocols.swift:94:1:113:1 | Entity | +| protocols.swift:117:11:117:21 | call to display() | | file://:0:0:0:0 | String | +| protocols.swift:118:7:118:7 | eid | | file://:0:0:0:0 | Int | +| protocols.swift:118:7:118:7 | eid | | file://:0:0:0:0 | Int | +| protocols.swift:118:13:118:13 | e | | protocols.swift:94:1:113:1 | Entity | +| protocols.swift:118:13:118:18 | call to id() | | file://:0:0:0:0 | Int | +| type_constraints.swift:4:27:4:32 | a | | type_constraints.swift:4:12:4:15 | T | +| type_constraints.swift:4:35:4:40 | b | | type_constraints.swift:4:12:4:15 | T | +| type_constraints.swift:5:6:5:6 | a | | type_constraints.swift:4:12:4:15 | T | +| type_constraints.swift:5:10:5:10 | b | | type_constraints.swift:4:12:4:15 | T | +| type_constraints.swift:5:21:5:21 | a | | type_constraints.swift:4:12:4:15 | T | +| type_constraints.swift:5:39:5:39 | b | | type_constraints.swift:4:12:4:15 | T | +| type_constraints.swift:9:15:9:20 | a | | type_constraints.swift:9:12:9:12 | T | +| type_constraints.swift:9:23:9:28 | b | | type_constraints.swift:9:12:9:12 | T | +| type_constraints.swift:10:6:10:6 | a | | type_constraints.swift:9:12:9:12 | T | +| type_constraints.swift:10:10:10:10 | b | | type_constraints.swift:9:12:9:12 | T | +| type_constraints.swift:10:21:10:21 | a | | type_constraints.swift:9:12:9:12 | T | +| type_constraints.swift:10:39:10:39 | b | | type_constraints.swift:9:12:9:12 | T | +| type_constraints.swift:14:18:14:18 | 3 | | file://:0:0:0:0 | Int | +| type_constraints.swift:14:21:14:21 | 7 | | file://:0:0:0:0 | Int | +| type_constraints.swift:15:18:15:18 | a | | file://:0:0:0:0 | String | +| type_constraints.swift:15:23:15:23 | z | | file://:0:0:0:0 | String | +| type_constraints.swift:16:18:16:18 | 3 | | file://:0:0:0:0 | Int | +| type_constraints.swift:16:21:16:21 | 7 | | file://:0:0:0:0 | Int | +| type_constraints.swift:17:18:17:18 | a | | file://:0:0:0:0 | String | +| type_constraints.swift:17:23:17:23 | z | | file://:0:0:0:0 | String | +| type_constraints.swift:33:7:33:7 | self | | type_constraints.swift:32:1:51:1 | TaggedItem | +| type_constraints.swift:33:7:33:7 | self | | type_constraints.swift:32:1:51:1 | TaggedItem | +| type_constraints.swift:33:7:33:7 | self | | type_constraints.swift:32:1:51:1 | TaggedItem | +| type_constraints.swift:33:7:33:7 | value | | file://:0:0:0:0 | String | +| type_constraints.swift:34:7:34:7 | self | | type_constraints.swift:32:1:51:1 | TaggedItem | +| type_constraints.swift:34:7:34:7 | self | | type_constraints.swift:32:1:51:1 | TaggedItem | +| type_constraints.swift:34:7:34:7 | self | | type_constraints.swift:32:1:51:1 | TaggedItem | +| type_constraints.swift:34:7:34:7 | value | | file://:0:0:0:0 | Int | +| type_constraints.swift:37:3:37:3 | self | | type_constraints.swift:32:1:51:1 | TaggedItem | +| type_constraints.swift:37:8:37:13 | tag | | file://:0:0:0:0 | String | +| type_constraints.swift:37:21:37:31 | priority | | file://:0:0:0:0 | Int | +| type_constraints.swift:38:5:38:5 | self | | type_constraints.swift:32:1:51:1 | TaggedItem | +| type_constraints.swift:38:5:38:10 | .tag | | file://:0:0:0:0 | String | +| type_constraints.swift:38:16:38:16 | tag | | file://:0:0:0:0 | String | +| type_constraints.swift:39:5:39:5 | self | | type_constraints.swift:32:1:51:1 | TaggedItem | +| type_constraints.swift:39:5:39:10 | .priority | | file://:0:0:0:0 | Int | +| type_constraints.swift:39:21:39:21 | priority | | file://:0:0:0:0 | Int | +| type_constraints.swift:43:8:43:8 | self | | type_constraints.swift:32:1:51:1 | TaggedItem | +| type_constraints.swift:44:12:44:12 | self | | type_constraints.swift:32:1:51:1 | TaggedItem | +| type_constraints.swift:48:8:48:8 | self | | type_constraints.swift:32:1:51:1 | TaggedItem | +| type_constraints.swift:49:12:49:12 | self | | type_constraints.swift:32:1:51:1 | TaggedItem | +| type_constraints.swift:54:46:54:54 | item | | type_constraints.swift:54:18:54:36 | T | +| type_constraints.swift:55:10:55:10 | item | | type_constraints.swift:54:18:54:36 | T | +| type_constraints.swift:55:10:55:23 | call to display() | | file://:0:0:0:0 | String | +| type_constraints.swift:59:28:59:33 | a | | type_constraints.swift:59:25:59:25 | T | +| type_constraints.swift:59:36:59:41 | b | | type_constraints.swift:59:25:59:25 | T | +| type_constraints.swift:60:10:60:10 | a | | type_constraints.swift:59:25:59:25 | T | +| type_constraints.swift:60:15:60:15 | b | | type_constraints.swift:59:25:59:25 | T | +| type_constraints.swift:64:7:64:7 | item | | type_constraints.swift:32:1:51:1 | TaggedItem | +| type_constraints.swift:64:7:64:7 | item | | type_constraints.swift:32:1:51:1 | TaggedItem | +| type_constraints.swift:64:14:64:46 | call to TaggedItem.init(tag:priority:) | | type_constraints.swift:32:1:51:1 | TaggedItem | +| type_constraints.swift:64:30:64:30 | x | | file://:0:0:0:0 | String | +| type_constraints.swift:64:45:64:45 | 1 | | file://:0:0:0:0 | Int | +| type_constraints.swift:65:23:65:23 | item | | type_constraints.swift:32:1:51:1 | TaggedItem | +| type_constraints.swift:66:31:66:31 | item | | type_constraints.swift:32:1:51:1 | TaggedItem | +| type_constraints.swift:66:37:66:37 | item | | type_constraints.swift:32:1:51:1 | TaggedItem | +| type_constraints.swift:71:7:71:7 | self | | type_constraints.swift:71:1:90:1 | SortedPair | +| type_constraints.swift:71:7:71:7 | self | T | type_constraints.swift:71:18:71:21 | T | +| type_constraints.swift:71:7:71:7 | self | U | type_constraints.swift:71:33:71:36 | U | +| type_constraints.swift:72:7:72:7 | self | | type_constraints.swift:71:1:90:1 | SortedPair | +| type_constraints.swift:72:7:72:7 | self | | type_constraints.swift:71:1:90:1 | SortedPair | +| type_constraints.swift:72:7:72:7 | self | | type_constraints.swift:71:1:90:1 | SortedPair | +| type_constraints.swift:72:7:72:7 | self | T | type_constraints.swift:71:18:71:21 | T | +| type_constraints.swift:72:7:72:7 | self | T | type_constraints.swift:71:18:71:21 | T | +| type_constraints.swift:72:7:72:7 | self | T | type_constraints.swift:71:18:71:21 | T | +| type_constraints.swift:72:7:72:7 | self | U | type_constraints.swift:71:33:71:36 | U | +| type_constraints.swift:72:7:72:7 | self | U | type_constraints.swift:71:33:71:36 | U | +| type_constraints.swift:72:7:72:7 | self | U | type_constraints.swift:71:33:71:36 | U | +| type_constraints.swift:72:7:72:7 | value | | type_constraints.swift:71:18:71:21 | T | +| type_constraints.swift:73:7:73:7 | self | | type_constraints.swift:71:1:90:1 | SortedPair | +| type_constraints.swift:73:7:73:7 | self | | type_constraints.swift:71:1:90:1 | SortedPair | +| type_constraints.swift:73:7:73:7 | self | | type_constraints.swift:71:1:90:1 | SortedPair | +| type_constraints.swift:73:7:73:7 | self | T | type_constraints.swift:71:18:71:21 | T | +| type_constraints.swift:73:7:73:7 | self | T | type_constraints.swift:71:18:71:21 | T | +| type_constraints.swift:73:7:73:7 | self | T | type_constraints.swift:71:18:71:21 | T | +| type_constraints.swift:73:7:73:7 | self | U | type_constraints.swift:71:33:71:36 | U | +| type_constraints.swift:73:7:73:7 | self | U | type_constraints.swift:71:33:71:36 | U | +| type_constraints.swift:73:7:73:7 | self | U | type_constraints.swift:71:33:71:36 | U | +| type_constraints.swift:73:7:73:7 | value | | type_constraints.swift:71:33:71:36 | U | +| type_constraints.swift:76:3:76:3 | self | | type_constraints.swift:71:1:90:1 | SortedPair | +| type_constraints.swift:76:3:76:3 | self | T | type_constraints.swift:71:18:71:21 | T | +| type_constraints.swift:76:3:76:3 | self | U | type_constraints.swift:71:33:71:36 | U | +| type_constraints.swift:76:8:76:15 | first | | type_constraints.swift:71:18:71:21 | T | +| type_constraints.swift:76:18:76:26 | second | | type_constraints.swift:71:33:71:36 | U | +| type_constraints.swift:77:5:77:5 | self | | type_constraints.swift:71:1:90:1 | SortedPair | +| type_constraints.swift:77:5:77:5 | self | T | type_constraints.swift:71:18:71:21 | T | +| type_constraints.swift:77:5:77:5 | self | U | type_constraints.swift:71:33:71:36 | U | +| type_constraints.swift:77:5:77:10 | .first | | type_constraints.swift:71:18:71:21 | T | +| type_constraints.swift:77:18:77:18 | first | | type_constraints.swift:71:18:71:21 | T | +| type_constraints.swift:78:5:78:5 | self | | type_constraints.swift:71:1:90:1 | SortedPair | +| type_constraints.swift:78:5:78:5 | self | T | type_constraints.swift:71:18:71:21 | T | +| type_constraints.swift:78:5:78:5 | self | U | type_constraints.swift:71:33:71:36 | U | +| type_constraints.swift:78:5:78:10 | .second | | type_constraints.swift:71:33:71:36 | U | +| type_constraints.swift:78:19:78:19 | second | | type_constraints.swift:71:33:71:36 | U | +| type_constraints.swift:82:8:82:8 | self | | type_constraints.swift:71:1:90:1 | SortedPair | +| type_constraints.swift:82:8:82:8 | self | T | type_constraints.swift:71:18:71:21 | T | +| type_constraints.swift:82:8:82:8 | self | U | type_constraints.swift:71:33:71:36 | U | +| type_constraints.swift:82:23:82:35 | other | | type_constraints.swift:71:18:71:21 | T | +| type_constraints.swift:83:12:83:12 | self | | type_constraints.swift:71:1:90:1 | SortedPair | +| type_constraints.swift:83:12:83:12 | self | T | type_constraints.swift:71:18:71:21 | T | +| type_constraints.swift:83:12:83:12 | self | U | type_constraints.swift:71:33:71:36 | U | +| type_constraints.swift:83:20:83:20 | other | | type_constraints.swift:71:18:71:21 | T | +| type_constraints.swift:87:8:87:8 | self | | type_constraints.swift:71:1:90:1 | SortedPair | +| type_constraints.swift:87:8:87:8 | self | T | type_constraints.swift:71:18:71:21 | T | +| type_constraints.swift:87:8:87:8 | self | U | type_constraints.swift:71:33:71:36 | U | +| type_constraints.swift:87:24:87:36 | other | | type_constraints.swift:71:33:71:36 | U | +| type_constraints.swift:88:12:88:12 | self | | type_constraints.swift:71:1:90:1 | SortedPair | +| type_constraints.swift:88:12:88:12 | self | T | type_constraints.swift:71:18:71:21 | T | +| type_constraints.swift:88:12:88:12 | self | U | type_constraints.swift:71:33:71:36 | U | +| type_constraints.swift:88:21:88:21 | other | | type_constraints.swift:71:33:71:36 | U | +| type_constraints.swift:93:7:93:7 | sp | | type_constraints.swift:71:1:90:1 | SortedPair | +| type_constraints.swift:93:7:93:7 | sp | | type_constraints.swift:71:1:90:1 | SortedPair | +| type_constraints.swift:93:7:93:7 | sp | T | file://:0:0:0:0 | Int | +| type_constraints.swift:93:7:93:7 | sp | T | file://:0:0:0:0 | Int | +| type_constraints.swift:93:7:93:7 | sp | U | file://:0:0:0:0 | String | +| type_constraints.swift:93:7:93:7 | sp | U | file://:0:0:0:0 | String | +| type_constraints.swift:93:12:93:44 | call to SortedPair.init(first:second:) | | type_constraints.swift:71:1:90:1 | SortedPair | +| type_constraints.swift:93:12:93:44 | call to SortedPair.init(first:second:) | T | file://:0:0:0:0 | Int | +| type_constraints.swift:93:12:93:44 | call to SortedPair.init(first:second:) | U | file://:0:0:0:0 | String | +| type_constraints.swift:93:30:93:30 | 3 | | file://:0:0:0:0 | Int | +| type_constraints.swift:93:41:93:41 | b | | file://:0:0:0:0 | String | +| type_constraints.swift:94:7:94:7 | r1 | | file://:0:0:0:0 | Bool | +| type_constraints.swift:94:7:94:7 | r1 | | file://:0:0:0:0 | Bool | +| type_constraints.swift:94:12:94:12 | sp | | type_constraints.swift:71:1:90:1 | SortedPair | +| type_constraints.swift:94:12:94:12 | sp | T | file://:0:0:0:0 | Int | +| type_constraints.swift:94:12:94:12 | sp | U | file://:0:0:0:0 | String | +| type_constraints.swift:94:12:94:37 | call to isFirstSmaller(than:) | | file://:0:0:0:0 | Bool | +| type_constraints.swift:94:36:94:36 | 5 | | file://:0:0:0:0 | Int | +| type_constraints.swift:95:7:95:7 | r2 | | file://:0:0:0:0 | Bool | +| type_constraints.swift:95:7:95:7 | r2 | | file://:0:0:0:0 | Bool | +| type_constraints.swift:95:12:95:12 | sp | | type_constraints.swift:71:1:90:1 | SortedPair | +| type_constraints.swift:95:12:95:12 | sp | T | file://:0:0:0:0 | Int | +| type_constraints.swift:95:12:95:12 | sp | U | file://:0:0:0:0 | String | +| type_constraints.swift:95:12:95:40 | call to isSecondSmaller(than:) | | file://:0:0:0:0 | Bool | +| type_constraints.swift:95:37:95:37 | z | | file://:0:0:0:0 | String | +| type_constraints.swift:108:7:108:7 | self | | type_constraints.swift:106:1:119:1 | IntArray | +| type_constraints.swift:108:7:108:7 | self | | type_constraints.swift:106:1:119:1 | IntArray | +| type_constraints.swift:108:7:108:7 | self | | type_constraints.swift:106:1:119:1 | IntArray | +| type_constraints.swift:111:3:111:3 | self | | type_constraints.swift:106:1:119:1 | IntArray | +| type_constraints.swift:112:5:112:5 | self | | type_constraints.swift:106:1:119:1 | IntArray | +| type_constraints.swift:116:8:116:8 | self | | type_constraints.swift:106:1:119:1 | IntArray | +| type_constraints.swift:117:12:117:12 | self | | type_constraints.swift:106:1:119:1 | IntArray | +| type_constraints.swift:117:18:117:18 | 0 | | file://:0:0:0:0 | Int | +| type_constraints.swift:123:7:123:7 | self | | type_constraints.swift:121:1:134:1 | StringArray | +| type_constraints.swift:123:7:123:7 | self | | type_constraints.swift:121:1:134:1 | StringArray | +| type_constraints.swift:123:7:123:7 | self | | type_constraints.swift:121:1:134:1 | StringArray | +| type_constraints.swift:126:3:126:3 | self | | type_constraints.swift:121:1:134:1 | StringArray | +| type_constraints.swift:127:5:127:5 | self | | type_constraints.swift:121:1:134:1 | StringArray | +| type_constraints.swift:131:8:131:8 | self | | type_constraints.swift:121:1:134:1 | StringArray | +| type_constraints.swift:132:12:132:12 | self | | type_constraints.swift:121:1:134:1 | StringArray | +| type_constraints.swift:132:18:132:18 | 0 | | file://:0:0:0:0 | Int | +| type_constraints.swift:137:40:137:56 | container | | type_constraints.swift:137:19:137:22 | C | +| type_constraints.swift:138:10:138:10 | container | | type_constraints.swift:137:19:137:22 | C | +| type_constraints.swift:142:40:142:56 | container | | type_constraints.swift:142:19:142:22 | C | +| type_constraints.swift:143:10:143:10 | container | | type_constraints.swift:142:19:142:22 | C | +| type_constraints.swift:147:7:147:7 | ia | | type_constraints.swift:106:1:119:1 | IntArray | +| type_constraints.swift:147:7:147:7 | ia | | type_constraints.swift:106:1:119:1 | IntArray | +| type_constraints.swift:147:12:147:36 | call to IntArray.init(items:) | | type_constraints.swift:106:1:119:1 | IntArray | +| type_constraints.swift:147:29:147:29 | 10 | | file://:0:0:0:0 | Int | +| type_constraints.swift:147:33:147:33 | 20 | | file://:0:0:0:0 | Int | +| type_constraints.swift:148:7:148:7 | sa | | type_constraints.swift:121:1:134:1 | StringArray | +| type_constraints.swift:148:7:148:7 | sa | | type_constraints.swift:121:1:134:1 | StringArray | +| type_constraints.swift:148:12:148:46 | call to StringArray.init(items:) | | type_constraints.swift:121:1:134:1 | StringArray | +| type_constraints.swift:148:32:148:32 | hi | | file://:0:0:0:0 | String | +| type_constraints.swift:148:38:148:38 | there | | file://:0:0:0:0 | String | +| type_constraints.swift:149:31:149:31 | ia | | type_constraints.swift:106:1:119:1 | IntArray | +| type_constraints.swift:150:31:150:31 | sa | | type_constraints.swift:121:1:134:1 | StringArray | +| type_constraints.swift:155:7:155:7 | self | | type_constraints.swift:155:1:167:1 | Vehicle | +| type_constraints.swift:156:7:156:7 | self | | type_constraints.swift:155:1:167:1 | Vehicle | +| type_constraints.swift:156:7:156:7 | self | | type_constraints.swift:155:1:167:1 | Vehicle | +| type_constraints.swift:156:7:156:7 | self | | type_constraints.swift:155:1:167:1 | Vehicle | +| type_constraints.swift:156:7:156:7 | value | | file://:0:0:0:0 | Int | +| type_constraints.swift:159:3:159:3 | self | | type_constraints.swift:155:1:167:1 | Vehicle | +| type_constraints.swift:159:8:159:15 | speed | | file://:0:0:0:0 | Int | +| type_constraints.swift:160:5:160:5 | self | | type_constraints.swift:155:1:167:1 | Vehicle | +| type_constraints.swift:160:5:160:10 | .speed | | file://:0:0:0:0 | Int | +| type_constraints.swift:160:18:160:18 | speed | | file://:0:0:0:0 | Int | +| type_constraints.swift:164:8:164:8 | self | | type_constraints.swift:155:1:167:1 | Vehicle | +| type_constraints.swift:165:12:165:12 | vehicle | | file://:0:0:0:0 | String | +| type_constraints.swift:169:7:169:7 | self | | type_constraints.swift:169:1:184:1 | Car | +| type_constraints.swift:171:12:171:12 | self | | type_constraints.swift:169:1:184:1 | Car | +| type_constraints.swift:171:17:171:24 | speed | | file://:0:0:0:0 | Int | +| type_constraints.swift:172:5:172:28 | call to Vehicle.init(speed:) | | type_constraints.swift:155:1:167:1 | Vehicle | +| type_constraints.swift:172:23:172:23 | speed | | file://:0:0:0:0 | Int | +| type_constraints.swift:176:17:176:17 | self | | type_constraints.swift:169:1:184:1 | Car | +| type_constraints.swift:177:12:177:12 | car | | file://:0:0:0:0 | String | +| type_constraints.swift:181:8:181:8 | self | | type_constraints.swift:169:1:184:1 | Car | +| type_constraints.swift:182:12:182:12 | beep | | file://:0:0:0:0 | String | +| type_constraints.swift:186:7:186:7 | self | | type_constraints.swift:186:1:201:1 | Truck | +| type_constraints.swift:188:12:188:12 | self | | type_constraints.swift:186:1:201:1 | Truck | +| type_constraints.swift:188:17:188:24 | speed | | file://:0:0:0:0 | Int | +| type_constraints.swift:189:5:189:28 | call to Vehicle.init(speed:) | | type_constraints.swift:155:1:167:1 | Vehicle | +| type_constraints.swift:189:23:189:23 | speed | | file://:0:0:0:0 | Int | +| type_constraints.swift:193:17:193:17 | self | | type_constraints.swift:186:1:201:1 | Truck | +| type_constraints.swift:194:12:194:12 | truck | | file://:0:0:0:0 | String | +| type_constraints.swift:198:8:198:8 | self | | type_constraints.swift:186:1:201:1 | Truck | +| type_constraints.swift:199:12:199:12 | hauling | | file://:0:0:0:0 | String | +| type_constraints.swift:204:34:204:39 | v | | type_constraints.swift:204:22:204:25 | T | +| type_constraints.swift:205:10:205:10 | v | | type_constraints.swift:204:22:204:25 | T | +| type_constraints.swift:205:10:205:21 | call to describe() | | file://:0:0:0:0 | String | +| type_constraints.swift:209:7:209:7 | car | | type_constraints.swift:169:1:184:1 | Car | +| type_constraints.swift:209:7:209:7 | car | | type_constraints.swift:169:1:184:1 | Car | +| type_constraints.swift:209:13:209:27 | call to Car.init(speed:) | | type_constraints.swift:169:1:184:1 | Car | +| type_constraints.swift:209:24:209:24 | 100 | | file://:0:0:0:0 | Int | +| type_constraints.swift:210:7:210:7 | truck | | type_constraints.swift:186:1:201:1 | Truck | +| type_constraints.swift:210:7:210:7 | truck | | type_constraints.swift:186:1:201:1 | Truck | +| type_constraints.swift:210:15:210:30 | call to Truck.init(speed:) | | type_constraints.swift:186:1:201:1 | Truck | +| type_constraints.swift:210:28:210:28 | 60 | | file://:0:0:0:0 | Int | +| type_constraints.swift:211:28:211:28 | car | | type_constraints.swift:169:1:184:1 | Car | +| type_constraints.swift:212:28:212:28 | truck | | type_constraints.swift:186:1:201:1 | Truck | +| type_constraints.swift:213:7:213:7 | h | | file://:0:0:0:0 | String | +| type_constraints.swift:213:7:213:7 | h | | file://:0:0:0:0 | String | +| type_constraints.swift:213:11:213:11 | car | | type_constraints.swift:169:1:184:1 | Car | +| type_constraints.swift:213:11:213:20 | call to honk() | | file://:0:0:0:0 | String | +| type_constraints.swift:214:7:214:7 | hl | | file://:0:0:0:0 | String | +| type_constraints.swift:214:7:214:7 | hl | | file://:0:0:0:0 | String | +| type_constraints.swift:214:12:214:12 | truck | | type_constraints.swift:186:1:201:1 | Truck | +| type_constraints.swift:214:12:214:23 | call to haul() | | file://:0:0:0:0 | String | +| type_constraints.swift:229:7:229:7 | self | | type_constraints.swift:228:1:240:1 | Accumulator | +| type_constraints.swift:229:7:229:7 | self | | type_constraints.swift:228:1:240:1 | Accumulator | +| type_constraints.swift:229:7:229:7 | self | | type_constraints.swift:228:1:240:1 | Accumulator | +| type_constraints.swift:229:7:229:7 | self | T | type_constraints.swift:228:20:228:20 | T | +| type_constraints.swift:229:7:229:7 | self | T | type_constraints.swift:228:20:228:20 | T | +| type_constraints.swift:229:7:229:7 | self | T | type_constraints.swift:228:20:228:20 | T | +| type_constraints.swift:232:3:232:3 | self | | type_constraints.swift:228:1:240:1 | Accumulator | +| type_constraints.swift:232:3:232:3 | self | T | type_constraints.swift:228:20:228:20 | T | +| type_constraints.swift:233:5:233:5 | self | | type_constraints.swift:228:1:240:1 | Accumulator | +| type_constraints.swift:233:5:233:5 | self | T | type_constraints.swift:228:20:228:20 | T | +| type_constraints.swift:237:8:237:8 | self | | type_constraints.swift:228:1:240:1 | Accumulator | +| type_constraints.swift:237:8:237:8 | self | T | type_constraints.swift:228:20:228:20 | T | +| type_constraints.swift:238:12:238:12 | self | | type_constraints.swift:228:1:240:1 | Accumulator | +| type_constraints.swift:238:12:238:12 | self | T | type_constraints.swift:228:20:228:20 | T | +| type_constraints.swift:244:8:244:8 | self | | type_constraints.swift:228:1:240:1 | Accumulator | +| type_constraints.swift:244:8:244:8 | self | T | type_constraints.swift:228:20:228:20 | T | +| type_constraints.swift:245:12:245:12 | self | | type_constraints.swift:228:1:240:1 | Accumulator | +| type_constraints.swift:245:12:245:12 | self | T | type_constraints.swift:228:20:228:20 | T | +| type_constraints.swift:245:19:245:19 | 0 | | file://:0:0:0:0 | Int | +| type_constraints.swift:245:24:245:24 | self | | type_constraints.swift:228:1:240:1 | Accumulator | +| type_constraints.swift:245:24:245:24 | self | T | type_constraints.swift:228:20:228:20 | T | +| type_constraints.swift:245:31:245:31 | 1 | | file://:0:0:0:0 | Int | +| type_constraints.swift:251:8:251:8 | self | | type_constraints.swift:228:1:240:1 | Accumulator | +| type_constraints.swift:251:8:251:8 | self | T | type_constraints.swift:228:20:228:20 | T | +| type_constraints.swift:251:17:251:25 | item | | type_constraints.swift:228:20:228:20 | T | +| type_constraints.swift:252:12:252:12 | self | | type_constraints.swift:228:1:240:1 | Accumulator | +| type_constraints.swift:252:12:252:12 | self | T | type_constraints.swift:228:20:228:20 | T | +| type_constraints.swift:252:12:252:49 | call to contains(where:) | | file://:0:0:0:0 | Bool | +| type_constraints.swift:252:35:252:35 | $0 | | type_constraints.swift:228:20:228:20 | T | +| type_constraints.swift:252:37:252:37 | $0 | | type_constraints.swift:228:20:228:20 | T | +| type_constraints.swift:252:43:252:43 | item | | type_constraints.swift:228:20:228:20 | T | +| type_constraints.swift:257:7:257:7 | intAcc | | type_constraints.swift:228:1:240:1 | Accumulator | +| type_constraints.swift:257:7:257:7 | intAcc | | type_constraints.swift:228:1:240:1 | Accumulator | +| type_constraints.swift:257:16:257:45 | call to Accumulator.init(values:) | | type_constraints.swift:228:1:240:1 | Accumulator | +| type_constraints.swift:257:37:257:37 | 1 | | file://:0:0:0:0 | Int | +| type_constraints.swift:257:40:257:40 | 2 | | file://:0:0:0:0 | Int | +| type_constraints.swift:257:43:257:43 | 3 | | file://:0:0:0:0 | Int | +| type_constraints.swift:258:7:258:7 | cnt | | file://:0:0:0:0 | Int | +| type_constraints.swift:258:7:258:7 | cnt | | file://:0:0:0:0 | Int | +| type_constraints.swift:258:13:258:13 | intAcc | | type_constraints.swift:228:1:240:1 | Accumulator | +| type_constraints.swift:258:13:258:26 | call to count() | | file://:0:0:0:0 | Int | +| type_constraints.swift:259:13:259:13 | intAcc | | type_constraints.swift:228:1:240:1 | Accumulator | +| type_constraints.swift:260:7:260:7 | has | | file://:0:0:0:0 | Bool | +| type_constraints.swift:260:7:260:7 | has | | file://:0:0:0:0 | Bool | +| type_constraints.swift:260:13:260:13 | intAcc | | type_constraints.swift:228:1:240:1 | Accumulator | +| type_constraints.swift:260:13:260:30 | call to contains(_:) | | file://:0:0:0:0 | Bool | +| type_constraints.swift:260:29:260:29 | 2 | | file://:0:0:0:0 | Int | +| type_constraints.swift:265:7:265:7 | self | | type_constraints.swift:265:1:278:1 | Transformer | +| type_constraints.swift:267:3:267:3 | self | | type_constraints.swift:265:1:278:1 | Transformer | +| type_constraints.swift:270:8:270:8 | self | | type_constraints.swift:265:1:278:1 | Transformer | +| type_constraints.swift:271:18:271:18 | 0 | | file://:0:0:0:0 | Int | +| type_constraints.swift:271:29:271:29 | 1 | | file://:0:0:0:0 | Int | +| type_constraints.swift:275:8:275:8 | self | | type_constraints.swift:265:1:278:1 | Transformer | +| type_constraints.swift:275:42:275:47 | a | | type_constraints.swift:275:14:275:17 | A | +| type_constraints.swift:275:50:275:55 | b | | type_constraints.swift:275:28:275:31 | B | +| type_constraints.swift:281:7:281:7 | t | | type_constraints.swift:265:1:278:1 | Transformer | +| type_constraints.swift:281:7:281:7 | t | | type_constraints.swift:265:1:278:1 | Transformer | +| type_constraints.swift:281:11:281:23 | call to Transformer.init() | | type_constraints.swift:265:1:278:1 | Transformer | +| type_constraints.swift:282:12:282:12 | t | | type_constraints.swift:265:1:278:1 | Transformer | +| type_constraints.swift:282:25:282:25 | 3 | | file://:0:0:0:0 | Int | +| type_constraints.swift:282:28:282:28 | 1 | | file://:0:0:0:0 | Int | +| type_constraints.swift:282:31:282:31 | 2 | | file://:0:0:0:0 | Int | +| type_constraints.swift:283:12:283:12 | t | | type_constraints.swift:265:1:278:1 | Transformer | +| type_constraints.swift:283:25:283:25 | c | | file://:0:0:0:0 | String | +| type_constraints.swift:283:30:283:30 | a | | file://:0:0:0:0 | String | +| type_constraints.swift:283:35:283:35 | b | | file://:0:0:0:0 | String | +| type_constraints.swift:284:7:284:7 | r3 | | file://:0:0:0:0 | Bool | +| type_constraints.swift:284:7:284:7 | r3 | | file://:0:0:0:0 | Bool | +| type_constraints.swift:284:12:284:12 | t | | type_constraints.swift:265:1:278:1 | Transformer | +| type_constraints.swift:284:12:284:26 | call to merge(_:_:) | | file://:0:0:0:0 | Bool | +| type_constraints.swift:284:20:284:20 | 1 | | file://:0:0:0:0 | Int | +| type_constraints.swift:284:23:284:23 | x | | file://:0:0:0:0 | String | +| type_constraints.swift:290:27:290:36 | value | | type_constraints.swift:290:12:290:15 | T | +| type_constraints.swift:290:39:290:50 | lower | | type_constraints.swift:290:12:290:15 | T | +| type_constraints.swift:290:53:290:64 | upper | | type_constraints.swift:290:12:290:15 | T | +| type_constraints.swift:291:6:291:6 | value | | type_constraints.swift:290:12:290:15 | T | +| type_constraints.swift:291:14:291:14 | lower | | type_constraints.swift:290:12:290:15 | T | +| type_constraints.swift:291:29:291:29 | lower | | type_constraints.swift:290:12:290:15 | T | +| type_constraints.swift:292:6:292:6 | value | | type_constraints.swift:290:12:290:15 | T | +| type_constraints.swift:292:14:292:14 | upper | | type_constraints.swift:290:12:290:15 | T | +| type_constraints.swift:292:29:292:29 | upper | | type_constraints.swift:290:12:290:15 | T | +| type_constraints.swift:293:10:293:10 | value | | type_constraints.swift:290:12:290:15 | T | +| type_constraints.swift:297:18:297:18 | 5 | | file://:0:0:0:0 | Int | +| type_constraints.swift:297:26:297:26 | 0 | | file://:0:0:0:0 | Int | +| type_constraints.swift:297:34:297:34 | 10 | | file://:0:0:0:0 | Int | +| type_constraints.swift:299:18:299:18 | m | | file://:0:0:0:0 | String | +| type_constraints.swift:299:28:299:28 | a | | file://:0:0:0:0 | String | +| type_constraints.swift:299:38:299:38 | z | | file://:0:0:0:0 | String | diff --git a/swift/ql/test/library-tests/type-inference/type-inference-ql.ql b/swift/ql/test/library-tests/type-inference/type-inference-ql.ql new file mode 100644 index 000000000000..b418e04b2c6b --- /dev/null +++ b/swift/ql/test/library-tests/type-inference/type-inference-ql.ql @@ -0,0 +1,29 @@ +import Common +import codeql.swift.typeinference.Type +import codeql.swift.typeinference.TypeInference as TypeInference +import TypeInference + +query predicate inferType(AstNode n, TypePath path, Type t) { + t = TypeInference::inferType(n, path) and + t != TUnknownType() and + toBeTested(n) +} + +module TypeTest implements TestSig { + string getARelevantTag() { result = "type" } + + predicate hasActualResult(Location location, string element, string tag, string value) { none() } + + predicate hasOptionalResult(Location location, string element, string tag, string value) { + exists(AstNode n, TypePath path, Type t, string at | + t = TypeInference::inferType(n, path) and + tag = "type" and + location = n.getLocation() and + (if path.isEmpty() then at = "" else at = "@" + TypePath::printTypePathVerbose(path)) and + value = element + at + ":" + t.toString() and + element = n.toString() + ) + } +} + +import MakeTest> diff --git a/swift/ql/test/library-tests/type-inference/type-inference.ql b/swift/ql/test/library-tests/type-inference/type-inference.ql index b34a72eb6937..f2ff25b2a767 100644 --- a/swift/ql/test/library-tests/type-inference/type-inference.ql +++ b/swift/ql/test/library-tests/type-inference/type-inference.ql @@ -1,49 +1,5 @@ import swift -import TestUtils -import utils.test.InlineExpectationsTest - -pragma[nomagic] -private predicate declHasPotentialCommentAt(Decl d, string path, int line) { - d.getLocation().hasLocationInfo(path, line + 1, _, _, _) -} - -pragma[nomagic] -private SingleLineComment getPrecedingComment(Decl d) { - exists(string path, int line | - declHasPotentialCommentAt(d, path, line) and - result.getLocation().hasLocationInfo(path, line, _, _, _) - ) -} - -module ResolveTest implements TestSig { - string getARelevantTag() { result = "target" } - - private predicate declHasName(Decl c, string value) { - exists(string s | - s = getPrecedingComment(c).getText() and - value = s.substring(3, s.length() - 1) - ) - or - not exists(getPrecedingComment(c)) and - value = [c.(EnumElementDecl).getName(), c.(Function).getName()] - } - - predicate hasActualResult(Location location, string element, string tag, string value) { - exists(AstNode source, Decl target | - location = source.getLocation() and - element = source.toString() and - target = - [ - source.(CallExpr).getStaticTarget().(Decl), source.(MethodLookupExpr).getMember(), - source.(EnumElementPattern).getElement() - ] and - declHasName(target, value) and - tag = "target" and - toBeTested(source) and - toBeTested(target) - ) - } -} +import Common private Type getTypeAt(Type t, string path) { path = "" and From b4c07b8ea97d75357c962dc92940f74afe51b42d Mon Sep 17 00:00:00 2001 From: Tom Hvitved Date: Tue, 12 May 2026 12:56:19 +0200 Subject: [PATCH 15/18] base --- .../internal/typeinference/TypeInference.qll | 4 - .../typeinference/internal/TypeInference.qll | 371 ++++++------------ .../swift/typeinference/TypeInference.qll | 2 - .../type-inference/generics.swift | 11 + .../type-inference/type-inference-ql.expected | 74 +++- 5 files changed, 193 insertions(+), 269 deletions(-) diff --git a/rust/ql/lib/codeql/rust/internal/typeinference/TypeInference.qll b/rust/ql/lib/codeql/rust/internal/typeinference/TypeInference.qll index 1285256c1fb5..669da25fe23a 100644 --- a/rust/ql/lib/codeql/rust/internal/typeinference/TypeInference.qll +++ b/rust/ql/lib/codeql/rust/internal/typeinference/TypeInference.qll @@ -219,8 +219,6 @@ private module Input2Common { } private module PreInput2 implements InputSig2 { - PreTypeMention getABaseTypeMention(Type t) { none() } - PreTypeMention getATypeParameterConstraint(TypeParameter tp) { result = Input2Common::getATypeParameterConstraint(tp) } @@ -245,8 +243,6 @@ private module PreInput2 implements InputSig2 { module PreM2 = Make2; private module Input2 implements InputSig2 { - TypeMention getABaseTypeMention(Type t) { none() } - TypeMention getATypeParameterConstraint(TypeParameter tp) { result = Input2Common::getATypeParameterConstraint(tp) } diff --git a/shared/typeinference/codeql/typeinference/internal/TypeInference.qll b/shared/typeinference/codeql/typeinference/internal/TypeInference.qll index 1027854f9365..6cbaec7e97d4 100644 --- a/shared/typeinference/codeql/typeinference/internal/TypeInference.qll +++ b/shared/typeinference/codeql/typeinference/internal/TypeInference.qll @@ -332,58 +332,31 @@ module Make1 Input1> { /** * Provides the input to `Make2`. * - * The `TypeMention` parameter is used to build the base type hierarchy based on - * `getABaseTypeMention` and to construct the constraint satisfaction - * hierarchy based on `conditionSatisfiesConstraint`. - * - * It will usually be based on syntactic occurrences of types in the source - * code. For example, in - * - * ```csharp - * class Base { } - * - * class C : Base, Interface { } - * ``` - * - * a type mention would exist for `Base` and resolve to the following - * types: - * - * `TypePath` | `Type` - * ---------- | ------- - * `""` | ``Base`1`` - * `"B"` | `T` + * The `TypeMention` parameter is used to construct the constraint satisfaction + * hierarchy based on `conditionSatisfiesConstraint`, which is general enough + * to model both class hierarchies and trait implementation hierarchies in Rust. */ signature module InputSig2 { - /** - * Gets a base type mention of `t`, if any. Example: - * - * ```csharp - * class C : Base, Interface { } - * // ^ `t` - * // ^^^^^^^ `result` - * // ^^^^^^^^^ `result` - * ``` - */ - TypeMention getABaseTypeMention(Type t); - /** * Gets a type constraint on the type parameter `tp`, if any. All * instantiations of the type parameter must satisfy the constraint. * * For example, in + * * ```csharp * class GenericClass : IComparable> * // ^ `tp` * where T : IComparable { } * // ^^^^^^^^^^^^^^ `result` * ``` + * * the type parameter `T` has the constraint `IComparable`. */ TypeMention getATypeParameterConstraint(TypeParameter tp); /** * Holds if - * - `abs` is a type abstraction that introduces type variables that are + * - `abs` is a type abstraction that may introduce type variables that are * free in `condition` and `constraint`, * - and for every instantiation of the type parameters from `abs` the * resulting `condition` satisfies the constraint given by `constraint`. @@ -391,6 +364,7 @@ module Make1 Input1> { * through `constraint` should also apply to `condition`. * * Example in C#: + * * ```csharp * class C : IComparable> { } * // ^^^ `abs` @@ -399,6 +373,7 @@ module Make1 Input1> { * ``` * * Example in Rust: + * * ```rust * impl Trait for Type { } * // ^^^ `abs` ^^^^^^^^^^^^^^^ `condition` @@ -407,18 +382,22 @@ module Make1 Input1> { * * To see how `abs` changes the meaning of the type parameters that occur in * `condition`, consider the following examples in Rust: + * * ```rust * impl Trait for T { } * // ^^^ `abs` ^ `condition` * // ^^^^^ `constraint` * ``` + * * Here the meaning is "for all type parameters `T` it is the case that `T` * implements `Trait`". On the other hand, in + * * ```rust * fn foo() { } * // ^ `condition` * // ^^^^^ `constraint` * ``` + * * the meaning is "`T` implements `Trait`" where the constraint is only * valid for the specific `T`. Note that `condition` and `condition` are * identical in the two examples. To encode the difference, `abs` in the @@ -836,99 +815,6 @@ module Make1 Input1> { predicate multipleConstraintImplementations(Type conditionRoot, Type constraintRoot) { countConstraintImplementations(conditionRoot, constraintRoot) > 1 } - - /** - * Holds if `baseMention` is a (transitive) base type mention of `sub`, - * and `t` is mentioned (implicitly) at `path` inside `baseMention`. For - * example, in - * - * ```csharp - * class C { } - * - * class Base { } - * - * class Mid : Base> { } - * - * class Sub : Mid> { } // Sub extends Base> - * ``` - * - * - ``C`1`` is mentioned at `T2` for immediate base type mention `Base>` - * of `Mid`, - * - `T3` is mentioned at `T2.T1` for immediate base type mention `Base>` - * of `Mid`, - * - ``C`1`` is mentioned at `T3` for immediate base type mention `Mid>` - * of `Sub`, - * - `T4` is mentioned at `T3.T1` for immediate base type mention `Mid>` - * of `Sub`, - * - ``C`1`` is mentioned at `T2` and implicitly at `T2.T1` for transitive base type - * mention `Base>` of `Sub`, and - * - `T4` is mentioned implicitly at `T2.T1.T1` for transitive base type mention - * `Base>` of `Sub`. - */ - pragma[nomagic] - predicate baseTypeMentionHasTypeAt(Type sub, TypeMention baseMention, TypePath path, Type t) { - exists(TypeMention immediateBaseMention | - pragma[only_bind_into](immediateBaseMention) = - getABaseTypeMention(pragma[only_bind_into](sub)) - | - // immediate base class - baseMention = immediateBaseMention and - t = immediateBaseMention.getTypeAt(path) - or - // transitive base class - exists(Type immediateBase | immediateBase = getTypeMentionRoot(immediateBaseMention) | - baseTypeMentionHasNonTypeParameterAt(immediateBase, baseMention, path, t) - or - exists(TypePath path0, TypePath prefix, TypePath suffix, TypeParameter tp | - /* - * Example: - * - * - `prefix = "T2.T1"`, - * - `path0 = "T3"`, - * - `suffix = ""`, - * - `path = "T2.T1"` - * - * ```csharp - * class C { } - * ^ `t` - * - * class Base { } - * - * class Mid : Base> { } - * // ^^^ `immediateBase` - * // ^^ `tp` - * // ^^^^^^^^^^^ `baseMention` - * - * class Sub : Mid> { } - * // ^^^ `sub` - * // ^^^^^^^^^^ `immediateBaseMention` - * ``` - */ - - baseTypeMentionHasTypeParameterAt(immediateBase, baseMention, prefix, tp) and - t = immediateBaseMention.getTypeAt(path0) and - path0.isCons(tp, suffix) and - path = prefix.append(suffix) - ) - ) - ) - } - - overlay[caller?] - pragma[inline] - predicate baseTypeMentionHasNonTypeParameterAt( - Type sub, TypeMention baseMention, TypePath path, Type t - ) { - not t = sub.getATypeParameter() and baseTypeMentionHasTypeAt(sub, baseMention, path, t) - } - - overlay[caller?] - pragma[inline] - predicate baseTypeMentionHasTypeParameterAt( - Type sub, TypeMention baseMention, TypePath path, TypeParameter tp - ) { - tp = sub.getATypeParameter() and baseTypeMentionHasTypeAt(sub, baseMention, path, tp) - } } private import BaseTypes @@ -1514,76 +1400,147 @@ module Make1 Input1> { private module AccessBaseType { /** - * Holds if inferring types at `a` in environment `e` might depend on the type at - * `path` of `apos` having `base` as a transitive base type. + * Holds if the type of `target` at `apos` and `pathToTp` is type parameter `tp`, + * and an argument with root type `argRootType` may be able to be matched against + * `tp` via the `conditionSatisfiesConstraint` hierarchy. */ - private predicate relevantAccess( - Access a, AccessEnvironment e, AccessPosition apos, Type base + pragma[nomagic] + private predicate argRootTypeSatisfiesTargetTypeCand( + Type argRootType, Declaration target, AccessPosition apos, TypeParameter tp, + TypePath pathToTp ) { - exists(Declaration target, DeclarationPosition dpos | - target = a.getTarget(e) and + exists( + DeclarationPosition dpos, TypeMention condition, TypeMention constraint, + Type constraintRootType + | accessDeclarationPositionMatch(apos, dpos) and - declarationBaseType(target, dpos, base, _, _) + tp = target.getDeclaredType(dpos, pathToTp) and + conditionSatisfiesConstraintTypeAt(_, condition, constraint, TypePath::nil(), + constraintRootType) and + constraintRootType = target.getDeclaredType(dpos, TypePath::nil()) and + argRootType = condition.getTypeAt(TypePath::nil()) ) } + private newtype TRelevantTarget = + MkRelevantTarget(Declaration target, AccessPosition apos) { + argRootTypeSatisfiesTargetTypeCand(_, target, apos, _, _) + } + + private class RelevantTarget extends MkRelevantTarget { + Declaration target; + AccessPosition apos; + + RelevantTarget() { this = MkRelevantTarget(target, apos) } + + Type getTypeAt(TypePath path) { + exists(DeclarationPosition dpos | + accessDeclarationPositionMatch(apos, dpos) and + result = target.getDeclaredType(dpos, path) + ) + } + + string toString() { result = target.toString() + ", " + apos.toString() } + + Location getLocation() { result = target.getLocation() } + } + pragma[nomagic] - private Type inferTypeAt( - Access a, AccessEnvironment e, AccessPosition apos, TypeParameter tp, TypePath suffix + private predicate argRootTypeSatisfiesTargetTypeCand( + Type argRootType, Access a, AccessEnvironment e, Declaration target, AccessPosition apos, + TypeParameter tp, TypePath pathToTp ) { - relevantAccess(a, e, apos, _) and - exists(TypePath path0 | - result = a.getInferredType(e, apos, path0) and - path0.isCons(tp, suffix) - ) + target = a.getTarget(e) and + argRootTypeSatisfiesTargetTypeCand(argRootType, target, apos, tp, pathToTp) and + not exists(getTypeArgument(a, target, tp, _)) + } + + private newtype TRelevantAccess = + MkRelevantAccess(Access a, AccessPosition apos, AccessEnvironment e) { + argRootTypeSatisfiesTargetTypeCand(a.getInferredType(e, apos, TypePath::nil()), a, e, _, + apos, _, _) + } + + private class RelevantAccess extends MkRelevantAccess { + Access a; + AccessPosition apos; + AccessEnvironment e; + + RelevantAccess() { this = MkRelevantAccess(a, apos, e) } + + RelevantTarget getTarget() { result = MkRelevantTarget(a.getTarget(e), apos) } + + pragma[nomagic] + Type getTypeAt(TypePath path) { result = a.getInferredType(e, apos, path) } + + string toString() { result = a.toString() + ", " + apos.toString() } + + Location getLocation() { result = a.getLocation() } + } + + private module SatisfiesParameterConstraintInput implements + SatisfiesConstraintWithTypeMatchingInputSig + { + predicate relevantConstraint(RelevantAccess at, RelevantTarget constraint) { + constraint = at.getTarget() + } + + class TypeMatchingContext = Access; + + TypeMatchingContext getTypeMatchingContext(RelevantAccess at) { + at = MkRelevantAccess(result, _, _) + } + + pragma[nomagic] + predicate typeMatch(TypeMatchingContext ctx, TypeParameter tp, TypePath path, Type t) { + typeMatch(ctx, _, _, path, t, tp) + } } + private module SatisfiesParameterConstraint = + SatisfiesConstraintWithTypeMatching; + /** - * Holds if `baseMention` is a (transitive) base type mention of the - * type of `a` at position `apos` at path `pathToSub` in environment - * `e`, and `t` is mentioned (implicitly) at `path` inside `base`. + * Holds if the (transitive) base type `t` at `path` of `a` in environment `e` + * for some `AccessPosition` matches the type parameter `tp`, which is used in + * the declared types of `target`. * * For example, in * * ```csharp * class C { } * - * class Base { } + * class Base { + * // ^^ `tp` + * public C Method() { ... } + * // ^^^^^^ `target` + * } * * class Mid : Base> { } * * class Sub : Mid> { } * - * new Sub().ToString(); - * // ^^^^^^^^^^^^^^ node at `apos` - * // ^^^^^^^^^^^^^^^^^^^^^^^^^ `a` + * new Sub().Method(); // Note: `Sub` is a subtype of `Base>>` + * // ^^^^^^^^^^^^^^^^^^^^^^^ `a` * ``` * - * where the method call is an access, `new Sub()` is at the access - * position which is the receiver of a method call, and `pathToSub` is - * `""` we have: + * we have that type parameter `T2` of `Base` is matched as follows: * - * `baseMention` | `path` | `t` - * ------------- | ------------ | --- - * `Mid>` | `"T3"` | ``C`1`` - * `Mid>` | `"T3.T1"` | `int` - * `Base>` | `"T2"` | ``C`1`` - * `Base>` | `"T2.T1"` | ``C`1`` - * `Base>` | `"T2.T1.T1"` | `int` + * `path` | `t` + * --------- | ------- + * `""` | ``C`1`` + * `"T1"` | ``C`1`` + * `"T1.T1"` | `int` */ - predicate hasBaseTypeMention( - Access a, AccessEnvironment e, AccessPosition apos, TypeMention baseMention, - TypePath path, Type t + pragma[nomagic] + predicate baseTypeMatch( + Access a, AccessEnvironment e, Declaration target, TypePath path, Type t, TypeParameter tp ) { - relevantAccess(a, e, apos, getTypeMentionRoot(baseMention)) and - exists(Type sub | sub = a.getInferredType(e, apos, TypePath::nil()) | - baseTypeMentionHasNonTypeParameterAt(sub, baseMention, path, t) - or - exists(TypePath prefix, TypePath suffix, TypeParameter tp | - baseTypeMentionHasTypeParameterAt(sub, baseMention, prefix, tp) and - t = inferTypeAt(a, e, apos, tp, suffix) and - path = prefix.append(suffix) - ) + exists(AccessPosition apos, TypePath pathToTp | + argRootTypeSatisfiesTargetTypeCand(_, a, e, target, apos, tp, pathToTp) and + SatisfiesParameterConstraint::satisfiesConstraint(MkRelevantAccess(a, apos, e), + MkRelevantTarget(target, apos), pathToTp.appendInverse(path), t) ) } } @@ -1694,77 +1651,6 @@ module Make1 Input1> { } } - /** - * Holds if the type of `a` at `apos` in environment `e` has the base type `base`, - * and when viewed as an element of that type has the type `t` at `path`. - */ - pragma[nomagic] - private predicate accessBaseType( - Access a, AccessEnvironment e, AccessPosition apos, Type base, TypePath path, Type t - ) { - exists(TypeMention tm | - AccessBaseType::hasBaseTypeMention(a, e, apos, tm, path, t) and - base = getTypeMentionRoot(tm) - ) - } - - /** - * Holds if the declared type at `decl` for `dpos` at the `path` is `tp` - * and `path` starts with a type parameter of `base`. - */ - pragma[nomagic] - private predicate declarationBaseType( - Declaration decl, DeclarationPosition dpos, Type base, TypePath path, TypeParameter tp - ) { - tp = decl.getDeclaredType(dpos, path) and - base.getATypeParameter() = path.getHead() - } - - /** - * Holds if the (transitive) base type `t` at `path` of `a` in environment `e` - * for some `AccessPosition` matches the type parameter `tp`, which is used in - * the declared types of `target`. - * - * For example, in - * - * ```csharp - * class C { } - * - * class Base { - * // ^^ `tp` - * public C Method() { ... } - * // ^^^^^^ `target` - * } - * - * class Mid : Base> { } - * - * class Sub : Mid> { } - * - * new Sub().Method(); // Note: `Sub` is a subtype of `Base>>` - * // ^^^^^^^^^^^^^^^^^^^^^^^ `a` - * ``` - * - * we have that type parameter `T2` of `Base` is matched as follows: - * - * `path` | `t` - * --------- | ------- - * `""` | ``C`1`` - * `"T1"` | ``C`1`` - * `"T1.T1"` | `int` - */ - pragma[nomagic] - private predicate baseTypeMatch( - Access a, AccessEnvironment e, Declaration target, TypePath path, Type t, TypeParameter tp - ) { - not exists(getTypeArgument(a, target, tp, _)) and - target = a.getTarget(e) and - exists(AccessPosition apos, DeclarationPosition dpos, Type base, TypePath pathToTypeParam | - accessBaseType(a, e, apos, base, pathToTypeParam.appendInverse(path), t) and - declarationBaseType(target, dpos, base, pathToTypeParam, tp) and - accessDeclarationPositionMatch(apos, dpos) - ) - } - /** * Holds if for `a` and corresponding `target` in environment `e`, the type parameter * `tp` is matched by a type argument at the access with type `t` and type path @@ -1849,11 +1735,10 @@ module Make1 Input1> { // We can infer the type of `tp` from one of the access positions directTypeMatch(a, e, target, path, t, tp) or - // We can infer the type of `tp` by going up the type hiearchy - baseTypeMatch(a, e, target, path, t, tp) - or // We can infer the type of `tp` by a type constraint typeConstraintBaseTypeMatch(a, e, target, path, t, tp) + or + AccessBaseType::baseTypeMatch(a, e, target, path, t, tp) } /** diff --git a/swift/ql/lib/codeql/swift/typeinference/TypeInference.qll b/swift/ql/lib/codeql/swift/typeinference/TypeInference.qll index 5c61476bf37b..0b1e38035f15 100644 --- a/swift/ql/lib/codeql/swift/typeinference/TypeInference.qll +++ b/swift/ql/lib/codeql/swift/typeinference/TypeInference.qll @@ -73,8 +73,6 @@ class TypePath = M1::TypePath; module TypePath = M1::TypePath; private module Input2 implements InputSig2 { - TypeMention getABaseTypeMention(Type t) { none() } // todo: check that `conditionSatisfiesConstraint` can be used - TypeMention getATypeParameterConstraint(TypeParameter tp) { result.(GenericContextMention).getContext() = tp.(SelfTypeParameter).getProtocol() or diff --git a/swift/ql/test/library-tests/type-inference/generics.swift b/swift/ql/test/library-tests/type-inference/generics.swift index 3509d3a8349c..652a6c70b2d4 100644 --- a/swift/ql/test/library-tests/type-inference/generics.swift +++ b/swift/ql/test/library-tests/type-inference/generics.swift @@ -172,8 +172,19 @@ class Derived : Base { } } +class DerivedDerived : Derived { + // DerivedDerived.init + init(_ v: D) { + super.init(v, true) // $ type=v:D target=Derived.init + } +} + func testDerived() { let d = Derived(1, "x") // $ type=d@Derived:Int type=d@Derived:String target=Derived.init let v1 = d.getValue1() // $ type=v1:String target=Base.getValue1 let v2 = d.getValue2() // $ type=v2:Int target=Base.getValue2 + + let dd = DerivedDerived("hello") // $ type=dd@DerivedDerived:String target=DerivedDerived.init + let vv1 = dd.getValue1() // $ type=vv1:Bool target=Base.getValue1 + let vv2 = dd.getValue2() // $ type=vv2:String target=Base.getValue2 } \ No newline at end of file diff --git a/swift/ql/test/library-tests/type-inference/type-inference-ql.expected b/swift/ql/test/library-tests/type-inference/type-inference-ql.expected index bf1808f359e0..89efd818fd2d 100644 --- a/swift/ql/test/library-tests/type-inference/type-inference-ql.expected +++ b/swift/ql/test/library-tests/type-inference/type-inference-ql.expected @@ -3,7 +3,6 @@ testFailures | basics.swift:21:22:22:1 | // $ type=super:C target=C.init\n | Missing result: type=super:C | | basics.swift:40:18:41:1 | // $ type=.value:T\n | Missing result: type=.value:T | | basics.swift:47:22:48:1 | // $ type=super@Generic:Int target=Generic.init\n | Missing result: type=super@Generic:Int | -| basics.swift:56:25:57:1 | // $ type=y:Int target=Generic.getValue\n | Missing result: type=y:Int | | basics.swift:64:22:65:1 | // $ type=.myInt:Int\n | Missing result: type=.myInt:Int | | classes.swift:74:25:75:1 | // $ type=.value:Int\n | Missing result: type=.value:Int | | classes.swift:106:18:107:1 | // $ type=.value:Int\n | Missing result: type=.value:Int | @@ -24,8 +23,6 @@ testFailures | generics.swift:120:26:121:1 | // $ type=x:MyType target=MyProtocol.bar\n | Missing result: type=x:MyType | | generics.swift:159:19:160:1 | // $ type=.value1:T1\n | Missing result: type=.value1:T1 | | generics.swift:164:19:165:1 | // $ type=.value2:T2\n | Missing result: type=.value2:T2 | -| generics.swift:177:26:178:1 | // $ type=v1:String target=Base.getValue1\n | Missing result: type=v1:String | -| generics.swift:178:26:179:1 | // $ type=v2:Int target=Base.getValue2\n | Missing result: type=v2:Int | | key_paths.swift:35:30:36:1 | // $ type=xVal:Double\n | Missing result: type=xVal:Double | | key_paths.swift:36:30:37:1 | // $ type=yVal:Double\n | Missing result: type=yVal:Double | | key_paths.swift:48:40:49:1 | // $ type=startX:Double\n | Missing result: type=startX:Double | @@ -148,7 +145,10 @@ inferType | basics.swift:55:7:55:7 | gd | | basics.swift:44:1:49:1 | GenericDerived | | basics.swift:55:7:55:7 | gd | | basics.swift:44:1:49:1 | GenericDerived | | basics.swift:55:12:55:27 | call to GenericDerived.init() | | basics.swift:44:1:49:1 | GenericDerived | +| basics.swift:56:7:56:7 | y | | file://:0:0:0:0 | Int | +| basics.swift:56:7:56:7 | y | | file://:0:0:0:0 | Int | | basics.swift:56:11:56:11 | gd | | basics.swift:44:1:49:1 | GenericDerived | +| basics.swift:56:11:56:23 | call to getValue() | | file://:0:0:0:0 | Int | | basics.swift:63:8:63:8 | self | | basics.swift:5:1:16:1 | C | | basics.swift:64:12:64:12 | self | | basics.swift:5:1:16:1 | C | | basics.swift:64:20:64:20 | 2 | | file://:0:0:0:0 | Int | @@ -602,23 +602,57 @@ inferType | generics.swift:171:5:171:22 | call to Base.init(_:_:) | T2 | generics.swift:168:15:168:15 | T1 | | generics.swift:171:16:171:16 | v2 | | generics.swift:168:19:168:19 | T2 | | generics.swift:171:20:171:20 | v1 | | generics.swift:168:15:168:15 | T1 | -| generics.swift:176:7:176:7 | d | | generics.swift:168:1:173:1 | Derived | -| generics.swift:176:7:176:7 | d | | generics.swift:168:1:173:1 | Derived | -| generics.swift:176:7:176:7 | d | T1 | file://:0:0:0:0 | Int | -| generics.swift:176:7:176:7 | d | T1 | file://:0:0:0:0 | Int | -| generics.swift:176:7:176:7 | d | T2 | file://:0:0:0:0 | String | -| generics.swift:176:7:176:7 | d | T2 | file://:0:0:0:0 | String | -| generics.swift:176:11:176:25 | call to Derived.init(_:_:) | | generics.swift:168:1:173:1 | Derived | -| generics.swift:176:11:176:25 | call to Derived.init(_:_:) | T1 | file://:0:0:0:0 | Int | -| generics.swift:176:11:176:25 | call to Derived.init(_:_:) | T2 | file://:0:0:0:0 | String | -| generics.swift:176:19:176:19 | 1 | | file://:0:0:0:0 | Int | -| generics.swift:176:22:176:22 | x | | file://:0:0:0:0 | String | -| generics.swift:177:12:177:12 | d | | generics.swift:168:1:173:1 | Derived | -| generics.swift:177:12:177:12 | d | T1 | file://:0:0:0:0 | Int | -| generics.swift:177:12:177:12 | d | T2 | file://:0:0:0:0 | String | -| generics.swift:178:12:178:12 | d | | generics.swift:168:1:173:1 | Derived | -| generics.swift:178:12:178:12 | d | T1 | file://:0:0:0:0 | Int | -| generics.swift:178:12:178:12 | d | T2 | file://:0:0:0:0 | String | +| generics.swift:175:7:175:7 | generics.DerivedDerived | | file://:0:0:0:0 | String | +| generics.swift:175:7:175:7 | self | | generics.swift:175:1:180:1 | DerivedDerived | +| generics.swift:175:7:175:7 | self | D | generics.swift:175:22:175:22 | D | +| generics.swift:175:44:175:44 | self | | generics.swift:175:1:180:1 | DerivedDerived | +| generics.swift:175:44:175:44 | self | D | generics.swift:175:22:175:22 | D | +| generics.swift:177:3:177:3 | self | | generics.swift:175:1:180:1 | DerivedDerived | +| generics.swift:177:3:177:3 | self | D | generics.swift:175:22:175:22 | D | +| generics.swift:177:8:177:13 | v | | generics.swift:175:22:175:22 | D | +| generics.swift:178:5:178:23 | call to Derived.init(_:_:) | | generics.swift:168:1:173:1 | Derived | +| generics.swift:178:5:178:23 | call to Derived.init(_:_:) | T1 | generics.swift:175:22:175:22 | D | +| generics.swift:178:16:178:16 | v | | generics.swift:175:22:175:22 | D | +| generics.swift:183:7:183:7 | d | | generics.swift:168:1:173:1 | Derived | +| generics.swift:183:7:183:7 | d | | generics.swift:168:1:173:1 | Derived | +| generics.swift:183:7:183:7 | d | T1 | file://:0:0:0:0 | Int | +| generics.swift:183:7:183:7 | d | T1 | file://:0:0:0:0 | Int | +| generics.swift:183:7:183:7 | d | T2 | file://:0:0:0:0 | String | +| generics.swift:183:7:183:7 | d | T2 | file://:0:0:0:0 | String | +| generics.swift:183:11:183:25 | call to Derived.init(_:_:) | | generics.swift:168:1:173:1 | Derived | +| generics.swift:183:11:183:25 | call to Derived.init(_:_:) | T1 | file://:0:0:0:0 | Int | +| generics.swift:183:11:183:25 | call to Derived.init(_:_:) | T2 | file://:0:0:0:0 | String | +| generics.swift:183:19:183:19 | 1 | | file://:0:0:0:0 | Int | +| generics.swift:183:22:183:22 | x | | file://:0:0:0:0 | String | +| generics.swift:184:7:184:7 | v1 | | file://:0:0:0:0 | String | +| generics.swift:184:7:184:7 | v1 | | file://:0:0:0:0 | String | +| generics.swift:184:12:184:12 | d | | generics.swift:168:1:173:1 | Derived | +| generics.swift:184:12:184:12 | d | T1 | file://:0:0:0:0 | Int | +| generics.swift:184:12:184:12 | d | T2 | file://:0:0:0:0 | String | +| generics.swift:184:12:184:24 | call to getValue1() | | file://:0:0:0:0 | String | +| generics.swift:185:7:185:7 | v2 | | file://:0:0:0:0 | Int | +| generics.swift:185:7:185:7 | v2 | | file://:0:0:0:0 | Int | +| generics.swift:185:12:185:12 | d | | generics.swift:168:1:173:1 | Derived | +| generics.swift:185:12:185:12 | d | T1 | file://:0:0:0:0 | Int | +| generics.swift:185:12:185:12 | d | T2 | file://:0:0:0:0 | String | +| generics.swift:185:12:185:24 | call to getValue2() | | file://:0:0:0:0 | Int | +| generics.swift:187:7:187:7 | dd | | generics.swift:175:1:180:1 | DerivedDerived | +| generics.swift:187:7:187:7 | dd | | generics.swift:175:1:180:1 | DerivedDerived | +| generics.swift:187:7:187:7 | dd | D | file://:0:0:0:0 | String | +| generics.swift:187:7:187:7 | dd | D | file://:0:0:0:0 | String | +| generics.swift:187:12:187:34 | call to DerivedDerived.init(_:) | | generics.swift:175:1:180:1 | DerivedDerived | +| generics.swift:187:12:187:34 | call to DerivedDerived.init(_:) | D | file://:0:0:0:0 | String | +| generics.swift:187:27:187:27 | hello | | file://:0:0:0:0 | String | +| generics.swift:188:7:188:7 | vv1 | | file://:0:0:0:0 | Bool | +| generics.swift:188:7:188:7 | vv1 | | file://:0:0:0:0 | Bool | +| generics.swift:188:13:188:13 | dd | | generics.swift:175:1:180:1 | DerivedDerived | +| generics.swift:188:13:188:13 | dd | D | file://:0:0:0:0 | String | +| generics.swift:188:13:188:26 | call to getValue1() | | file://:0:0:0:0 | Bool | +| generics.swift:189:7:189:7 | vv2 | | file://:0:0:0:0 | String | +| generics.swift:189:7:189:7 | vv2 | | file://:0:0:0:0 | String | +| generics.swift:189:13:189:13 | dd | | generics.swift:175:1:180:1 | DerivedDerived | +| generics.swift:189:13:189:13 | dd | D | file://:0:0:0:0 | String | +| generics.swift:189:13:189:26 | call to getValue2() | | file://:0:0:0:0 | String | | key_paths.swift:4:7:4:7 | self | | key_paths.swift:3:1:17:1 | Point | | key_paths.swift:4:7:4:7 | self | | key_paths.swift:3:1:17:1 | Point | | key_paths.swift:4:7:4:7 | self | | key_paths.swift:3:1:17:1 | Point | From 2e11a897fa7060704c71849512a53ad8c2e96499 Mon Sep 17 00:00:00 2001 From: Tom Hvitved Date: Tue, 12 May 2026 14:19:22 +0200 Subject: [PATCH 16/18] cleanup --- .../swift/typeinference/TypeInference.qll | 3037 +---------------- 1 file changed, 7 insertions(+), 3030 deletions(-) diff --git a/swift/ql/lib/codeql/swift/typeinference/TypeInference.qll b/swift/ql/lib/codeql/swift/typeinference/TypeInference.qll index 0b1e38035f15..a0e2f11c3172 100644 --- a/swift/ql/lib/codeql/swift/typeinference/TypeInference.qll +++ b/swift/ql/lib/codeql/swift/typeinference/TypeInference.qll @@ -82,7 +82,7 @@ private module Input2 implements InputSig2 { /** * Use the constraint mechanism in the shared type inference library to - * support traits. In Rust `constraint` is always a trait. + * support class hierarchies. * * See the documentation of `conditionSatisfiesConstraint` in the shared type * inference module for more information. @@ -96,55 +96,10 @@ private module Input2 implements InputSig2 { constraint.(TypeDeclBaseTypeMention).getDecl() = ctx ) and transitive = true - // // `impl` blocks implementing traits - // transitive = false and - // exists(Impl impl | - // abs = impl and - // condition = impl.getSelfTy() and - // constraint = impl.getTrait() - // ) - // or - // transitive = true and - // ( - // // super protocols - // exists(ProtocolDecl protocol | - // abs = protocol and - // condition = protocol and - // constraint =protocol.getABaseTypeDecl() trait.getATypeBound().getTypeRepr() - // ) - // or - // // trait bounds on type parameters - // exists(TypeParam param | - // abs = param.getATypeBound() and - // condition = param and - // constraint = abs.(TypeBound).getTypeRepr() - // ) - // or - // // the implicit `Self` type parameter satisfies the trait - // exists(SelfTypeParameterMention self | - // abs = self and - // condition = self and - // constraint = self.getTrait() - // ) - // or - // exists(ImplTraitTypeRepr impl | - // abs = impl and - // condition = impl and - // constraint = impl.getTypeBoundList().getABound().getTypeRepr() - // ) - // or - // // a `dyn Trait` type implements `Trait`. See the comment on - // // `DynTypeBoundListMention` for further details. - // exists(DynTraitTypeRepr object | - // abs = object and - // condition = object.getTypeBoundList() and - // constraint = object.getTrait() - // ) - // ) } predicate typeParameterIsFunctionallyDetermined(TypeParameter tp) { - tp instanceof AssociatedTypeTypeParameter + tp instanceof AssociatedTypeTypeParameter // todo: check } predicate typeAbstractionHasAmbiguousConstraintAt( @@ -175,19 +130,7 @@ private module Input3 implements InputSig3 { TypeMention getTypeAnnotation(AstNode n) { result.(ParamDeclTypeMention).getDecl() = n - // exists(LetStmt let | - // n = let.getPat() and - // result = let.getTypeRepr() - // ) - // or - // result = n.(SelfParam).getTypeRepr() - // or - // exists(Param p | - // n = p.getPat() and - // result = p.getTypeRepr() - // ) - // or - // result = n.(ShorthandSelfParameterMention) + // todo: more cases, like local variables with explicit type annotations and casts } class Expr = Swift::Expr; @@ -282,7 +225,7 @@ private module Input3 implements InputSig3 { result.(GenericTypeParamDeclTypeParameter).getDecl() = this.getGenericTypeParam(i) } - TypeMention getAdditionalTypeParameterConstraint(TypeParameter tp) { none() } + TypeMention getAdditionalTypeParameterConstraint(TypeParameter tp) { none() } // todo Type getDeclaredType(TypePosition pos, TypePath path) { exists(ParamDeclTypeMention param | result = param.getTypeAt(path) | @@ -313,7 +256,6 @@ private module Input3 implements InputSig3 { result = this } - /** Gets the target of this call. */ Callable getTargetCertain() { none() } Callable getTarget(CallResolutionContext ctx) { @@ -331,144 +273,8 @@ private module Input3 implements InputSig3 { result = M3::inferCallArgumentTypeTopDown(_, _, _, n, path) } - predicate inferStepSymmetricCertain(AstNode n1, TypePath prefix1, AstNode n2, TypePath prefix2) { - none() - // n1 = - // any(IdentPat ip | - // n2 = ip.getName() and - // prefix1.isEmpty() and - // if ip.isRef() - // then - // exists(boolean isMutable | if ip.isMut() then isMutable = true else isMutable = false | - // prefix2 = TypePath::singleton(getRefTypeParameter(isMutable)) - // ) - // 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() - // ) - } - - Type inferTypeCertainSpecific(AstNode n, TypePath path) { - none() - // result = inferFunctionBodyType(n, path) - // or - // result = inferLiteralType(n, path, true) - // or - // result = inferRefPatType(n) and - // path.isEmpty() - // or - // result = inferRefExprType(n) and - // path.isEmpty() - // or - // result = inferCertainStructExprType(n, path) - // or - // result = inferCertainStructPatType(n, path) - // or - // result = inferRangeExprType(n) and - // path.isEmpty() - // or - // result = inferTupleRootType(n) and - // path.isEmpty() - // or - // result = inferBlockExprType(n, path) - // or - // result = inferArrayExprType(n) and - // path.isEmpty() - // or - // result = inferCastExprType(n, path) - // or - // exprHasUnitType(n) and - // path.isEmpty() and - // result instanceof UnitType - // or - // isPanicMacroCall(n) and - // path.isEmpty() and - // result instanceof NeverType - // or - // n instanceof ClosureExpr and - // path.isEmpty() and - // result = closureRootType() - } - predicate inferStepSymmetric(AstNode n1, TypePath prefix1, AstNode n2, TypePath prefix2) { none() - // prefix1.isEmpty() and - // prefix2.isEmpty() and - // ( - // n1 = n2.(OrPat).getAPat() - // or - // n1 = n2.(ParenPat).getPat() - // or - // n1 = n2.(LiteralPat).getLiteral() - // or - // exists(BreakExpr break | - // break.getExpr() = n1 and - // break.getTarget() = n2.(LoopExpr) - // ) - // or - // n1 = n2.(MacroExpr).getMacroCall().getMacroCallExpansion() and - // not isPanicMacroCall(n2) - // or - // n1 = n2.(MacroPat).getMacroCall().getMacroCallExpansion() - // ) - // or - // n2 = - // any(RefExpr re | - // n1 = re.getExpr() and - // prefix1.isEmpty() and - // prefix2 = TypePath::singleton(inferRefExprType(re).getPositionalTypeParameter(0)) - // ) - // or - // n2 = - // any(RefPat rp | - // n1 = rp.getPat() and - // prefix1.isEmpty() and - // exists(boolean isMutable | if rp.isMut() then isMutable = true else isMutable = false | - // prefix2 = TypePath::singleton(getRefTypeParameter(isMutable)) - // ) - // ) - // or - // exists(int i, int arity | - // prefix1.isEmpty() and - // prefix2 = TypePath::singleton(getTupleTypeParameter(arity, i)) - // | - // arity = n2.(TupleExpr).getNumberOfFields() and - // n1 = n2.(TupleExpr).getField(i) - // or - // arity = n2.(TuplePat).getTupleArity() and - // n1 = n2.(TuplePat).getField(i) - // ) - // or - // exists(BlockExpr be | - // n1 = be and - // n2 = be.getStmtList().getTailExpr() and - // if be.isAsync() - // then - // prefix1 = TypePath::singleton(getDynFutureOutputTypeParameter()) and - // prefix2.isEmpty() - // else ( - // prefix1.isEmpty() and - // prefix2.isEmpty() - // ) - // ) - // or - // // an array repeat expression (`[1; 3]`) has the type of the repeat operand - // n1.(ArrayRepeatExpr).getRepeatOperand() = n2 and - // prefix1 = TypePath::singleton(getArrayTypeParameter()) and - // prefix2.isEmpty() } predicate inferStep(AstNode n1, TypePath path1, AstNode n2, TypePath path2) { @@ -478,53 +284,11 @@ private module Input3 implements InputSig3 { n1 = v.getParentInitializer() and n2 = v.getDefiningNode() ) - // // When `n2` is `*n1` propagate type information from a raw pointer type - // // parameter at `n1`. The other direction is handled in - // // `inferDereferencedExprPtrType`. - // n1 = n2.(DerefExpr).getExpr() and - // prefix1 = TypePath::singleton(getPtrTypeParameter()) and - // prefix2.isEmpty() - // or - // n2 = any(ClosureExpr ce | not ce.hasRetType() and ce.getClosureBody() = n1) and - // prefix2 = closureReturnPath() and - // prefix1.isEmpty() } - predicate inferLubStep(AstNode n1, TypePath path1, AstNode n2, TypePath path2) { - none() - // path1.isEmpty() and - // ( - // n1 = n2.(ArrayListExpr).getAnExpr() and - // path2 = TypePath::singleton(getArrayTypeParameter()) - // or - // exists(ReturnExpr re, Rust::Callable c | - // n1 = re.getExpr() and - // c = re.getEnclosingCallable() and - // n2 = c.getBody() and - // path2.isEmpty() - // ) - // or - // exists(Struct s | - // n1 = [n2.(RangeExpr).getStart(), n2.(RangeExpr).getEnd()] and - // path2 = - // TypePath::singleton(TTypeParamTypeParameter(s.getGenericParamList().getATypeParam())) and - // s = getRangeType(n2) - // ) - // ) - } + predicate inferLubStep(AstNode n1, TypePath path1, AstNode n2, TypePath path2) { none() } - Type inferTypeTopDown(AstNode n, TypePath path) { - none() - // result = inferTypeFromAnnotationTopDown(n, path) - // or - // result = inferClosureExprBodyTypeTopDown(n, path) - // or - // exists(FunctionPosition pos | not pos.isReturn() | - // result = inferConstructionType(n, pos, path) - // or - // result = inferOperationType(n, pos, path) - // ) - } + Type inferTypeTopDown(AstNode n, TypePath path) { none() } pragma[nomagic] private Type inferLiteralType(AstNode n, TypePath path) { @@ -538,36 +302,7 @@ private module Input3 implements InputSig3 { ) } - Type inferTypeSpecific(AstNode n, TypePath path) { - result = inferLiteralType(n, path) - // result = inferAssignmentOperationType(n, path) - // or - // exists(FunctionPosition pos | pos.isReturn() | - // result = inferConstructionType(n, pos, path) - // or - // result = inferOperationType(n, pos, path) - // ) - // or - // result = inferFieldExprType(n, path) - // or - // result = inferTryExprType(n, path) - // or - // result = inferLiteralType(n, path, false) - // or - // result = inferAwaitExprType(n, path) - // or - // result = inferDereferencedExprPtrType(n, path) - // or - // result = inferForLoopExprType(n, path) - // or - // result = inferClosureExprType(n, path) - // or - // result = inferArgList(n, path) - // or - // result = inferDeconstructionPatType(n, path) - // or - // result = inferUnknownType(n, path) - } + Type inferTypeSpecific(AstNode n, TypePath path) { result = inferLiteralType(n, path) } } private module M3 = Make3; @@ -581,2761 +316,3 @@ predicate inferTypeCertain = M3::inferTypeCertain/2; module Consistency { import M2::Consistency } -// /** A function without a `self` parameter. */ -// private class NonMethodFunction extends Function { -// NonMethodFunction() { not this.hasSelfParam() } -// } -// private module ImplOrTraitItemNodeOption = Option; -// private class ImplOrTraitItemNodeOption = ImplOrTraitItemNodeOption::Option; -// private class FunctionDeclaration extends Function { -// private ImplOrTraitItemNodeOption parent; -// FunctionDeclaration() { -// not this = any(ImplOrTraitItemNode i).getAnAssocItem() and parent.isNone() -// or -// this = parent.asSome().getASuccessor(_) -// } -// /** Holds if this function is associated with `i`. */ -// predicate isAssoc(ImplOrTraitItemNode i) { i = parent.asSome() } -// /** Holds if this is a free function. */ -// predicate isFree() { parent.isNone() } -// /** Holds if this function is valid for `i`. */ -// predicate isFor(ImplOrTraitItemNodeOption i) { i = parent } -// /** -// * Holds if this function is valid for `i`. If `i` is a trait or `impl` block then -// * this function must be declared directly inside `i`. -// */ -// predicate isDirectlyFor(ImplOrTraitItemNodeOption i) { -// i.isNone() and -// this.isFree() -// or -// this = i.asSome().getAnAssocItem() -// } -// TypeParam getTypeParam(ImplOrTraitItemNodeOption i) { -// i = parent and -// result = [this.getGenericParamList().getATypeParam(), i.asSome().getTypeParam(_)] -// } -// TypeParameter getTypeParameter(ImplOrTraitItemNodeOption i, TypeParameterPosition ppos) { -// typeParamMatchPosition(this.getTypeParam(i), result, ppos) -// or -// // For every `TypeParam` of this function, any associated types accessed on -// // the type parameter are also type parameters. -// ppos.isImplicit() and -// result.(TypeParamAssociatedTypeTypeParameter).getTypeParam() = this.getTypeParam(i) -// or -// i = parent and -// ( -// ppos.isImplicit() and result = TSelfTypeParameter(i.asSome()) -// or -// ppos.isImplicit() and result.(AssociatedTypeTypeParameter).getTrait() = i.asSome() -// or -// ppos.isImplicit() and this = result.(ImplTraitTypeTypeParameter).getFunction() -// ) -// } -// pragma[nomagic] -// Type getParameterType(ImplOrTraitItemNodeOption i, FunctionPosition pos, TypePath path) { -// i = parent and -// ( -// not pos.isReturn() and -// result = getAssocFunctionTypeAt(this, i.asSome(), pos, path) -// or -// i.isNone() and -// result = this.getParam(pos.asPosition()).getTypeRepr().(TypeMention).getTypeAt(path) -// ) -// } -// private Type resolveRetType(ImplOrTraitItemNodeOption i, TypePath path) { -// i = parent and -// ( -// result = -// getAssocFunctionTypeAt(this, i.asSome(), any(FunctionPosition ret | ret.isReturn()), path) -// or -// i.isNone() and -// result = getReturnTypeMention(this).getTypeAt(path) -// ) -// } -// Type getReturnType(ImplOrTraitItemNodeOption i, TypePath path) { -// if this.isAsync() -// then -// i = parent and -// path.isEmpty() and -// result = getFutureTraitType() -// or -// exists(TypePath suffix | -// result = this.resolveRetType(i, suffix) and -// path = TypePath::cons(getDynFutureOutputTypeParameter(), suffix) -// ) -// else result = this.resolveRetType(i, path) -// } -// string toStringExt(ImplOrTraitItemNode i) { -// i = parent.asSome() and -// if this = i.getAnAssocItem() -// then result = this.toString() -// else -// result = this + " [" + [i.(Impl).getSelfTy().toString(), i.(Trait).getName().toString()] + "]" -// } -// } -// private class AssocFunctionDeclaration extends FunctionDeclaration { -// AssocFunctionDeclaration() { this.isAssoc(_) } -// } -// pragma[nomagic] -// private TypePath getPathToImplSelfTypeParam(TypeParam tp) { -// exists(ImplItemNode impl | -// tp = impl.getTypeParam(_) and -// TTypeParamTypeParameter(tp) = impl.(Impl).getSelfTy().(TypeMention).getTypeAt(result) -// ) -// } -// pragma[nomagic] -// private Type getCallExprTypeArgument(CallExpr ce, TypeArgumentPosition apos, TypePath path) { -// exists(Path p, ItemNode resolved, TypeParam tp | -// p = CallExprImpl::getFunctionPath(ce) and -// resolved = resolvePath(p) and -// apos.asTypeParam() = tp -// | -// // For type parameters of the function we must resolve their -// // instantiation from the path. For instance, for `fn bar(a: A) -> A` -// // and the path `bar`, we must resolve `A` to `i64`. -// exists(int i | -// tp = resolved.getTypeParam(pragma[only_bind_into](i)) and -// result = getPathTypeArgument(p, pragma[only_bind_into](i)).getTypeAt(path) -// ) -// or -// // For type parameters of the `impl` block we must resolve their -// // instantiation from the path. For instance, for `impl for Foo` -// // and the path `Foo::bar` we must resolve `A` to `i64`. -// exists(ImplItemNode impl, TypePath pathToTp | -// resolved = impl.getASuccessor(_) and -// tp = impl.getTypeParam(_) and -// pathToTp = getPathToImplSelfTypeParam(tp) and -// result = p.getQualifier().(TypeMention).getTypeAt(pathToTp.appendInverse(path)) -// ) -// ) -// or -// // Handle constructions that use `Self(...)` syntax -// exists(Path p, TypePath path0 | -// p = CallExprImpl::getFunctionPath(ce) and -// result = p.(TypeMention).getTypeAt(path0) and -// path0.isCons(TTypeParamTypeParameter(apos.asTypeParam()), path) -// ) -// } -// pragma[nomagic] -// private Type inferFunctionBodyType(AstNode n, TypePath path) { -// exists(Function f | -// n = f.getFunctionBody() and -// result = getReturnTypeMention(f).getTypeAt(path) and -// not exists(ImplTraitReturnType i | i.getFunction() = f | -// result = i or result = i.getATypeParameter() -// ) -// ) -// } -// /** -// * Holds if `me` is a call to the `panic!` macro. -// * -// * `panic!` needs special treatment, because it expands to a block expression -// * that looks like it should have type `()` instead of the correct `!` type. -// */ -// pragma[nomagic] -// 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))) -// } -// private Type inferCertainStructExprType(StructExpr se, TypePath path) { -// result = se.getPath().(TypeMention).getTypeAt(path) -// } -// private Type inferCertainStructPatType(StructPat sp, TypePath path) { -// result = sp.getPath().(TypeMention).getTypeAt(path) -// } -// private Type inferAssignmentOperationType(AstNode n, TypePath path) { -// n instanceof AssignmentOperation and -// path.isEmpty() and -// result instanceof UnitType -// } -// pragma[nomagic] -// private Struct getRangeType(RangeExpr re) { -// re instanceof RangeFromExpr and -// result instanceof RangeFromStruct -// or -// re instanceof RangeToExpr and -// result instanceof RangeToStruct -// or -// re instanceof RangeFullExpr and -// result instanceof RangeFullStruct -// or -// re instanceof RangeFromToExpr and -// result instanceof RangeStruct -// or -// re instanceof RangeInclusiveExpr and -// result instanceof RangeInclusiveStruct -// or -// re instanceof RangeToInclusiveExpr and -// result instanceof RangeToInclusiveStruct -// } -// pragma[nomagic] -// private Type inferTypeFromAnnotationTopDown(AstNode n, TypePath path) { -// // 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())) -// ) -// } -// pragma[nomagic] -// private TupleType inferTupleRootType(AstNode n) { -// // `typeEquality` handles the non-root cases -// result.getArity() = [n.(TupleExpr).getNumberOfFields(), n.(TuplePat).getTupleArity()] -// } -// pragma[nomagic] -// private Path getCallExprPathQualifier(CallExpr ce) { -// result = CallExprImpl::getFunctionPath(ce).getQualifier() -// } -// /** -// * Gets the type qualifier of function call `ce`, if any. -// * -// * For example, the type qualifier of `Foo::::default()` is `Foo::`, -// * but only when `Foo` is not a trait. The type qualifier of `::baz()` -// * is `Foo`. -// * -// * `isDefaultTypeArg` indicates whether the returned type is a default type -// * argument, for example in `Vec::new()` the default type for the type parameter -// * `A` of `Vec` is `Global`. -// */ -// pragma[nomagic] -// private Type getCallExprTypeQualifier(CallExpr ce, TypePath path, boolean isDefaultTypeArg) { -// exists(Path p, TypeMention tm | -// p = getCallExprPathQualifier(ce) and -// tm = [p.(AstNode), p.getSegment().getTypeRepr()] -// | -// result = tm.getTypeAt(path) and -// not resolvePath(tm) instanceof Trait and -// isDefaultTypeArg = false -// or -// exists(TypeParameter tp, TypePath suffix | -// result = -// tm.(NonAliasPathTypeMention).getDefaultTypeForTypeParameterInNonAnnotationAt(tp, suffix) and -// path = TypePath::cons(tp, suffix) and -// isDefaultTypeArg = true -// ) -// ) -// } -// /** -// * Gets the trait qualifier of function call `ce`, if any. -// * -// * For example, the trait qualifier of `Default::::default()` is `Default`. -// */ -// pragma[nomagic] -// private Trait getCallExprTraitQualifier(CallExpr ce) { -// exists(PathExt qualifierPath | -// qualifierPath = getCallExprPathQualifier(ce) and -// result = resolvePath(qualifierPath) and -// // When the qualifier is `Self` and resolves to a trait, it's inside a -// // trait method's default implementation. This is not a dispatch whose -// // target is inferred from the type of the receiver, but should always -// // resolve to the function in the trait block as path resolution does. -// not qualifierPath.isUnqualified("Self") -// ) -// } -// pragma[nomagic] -// private predicate nonAssocFunction(ItemNode i) { not i instanceof AssocFunctionDeclaration } -// /** -// * A call expression that can only resolve to something that is not an associated -// * function, and hence does not need type inference for resolution. -// */ -// private class NonAssocCallExpr extends CallExpr { -// NonAssocCallExpr() { -// forex(ItemNode i | i = CallExprImpl::getResolvedFunction(this) | nonAssocFunction(i)) -// } -// /** -// * Gets the target of this call, which can be resolved using only path resolution. -// */ -// ItemNode resolveCallTargetViaPathResolution() { result = CallExprImpl::getResolvedFunction(this) } -// pragma[nomagic] -// Type getTypeArgument(TypeArgumentPosition apos, TypePath path) { -// result = getCallExprTypeArgument(this, apos, path) -// } -// AstNode getNodeAt(FunctionPosition pos) { -// result = this.getSyntacticArgument(pos.asArgumentPosition()) -// or -// result = this and pos.isReturn() -// } -// pragma[nomagic] -// Type getInferredType(FunctionPosition pos, TypePath path) { -// pos.isTypeQualifier() and -// result = getCallExprTypeQualifier(this, path, false) -// or -// result = inferType(this.getNodeAt(pos), path) -// } -// } -// /** -// * Provides functionality related to context-based typing of calls. -// */ -// private module ContextTyping { -// /** -// * Holds if `f` mentions type parameter `tp` at some non-return position, -// * possibly via a constraint on another mentioned type parameter. -// */ -// pragma[nomagic] -// private predicate assocFunctionMentionsTypeParameterAtNonRetPos( -// ImplOrTraitItemNode i, Function f, TypeParameter tp -// ) { -// exists(FunctionPosition nonRetPos | -// not nonRetPos.isReturn() and -// not nonRetPos.isTypeQualifier() and -// tp = getAssocFunctionTypeAt(f, i, nonRetPos, _) -// ) -// or -// exists(TypeParameter mid | -// assocFunctionMentionsTypeParameterAtNonRetPos(i, f, mid) and -// tp = getATypeParameterConstraint(mid).getTypeAt(_) -// ) -// } -// /** -// * Holds if the return type of the function `f` inside `i` at `path` is type -// * parameter `tp`, and `tp` does not appear in the type of any parameter of -// * `f`. -// * -// * In this case, the context in which `f` is called may be needed to infer -// * the instantiation of `tp`. -// * -// * This covers functions like `Default::default` and `Vec::new`. -// */ -// pragma[nomagic] -// private predicate assocFunctionReturnContextTypedAt( -// ImplOrTraitItemNode i, Function f, FunctionPosition pos, TypePath path, TypeParameter tp -// ) { -// pos.isReturn() and -// tp = getAssocFunctionTypeAt(f, i, pos, path) and -// not assocFunctionMentionsTypeParameterAtNonRetPos(i, f, tp) -// } -// /** -// * A call where the type of the result may have to be inferred from the -// * context in which the call appears, for example a call like -// * `Default::default()`. -// */ -// abstract class ContextTypedCallCand extends Expr { -// abstract Type getTypeArgument(TypeArgumentPosition apos, TypePath path); -// predicate hasTypeArgument(TypeArgumentPosition apos) { exists(this.getTypeArgument(apos, _)) } -// /** -// * Holds if this call resolves to `target` inside `i`, and the return type -// * at `pos` and `path` may have to be inferred from the context. -// */ -// bindingset[this, i, target] -// predicate hasUnknownTypeAt( -// ImplOrTraitItemNode i, Function target, FunctionPosition pos, TypePath path -// ) { -// exists(TypeParameter tp | -// assocFunctionReturnContextTypedAt(i, target, pos, path, tp) and -// // check that no explicit type arguments have been supplied for `tp` -// not exists(TypeArgumentPosition tapos | this.hasTypeArgument(tapos) | -// exists(int j | -// j = tapos.asMethodTypeArgumentPosition() and -// tp = TTypeParamTypeParameter(target.getGenericParamList().getTypeParam(j)) -// ) -// or -// TTypeParamTypeParameter(tapos.asTypeParam()) = tp -// ) and -// not ( -// tp instanceof TSelfTypeParameter and -// exists(getCallExprTypeQualifier(this, _, _)) -// ) -// ) -// } -// } -// } -// /** -// * Holds if the type path `path` pointing to `type` is stripped of any leading -// * complex root type allowed for `self` parameters, such as `&`, `Box`, `Rc`, -// * `Arc`, and `Pin`. -// * -// * We strip away the complex root type for performance reasons only, which will -// * allow us to construct a much smaller set of candidate call targets (otherwise, -// * for example _a lot_ of methods have a `self` parameter with a `&` root type). -// */ -// bindingset[path, type] -// private predicate isComplexRootStripped(TypePath path, Type type) { -// ( -// path.isEmpty() and -// not validSelfType(type) -// or -// exists(TypeParameter tp | -// complexSelfRoot(_, tp) and -// path = TypePath::singleton(tp) and -// exists(type) -// ) -// ) and -// type != TNeverType() -// } -// private newtype TBorrowKind = -// TNoBorrowKind() or -// TSomeBorrowKind(Boolean isMutable) -// private class BorrowKind extends TBorrowKind { -// predicate isNoBorrow() { this = TNoBorrowKind() } -// predicate isSharedBorrow() { this = TSomeBorrowKind(false) } -// predicate isMutableBorrow() { this = TSomeBorrowKind(true) } -// RefType getRefType() { -// exists(boolean isMutable | -// this = TSomeBorrowKind(isMutable) and -// result = getRefType(isMutable) -// ) -// } -// string toString() { -// this.isNoBorrow() and -// result = "" -// or -// this.isMutableBorrow() and -// result = "&mut" -// or -// this.isSharedBorrow() and -// result = "&" -// } -// } -// /** -// * Provides logic for resolving calls to associated functions. -// * -// * When resolving a method call, a list of [candidate receiver types][1] is constructed -// * -// * > by repeatedly dereferencing the receiver expression's type, adding each type -// * > encountered to the list, then finally attempting an unsized coercion at the end, -// * > and adding the result type if that is successful. -// * > -// * > Then, for each candidate `T`, add `&T` and `&mut T` to the list immediately after `T`. -// * -// * We do not currently model unsized coercions, and we do not yet model the `Deref` trait, -// * instead we limit dereferencing to standard dereferencing and the fact that `String` -// * dereferences to `str`. -// * -// * Instead of constructing the full list of candidate receiver types -// * -// * ``` -// * T1, &T1, &mut T1, ..., Tn, &Tn, &mut Tn -// * ``` -// * -// * we recursively compute a set of candidates, only adding a new candidate receiver type -// * to the set when we can rule out that the method cannot be found for the current -// * candidate: -// * -// * ```text -// * forall method: -// * not current_candidate matches method -// * ``` -// * -// * Care must be taken to ensure that the `not current_candidate matches method` check is -// * monotonic, which we achieve using the monotonic `isNotInstantiationOf` predicate. -// * -// * [1]: https://doc.rust-lang.org/reference/expressions/method-call-expr.html#r-expr.method.candidate-receivers -// */ -// private module AssocFunctionResolution { -// /** -// * Holds if function `f` with the name `name` and the arity `arity` exists in -// * `i`, and the type at function-call adjusted position `pos` is `t`. -// */ -// pragma[nomagic] -// private predicate assocFunctionInfo( -// Function f, string name, int arity, ImplOrTraitItemNode i, FunctionPosition pos, -// AssocFunctionType t -// ) { -// f = i.getASuccessor(name) and -// arity = f.getNumberOfParamsInclSelf() and -// t.appliesTo(f, i, pos) -// } -// /** -// * Holds if the non-method trait function `f` mentions the implicit `Self` type -// * parameter at position `pos`. -// */ -// pragma[nomagic] -// private predicate traitSelfTypeParameterOccurrence( -// TraitItemNode trait, NonMethodFunction f, FunctionPosition pos -// ) { -// FunctionOverloading::traitTypeParameterOccurrence(trait, f, _, pos, _, TSelfTypeParameter(trait)) -// } -// /** -// * Holds if the non-method function `f` implements a trait function that mentions -// * the implicit `Self` type parameter at position `pos`. -// */ -// pragma[nomagic] -// private predicate traitImplSelfTypeParameterOccurrence( -// ImplItemNode impl, NonMethodFunction f, FunctionPosition pos -// ) { -// exists(NonMethodFunction traitFunction | -// f = impl.getAnAssocItem() and -// f.implements(traitFunction) and -// traitSelfTypeParameterOccurrence(_, traitFunction, pos) -// ) -// } -// private module TypeOption = Option; -// private class TypeOption = TypeOption::Option; -// /** -// * Holds if function `f` with the name `name` and the arity `arity` exists in -// * `i`, and the type at function-call adjusted position `selfPos` is `selfType`. -// * -// * `selfPos` is a position relevant for call resolution: either a position -// * corresponding to the `self` parameter of `f` (if present); a type qualifier -// * position; or a position where the implicit `Self` type parameter of some trait -// * is mentioned in some non-method function `f_trait`, and either `f = f_trait` -// * or `f` implements `f_trait`. -// * -// * `strippedTypePath` points to the type `strippedType` inside `selfType`, which -// * is the (possibly complex-stripped) root type of `selfType`. For example, if -// * `f` has a `&self` parameter, then `strippedTypePath` is `getRefSharedTypeParameter()` -// * and `strippedType` is the type inside the reference. -// * -// * `implType` is the type being implemented by `i` (`None` when `i` is a trait). -// * -// * `trait` is the trait being implemented by `i` or `i` itself (`None` when `i` is inherent). -// * -// * `isMethod` indicates whether `f` is a method. -// */ -// pragma[nomagic] -// private predicate assocFunctionInfo( -// Function f, string name, int arity, FunctionPosition selfPos, ImplOrTraitItemNode i, -// AssocFunctionType selfType, TypePath strippedTypePath, Type strippedType, TypeOption implType, -// TypeOption trait, boolean isMethod -// ) { -// assocFunctionInfo(f, name, arity, i, selfPos, selfType) and -// strippedType = selfType.getTypeAt(strippedTypePath) and -// ( -// isComplexRootStripped(strippedTypePath, strippedType) -// or -// selfPos.isTypeQualifier() and strippedTypePath.isEmpty() -// ) and -// ( -// f instanceof Method and -// selfPos.asPosition() = 0 -// or -// selfPos.isTypeQualifier() -// or -// traitSelfTypeParameterOccurrence(i, f, selfPos) -// or -// traitImplSelfTypeParameterOccurrence(i, f, selfPos) -// ) and -// ( -// implType.asSome() = resolveImplSelfTypeAt(i, TypePath::nil()) -// or -// i instanceof Trait and -// implType.isNone() -// ) and -// ( -// trait.asSome() = -// [ -// TTrait(i).(Type), -// TTrait(i.(ImplItemNode).resolveTraitTy()).(Type) -// ] -// or -// i.(Impl).isInherent() and trait.isNone() -// ) and -// if f instanceof Method then isMethod = true else isMethod = false -// } -// /** -// * Holds if function `f` with the name `name` and the arity `arity` exists in -// * blanket (like) implementation `impl`, and the type at function-call adjusted -// * position `selfPos` is `selfType`. -// * -// * `selfPos` is a position relevant for call resolution: either a position -// * corresponding to the `self` parameter of `f` (if present); a type qualifier -// * position; or a position where the implicit `Self` type parameter of some trait -// * is mentioned in some non-method function `f_trait`, and `f` implements `f_trait`. -// * -// * `blanketPath` points to the type `blanketTypeParam` inside `selfType`, which -// * is the type parameter used in the blanket implementation. -// * -// * `implType` is the type being implemented by `i`. -// * -// * `trait` is the trait being implemented by `i`. -// * -// * `isMethod` indicates whether `f` is a method. -// */ -// pragma[nomagic] -// private predicate assocFunctionInfoBlanketLike( -// Function f, string name, int arity, ImplItemNode impl, TypeOption implType, TypeOption trait, -// FunctionPosition selfPos, AssocFunctionType selfType, TypePath blanketPath, -// TypeParam blanketTypeParam, boolean isMethod -// ) { -// exists(TypePath blanketSelfPath | -// assocFunctionInfo(f, name, arity, selfPos, impl, selfType, _, _, implType, trait, isMethod) and -// TTypeParamTypeParameter(blanketTypeParam) = selfType.getTypeAt(blanketPath) and -// blanketPath = any(string s) + blanketSelfPath and -// BlanketImplementation::isBlanketLike(impl, blanketSelfPath, blanketTypeParam) -// ) -// } -// pragma[nomagic] -// private predicate assocFunctionTraitInfo(string name, int arity, Trait trait) { -// exists(ImplItemNode i | -// assocFunctionInfo(_, name, arity, i, _, _) and -// trait = i.resolveTraitTy() -// ) -// or -// assocFunctionInfo(_, name, arity, trait, _, _) -// } -// pragma[nomagic] -// private predicate assocFunctionCallTraitCandidate(Element afc, Trait trait) { -// afc = -// any(AssocFunctionCall afc0 | -// exists(string name, int arity | -// afc0.hasNameAndArity(name, arity) and -// assocFunctionTraitInfo(name, arity, trait) and -// // we only need to check visibility of traits that are not mentioned explicitly -// not afc0.hasATrait() -// ) -// ) -// } -// private module AssocFunctionTraitIsVisible = TraitIsVisible; -// bindingset[afc, impl] -// pragma[inline_late] -// private predicate callVisibleImplTraitCandidate(AssocFunctionCall afc, ImplItemNode impl) { -// AssocFunctionTraitIsVisible::traitIsVisible(afc, impl.resolveTraitTy()) -// } -// /** -// * Checks that the explicit type qualifier of a call (if any), `typeQualifier`, -// * matches the type being implemented by the target, `implType`. -// */ -// bindingset[implType] -// private predicate callTypeQualifierCheck(TypeOption implType, TypeOption typeQualifier) { -// typeQualifier = [implType, TypeOption::none_()] -// } -// /** -// * Checks that the explicit trait qualifier of a call (if any), `traitQualifier`, -// * matches the trait being implemented by the target (or in which the target is defined), -// * `trait`, and that when a receiver is present in the call, the target is a method. -// */ -// bindingset[trait, isMethod] -// pragma[inline_late] -// private predicate callTraitQualifierAndReceiverCheck( -// TypeOption trait, Boolean isMethod, TypeOption traitQualifier, boolean hasReceiver -// ) { -// traitQualifier = [trait, TypeOption::none_()] and -// hasReceiver = [isMethod, false] -// } -// bindingset[implType, trait, isMethod] -// private predicate callCheck( -// TypeOption implType, TypeOption trait, Boolean isMethod, TypeOption typeQualifier, -// TypeOption traitQualifier, boolean hasReceiver -// ) { -// callTypeQualifierCheck(implType, typeQualifier) and -// callTraitQualifierAndReceiverCheck(trait, isMethod, traitQualifier, hasReceiver) -// } -// pragma[nomagic] -// private predicate assocFunctionInfoNonBlanketLikeCheck( -// Function f, string name, int arity, FunctionPosition selfPos, ImplOrTraitItemNode i, -// AssocFunctionType selfType, TypePath strippedTypePath, Type strippedType, -// TypeOption typeQualifier, TypeOption traitQualifier, boolean hasReceiver -// ) { -// exists(TypeOption implType, TypeOption trait, boolean isMethod | -// assocFunctionInfo(f, name, arity, selfPos, i, selfType, strippedTypePath, strippedType, -// implType, trait, isMethod) and -// not BlanketImplementation::isBlanketLike(i, _, _) and -// callCheck(implType, trait, isMethod, typeQualifier, traitQualifier, hasReceiver) -// ) -// } -// pragma[nomagic] -// private predicate assocFunctionInfoNonBlanketLikeTypeParamCheck( -// Function f, string name, int arity, FunctionPosition selfPos, ImplOrTraitItemNode i, -// AssocFunctionType selfType, TypePath strippedTypePath, TypeOption typeQualifier, -// TypeOption traitQualifier, boolean hasReceiver -// ) { -// assocFunctionInfoNonBlanketLikeCheck(f, name, arity, selfPos, i, selfType, strippedTypePath, -// TTypeParamTypeParameter(_), typeQualifier, traitQualifier, hasReceiver) -// } -// /** -// * Holds if call `afc` may target function `f` in `i` with type `selfType` at -// * function-call adjusted position `selfPos`. -// * -// * `strippedTypePath` points to the type `strippedType` inside `selfType`, -// * which is the (possibly complex-stripped) root type of `selfType`. -// */ -// bindingset[afc, strippedTypePath, strippedType] -// pragma[inline_late] -// private predicate nonBlanketLikeCandidate( -// AssocFunctionCall afc, Function f, FunctionPosition selfPos, ImplOrTraitItemNode i, -// AssocFunctionType selfType, TypePath strippedTypePath, Type strippedType -// ) { -// exists( -// string name, int arity, TypeOption typeQualifier, TypeOption traitQualifier, -// boolean hasReceiver -// | -// afc.hasSyntacticInfo(name, arity, typeQualifier, traitQualifier, hasReceiver) and -// if not afc.hasATrait() and i.(Impl).hasTrait() -// then callVisibleImplTraitCandidate(afc, i) -// else any() -// | -// assocFunctionInfoNonBlanketLikeCheck(f, name, arity, selfPos, i, selfType, strippedTypePath, -// strippedType, typeQualifier, traitQualifier, hasReceiver) -// or -// assocFunctionInfoNonBlanketLikeTypeParamCheck(f, name, arity, selfPos, i, selfType, -// strippedTypePath, typeQualifier, traitQualifier, hasReceiver) -// ) -// } -// bindingset[name, arity, typeQualifier, traitQualifier, hasReceiver] -// pragma[inline_late] -// private predicate assocFunctionInfoBlanketLikeCheck( -// Function f, string name, int arity, FunctionPosition selfPos, ImplItemNode impl, -// AssocFunctionType selfType, TypePath blanketPath, TypeParam blanketTypeParam, -// TypeOption typeQualifier, TypeOption traitQualifier, boolean hasReceiver -// ) { -// exists(TypeOption implType, TypeOption trait, boolean isMethod | -// assocFunctionInfoBlanketLike(f, name, arity, impl, implType, trait, selfPos, selfType, -// blanketPath, blanketTypeParam, isMethod) and -// callTraitQualifierAndReceiverCheck(trait, isMethod, traitQualifier, hasReceiver) and -// if impl.isBlanketImplementation() -// then any() -// else callTypeQualifierCheck(implType, typeQualifier) -// ) -// } -// /** -// * Holds if call `afc` may target function `f` in blanket (like) implementation -// * `impl` with type `selfType` at function-call adjusted position `selfPos`. -// * -// * `blanketPath` points to the type `blanketTypeParam` inside `selfType`, which -// * is the type parameter used in the blanket implementation. -// */ -// bindingset[afc] -// pragma[inline_late] -// private predicate blanketLikeCandidate( -// AssocFunctionCall afc, Function f, FunctionPosition selfPos, ImplItemNode impl, -// AssocFunctionType self, TypePath blanketPath, TypeParam blanketTypeParam -// ) { -// exists( -// string name, int arity, TypeOption typeQualifier, TypeOption traitQualifier, -// boolean hasReceiver -// | -// afc.hasSyntacticInfo(name, arity, typeQualifier, traitQualifier, hasReceiver) and -// assocFunctionInfoBlanketLikeCheck(f, name, arity, selfPos, impl, self, blanketPath, -// blanketTypeParam, typeQualifier, traitQualifier, hasReceiver) -// | -// if not afc.hasATrait() then callVisibleImplTraitCandidate(afc, impl) else any() -// ) -// } -// /** -// * A (potential) call to an associated function. -// * -// * This is either: -// * -// * 1. `MethodCallExprAssocFunctionCall`: a method call, `x.m()`; -// * 2. `IndexExprAssocFunctionCall`: an index expression, `x[i]`, which is [syntactic sugar][1] -// * for `*x.index(i)`; -// * 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. -// * -// * Note also that only case 3 is a _potential_ call; in all other cases, we are guaranteed that -// * the target is an associated function (in fact, a method). -// * -// * [1]: https://doc.rust-lang.org/std/ops/trait.Index.html -// */ -// abstract class AssocFunctionCall extends Expr { -// /** -// * Holds if this call targets a function named `name` with `arity` parameters -// * (including `self`). -// */ -// pragma[nomagic] -// abstract predicate hasNameAndArity(string name, int arity); -// abstract AstNode getNonReturnNodeAt(FunctionPosition pos); -// AstNode getNodeAt(FunctionPosition pos) { -// result = this.getNonReturnNodeAt(pos) -// or -// result = this and pos.isReturn() -// } -// /** Holds if this call has a receiver and hence must target a method. */ -// abstract predicate hasReceiver(); -// abstract predicate supportsAutoDerefAndBorrow(); -// /** Gets the trait targeted by this call, if any. */ -// abstract Trait getTrait(); -// /** Holds if this call targets a trait. */ -// predicate hasTrait() { exists(this.getTrait()) } -// Trait getATrait() { -// result = this.getTrait() -// or -// result = getALookupTrait(this, getCallExprTypeQualifier(this, TypePath::nil(), _)) -// } -// predicate hasATrait() { exists(this.getATrait()) } -// private Type getNonTypeParameterTypeQualifier() { -// result = getCallExprTypeQualifier(this, TypePath::nil(), _) and -// not result instanceof TypeParameter -// } -// /** -// * Holds if this call has the given purely syntactic information, that is, -// * information that does not rely on type inference. -// */ -// pragma[nomagic] -// predicate hasSyntacticInfo( -// string name, int arity, TypeOption typeQualifier, TypeOption traitQualifier, -// boolean hasReceiver -// ) { -// this.hasNameAndArity(name, arity) and -// (if this.hasReceiver() then hasReceiver = true else hasReceiver = false) and -// ( -// typeQualifier.asSome() = this.getNonTypeParameterTypeQualifier() -// or -// not exists(this.getNonTypeParameterTypeQualifier()) and -// typeQualifier.isNone() -// ) and -// ( -// traitQualifier.asSome() = TTrait(this.getATrait()) -// or -// not this.hasATrait() and -// traitQualifier.isNone() -// ) -// } -// Type getTypeAt(FunctionPosition pos, TypePath path) { -// result = inferType(this.getNodeAt(pos), path) -// } -// /** -// * Holds if `selfPos` is a potentially relevant function-call adjusted position -// * for resolving this call. -// * -// * Only holds when we don't know for sure that the target is a method (in those -// * cases we rely on the receiver only). -// */ -// pragma[nomagic] -// private predicate isRelevantSelfPos(FunctionPosition selfPos) { -// not this.hasReceiver() and -// exists(TypePath strippedTypePath, Type strippedType | -// strippedType = substituteLookupTraits(this, this.getTypeAt(selfPos, strippedTypePath)) and -// strippedType != TNeverType() and -// strippedType != TUnknownType() -// | -// nonBlanketLikeCandidate(this, _, selfPos, _, _, strippedTypePath, strippedType) -// or -// blanketLikeCandidate(this, _, selfPos, _, _, strippedTypePath, _) -// ) -// } -// predicate hasReceiverAtPos(FunctionPosition pos) { this.hasReceiver() and pos.asPosition() = 0 } -// pragma[nomagic] -// private predicate hasIncompatibleArgsTarget( -// ImplOrTraitItemNode i, FunctionPosition selfPos, DerefChain derefChain, BorrowKind borrow, -// AssocFunctionType selfType -// ) { -// SelfArgIsInstantiationOf::argIsInstantiationOf(this, i, selfPos, derefChain, borrow, selfType) and -// OverloadedCallArgsAreInstantiationsOf::argsAreNotInstantiationsOf(this, i) -// } -// /** -// * Holds if the function inside `i` with matching name and arity can be ruled -// * out as a target of this call, because the candidate receiver type represented -// * by `derefChain` and `borrow` is incompatible with the type at function-call -// * adjusted position `selfPos`. -// * -// * The types are incompatible because they disagree on a concrete type somewhere -// * inside `root`. -// */ -// pragma[nomagic] -// predicate hasIncompatibleTarget( -// ImplOrTraitItemNode i, FunctionPosition selfPos, DerefChain derefChain, BorrowKind borrow, -// Type root -// ) { -// exists(AssocFunctionType selfType | root = selfType.getTypeAt(TypePath::nil()) | -// this.hasIncompatibleArgsTarget(i, selfPos, derefChain, borrow, selfType) -// or -// SelfArgIsInstantiationOf::argIsNotInstantiationOf(this, i, selfPos, derefChain, borrow, -// selfType) -// ) -// } -// /** -// * Holds if the function inside blanket-like implementation `impl` with matching name -// * and arity can be ruled out as a target of this call, either because the candidate -// * receiver type represented by `derefChain` and `borrow` is incompatible with the type -// * at function-call adjusted position `selfPos`, or because the blanket constraint -// * is not satisfied. -// */ -// pragma[nomagic] -// predicate hasIncompatibleBlanketLikeTarget( -// ImplItemNode impl, FunctionPosition selfPos, DerefChain derefChain, BorrowKind borrow -// ) { -// SelfArgIsNotInstantiationOfBlanketLike::argIsNotInstantiationOf(MkAssocFunctionCallCand(this, -// selfPos, derefChain, borrow), impl, _, _) -// or -// ArgSatisfiesBlanketLikeConstraint::dissatisfiesBlanketConstraint(MkAssocFunctionCallCand(this, -// selfPos, derefChain, borrow), impl) -// } -// pragma[nomagic] -// private predicate hasNoInherentTargetCheck( -// FunctionPosition selfPos, DerefChain derefChain, BorrowKind borrow -// ) { -// MkAssocFunctionCallCand(this, selfPos, derefChain, borrow) -// .(AssocFunctionCallCand) -// .hasNoInherentTargetCheck() -// } -// pragma[nomagic] -// private predicate hasNoInherentTargetTypeQualifierCheck() { -// exists(FunctionPosition typeQualifierPos | -// typeQualifierPos.isTypeQualifier() and -// this.hasNoInherentTargetCheck(typeQualifierPos, DerefChain::nil(), TNoBorrowKind()) -// ) -// } -// pragma[nomagic] -// predicate hasNoInherentTarget(FunctionPosition selfPos, DerefChain derefChain, BorrowKind borrow) { -// this.hasNoInherentTargetCheck(selfPos, derefChain, borrow) and -// if exists(this.getNonTypeParameterTypeQualifier()) and not selfPos.isTypeQualifier() -// then -// // If this call is of the form `Foo::bar(x)` and we are resolving with respect to the type -// // of `x`, then we additionally need to check that the type qualifier does not give rise -// // to an inherent target -// this.hasNoInherentTargetTypeQualifierCheck() -// else any() -// } -// /** -// * Same as `getSelfTypeAt`, but excludes pseudo types `!` and `unknown`. -// */ -// pragma[nomagic] -// Type getANonPseudoSelfTypeAt( -// FunctionPosition selfPos, DerefChain derefChain, BorrowKind borrow, TypePath path -// ) { -// result = this.getSelfTypeAt(selfPos, derefChain, borrow, path) and -// result != TNeverType() and -// result != TUnknownType() -// } -// pragma[nomagic] -// Type getComplexStrippedSelfType( -// FunctionPosition selfPos, DerefChain derefChain, BorrowKind borrow, TypePath strippedTypePath -// ) { -// result = this.getANonPseudoSelfTypeAt(selfPos, derefChain, borrow, strippedTypePath) and -// ( -// isComplexRootStripped(strippedTypePath, result) -// or -// selfPos.isTypeQualifier() and strippedTypePath.isEmpty() -// ) -// } -// /** -// * Holds if the candidate receiver type represented by `derefChain` and `borrow` -// * does not have a matching call target at function-call adjusted position `selfPos`. -// */ -// predicate hasNoCompatibleTarget( -// FunctionPosition selfPos, DerefChain derefChain, BorrowKind borrow -// ) { -// NoCompatibleTarget::hasNoCompatibleTarget(this, selfPos, derefChain, borrow) -// } -// /** -// * Holds if the candidate receiver type represented by `derefChain` and `borrow` -// * does not have a matching non-blanket call target at function-call adjusted -// * position `selfPos`. -// */ -// predicate hasNoCompatibleNonBlanketTarget( -// FunctionPosition selfPos, DerefChain derefChain, BorrowKind borrow -// ) { -// NoCompatibleTarget::hasNoCompatibleNonBlanketTarget(this, selfPos, derefChain, borrow) -// } -// /** -// * Same as `getSelfTypeAt`, but without borrows. -// */ -// pragma[nomagic] -// Type getSelfTypeAtNoBorrow(FunctionPosition selfPos, DerefChain derefChain, TypePath path) { -// result = this.getTypeAt(selfPos, path) and -// derefChain.isEmpty() and -// ( -// this.hasReceiverAtPos(selfPos) -// or -// selfPos.isTypeQualifier() -// or -// this.isRelevantSelfPos(selfPos) -// ) -// or -// exists(DerefImplItemNode impl, DerefChain suffix | -// result = -// ImplicitDeref::getDereferencedCandidateReceiverType(this, selfPos, impl, suffix, path) and -// derefChain = DerefChain::cons(impl, suffix) -// ) -// } -// /** -// * Holds if this call may have an implicit borrow of kind `borrow` at -// * function-call adjusted position `selfPos` with the given `derefChain`. -// */ -// pragma[nomagic] -// predicate hasImplicitBorrowCand( -// FunctionPosition selfPos, DerefChain derefChain, BorrowKind borrow -// ) { -// exists(BorrowKind prev | this.hasNoCompatibleTarget(selfPos, derefChain, prev) | -// // first try shared borrow -// prev.isNoBorrow() and -// borrow.isSharedBorrow() -// or -// // then try mutable borrow -// prev.isSharedBorrow() and -// borrow.isMutableBorrow() -// ) -// } -// /** -// * Gets the type of this call at function-call adjusted position `selfPos` and -// * type path `path`. -// * -// * In case this call supports auto-dereferencing and borrowing and `selfPos` is -// * position 0 (corresponding to the receiver), the result is a -// * [candidate receiver type][1]: -// * -// * The type is obtained by repeatedly dereferencing the receiver expression's type, -// * as long as the method cannot be resolved in an earlier candidate type, and possibly -// * applying a borrow at the end. -// * -// * The parameter `derefChain` encodes the sequence of dereferences, and `borrows` indicates -// * whether a borrow has been applied. -// * -// * [1]: https://doc.rust-lang.org/reference/expressions/method-call-expr.html#r-expr.method.candidate-receivers -// */ -// pragma[nomagic] -// Type getSelfTypeAt( -// FunctionPosition selfPos, DerefChain derefChain, BorrowKind borrow, TypePath path -// ) { -// result = this.getSelfTypeAtNoBorrow(selfPos, derefChain, path) and -// borrow.isNoBorrow() -// or -// exists(RefType rt | -// this.hasImplicitBorrowCand(selfPos, derefChain, borrow) and -// rt = borrow.getRefType() -// | -// path.isEmpty() and -// result = rt -// or -// exists(TypePath suffix | -// result = this.getSelfTypeAtNoBorrow(selfPos, derefChain, suffix) and -// path = TypePath::cons(rt.getPositionalTypeParameter(0), suffix) -// ) -// ) -// } -// /** -// * Gets a function that this call resolves to after having applied a sequence of -// * dereferences and possibly a borrow on the receiver type at `selfPos`, encoded -// * in `derefChain` and `borrow`. -// */ -// pragma[nomagic] -// AssocFunctionDeclaration resolveCallTarget( -// ImplOrTraitItemNode i, FunctionPosition selfPos, DerefChain derefChain, BorrowKind borrow -// ) { -// exists(AssocFunctionCallCand afcc | -// afcc = MkAssocFunctionCallCand(this, selfPos, derefChain, borrow) and -// result = afcc.resolveCallTarget(i) -// ) -// } -// /** -// * Holds if the argument `arg` of this call has been implicitly dereferenced -// * and borrowed according to `derefChain` and `borrow`, in order to be able to -// * resolve the call target. -// */ -// predicate argumentHasImplicitDerefChainBorrow(Expr arg, DerefChain derefChain, BorrowKind borrow) { -// exists(FunctionPosition selfAdj | -// this.hasReceiverAtPos(selfAdj) and -// exists(this.resolveCallTarget(_, selfAdj, derefChain, borrow)) and -// arg = this.getNodeAt(selfAdj) and -// not (derefChain.isEmpty() and borrow.isNoBorrow()) -// ) -// } -// } -// private class MethodCallExprAssocFunctionCall extends AssocFunctionCall instanceof MethodCallExpr { -// override predicate hasNameAndArity(string name, int arity) { -// name = super.getIdentifier().getText() and -// arity = super.getNumberOfSyntacticArguments() -// } -// override predicate hasReceiver() { any() } -// override Expr getNonReturnNodeAt(FunctionPosition pos) { -// result = super.getReceiver() and -// pos.asPosition() = 0 -// or -// result = super.getPositionalArgument(pos.asPosition() - 1) -// } -// override predicate supportsAutoDerefAndBorrow() { any() } -// override Trait getTrait() { none() } -// } -// private class IndexExprAssocFunctionCall extends AssocFunctionCall, IndexExpr { -// private predicate isInMutableContext() { -// // todo: does not handle all cases yet -// VariableImpl::assignmentOperationDescendant(_, this) -// } -// override predicate hasNameAndArity(string name, int arity) { -// (if this.isInMutableContext() then name = "index_mut" else name = "index") and -// arity = 2 -// } -// override predicate hasReceiver() { any() } -// override Expr getNonReturnNodeAt(FunctionPosition pos) { -// pos.asPosition() = 0 and -// result = this.getBase() -// or -// pos.asPosition() = 1 and -// result = this.getIndex() -// } -// override predicate supportsAutoDerefAndBorrow() { any() } -// override Trait getTrait() { -// if this.isInMutableContext() -// then result instanceof IndexMutTrait -// else result instanceof IndexTrait -// } -// } -// 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`) -// forall(ItemNode i | i = CallExprImpl::getResolvedFunction(this) | -// i instanceof AssocFunctionDeclaration -// ) -// } -// override predicate hasNameAndArity(string name, int arity) { -// name = CallExprImpl::getFunctionPath(this).getText() and -// arity = this.getNumberOfSyntacticArguments() -// } -// override predicate hasReceiver() { none() } -// override Expr getNonReturnNodeAt(FunctionPosition pos) { -// result = this.getSyntacticPositionalArgument(pos.asPosition()) -// } -// override Type getTypeAt(FunctionPosition pos, TypePath path) { -// result = super.getTypeAt(pos, path) -// or -// pos.isTypeQualifier() and -// result = getCallExprTypeQualifier(this, path, _) -// } -// override predicate supportsAutoDerefAndBorrow() { none() } -// override Trait getTrait() { result = getCallExprTraitQualifier(this) } -// } -// final class OperationAssocFunctionCall extends AssocFunctionCall, Operation { -// override predicate hasNameAndArity(string name, int arity) { -// this.isOverloaded(_, name, _) and -// arity = this.getNumberOfOperands() -// } -// override predicate hasReceiver() { any() } -// override Expr getNonReturnNodeAt(FunctionPosition pos) { -// result = this.getOperand(pos.asPosition()) -// } -// private predicate implicitBorrowAt(FunctionPosition pos, boolean isMutable) { -// exists(int borrows | this.isOverloaded(_, _, borrows) | -// pos.asPosition() = 0 and -// borrows >= 1 and -// if this instanceof CompoundAssignmentExpr then isMutable = true else isMutable = false -// or -// pos.asPosition() = 1 and -// borrows = 2 and -// isMutable = false -// ) -// } -// override Type getTypeAt(FunctionPosition pos, TypePath path) { -// exists(boolean isMutable, RefType rt | -// this.implicitBorrowAt(pos, isMutable) and -// rt = getRefType(isMutable) -// | -// result = rt and -// path.isEmpty() -// or -// exists(TypePath path0 | -// result = inferType(this.getNodeAt(pos), path0) and -// path = TypePath::cons(rt.getPositionalTypeParameter(0), path0) -// ) -// ) -// or -// not this.implicitBorrowAt(pos, _) and -// result = inferType(this.getNodeAt(pos), path) -// } -// override predicate argumentHasImplicitDerefChainBorrow( -// Expr arg, DerefChain derefChain, BorrowKind borrow -// ) { -// exists(FunctionPosition pos, boolean isMutable | -// this.implicitBorrowAt(pos, isMutable) and -// arg = this.getNodeAt(pos) and -// derefChain = DerefChain::nil() and -// borrow = TSomeBorrowKind(isMutable) -// ) -// } -// override predicate supportsAutoDerefAndBorrow() { none() } -// 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 } -// } -// /** -// * Provides logic for efficiently checking that there are no compatible call -// * targets for a given candidate receiver type. -// * -// * For calls with non-blanket target candidates, we need to check: -// * -// * ```text -// * forall types `t` where `t` is a lookup type for the given candidate receiver type: -// * forall non-blanket candidates `c` matching `t`: -// * check that `c` is not a compatible target -// * ``` -// * -// * Instead of implementing the above using `forall`, we apply the standard trick -// * of using ranked recursion. -// */ -// private module NoCompatibleTarget { -// private import codeql.rust.elements.internal.generated.Raw -// private import codeql.rust.elements.internal.generated.Synth -// private class RawImplOrTrait = @impl or @trait; -// private predicate id(RawImplOrTrait x, RawImplOrTrait y) { x = y } -// private predicate idOfRaw(RawImplOrTrait x, int y) = equivalenceRelation(id/2)(x, y) -// private int idOfImplOrTraitItemNode(ImplOrTraitItemNode i) { -// idOfRaw(Synth::convertAstNodeToRaw(i), result) -// } -// /** -// * Holds if `t` is the `n`th lookup type for the candidate receiver type -// * represented by `derefChain` and `borrow` at function-call adjusted position -// * `selfPos` of `afc`. -// * -// * There are no compatible non-blanket-like candidates for lookup types `0` to `n - 1`. -// */ -// pragma[nomagic] -// private predicate noCompatibleNonBlanketLikeTargetCandNthLookupType( -// AssocFunctionCall afc, FunctionPosition selfPos, DerefChain derefChain, BorrowKind borrow, -// TypePath strippedTypePath, Type strippedType, int n, Type t -// ) { -// ( -// ( -// ( -// afc.supportsAutoDerefAndBorrow() and -// afc.hasReceiverAtPos(selfPos) -// or -// // needed for the `hasNoCompatibleNonBlanketTarget` check in -// // `ArgSatisfiesBlanketLikeConstraintInput::hasBlanketCandidate` -// exists(ImplItemNode i | -// derefChain.isEmpty() and -// blanketLikeCandidate(afc, _, selfPos, i, _, _, _) and -// i.isBlanketImplementation() -// ) -// ) and -// borrow.isNoBorrow() -// or -// afc.hasImplicitBorrowCand(selfPos, derefChain, borrow) -// ) and -// strippedType = afc.getComplexStrippedSelfType(selfPos, derefChain, borrow, strippedTypePath) and -// n = 0 -// or -// hasNoCompatibleNonBlanketLikeTargetForNthLookupType(afc, selfPos, derefChain, borrow, -// strippedTypePath, strippedType, n - 1) -// ) and -// t = getNthLookupType(afc, strippedType, n) -// } -// pragma[nomagic] -// private ImplOrTraitItemNode getKthNonBlanketLikeCandidateForNthLookupType( -// AssocFunctionCall afc, FunctionPosition selfPos, DerefChain derefChain, BorrowKind borrow, -// TypePath strippedTypePath, Type strippedType, int n, Type t, int k -// ) { -// noCompatibleNonBlanketLikeTargetCandNthLookupType(afc, selfPos, derefChain, borrow, -// strippedTypePath, strippedType, n, t) and -// result = -// rank[k + 1](ImplOrTraitItemNode i, int id | -// nonBlanketLikeCandidate(afc, _, selfPos, i, _, strippedTypePath, t) and -// id = idOfImplOrTraitItemNode(i) -// | -// i order by id -// ) -// } -// pragma[nomagic] -// private int getLastNonBlanketLikeCandidateForNthLookupType( -// AssocFunctionCall afc, FunctionPosition selfPos, DerefChain derefChain, BorrowKind borrow, -// TypePath strippedTypePath, Type strippedType, int n -// ) { -// exists(Type t | -// noCompatibleNonBlanketLikeTargetCandNthLookupType(afc, selfPos, derefChain, borrow, -// strippedTypePath, strippedType, n, t) and -// result = -// count(ImplOrTraitItemNode i | -// nonBlanketLikeCandidate(afc, _, selfPos, i, _, strippedTypePath, t) -// ) - 1 -// ) -// } -// pragma[nomagic] -// private predicate hasNoCompatibleNonBlanketLikeTargetForNthLookupTypeToIndex( -// AssocFunctionCall afc, FunctionPosition selfPos, DerefChain derefChain, BorrowKind borrow, -// TypePath strippedTypePath, Type strippedType, int n, int k -// ) { -// exists(Type t | -// noCompatibleNonBlanketLikeTargetCandNthLookupType(afc, selfPos, derefChain, borrow, -// strippedTypePath, strippedType, n, t) -// | -// k = -1 -// or -// hasNoCompatibleNonBlanketLikeTargetForNthLookupTypeToIndex(afc, selfPos, derefChain, borrow, -// strippedTypePath, strippedType, n, k - 1) and -// exists(ImplOrTraitItemNode i | -// i = -// getKthNonBlanketLikeCandidateForNthLookupType(afc, selfPos, derefChain, borrow, -// strippedTypePath, strippedType, n, t, k) and -// afc.hasIncompatibleTarget(i, selfPos, derefChain, borrow, t) -// ) -// ) -// } -// pragma[nomagic] -// private predicate hasNoCompatibleNonBlanketLikeTargetForNthLookupType( -// AssocFunctionCall afc, FunctionPosition selfPos, DerefChain derefChain, BorrowKind borrow, -// TypePath strippedTypePath, Type strippedType, int n -// ) { -// exists(int last | -// last = -// getLastNonBlanketLikeCandidateForNthLookupType(afc, selfPos, derefChain, borrow, -// strippedTypePath, strippedType, n) and -// hasNoCompatibleNonBlanketLikeTargetForNthLookupTypeToIndex(afc, selfPos, derefChain, borrow, -// strippedTypePath, strippedType, n, last) -// ) -// } -// pragma[nomagic] -// private predicate hasNoCompatibleNonBlanketLikeTarget( -// AssocFunctionCall afc, FunctionPosition selfPos, DerefChain derefChain, BorrowKind borrow -// ) { -// exists(Type strippedType | -// hasNoCompatibleNonBlanketLikeTargetForNthLookupType(afc, selfPos, derefChain, borrow, _, -// strippedType, getLastLookupTypeIndex(afc, strippedType)) -// ) -// } -// pragma[nomagic] -// predicate hasNoCompatibleTarget( -// AssocFunctionCall afc, FunctionPosition selfPos, DerefChain derefChain, BorrowKind borrow -// ) { -// hasNoCompatibleNonBlanketLikeTarget(afc, selfPos, derefChain, borrow) and -// // todo: replace with ranked recursion if needed -// forall(ImplItemNode i | blanketLikeCandidate(afc, _, selfPos, i, _, _, _) | -// afc.hasIncompatibleBlanketLikeTarget(i, selfPos, derefChain, borrow) -// ) -// } -// pragma[nomagic] -// predicate hasNoCompatibleNonBlanketTarget( -// AssocFunctionCall afc, FunctionPosition selfPos, DerefChain derefChain, BorrowKind borrow -// ) { -// hasNoCompatibleNonBlanketLikeTarget(afc, selfPos, derefChain, borrow) and -// // todo: replace with ranked recursion if needed -// forall(ImplItemNode i | -// blanketLikeCandidate(afc, _, selfPos, i, _, _, _) and -// not i.isBlanketImplementation() -// | -// afc.hasIncompatibleBlanketLikeTarget(i, selfPos, derefChain, borrow) -// ) -// } -// } -// pragma[nomagic] -// private AssocFunctionDeclaration getAssocFunctionSuccessor( -// ImplOrTraitItemNode i, string name, int arity -// ) { -// result = i.getASuccessor(name) and -// arity = result.getNumberOfParamsInclSelf() -// } -// private newtype TAssocFunctionCallCand = -// MkAssocFunctionCallCand( -// AssocFunctionCall afc, FunctionPosition selfPos, DerefChain derefChain, BorrowKind borrow -// ) { -// exists(afc.getANonPseudoSelfTypeAt(selfPos, derefChain, borrow, _)) -// } -// /** A call with a dereference chain and a potential borrow at a given position. */ -// final private class AssocFunctionCallCand extends MkAssocFunctionCallCand { -// AssocFunctionCall afc_; -// FunctionPosition selfPos_; -// DerefChain derefChain; -// BorrowKind borrow; -// AssocFunctionCallCand() { this = MkAssocFunctionCallCand(afc_, selfPos_, derefChain, borrow) } -// AssocFunctionCall getAssocFunctionCall() { result = afc_ } -// ItemNode getEnclosingItemNode() { result.getADescendant() = afc_ } -// Type getTypeAt(TypePath path) { -// result = -// substituteLookupTraits(afc_, -// afc_.getANonPseudoSelfTypeAt(selfPos_, derefChain, borrow, path)) -// } -// pragma[nomagic] -// predicate hasNoCompatibleNonBlanketTarget() { -// afc_.hasNoCompatibleNonBlanketTarget(selfPos_, derefChain, borrow) -// } -// pragma[nomagic] -// predicate hasSignature( -// AssocFunctionCall afc, FunctionPosition selfPos, TypePath strippedTypePath, Type strippedType, -// string name, int arity -// ) { -// strippedType = this.getTypeAt(strippedTypePath) and -// ( -// isComplexRootStripped(strippedTypePath, strippedType) -// or -// selfPos_.isTypeQualifier() and strippedTypePath.isEmpty() -// ) and -// afc = afc_ and -// afc.hasNameAndArity(name, arity) and -// selfPos = selfPos_ -// } -// /** -// * Holds if the inherent function inside `impl` with matching name and arity can be -// * ruled out as a candidate for this call. -// */ -// pragma[nomagic] -// private predicate hasIncompatibleInherentTarget(Impl impl) { -// SelfArgIsNotInstantiationOfInherent::argIsNotInstantiationOf(this, impl, _, _) -// } -// pragma[nomagic] -// predicate hasNoInherentTargetCheck() { -// exists( -// TypePath strippedTypePath, Type strippedType, string name, int arity, -// TypeOption typeQualifier, TypeOption traitQualifier, boolean hasReceiver, -// boolean targetMustBeMethod -// | -// // Calls to inherent functions are always of the form `x.m(...)` or `Foo::bar(...)`, -// // where `Foo` is a type. In case `bar` is a method, we can use both the type qualifier -// // and the type of the first argument to rule out candidates -// selfPos_.isTypeQualifier() and targetMustBeMethod = false -// or -// selfPos_.asPosition() = 0 and targetMustBeMethod = true -// | -// afc_.hasSyntacticInfo(name, arity, typeQualifier, traitQualifier, hasReceiver) and -// (if hasReceiver = true then targetMustBeMethod = true else any()) and -// this.hasSignature(_, selfPos_, strippedTypePath, strippedType, name, arity) and -// forall(Impl i | -// i.isInherent() and -// ( -// assocFunctionInfoNonBlanketLikeCheck(_, name, arity, selfPos_, i, _, strippedTypePath, -// strippedType, typeQualifier, traitQualifier, targetMustBeMethod) -// or -// assocFunctionInfoNonBlanketLikeTypeParamCheck(_, name, arity, selfPos_, i, _, -// strippedTypePath, typeQualifier, traitQualifier, targetMustBeMethod) -// ) -// | -// this.hasIncompatibleInherentTarget(i) -// ) -// ) -// } -// /** -// * Holds if this function call has no inherent target, i.e., it does not -// * resolve to a function in an `impl` block for the type of the receiver. -// */ -// pragma[nomagic] -// predicate hasNoInherentTarget() { -// afc_.hasTrait() -// or -// afc_.hasNoInherentTarget(selfPos_, derefChain, borrow) -// } -// pragma[nomagic] -// private predicate selfArgIsInstantiationOf(ImplOrTraitItemNode i, string name, int arity) { -// SelfArgIsInstantiationOf::argIsInstantiationOf(this, i, _) and -// afc_.hasNameAndArity(name, arity) -// } -// pragma[nomagic] -// AssocFunctionDeclaration resolveCallTargetCand(ImplOrTraitItemNode i) { -// exists(string name, int arity | -// this.selfArgIsInstantiationOf(i, name, arity) and -// result = getAssocFunctionSuccessor(i, name, arity) -// ) -// } -// /** Gets the associated function targeted by this call, if any. */ -// pragma[nomagic] -// AssocFunctionDeclaration resolveCallTarget(ImplOrTraitItemNode i) { -// result = this.resolveCallTargetCand(i) and -// not FunctionOverloading::functionResolutionDependsOnArgument(i, result, _, _) -// or -// OverloadedCallArgsAreInstantiationsOf::argsAreInstantiationsOf(this, i, result) -// } -// string toString() { -// result = afc_ + " at " + selfPos_ + " [" + derefChain.toString() + "; " + borrow + "]" -// } -// Location getLocation() { result = afc_.getLocation() } -// } -// /** -// * Provides logic for resolving implicit `Deref::deref` calls. -// */ -// private module ImplicitDeref { -// private newtype TCallDerefCand = -// MkCallDerefCand(AssocFunctionCall afc, FunctionPosition selfPos, DerefChain derefChain) { -// afc.supportsAutoDerefAndBorrow() and -// afc.hasReceiverAtPos(selfPos) and -// afc.hasNoCompatibleTarget(selfPos, derefChain, TSomeBorrowKind(true)) and -// exists(afc.getSelfTypeAtNoBorrow(selfPos, derefChain, TypePath::nil())) -// } -// /** A call with a dereference chain. */ -// private class CallDerefCand extends MkCallDerefCand { -// AssocFunctionCall afc; -// FunctionPosition selfPos; -// DerefChain derefChain; -// CallDerefCand() { this = MkCallDerefCand(afc, selfPos, derefChain) } -// Type getTypeAt(TypePath path) { -// result = substituteLookupTraits(afc, afc.getSelfTypeAtNoBorrow(selfPos, derefChain, path)) and -// result != TNeverType() and -// result != TUnknownType() -// } -// string toString() { result = afc + " [" + derefChain.toString() + "]" } -// Location getLocation() { result = afc.getLocation() } -// } -// private module CallSatisfiesDerefConstraintInput implements SatisfiesTypeInputSig -// { -// pragma[nomagic] -// predicate relevantConstraint(CallDerefCand mc, Type constraint) { -// exists(mc) and -// constraint.(TraitType).getTrait() instanceof DerefTrait -// } -// } -// private module CallSatisfiesDerefConstraint = -// SatisfiesType; -// pragma[nomagic] -// private AssociatedTypeTypeParameter getDerefTargetTypeParameter() { -// result.getTypeAlias() = any(DerefTrait ft).getTargetType() -// } -// /** -// * Gets the type of the receiver of `afc` at `path` after applying the implicit -// * dereference inside `impl`, following the existing dereference chain `derefChain`. -// */ -// pragma[nomagic] -// Type getDereferencedCandidateReceiverType( -// AssocFunctionCall afc, FunctionPosition selfPos, DerefImplItemNode impl, -// DerefChain derefChain, TypePath path -// ) { -// exists(CallDerefCand cdc, TypePath exprPath | -// cdc = MkCallDerefCand(afc, selfPos, derefChain) and -// CallSatisfiesDerefConstraint::satisfiesConstraintThrough(cdc, impl, _, exprPath, result) and -// exprPath.isCons(getDerefTargetTypeParameter(), path) -// ) -// } -// } -// private module ArgSatisfiesBlanketLikeConstraintInput implements -// BlanketImplementation::SatisfiesBlanketConstraintInputSig -// { -// pragma[nomagic] -// predicate hasBlanketCandidate( -// AssocFunctionCallCand afcc, ImplItemNode impl, TypePath blanketPath, -// TypeParam blanketTypeParam -// ) { -// exists(AssocFunctionCall afc, FunctionPosition selfPos, BorrowKind borrow | -// afcc = MkAssocFunctionCallCand(afc, selfPos, _, borrow) and -// blanketLikeCandidate(afc, _, selfPos, impl, _, blanketPath, blanketTypeParam) and -// // Only apply blanket implementations when no other implementations are possible; -// // this is to account for codebases that use the (unstable) specialization feature -// // (https://rust-lang.github.io/rfcs/1210-impl-specialization.html), as well as -// // cases where our blanket implementation filtering is not precise enough. -// if impl.isBlanketImplementation() then afcc.hasNoCompatibleNonBlanketTarget() else any() -// ) -// } -// } -// private module ArgSatisfiesBlanketLikeConstraint = -// BlanketImplementation::SatisfiesBlanketConstraint; -// /** -// * A configuration for matching the type of an argument against the type of -// * a function at a function-call adjusted position relevant for dispatch -// * (such as a `self` parameter). -// */ -// private module SelfArgIsInstantiationOfInput implements -// IsInstantiationOfInputSig -// { -// pragma[nomagic] -// additional predicate potentialInstantiationOf0( -// AssocFunctionCallCand afcc, ImplOrTraitItemNode i, AssocFunctionType selfType -// ) { -// exists( -// AssocFunctionCall afc, FunctionPosition selfPos, Function f, TypePath strippedTypePath, -// Type strippedType -// | -// afcc.hasSignature(afc, selfPos, strippedTypePath, strippedType, _, _) -// | -// nonBlanketLikeCandidate(afc, f, selfPos, i, selfType, strippedTypePath, strippedType) -// or -// blanketLikeCandidate(afc, f, selfPos, i, selfType, _, _) and -// ArgSatisfiesBlanketLikeConstraint::satisfiesBlanketConstraint(afcc, i) -// ) -// } -// pragma[nomagic] -// predicate potentialInstantiationOf( -// AssocFunctionCallCand afcc, TypeAbstraction abs, AssocFunctionType constraint -// ) { -// potentialInstantiationOf0(afcc, abs, constraint) and -// if abs.(Impl).hasTrait() -// then -// // inherent functions take precedence over trait functions, so only allow -// // trait functions when there are no matching inherent functions -// afcc.hasNoInherentTarget() -// else any() -// } -// predicate relevantConstraint(AssocFunctionType constraint) { -// assocFunctionInfo(_, _, _, _, _, constraint, _, _, _, _, _) -// } -// } -// private module SelfArgIsInstantiationOf { -// import ArgIsInstantiationOf -// pragma[nomagic] -// predicate argIsNotInstantiationOf( -// AssocFunctionCall afc, ImplOrTraitItemNode i, FunctionPosition selfPos, DerefChain derefChain, -// BorrowKind borrow, AssocFunctionType selfType -// ) { -// exists(TypePath path | -// argIsNotInstantiationOf(MkAssocFunctionCallCand(afc, selfPos, derefChain, borrow), i, -// selfType, path) and -// not path.isEmpty() -// ) -// } -// pragma[nomagic] -// predicate argIsInstantiationOf( -// AssocFunctionCall afc, ImplOrTraitItemNode i, FunctionPosition selfPos, DerefChain derefChain, -// BorrowKind borrow, AssocFunctionType selfType -// ) { -// argIsInstantiationOf(MkAssocFunctionCallCand(afc, selfPos, derefChain, borrow), i, selfType) -// } -// } -// /** -// * A configuration for anti-matching the type of an argument against the type of -// * a function at a function-call adjusted position relevant for dispatch -// * (such as a `self` parameter) in a blanket (like) implementation. -// */ -// private module SelfArgIsNotInstantiationOfBlanketLikeInput implements -// IsInstantiationOfInputSig -// { -// pragma[nomagic] -// predicate potentialInstantiationOf( -// AssocFunctionCallCand afcc, TypeAbstraction abs, AssocFunctionType constraint -// ) { -// exists(AssocFunctionCall afc, FunctionPosition selfPos | -// afcc = MkAssocFunctionCallCand(afc, selfPos, _, _) and -// blanketLikeCandidate(afc, _, selfPos, abs, constraint, _, _) and -// if abs.(Impl).hasTrait() -// then -// // inherent functions take precedence over trait functions, so only allow -// // trait functions when there are no matching inherent functions -// afcc.hasNoInherentTarget() -// else any() -// ) -// } -// } -// private module SelfArgIsNotInstantiationOfBlanketLike = -// ArgIsInstantiationOf; -// /** -// * A configuration for anti-matching the type of an argument against the type of -// * a function at a function-call adjusted position relevant for dispatch (such as -// * a `self` parameter) in an inherent function. -// */ -// private module SelfArgIsNotInstantiationOfInherentInput implements -// IsInstantiationOfInputSig -// { -// pragma[nomagic] -// predicate potentialInstantiationOf( -// AssocFunctionCallCand afcc, TypeAbstraction abs, AssocFunctionType constraint -// ) { -// SelfArgIsInstantiationOfInput::potentialInstantiationOf0(afcc, abs, constraint) and -// abs.(Impl).isInherent() and -// exists(AssocFunctionCall afc, FunctionPosition selfPos | -// afcc = MkAssocFunctionCallCand(afc, selfPos, _, _) -// | -// selfPos.isTypeQualifier() or -// afc.hasReceiverAtPos(selfPos) -// ) -// } -// } -// private module SelfArgIsNotInstantiationOfInherent = -// ArgIsInstantiationOf; -// /** -// * A configuration for matching the types of positional arguments against the -// * types of parameters, when needed to disambiguate the call. -// */ -// private module OverloadedCallArgsAreInstantiationsOfInput implements -// ArgsAreInstantiationsOfInputSig -// { -// predicate toCheck(ImplOrTraitItemNode i, Function f, TypeParameter traitTp, FunctionPosition pos) { -// FunctionOverloading::functionResolutionDependsOnArgument(i, f, traitTp, pos) -// } -// class Call extends AssocFunctionCallCand { -// Type getArgType(FunctionPosition pos, TypePath path) { -// result = this.getAssocFunctionCall().getTypeAt(pos, path) -// } -// predicate hasTargetCand(ImplOrTraitItemNode i, Function f) { -// f = this.resolveCallTargetCand(i) -// } -// } -// } -// private module OverloadedCallArgsAreInstantiationsOf { -// import ArgsAreInstantiationsOf -// pragma[nomagic] -// predicate argsAreNotInstantiationsOf(AssocFunctionCall afc, ImplOrTraitItemNode i) { -// argsAreNotInstantiationsOf(MkAssocFunctionCallCand(afc, _, _, _), i, _) -// } -// } -// } -// /** -// * A matching configuration for resolving types of function call expressions -// * like `foo.bar(baz)` and `Foo::bar(baz)`. -// */ -// private module FunctionCallMatchingInput implements MatchingWithEnvironmentInputSig { -// import FunctionPositionMatchingInput -// private newtype TDeclaration = -// TFunctionDeclaration(ImplOrTraitItemNodeOption i, FunctionDeclaration f) { f.isFor(i) } -// final class Declaration extends TFunctionDeclaration { -// ImplOrTraitItemNodeOption i; -// FunctionDeclaration f; -// Declaration() { this = TFunctionDeclaration(i, f) } -// FunctionDeclaration getFunction() { result = f } -// predicate isFunction(ImplOrTraitItemNodeOption i_, Function f_) { -// i_ = i and -// f_ = f -// } -// predicate isAssocFunction(ImplOrTraitItemNode i_, Function f_) { -// i_ = i.asSome() and -// f_ = f -// } -// TypeParameter getTypeParameter(TypeParameterPosition ppos) { -// result = f.getTypeParameter(i, ppos) -// } -// Type getDeclaredType(FunctionPosition pos, TypePath path) { -// result = f.getParameterType(i, pos, path) -// or -// pos.isReturn() and -// result = f.getReturnType(i, path) -// } -// string toString() { -// i.isNone() and result = f.toString() -// or -// result = f.toStringExt(i.asSome()) -// } -// Location getLocation() { result = f.getLocation() } -// } -// pragma[nomagic] -// private TypeMention getAdditionalTypeParameterConstraint(TypeParameter tp, Declaration decl) { -// result = -// tp.(TypeParamTypeParameter) -// .getTypeParam() -// .getAdditionalTypeBound(decl.getFunction(), _) -// .getTypeRepr() -// } -// bindingset[decl] -// TypeMention getATypeParameterConstraint(TypeParameter tp, Declaration decl) { -// result = Input2::getATypeParameterConstraint(tp) and -// exists(decl) -// or -// result = getAdditionalTypeParameterConstraint(tp, decl) -// } -// class AccessEnvironment = string; -// bindingset[derefChain, borrow] -// private AccessEnvironment encodeDerefChainBorrow(DerefChain derefChain, BorrowKind borrow) { -// result = derefChain + ";" + borrow -// } -// bindingset[derefChainBorrow] -// additional predicate decodeDerefChainBorrow( -// string derefChainBorrow, DerefChain derefChain, BorrowKind borrow -// ) { -// exists(int i | -// i = derefChainBorrow.indexOf(";") and -// derefChain = derefChainBorrow.prefix(i) and -// borrow.toString() = derefChainBorrow.suffix(i + 1) -// ) -// } -// private string noDerefChainBorrow() { -// exists(DerefChain derefChain, BorrowKind borrow | -// derefChain.isEmpty() and -// borrow.isNoBorrow() and -// result = encodeDerefChainBorrow(derefChain, borrow) -// ) -// } -// final class Access = AccessImpl; -// abstract private class AccessImpl extends ContextTyping::ContextTypedCallCand { -// abstract AstNode getNodeAt(FunctionPosition pos); -// bindingset[derefChainBorrow] -// abstract Type getInferredType(string derefChainBorrow, FunctionPosition pos, TypePath path); -// abstract Declaration getTarget(string derefChainBorrow); -// /** -// * Holds if the return type of this call at `path` may have to be inferred -// * from the context. -// */ -// abstract predicate hasUnknownTypeAt(string derefChainBorrow, FunctionPosition pos, TypePath path); -// } -// private class AssocFunctionCallAccess extends AccessImpl instanceof AssocFunctionResolution::AssocFunctionCall -// { -// AssocFunctionCallAccess() { -// // handled in the `OperationMatchingInput` module -// not this instanceof Operation -// } -// pragma[nomagic] -// override Type getTypeArgument(TypeArgumentPosition apos, TypePath path) { -// result = -// this.(MethodCallExpr) -// .getGenericArgList() -// .getTypeArg(apos.asMethodTypeArgumentPosition()) -// .(TypeMention) -// .getTypeAt(path) -// or -// result = getCallExprTypeArgument(this, apos, path) -// } -// override AstNode getNodeAt(FunctionPosition pos) { -// result = AssocFunctionResolution::AssocFunctionCall.super.getNodeAt(pos) -// } -// pragma[nomagic] -// private Type getInferredSelfType(FunctionPosition pos, string derefChainBorrow, TypePath path) { -// exists(DerefChain derefChain, BorrowKind borrow | -// result = super.getSelfTypeAt(pos, derefChain, borrow, path) and -// derefChainBorrow = encodeDerefChainBorrow(derefChain, borrow) and -// super.hasReceiverAtPos(pos) -// ) -// } -// pragma[nomagic] -// private Type getInferredNonSelfType(FunctionPosition pos, TypePath path) { -// if -// // index expression `x[i]` desugars to `*x.index(i)`, so we must account for -// // the implicit deref -// pos.isReturn() and -// this instanceof IndexExpr -// then -// path.isEmpty() and -// result instanceof RefType -// or -// exists(TypePath suffix | -// result = super.getTypeAt(pos, suffix) and -// path = TypePath::cons(getRefTypeParameter(_), suffix) -// ) -// else ( -// not super.hasReceiverAtPos(pos) and -// result = super.getTypeAt(pos, path) -// ) -// } -// bindingset[derefChainBorrow] -// override Type getInferredType(string derefChainBorrow, FunctionPosition pos, TypePath path) { -// result = this.getInferredSelfType(pos, derefChainBorrow, path) -// or -// result = this.getInferredNonSelfType(pos, path) -// } -// private AssocFunctionDeclaration getTarget(ImplOrTraitItemNode i, string derefChainBorrow) { -// exists(DerefChain derefChain, BorrowKind borrow | -// derefChainBorrow = encodeDerefChainBorrow(derefChain, borrow) and -// result = super.resolveCallTarget(i, _, derefChain, borrow) // mutual recursion; resolving method calls requires resolving types and vice versa -// ) -// } -// override Declaration getTarget(string derefChainBorrow) { -// exists(ImplOrTraitItemNode i | result.isAssocFunction(i, this.getTarget(i, derefChainBorrow))) -// } -// pragma[nomagic] -// override predicate hasUnknownTypeAt(string derefChainBorrow, FunctionPosition pos, TypePath path) { -// exists(ImplOrTraitItemNode i | -// this.hasUnknownTypeAt(i, this.getTarget(i, derefChainBorrow), pos, path) -// ) -// or -// derefChainBorrow = noDerefChainBorrow() and -// forex(ImplOrTraitItemNode i, Function f | -// f = CallExprImpl::getResolvedFunction(this) and -// f = i.getAnAssocItem() -// | -// this.hasUnknownTypeAt(i, f, pos, path) -// ) -// } -// } -// private class NonAssocFunctionCallAccess extends AccessImpl instanceof NonAssocCallExpr, -// CallExprImpl::CallExprCall -// { -// pragma[nomagic] -// override Type getTypeArgument(TypeArgumentPosition apos, TypePath path) { -// result = NonAssocCallExpr.super.getTypeArgument(apos, path) -// } -// override AstNode getNodeAt(FunctionPosition pos) { -// result = NonAssocCallExpr.super.getNodeAt(pos) -// } -// pragma[nomagic] -// private Type getInferredType(FunctionPosition pos, TypePath path) { -// result = super.getInferredType(pos, path) -// } -// bindingset[derefChainBorrow] -// override Type getInferredType(string derefChainBorrow, FunctionPosition pos, TypePath path) { -// exists(derefChainBorrow) and -// result = this.getInferredType(pos, path) -// } -// pragma[nomagic] -// private Declaration getTarget() { -// result = -// TFunctionDeclaration(ImplOrTraitItemNodeOption::none_(), -// super.resolveCallTargetViaPathResolution()) -// } -// override Declaration getTarget(string derefChainBorrow) { -// result = this.getTarget() and -// derefChainBorrow = noDerefChainBorrow() -// } -// pragma[nomagic] -// override predicate hasUnknownTypeAt(string derefChainBorrow, FunctionPosition pos, TypePath path) { -// derefChainBorrow = noDerefChainBorrow() and -// exists(FunctionDeclaration f, TypeParameter tp | -// f = super.resolveCallTargetViaPathResolution() and -// pos.isReturn() and -// tp = f.getReturnType(_, path) and -// not tp = f.getParameterType(_, _, _) and -// // check that no explicit type arguments have been supplied for `tp` -// not exists(TypeArgumentPosition tapos | -// this.hasTypeArgument(tapos) and -// TTypeParamTypeParameter(tapos.asTypeParam()) = tp -// ) -// ) -// } -// } -// } -// pragma[nomagic] -// private Type inferCallArgumentTypeTopDown( -// FunctionCallMatchingInput::Access call, FunctionPosition pos, AstNode n, DerefChain derefChain, -// BorrowKind borrow, TypePath path -// ) { -// exists(string derefChainBorrow | -// FunctionCallMatchingInput::decodeDerefChainBorrow(derefChainBorrow, derefChain, borrow) and -// result = M3::inferCallArgumentTypeTopDown(call, derefChainBorrow, pos, n, path) -// ) -// } -// /** -// * Gets the type of `n` at `path` after applying `derefChain`, where `n` is the -// * `self` argument of a method call. -// * -// * The predicate recursively pops the head of `derefChain` until it becomes -// * empty, at which point the inferred type can be applied back to `n`. -// */ -// pragma[nomagic] -// private Type inferFunctionCallSelfArgumentTypeTopDown( -// FunctionCallMatchingInput::Access call, AstNode n, DerefChain derefChain, TypePath path -// ) { -// exists(FunctionPosition pos, BorrowKind borrow, TypePath path0 | -// call.(AssocFunctionResolution::AssocFunctionCall).hasReceiverAtPos(pos) and -// result = inferCallArgumentTypeTopDown(call, pos, n, derefChain, borrow, path0) -// | -// borrow.isNoBorrow() and -// path = path0 -// or -// // adjust for implicit borrow -// exists(TypePath prefix | -// prefix = TypePath::singleton(borrow.getRefType().getPositionalTypeParameter(0)) and -// path0 = prefix.appendInverse(path) -// ) -// ) -// or -// // adjust for implicit deref -// exists( -// DerefChain derefChain0, Type t0, TypePath path0, DerefImplItemNode impl, Type selfParamType, -// TypePath selfPath -// | -// t0 = inferFunctionCallSelfArgumentTypeTopDown(call, n, derefChain0, path0) and -// derefChain0.isCons(impl, derefChain) and -// selfParamType = impl.resolveSelfTypeAt(selfPath) -// | -// result = selfParamType and -// path = selfPath and -// not result instanceof TypeParameter -// or -// exists(TypePath pathToTypeParam, TypePath suffix | -// impl.targetHasTypeParameterAt(pathToTypeParam, selfParamType) and -// path0 = pathToTypeParam.appendInverse(suffix) and -// result = t0 and -// path = selfPath.append(suffix) -// ) -// ) -// } -// abstract private class Constructor extends Addressable { -// final TypeParameter getTypeParameter(TypeParameterPosition ppos) { -// typeParamMatchPosition(this.getTypeItem().getGenericParamList().getATypeParam(), result, ppos) -// } -// abstract TypeItem getTypeItem(); -// abstract TypeRepr getParameterTypeRepr(int pos); -// Type getReturnType(TypePath path) { -// result = TDataType(this.getTypeItem()) and -// path.isEmpty() -// or -// result = TTypeParamTypeParameter(this.getTypeItem().getGenericParamList().getATypeParam()) and -// path = TypePath::singleton(result) -// } -// Type getDeclaredType(FunctionPosition pos, TypePath path) { -// result = this.getParameterType(pos.asPosition(), path) -// or -// pos.isReturn() and -// result = this.getReturnType(path) -// } -// Type getParameterType(int pos, TypePath path) { -// result = this.getParameterTypeRepr(pos).(TypeMention).getTypeAt(path) -// } -// } -// private class StructConstructor extends Constructor instanceof Struct { -// override TypeItem getTypeItem() { result = this } -// override TypeRepr getParameterTypeRepr(int i) { -// result = [super.getTupleField(i).getTypeRepr(), super.getNthStructField(i).getTypeRepr()] -// } -// } -// private class VariantConstructor extends Constructor instanceof Variant { -// override TypeItem getTypeItem() { result = super.getEnum() } -// override TypeRepr getParameterTypeRepr(int i) { -// result = [super.getTupleField(i).getTypeRepr(), super.getNthStructField(i).getTypeRepr()] -// } -// } -// /** -// * A matching configuration for resolving types of constructions of enums and -// * structs, such as `Result::Ok(42)`, `Foo { bar: 1 }` and `None`. -// */ -// private module ConstructionMatchingInput implements MatchingInputSig { -// import FunctionPositionMatchingInput -// class Declaration = Constructor; -// abstract class Access extends AstNode { -// abstract Type getInferredType(FunctionPosition pos, TypePath path); -// abstract Declaration getTarget(); -// abstract AstNode getNodeAt(AccessPosition apos); -// abstract Type getTypeArgument(TypeArgumentPosition apos, TypePath path); -// /** -// * Holds if the return type of this construction expression at `path` may -// * have to be inferred from the context. For example in `Result::Ok(42)` the -// * error type has to be inferred from the context. -// */ -// pragma[nomagic] -// predicate hasUnknownTypeAt(FunctionPosition pos, TypePath path) { -// exists(Declaration d, TypeParameter tp | -// d = this.getTarget() and -// pos.isReturn() and -// tp = d.getReturnType(path) and -// not exists(FunctionPosition pos2 | not pos2.isReturn() and tp = d.getDeclaredType(pos2, _)) and -// // check that no explicit type arguments have been supplied for `tp` -// not exists(TypeArgumentPosition tapos | -// exists(this.getTypeArgument(tapos, _)) and -// TTypeParamTypeParameter(tapos.asTypeParam()) = tp -// ) -// ) -// } -// } -// private class NonAssocCallAccess extends Access, NonAssocCallExpr, -// ContextTyping::ContextTypedCallCand -// { -// NonAssocCallAccess() { -// this instanceof CallExprImpl::TupleStructExpr or -// this instanceof CallExprImpl::TupleVariantExpr -// } -// override Type getTypeArgument(TypeArgumentPosition apos, TypePath path) { -// result = NonAssocCallExpr.super.getTypeArgument(apos, path) -// } -// override AstNode getNodeAt(AccessPosition apos) { -// result = NonAssocCallExpr.super.getNodeAt(apos) -// } -// override Type getInferredType(FunctionPosition pos, TypePath path) { -// result = NonAssocCallExpr.super.getInferredType(pos, path) -// } -// override Declaration getTarget() { result = this.resolveCallTargetViaPathResolution() } -// } -// abstract private class StructAccess extends Access instanceof PathAstNode { -// pragma[nomagic] -// override Type getInferredType(AccessPosition apos, TypePath path) { -// result = inferType(this.getNodeAt(apos), path) -// } -// pragma[nomagic] -// override Declaration getTarget() { result = resolvePath(super.getPath()) } -// pragma[nomagic] -// override Type getTypeArgument(TypeArgumentPosition apos, TypePath path) { -// // Handle constructions that use `Self {...}` syntax -// exists(TypeMention tm, TypePath path0 | -// tm = super.getPath() and -// result = tm.getTypeAt(path0) and -// path0.isCons(TTypeParamTypeParameter(apos.asTypeParam()), path) -// ) -// } -// } -// private class StructExprAccess extends StructAccess, StructExpr { -// override Type getTypeArgument(TypeArgumentPosition apos, TypePath path) { -// result = super.getTypeArgument(apos, path) -// or -// exists(TypePath suffix | -// suffix.isCons(TTypeParamTypeParameter(apos.asTypeParam()), path) and -// result = inferTypeCertain(this, suffix) -// ) -// } -// override AstNode getNodeAt(AccessPosition apos) { -// result = -// this.getFieldExpr(pragma[only_bind_into](this.getNthStructField(apos.asPosition()) -// .getName() -// .getText())).getExpr() -// or -// result = this and apos.isReturn() -// } -// } -// /** A potential nullary struct/variant construction such as `None`. */ -// private class PathExprAccess extends StructAccess, PathExpr { -// PathExprAccess() { not exists(CallExpr ce | this = ce.getFunction()) } -// override AstNode getNodeAt(AccessPosition apos) { result = this and apos.isReturn() } -// } -// } -// private module ConstructionMatching = Matching; -// pragma[nomagic] -// private Type inferConstructionType(AstNode n, FunctionPosition pos, TypePath path) { -// exists(ConstructionMatchingInput::Access a | -// n = a.getNodeAt(pos) and -// result = ConstructionMatching::inferAccessType(a, pos, path) -// ) -// } -// pragma[nomagic] -// private Type inferUnknownType(AstNode n, TypePath path) { -// result = TUnknownType() and -// ( -// exists(FunctionCallMatchingInput::Access call, FunctionPosition pos | -// n = call.getNodeAt(pos) and -// call.hasUnknownTypeAt(_, pos, path) -// ) -// or -// exists(ConstructionMatchingInput::Access a, FunctionPosition pos | -// n = a.getNodeAt(pos) and -// a.hasUnknownTypeAt(pos, path) -// ) -// or -// exists(Param p | -// not p.hasTypeRepr() and -// n = p.getPat() and -// path.isEmpty() -// ) -// ) -// } -// /** -// * A matching configuration for resolving types of operations like `a + b`. -// */ -// private module OperationMatchingInput implements MatchingInputSig { -// private import codeql.rust.elements.internal.OperationImpl::Impl as OperationImpl -// import FunctionPositionMatchingInput -// class Declaration extends FunctionCallMatchingInput::Declaration { -// private Method getSelfOrImpl() { -// result = f -// or -// f.implements(result) -// } -// pragma[nomagic] -// private predicate borrowsAt(FunctionPosition pos) { -// exists(TraitItemNode t, string path, string method | -// this.getSelfOrImpl() = t.getAssocItem(method) and -// path = t.getCanonicalPath(_) and -// exists(int borrows | OperationImpl::isOverloaded(_, _, path, method, borrows) | -// pos.asPosition() = 0 and borrows >= 1 -// or -// pos.asPosition() = 1 and -// borrows >= 2 -// ) -// ) -// } -// pragma[nomagic] -// private predicate derefsReturn() { this.getSelfOrImpl() = any(DerefTrait t).getDerefFunction() } -// Type getDeclaredType(FunctionPosition pos, TypePath path) { -// exists(TypePath path0 | -// result = super.getDeclaredType(pos, path0) and -// if -// this.borrowsAt(pos) -// or -// pos.isReturn() and this.derefsReturn() -// then path0.isCons(getRefTypeParameter(_), path) -// else path0 = path -// ) -// } -// } -// class Access extends AssocFunctionResolution::OperationAssocFunctionCall { -// Type getTypeArgument(TypeArgumentPosition apos, TypePath path) { none() } -// pragma[nomagic] -// Type getInferredType(FunctionPosition pos, TypePath path) { -// result = inferType(this.getNodeAt(pos), path) -// } -// Declaration getTarget() { -// exists(ImplOrTraitItemNode i | -// result.isAssocFunction(i, this.resolveCallTarget(i, _, _, _)) // mutual recursion -// ) -// } -// } -// } -// private module OperationMatching = Matching; -// pragma[nomagic] -// private Type inferOperationType(AstNode n, FunctionPosition pos, TypePath path) { -// exists(OperationMatchingInput::Access a | -// n = a.getNodeAt(pos) and -// result = OperationMatching::inferAccessType(a, pos, path) and -// if pos.asPosition() = 0 then not path.isEmpty() else any() -// ) -// } -// pragma[nomagic] -// private Type getFieldExprLookupType(FieldExpr fe, string name, DerefChain derefChain) { -// exists(TypePath path | -// result = inferType(fe.getContainer(), path) and -// name = fe.getIdentifier().getText() and -// isComplexRootStripped(path, result) -// | -// // TODO: Support full derefence chains as for method calls -// path.isEmpty() and -// derefChain = DerefChain::nil() -// or -// exists(DerefImplItemNode impl, TypeParamTypeParameter tp | -// tp = impl.getFirstSelfTypeParameter() and -// path.getHead() = tp and -// derefChain = DerefChain::singleton(impl) -// ) -// ) -// } -// /** -// * Gets the struct field that the field expression `fe` resolves to, if any. -// */ -// cached -// StructField resolveStructFieldExpr(FieldExpr fe, DerefChain derefChain) { -// M3::CachedStage::ref() and -// exists(string name, DataType ty | -// ty = getFieldExprLookupType(fe, pragma[only_bind_into](name), derefChain) -// | -// result = ty.(StructType).getTypeItem().getStructField(pragma[only_bind_into](name)) or -// result = ty.(UnionType).getTypeItem().getStructField(pragma[only_bind_into](name)) -// ) -// } -// pragma[nomagic] -// private Type getTupleFieldExprLookupType(FieldExpr fe, int pos, DerefChain derefChain) { -// exists(string name | -// result = getFieldExprLookupType(fe, name, derefChain) and -// pos = name.toInt() -// ) -// } -// /** -// * Gets the tuple field that the field expression `fe` resolves to, if any. -// */ -// cached -// TupleField resolveTupleFieldExpr(FieldExpr fe, DerefChain derefChain) { -// M3::CachedStage::ref() and -// exists(int i | -// result = -// getTupleFieldExprLookupType(fe, pragma[only_bind_into](i), derefChain) -// .(StructType) -// .getTypeItem() -// .getTupleField(pragma[only_bind_into](i)) -// ) -// } -// /** -// * A matching configuration for resolving types of field expressions like `x.field`. -// */ -// private module FieldExprMatchingInput implements MatchingInputSig { -// private newtype TDeclarationPosition = -// TSelfDeclarationPosition() or -// TFieldPos() -// class DeclarationPosition extends TDeclarationPosition { -// predicate isSelf() { this = TSelfDeclarationPosition() } -// predicate isField() { this = TFieldPos() } -// string toString() { -// this.isSelf() and -// result = "self" -// or -// this.isField() and -// result = "(field)" -// } -// } -// private newtype TDeclaration = -// TStructFieldDecl(StructField sf) or -// TTupleFieldDecl(TupleField tf) -// abstract class Declaration extends TDeclaration { -// TypeParameter getTypeParameter(TypeParameterPosition ppos) { none() } -// abstract Type getDeclaredType(DeclarationPosition dpos, TypePath path); -// abstract string toString(); -// abstract Location getLocation(); -// } -// abstract private class StructOrTupleFieldDecl extends Declaration { -// abstract AstNode getAstNode(); -// abstract TypeRepr getTypeRepr(); -// override Type getDeclaredType(DeclarationPosition dpos, TypePath path) { -// dpos.isSelf() and -// // no case for variants as those can only be destructured using pattern matching -// exists(Struct s | this.getAstNode() = [s.getStructField(_).(AstNode), s.getTupleField(_)] | -// result = TDataType(s) and -// path.isEmpty() -// or -// result = TTypeParamTypeParameter(s.getGenericParamList().getATypeParam()) and -// path = TypePath::singleton(result) -// ) -// or -// dpos.isField() and -// result = this.getTypeRepr().(TypeMention).getTypeAt(path) -// } -// override string toString() { result = this.getAstNode().toString() } -// override Location getLocation() { result = this.getAstNode().getLocation() } -// } -// private class StructFieldDecl extends StructOrTupleFieldDecl, TStructFieldDecl { -// private StructField sf; -// StructFieldDecl() { this = TStructFieldDecl(sf) } -// override AstNode getAstNode() { result = sf } -// override TypeRepr getTypeRepr() { result = sf.getTypeRepr() } -// } -// private class TupleFieldDecl extends StructOrTupleFieldDecl, TTupleFieldDecl { -// private TupleField tf; -// TupleFieldDecl() { this = TTupleFieldDecl(tf) } -// override AstNode getAstNode() { result = tf } -// override TypeRepr getTypeRepr() { result = tf.getTypeRepr() } -// } -// class AccessPosition = DeclarationPosition; -// class Access extends FieldExpr { -// Type getTypeArgument(TypeArgumentPosition apos, TypePath path) { none() } -// AstNode getNodeAt(AccessPosition apos) { -// result = this.getContainer() and -// apos.isSelf() -// or -// result = this and -// apos.isField() -// } -// Type getInferredType(AccessPosition apos, TypePath path) { -// exists(TypePath path0 | result = inferType(this.getNodeAt(apos), path0) | -// if apos.isSelf() -// then -// // adjust for implicit deref -// path0.isCons(getRefTypeParameter(_), path) -// or -// not path0.isCons(getRefTypeParameter(_), _) and -// not (result instanceof RefType and path0.isEmpty()) and -// path = path0 -// else path = path0 -// ) -// } -// Declaration getTarget() { -// // mutual recursion; resolving fields requires resolving types and vice versa -// result = -// [ -// TStructFieldDecl(resolveStructFieldExpr(this, _)).(TDeclaration), -// TTupleFieldDecl(resolveTupleFieldExpr(this, _)) -// ] -// } -// } -// predicate accessDeclarationPositionMatch(AccessPosition apos, DeclarationPosition dpos) { -// apos = dpos -// } -// } -// private module FieldExprMatching = Matching; -// /** -// * Gets the type of `n` at `path`, where `n` is either a field expression or -// * the receiver of field expression call. -// */ -// pragma[nomagic] -// private Type inferFieldExprType(AstNode n, TypePath path) { -// exists( -// FieldExprMatchingInput::Access a, FieldExprMatchingInput::AccessPosition apos, TypePath path0 -// | -// n = a.getNodeAt(apos) and -// result = FieldExprMatching::inferAccessType(a, apos, path0) -// | -// if apos.isSelf() -// then -// exists(Type receiverType | receiverType = inferType(n) | -// if receiverType instanceof RefType -// then -// // adjust for implicit deref -// not path0.isCons(getRefTypeParameter(_), _) and -// not (path0.isEmpty() and result instanceof RefType) and -// path = TypePath::cons(getRefTypeParameter(_), path0) -// else path = path0 -// ) -// else path = path0 -// ) -// } -// /** Gets the root type of the reference expression `ref`. */ -// pragma[nomagic] -// private Type inferRefExprType(RefExpr ref) { -// if ref.isRaw() -// then -// ref.isMut() and result instanceof PtrMutType -// or -// ref.isConst() and result instanceof PtrConstType -// else -// if ref.isMut() -// then result instanceof RefMutType -// else result instanceof RefSharedType -// } -// /** Gets the root type of the reference node `ref`. */ -// pragma[nomagic] -// private Type inferRefPatType(AstNode ref) { -// exists(boolean isMut | -// ref = -// any(IdentPat ip | -// ip.isRef() and -// if ip.isMut() then isMut = true else isMut = false -// ).getName() -// or -// ref = any(RefPat rp | if rp.isMut() then isMut = true else isMut = false) -// | -// result = getRefType(isMut) -// ) -// } -// pragma[nomagic] -// private Type inferTryExprType(TryExpr te, TypePath path) { -// exists(TypeParam tp, TypePath path0 | -// result = inferType(te.getExpr(), path0) and -// path0.isCons(TTypeParamTypeParameter(tp), path) -// | -// tp = any(ResultEnum r).getGenericParamList().getGenericParam(0) -// or -// tp = any(OptionEnum o).getGenericParamList().getGenericParam(0) -// ) -// } -// pragma[nomagic] -// private StructType getStrStruct() { result = TDataType(any(Builtins::Str s)) } -// pragma[nomagic] -// private Type inferLiteralType(LiteralExpr le, TypePath path, boolean certain) { -// path.isEmpty() and -// exists(Builtins::BuiltinType t | result = TDataType(t) | -// le instanceof CharLiteralExpr and -// t instanceof Builtins::Char and -// certain = true -// or -// le = -// any(NumberLiteralExpr ne | -// t.getName() = ne.getSuffix() and -// certain = true -// or -// // When a number literal has no suffix, the type may depend on the context. -// // For simplicity, we assume either `i32` or `f64`. -// not exists(ne.getSuffix()) and -// certain = false and -// ( -// ne instanceof IntegerLiteralExpr and -// t instanceof Builtins::I32 -// or -// ne instanceof FloatLiteralExpr and -// t instanceof Builtins::F64 -// ) -// ) -// or -// le instanceof BooleanLiteralExpr and -// t instanceof Builtins::Bool and -// certain = true -// ) -// or -// le instanceof StringLiteralExpr and -// ( -// path.isEmpty() and result instanceof RefSharedType -// or -// path = TypePath::singleton(getRefTypeParameter(false)) and -// result = getStrStruct() -// ) and -// certain = true -// } -// pragma[nomagic] -// private DynTraitType getFutureTraitType() { result.getTrait() instanceof FutureTrait } -// pragma[nomagic] -// private AssociatedTypeTypeParameter getFutureOutputTypeParameter() { -// result = getAssociatedTypeTypeParameter(any(FutureTrait ft).getOutputType()) -// } -// pragma[nomagic] -// private DynTraitTypeParameter getDynFutureOutputTypeParameter() { -// result.getTraitTypeParameter() = getFutureOutputTypeParameter() -// } -// pragma[nomagic] -// predicate isUnitBlockExpr(BlockExpr be) { -// not be.getStmtList().hasTailExpr() and -// not be = any(Callable c).getBody() and -// not be.hasLabel() -// } -// pragma[nomagic] -// private Type inferBlockExprType(BlockExpr be, TypePath path) { -// // `typeEquality` handles the non-root case -// if be instanceof AsyncBlockExpr -// then ( -// path.isEmpty() and -// result = getFutureTraitType() -// or -// isUnitBlockExpr(be) and -// path = TypePath::singleton(getDynFutureOutputTypeParameter()) and -// result instanceof UnitType -// ) else ( -// isUnitBlockExpr(be) and -// path.isEmpty() and -// result instanceof UnitType -// ) -// } -// pragma[nomagic] -// private predicate exprHasUnitType(Expr e) { -// e = any(IfExpr ie | not ie.hasElse()) -// or -// e instanceof WhileExpr -// or -// e instanceof ForExpr -// } -// final private class AwaitTarget extends Expr { -// AwaitTarget() { this = any(AwaitExpr ae).getExpr() } -// Type getTypeAt(TypePath path) { result = inferType(this, path) } -// } -// private module AwaitSatisfiesTypeInput implements SatisfiesTypeInputSig { -// pragma[nomagic] -// predicate relevantConstraint(AwaitTarget term, Type constraint) { -// exists(term) and -// constraint.(TraitType).getTrait() instanceof FutureTrait -// } -// } -// private module AwaitSatisfiesType = SatisfiesType; -// pragma[nomagic] -// private Type inferAwaitExprType(AstNode n, TypePath path) { -// exists(TypePath exprPath | -// AwaitSatisfiesType::satisfiesConstraint(n.(AwaitExpr).getExpr(), _, exprPath, result) and -// exprPath.isCons(getFutureOutputTypeParameter(), path) -// ) -// } -// /** -// * Gets the root type of the array expression `ae`. -// */ -// pragma[nomagic] -// private Type inferArrayExprType(ArrayExpr ae) { exists(ae) and result instanceof ArrayType } -// /** -// * Gets the root type of the range expression `re`. -// */ -// pragma[nomagic] -// private Type inferRangeExprType(RangeExpr re) { result = TDataType(getRangeType(re)) } -// pragma[nomagic] -// private Type getInferredDerefType(DerefExpr de, TypePath path) { result = inferType(de, path) } -// pragma[nomagic] -// private PtrType getInferredDerefExprPtrType(DerefExpr de) { result = inferType(de.getExpr()) } -// /** -// * Gets the inferred type of `n` at `path` when `n` occurs in a dereference -// * expression `*n` and when `n` is known to have a raw pointer type. -// * -// * The other direction is handled in `typeEqualityAsymmetric`. -// */ -// private Type inferDereferencedExprPtrType(AstNode n, TypePath path) { -// exists(DerefExpr de, PtrType type, TypePath suffix | -// de.getExpr() = n and -// type = getInferredDerefExprPtrType(de) and -// result = getInferredDerefType(de, suffix) and -// path = TypePath::cons(type.getPositionalTypeParameter(0), suffix) -// ) -// } -// /** -// * A matching configuration for resolving types of deconstruction patterns like -// * `let Foo { bar } = ...` or `let Some(x) = ...`. -// */ -// private module DeconstructionPatMatchingInput implements MatchingInputSig { -// import FunctionPositionMatchingInput -// class Declaration = ConstructionMatchingInput::Declaration; -// class Access extends Pat instanceof PathAstNode { -// Access() { this instanceof TupleStructPat or this instanceof StructPat } -// Type getTypeArgument(TypeArgumentPosition apos, TypePath path) { none() } -// AstNode getNodeAt(AccessPosition apos) { -// this = -// any(StructPat sp | -// result = -// sp.getPatField(pragma[only_bind_into](sp.getNthStructField(apos.asPosition()) -// .getName() -// .getText())).getPat() -// ) -// or -// result = this.(TupleStructPat).getField(apos.asPosition()) -// or -// result = this and -// apos.isReturn() -// } -// Type getInferredType(AccessPosition apos, TypePath path) { -// result = inferType(this.getNodeAt(apos), path) -// or -// // The struct/enum type is supplied explicitly as a type qualifier, e.g. -// // `let Foo::::Variant { ... } = ...` or -// // `let Option::::Some(x) = ...`. -// apos.isReturn() and -// result = super.getPath().(TypeMention).getTypeAt(path) -// } -// Declaration getTarget() { result = resolvePath(super.getPath()) } -// } -// } -// private module DeconstructionPatMatching = Matching; -// /** -// * Gets the type of `n` at `path`, where `n` is a pattern for a constructor, -// * either a struct pattern or a tuple-struct pattern. -// */ -// pragma[nomagic] -// private Type inferDeconstructionPatType(AstNode n, TypePath path) { -// exists(DeconstructionPatMatchingInput::Access a, FunctionPosition apos | -// n = a.getNodeAt(apos) and -// result = DeconstructionPatMatching::inferAccessType(a, apos, path) -// ) -// } -// final private class ForIterableExpr extends Expr { -// ForIterableExpr() { this = any(ForExpr fe).getIterable() } -// Type getTypeAt(TypePath path) { result = inferType(this, path) } -// } -// private module ForIterableSatisfiesTypeInput implements SatisfiesTypeInputSig { -// predicate relevantConstraint(ForIterableExpr term, Type constraint) { -// exists(term) and -// exists(Trait t | t = constraint.(TraitType).getTrait() | -// // TODO: Remove the line below once we can handle the `impl IntoIterator for I` implementation -// t instanceof IteratorTrait or -// t instanceof IntoIteratorTrait -// ) -// } -// } -// pragma[nomagic] -// private AssociatedTypeTypeParameter getIteratorItemTypeParameter() { -// result = getAssociatedTypeTypeParameter(any(IteratorTrait t).getItemType()) -// } -// pragma[nomagic] -// private AssociatedTypeTypeParameter getIntoIteratorItemTypeParameter() { -// result = getAssociatedTypeTypeParameter(any(IntoIteratorTrait t).getItemType()) -// } -// private module ForIterableSatisfiesType = -// SatisfiesType; -// 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 -// ForIterableSatisfiesType::satisfiesConstraint(fe.getIterable(), _, exprPath, result) and -// exprPath.isCons(tp, path) -// | -// tp = getIntoIteratorItemTypeParameter() -// or -// // TODO: Remove once we can handle the `impl IntoIterator for I` implementation -// tp = getIteratorItemTypeParameter() and -// inferType(fe.getIterable()) != getArrayTypeParameter() -// ) -// } -// pragma[nomagic] -// private Type inferClosureExprBodyTypeTopDown(AstNode n, TypePath path) { -// exists(ClosureExpr ce | -// n = ce.getClosureBody() and -// result = inferType(ce, closureReturnPath().appendInverse(path)) -// ) -// } -// pragma[nomagic] -// private Type inferClosureExprType(ClosureExpr ce, TypePath path) { -// path = TypePath::singleton(TDynTraitTypeParameter(_, any(FnTrait t).getTypeParam())) and -// result.(TupleType).getArity() = ce.getNumberOfParams() -// or -// exists(TypePath suffix | -// result = ce.getRetType().getTypeRepr().(TypeMention).getTypeAt(suffix) and -// path = closureReturnPath().append(suffix) -// ) -// } -// pragma[nomagic] -// private TupleType inferArgList(ArgList args, TypePath path) { -// exists(CallExprImpl::DynamicCallExpr dce | -// args = dce.getArgList() and -// result.getArity() = dce.getNumberOfSyntacticArguments() and -// path.isEmpty() -// ) -// } -// pragma[nomagic] -// private Type inferCastExprType(CastExpr ce, TypePath path) { -// result = ce.getTypeRepr().(TypeMention).getTypeAt(path) -// } -// /** Holds if `n` is implicitly dereferenced and/or borrowed. */ -// cached -// predicate implicitDerefChainBorrow(Expr e, DerefChain derefChain, boolean borrow) { -// M3::CachedStage::ref() and -// exists(BorrowKind bk | -// any(AssocFunctionResolution::AssocFunctionCall afc) -// .argumentHasImplicitDerefChainBorrow(e, derefChain, bk) and -// if bk.isNoBorrow() then borrow = false else borrow = true -// ) -// or -// e = -// any(FieldExpr fe | -// exists(resolveStructFieldExpr(fe, derefChain)) -// or -// exists(resolveTupleFieldExpr(fe, derefChain)) -// ).getContainer() and -// not derefChain.isEmpty() and -// borrow = false -// } -// /** -// * Gets an item (function or tuple struct/variant) that `call` resolves to, if -// * any. -// * -// * The parameter `dispatch` is `true` if and only if the resolved target is a -// * trait item because a precise target could not be determined from the -// * types (for instance in the presence of generics or `dyn` types) -// */ -// cached -// Addressable resolveCallTarget(InvocationExpr call, boolean dispatch) { -// M3::CachedStage::ref() and -// dispatch = false and -// result = call.(NonAssocCallExpr).resolveCallTargetViaPathResolution() -// or -// exists(ImplOrTraitItemNode i | -// i instanceof TraitItemNode and dispatch = true -// or -// i instanceof ImplItemNode and dispatch = false -// | -// result = call.(AssocFunctionResolution::AssocFunctionCall).resolveCallTarget(i, _, _, _) and -// not call instanceof CallExprImpl::DynamicCallExpr and -// not i instanceof Builtins::BuiltinImpl -// ) -// } -// /** Provides predicates for debugging the type inference implementation. */ -// private module Debug { -// Locatable getRelevantLocatable() { -// exists(string filepath, int startline, int startcolumn, int endline, int endcolumn | -// result.getLocation().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) and -// filepath.matches("%/main.rs") and -// startline = 103 -// ) -// } -// Type debugInferType(AstNode n, TypePath path) { -// n = getRelevantLocatable() and -// result = inferType(n, path) -// } -// Addressable debugResolveCallTarget(InvocationExpr c, boolean dispatch) { -// c = getRelevantLocatable() and -// result = resolveCallTarget(c, dispatch) -// } -// predicate debugConditionSatisfiesConstraint( -// TypeAbstraction abs, TypeMention condition, TypeMention constraint, boolean transitive -// ) { -// abs = getRelevantLocatable() and -// Input2::conditionSatisfiesConstraint(abs, condition, constraint, transitive) -// } -// predicate debugInferShorthandSelfType(ShorthandSelfParameterMention self, TypePath path, Type t) { -// self = getRelevantLocatable() and -// t = self.getTypeAt(path) -// } -// predicate debugTypeMention(TypeMention tm, TypePath path, Type type) { -// tm = getRelevantLocatable() and -// tm.getTypeAt(path) = type -// } -// pragma[nomagic] -// private int countTypesAtPath(AstNode n, TypePath path, Type t) { -// t = inferType(n, path) and -// result = strictcount(Type t0 | t0 = inferType(n, path)) -// } -// pragma[nomagic] -// private predicate atLimit(AstNode n) { -// exists(TypePath path0 | exists(inferType(n, path0)) and path0.length() >= getTypePathLimit()) -// } -// Type debugInferTypeForNodeAtLimit(AstNode n, TypePath path) { -// result = inferType(n, path) and -// atLimit(n) -// } -// predicate countTypesForNodeAtLimit(AstNode n, int c) { -// n = getRelevantLocatable() and -// c = strictcount(Type t, TypePath path | t = debugInferTypeForNodeAtLimit(n, path)) -// } -// predicate maxTypes(AstNode n, TypePath path, Type t, int c) { -// c = countTypesAtPath(n, path, t) and -// c = max(countTypesAtPath(_, _, _)) -// } -// pragma[nomagic] -// private predicate typePathLength(AstNode n, TypePath path, Type t, int len) { -// t = inferType(n, path) and -// len = path.length() -// } -// predicate maxTypePath(AstNode n, TypePath path, Type t, int len) { -// typePathLength(n, path, t, len) and -// len = max(int i | typePathLength(_, _, _, i)) -// } -// pragma[nomagic] -// private int countTypePaths(AstNode n, TypePath path, Type t) { -// t = inferType(n, path) and -// result = strictcount(TypePath path0, Type t0 | t0 = inferType(n, path0)) -// } -// predicate maxTypePaths(AstNode n, TypePath path, Type t, int c) { -// c = countTypePaths(n, path, t) and -// c = max(countTypePaths(_, _, _)) -// } -// Type debugInferTypeCertain(AstNode n, TypePath path) { -// n = getRelevantLocatable() and -// result = inferTypeCertain(n, path) -// } -// Type debugInferCertainNonUniqueType(AstNode n, TypePath path) { -// n = getRelevantLocatable() and -// Consistency::nonUniqueCertainType(n, path, result) -// } -// } From 1ca1bc35cab065d4ccf3209471d689e8e4aa0717 Mon Sep 17 00:00:00 2001 From: Tom Hvitved Date: Tue, 12 May 2026 15:16:33 +0200 Subject: [PATCH 17/18] more tests --- .../typeinference/internal/TypeInference.qll | 16 +----- .../swift/typeinference/TypeInference.qll | 13 +++-- .../swift/typeinference/TypeMention.qll | 30 +++++++++- .../type-inference/generics.swift | 53 +++++++++++++++++- .../type-inference/type-inference-ql.expected | 56 +++++++++++++++++++ .../type-inference/type-inference.expected | 1 + 6 files changed, 149 insertions(+), 20 deletions(-) diff --git a/shared/typeinference/codeql/typeinference/internal/TypeInference.qll b/shared/typeinference/codeql/typeinference/internal/TypeInference.qll index 6cbaec7e97d4..e7cffc0c1269 100644 --- a/shared/typeinference/codeql/typeinference/internal/TypeInference.qll +++ b/shared/typeinference/codeql/typeinference/internal/TypeInference.qll @@ -1479,27 +1479,15 @@ module Make1 Input1> { } private module SatisfiesParameterConstraintInput implements - SatisfiesConstraintWithTypeMatchingInputSig + SatisfiesConstraintInputSig { predicate relevantConstraint(RelevantAccess at, RelevantTarget constraint) { constraint = at.getTarget() } - - class TypeMatchingContext = Access; - - TypeMatchingContext getTypeMatchingContext(RelevantAccess at) { - at = MkRelevantAccess(result, _, _) - } - - pragma[nomagic] - predicate typeMatch(TypeMatchingContext ctx, TypeParameter tp, TypePath path, Type t) { - typeMatch(ctx, _, _, path, t, tp) - } } private module SatisfiesParameterConstraint = - SatisfiesConstraintWithTypeMatching; + SatisfiesConstraint; /** * Holds if the (transitive) base type `t` at `path` of `a` in environment `e` diff --git a/swift/ql/lib/codeql/swift/typeinference/TypeInference.qll b/swift/ql/lib/codeql/swift/typeinference/TypeInference.qll index a0e2f11c3172..9ceef12facc8 100644 --- a/swift/ql/lib/codeql/swift/typeinference/TypeInference.qll +++ b/swift/ql/lib/codeql/swift/typeinference/TypeInference.qll @@ -77,7 +77,8 @@ private module Input2 implements InputSig2 { result.(GenericContextMention).getContext() = tp.(SelfTypeParameter).getProtocol() or // todo: type parameter constraints - none() + result.(GenericContextMention).getContext() = + tp.(GenericTypeParamDeclTypeParameter).getDecl().(GenericTypeParamDecl).getABaseTypeDecl() } /** @@ -90,12 +91,16 @@ private module Input2 implements InputSig2 { predicate conditionSatisfiesConstraint( TypeAbstraction abs, TypeMention condition, TypeMention constraint, boolean transitive ) { + transitive = true and condition.(GenericContextMention).getContext() = any(GenericContext ctx | abs = ctx and - constraint.(TypeDeclBaseTypeMention).getDecl() = ctx - ) and - transitive = true + ctx = + [ + constraint.(TypeDeclBaseTypeMention).getDecl().(GenericContext), + constraint.(ExtensionDeclBaseTypeMention).getDecl() + ] + ) } predicate typeParameterIsFunctionallyDetermined(TypeParameter tp) { diff --git a/swift/ql/lib/codeql/swift/typeinference/TypeMention.qll b/swift/ql/lib/codeql/swift/typeinference/TypeMention.qll index 06b29cae23da..aaaa075f59a9 100644 --- a/swift/ql/lib/codeql/swift/typeinference/TypeMention.qll +++ b/swift/ql/lib/codeql/swift/typeinference/TypeMention.qll @@ -43,6 +43,7 @@ private Type getTypeAt(Swift::Type t, TypePath path) { newtype TTypeMention = TGenericContextMention(GenericContext context) or TTypeDeclBaseTypeMention(TypeDecl decl, int i) { exists(decl.getInheritedType(i)) } or + TExtensionDeclBaseTypeMention(ExtensionDecl decl, int i) { exists(decl.getProtocol(i)) } or TParamDeclTypeMention(ParamDecl decl) or TCallableReturnTypeMention(Callable c) @@ -79,6 +80,9 @@ class GenericContextMention extends TypeMentionImpl, TGenericContextMention { result = t.getATypeParameter() and path = TypePath::singleton(result) ) + or + result = + getTypeAt(context.(ExtensionDecl).getExtendedTypeDecl().getDeclaredInterfaceType(), path) } override string toString() { result = context.toString() } @@ -103,6 +107,25 @@ class TypeDeclBaseTypeMention extends TypeMentionImpl, TTypeDeclBaseTypeMention override Location getLocation() { result = decl.getLocation() } } +class ExtensionDeclBaseTypeMention extends TypeMentionImpl, TExtensionDeclBaseTypeMention { + ExtensionDecl decl; + int index; + + ExtensionDeclBaseTypeMention() { this = TExtensionDeclBaseTypeMention(decl, index) } + + ExtensionDecl getDecl() { result = decl } + + int getIndex() { result = index } + + override Type getTypeAt(TypePath path) { + result = getTypeAt(decl.getProtocol(index).getDeclaredInterfaceType(), path) + } + + override string toString() { result = decl.toString() + " [extended type " + index + "]" } + + override Location getLocation() { result = decl.getLocation() } +} + class ParamDeclTypeMention extends TypeMentionImpl, TParamDeclTypeMention { ParamDecl decl; @@ -110,7 +133,12 @@ class ParamDeclTypeMention extends TypeMentionImpl, TParamDeclTypeMention { ParamDecl getDecl() { result = decl } - override Type getTypeAt(TypePath path) { result = getTypeAt(decl.getType(), path) } + override Type getTypeAt(TypePath path) { + result = getTypeAt(decl.getType(), path) + or + result = TSelfTypeParameter(decl.(SelfParamDecl).getEnclosingDecl().getEnclosingDecl()) and + path.isEmpty() + } override string toString() { result = decl.toString() + " [parameter type]" } diff --git a/swift/ql/test/library-tests/type-inference/generics.swift b/swift/ql/test/library-tests/type-inference/generics.swift index 652a6c70b2d4..ed835016e4e6 100644 --- a/swift/ql/test/library-tests/type-inference/generics.swift +++ b/swift/ql/test/library-tests/type-inference/generics.swift @@ -187,4 +187,55 @@ func testDerived() { let dd = DerivedDerived("hello") // $ type=dd@DerivedDerived:String target=DerivedDerived.init let vv1 = dd.getValue1() // $ type=vv1:Bool target=Base.getValue1 let vv2 = dd.getValue2() // $ type=vv2:String target=Base.getValue2 -} \ No newline at end of file +} + +// --- Generics and protocols --- + +protocol MyProtocol2 { + associatedtype MyType + + // MyProtocol2.baz + func baz() -> MyType +} + +extension MyProtocol2 { + // MyProtocol2.foo + func foo() -> Self { + return self // $ type=self:Self + } +} + +class MyClass { + var value : T + + // MyClass.init + init(_ value: T) { + self.value = value // $ type=value:T + } +} + +extension MyClass : MyProtocol2 { + typealias MyType = T + + // MyClass.baz + func baz() -> T { + return value // $ type=.value:T + } +} + +// callFoo +func callFoo(_ c: T) -> T { + return c.foo() // $ target=MyProtocol2.foo +} + +// callBaz +func callBaz(_ c: T) -> T.MyType { + return c.baz() // $ target=MyProtocol2.baz +} + +func testProtocolExtension() { + let c = MyClass(42) // $ type=c@MyClass:Int target=MyClass.init + let s = c.foo() // $ type=s@MyClass:Int target=MyProtocol2.foo + let v = c.baz() // $ type=v:Int target=MyClass.baz + let v2 = callBaz(c) // $ type=v2:Int target=callBaz +} diff --git a/swift/ql/test/library-tests/type-inference/type-inference-ql.expected b/swift/ql/test/library-tests/type-inference/type-inference-ql.expected index 89efd818fd2d..06dc246e3168 100644 --- a/swift/ql/test/library-tests/type-inference/type-inference-ql.expected +++ b/swift/ql/test/library-tests/type-inference/type-inference-ql.expected @@ -23,6 +23,11 @@ testFailures | generics.swift:120:26:121:1 | // $ type=x:MyType target=MyProtocol.bar\n | Missing result: type=x:MyType | | generics.swift:159:19:160:1 | // $ type=.value1:T1\n | Missing result: type=.value1:T1 | | generics.swift:164:19:165:1 | // $ type=.value2:T2\n | Missing result: type=.value2:T2 | +| generics.swift:204:17:205:1 | // $ type=self:Self\n | Missing result: type=self:Self | +| generics.swift:222:18:223:1 | // $ type=.value:T\n | Missing result: type=.value:T | +| generics.swift:238:19:239:1 | // $ type=s@MyClass:Int target=MyProtocol2.foo\n | Missing result: type=s@MyClass:Int | +| generics.swift:239:19:240:1 | // $ type=v:Int target=MyClass.baz\n | Missing result: type=v:Int | +| generics.swift:240:23:241:1 | // $ type=v2:Int target=callBaz\n | Missing result: type=v2:Int | | key_paths.swift:35:30:36:1 | // $ type=xVal:Double\n | Missing result: type=xVal:Double | | key_paths.swift:36:30:37:1 | // $ type=yVal:Double\n | Missing result: type=yVal:Double | | key_paths.swift:48:40:49:1 | // $ type=startX:Double\n | Missing result: type=startX:Double | @@ -485,6 +490,8 @@ inferType | generics.swift:84:38:84:38 | x | | file://:0:0:0:0 | Int | | generics.swift:84:43:84:51 | call to String.init(_:) | | file://:0:0:0:0 | String | | generics.swift:84:50:84:50 | x | | file://:0:0:0:0 | Int | +| generics.swift:93:8:93:8 | self | | generics.swift:89:1:97:1 | Self [MyProtocol] | +| generics.swift:96:8:96:8 | self | | generics.swift:89:1:97:1 | Self [MyProtocol] | | generics.swift:99:7:99:7 | self | | generics.swift:99:1:123:1 | Wrapper | | generics.swift:99:7:99:7 | self | T | generics.swift:99:15:99:18 | T | | generics.swift:100:7:100:7 | self | | generics.swift:99:1:123:1 | Wrapper | @@ -653,6 +660,44 @@ inferType | generics.swift:189:13:189:13 | dd | | generics.swift:175:1:180:1 | DerivedDerived | | generics.swift:189:13:189:13 | dd | D | file://:0:0:0:0 | String | | generics.swift:189:13:189:26 | call to getValue2() | | file://:0:0:0:0 | String | +| generics.swift:198:8:198:8 | self | | generics.swift:194:1:199:1 | Self [MyProtocol2] | +| generics.swift:208:7:208:7 | self | | generics.swift:208:1:215:1 | MyClass | +| generics.swift:208:7:208:7 | self | T | generics.swift:208:15:208:15 | T | +| generics.swift:209:7:209:7 | self | | generics.swift:208:1:215:1 | MyClass | +| generics.swift:209:7:209:7 | self | | generics.swift:208:1:215:1 | MyClass | +| generics.swift:209:7:209:7 | self | | generics.swift:208:1:215:1 | MyClass | +| generics.swift:209:7:209:7 | self | T | generics.swift:208:15:208:15 | T | +| generics.swift:209:7:209:7 | self | T | generics.swift:208:15:208:15 | T | +| generics.swift:209:7:209:7 | self | T | generics.swift:208:15:208:15 | T | +| generics.swift:209:7:209:7 | value | | generics.swift:208:15:208:15 | T | +| generics.swift:212:3:212:3 | self | | generics.swift:208:1:215:1 | MyClass | +| generics.swift:212:3:212:3 | self | T | generics.swift:208:15:208:15 | T | +| generics.swift:212:8:212:17 | value | | generics.swift:208:15:208:15 | T | +| generics.swift:213:5:213:5 | self | | generics.swift:208:1:215:1 | MyClass | +| generics.swift:213:5:213:5 | self | T | generics.swift:208:15:208:15 | T | +| generics.swift:213:5:213:10 | .value | | generics.swift:208:15:208:15 | T | +| generics.swift:213:18:213:18 | value | | generics.swift:208:15:208:15 | T | +| generics.swift:221:8:221:8 | self | | generics.swift:208:1:215:1 | MyClass | +| generics.swift:221:8:221:8 | self | T | generics.swift:208:15:208:15 | T | +| generics.swift:222:12:222:12 | self | | generics.swift:208:1:215:1 | MyClass | +| generics.swift:222:12:222:12 | self | T | generics.swift:208:15:208:15 | T | +| generics.swift:227:31:227:36 | c | | generics.swift:227:14:227:18 | T | +| generics.swift:228:10:228:10 | c | | generics.swift:227:14:227:18 | T | +| generics.swift:232:31:232:36 | c | | generics.swift:232:14:232:18 | T | +| generics.swift:233:10:233:10 | c | | generics.swift:232:14:232:18 | T | +| generics.swift:237:7:237:7 | c | | generics.swift:208:1:215:1 | MyClass | +| generics.swift:237:7:237:7 | c | | generics.swift:208:1:215:1 | MyClass | +| generics.swift:237:7:237:7 | c | T | file://:0:0:0:0 | Int | +| generics.swift:237:7:237:7 | c | T | file://:0:0:0:0 | Int | +| generics.swift:237:11:237:21 | call to MyClass.init(_:) | | generics.swift:208:1:215:1 | MyClass | +| generics.swift:237:11:237:21 | call to MyClass.init(_:) | T | file://:0:0:0:0 | Int | +| generics.swift:237:19:237:19 | 42 | | file://:0:0:0:0 | Int | +| generics.swift:238:11:238:11 | c | | generics.swift:208:1:215:1 | MyClass | +| generics.swift:238:11:238:11 | c | T | file://:0:0:0:0 | Int | +| generics.swift:239:11:239:11 | c | | generics.swift:208:1:215:1 | MyClass | +| generics.swift:239:11:239:11 | c | T | file://:0:0:0:0 | Int | +| generics.swift:240:20:240:20 | c | | generics.swift:208:1:215:1 | MyClass | +| generics.swift:240:20:240:20 | c | T | file://:0:0:0:0 | Int | | key_paths.swift:4:7:4:7 | self | | key_paths.swift:3:1:17:1 | Point | | key_paths.swift:4:7:4:7 | self | | key_paths.swift:3:1:17:1 | Point | | key_paths.swift:4:7:4:7 | self | | key_paths.swift:3:1:17:1 | Point | @@ -1152,6 +1197,7 @@ inferType | overload_resolution.swift:226:7:226:7 | r2 | | file://:0:0:0:0 | String | | overload_resolution.swift:226:7:226:7 | r2 | | file://:0:0:0:0 | String | | overload_resolution.swift:226:12:226:36 | call to action() | | file://:0:0:0:0 | String | +| overload_resolution.swift:233:8:233:8 | self | | overload_resolution.swift:231:1:234:1 | Self [Describable] | | overload_resolution.swift:239:12:239:12 | default | | file://:0:0:0:0 | String | | overload_resolution.swift:244:12:244:12 | extra | | file://:0:0:0:0 | String | | overload_resolution.swift:248:7:248:7 | self | | overload_resolution.swift:248:1:256:1 | DescribableImpl | @@ -1256,6 +1302,7 @@ inferType | overload_resolution.swift:348:12:348:37 | call to send(to:from:) | | file://:0:0:0:0 | String | | overload_resolution.swift:348:23:348:23 | x | | file://:0:0:0:0 | String | | overload_resolution.swift:348:34:348:34 | y | | file://:0:0:0:0 | String | +| overload_resolution.swift:355:15:355:15 | self | | overload_resolution.swift:353:1:356:1 | Self [Numeric2] | | overload_resolution.swift:360:38:360:38 | 0 | | file://:0:0:0:0 | Int | | overload_resolution.swift:369:33:369:38 | x | | overload_resolution.swift:369:20:369:23 | T | | overload_resolution.swift:370:10:370:10 | x | | overload_resolution.swift:369:20:369:23 | T | @@ -1434,6 +1481,7 @@ inferType | overload_resolution.swift:494:12:494:12 | ms | | overload_resolution.swift:473:1:489:1 | MultiSubscript | | overload_resolution.swift:494:12:494:22 | call to get(_:) | | file://:0:0:0:0 | Int | | overload_resolution.swift:494:19:494:19 | a | | file://:0:0:0:0 | String | +| protocols.swift:5:8:5:8 | self | | protocols.swift:3:1:6:1 | Self [Shape] | | protocols.swift:9:7:9:7 | self | | protocols.swift:8:1:20:1 | Circle | | protocols.swift:9:7:9:7 | self | | protocols.swift:8:1:20:1 | Circle | | protocols.swift:9:7:9:7 | self | | protocols.swift:8:1:20:1 | Circle | @@ -1480,6 +1528,8 @@ inferType | protocols.swift:43:7:43:7 | a2 | | file://:0:0:0:0 | Double | | protocols.swift:43:12:43:12 | r | | protocols.swift:22:1:36:1 | Rectangle | | protocols.swift:43:12:43:19 | call to area() | | file://:0:0:0:0 | Double | +| protocols.swift:51:8:51:8 | self | | protocols.swift:48:1:54:1 | Self [Container] | +| protocols.swift:53:8:53:8 | self | | protocols.swift:48:1:54:1 | Self [Container] | | protocols.swift:58:7:58:7 | self | | protocols.swift:56:1:74:1 | IntContainer | | protocols.swift:58:7:58:7 | self | | protocols.swift:56:1:74:1 | IntContainer | | protocols.swift:58:7:58:7 | self | | protocols.swift:56:1:74:1 | IntContainer | @@ -1504,6 +1554,8 @@ inferType | protocols.swift:79:7:79:7 | cnt | | file://:0:0:0:0 | Int | | protocols.swift:79:13:79:13 | ic | | protocols.swift:56:1:74:1 | IntContainer | | protocols.swift:79:13:79:22 | call to count() | | file://:0:0:0:0 | Int | +| protocols.swift:86:8:86:8 | self | | protocols.swift:84:1:87:1 | Self [Printable] | +| protocols.swift:91:8:91:8 | self | | protocols.swift:89:1:92:1 | Self [Identifiable] | | protocols.swift:94:7:94:7 | self | | protocols.swift:94:1:113:1 | Entity | | protocols.swift:95:7:95:7 | self | | protocols.swift:94:1:113:1 | Entity | | protocols.swift:95:7:95:7 | self | | protocols.swift:94:1:113:1 | Entity | @@ -1559,6 +1611,8 @@ inferType | type_constraints.swift:16:21:16:21 | 7 | | file://:0:0:0:0 | Int | | type_constraints.swift:17:18:17:18 | a | | file://:0:0:0:0 | String | | type_constraints.swift:17:23:17:23 | z | | file://:0:0:0:0 | String | +| type_constraints.swift:24:8:24:8 | self | | type_constraints.swift:22:1:25:1 | Self [Displayable2] | +| type_constraints.swift:29:8:29:8 | self | | type_constraints.swift:27:1:30:1 | Self [Sortable] | | type_constraints.swift:33:7:33:7 | self | | type_constraints.swift:32:1:51:1 | TaggedItem | | type_constraints.swift:33:7:33:7 | self | | type_constraints.swift:32:1:51:1 | TaggedItem | | type_constraints.swift:33:7:33:7 | self | | type_constraints.swift:32:1:51:1 | TaggedItem | @@ -1674,6 +1728,7 @@ inferType | type_constraints.swift:95:12:95:12 | sp | U | file://:0:0:0:0 | String | | type_constraints.swift:95:12:95:40 | call to isSecondSmaller(than:) | | file://:0:0:0:0 | Bool | | type_constraints.swift:95:37:95:37 | z | | file://:0:0:0:0 | String | +| type_constraints.swift:103:8:103:8 | self | | type_constraints.swift:100:1:104:1 | Self [ElementContainer] | | type_constraints.swift:108:7:108:7 | self | | type_constraints.swift:106:1:119:1 | IntArray | | type_constraints.swift:108:7:108:7 | self | | type_constraints.swift:106:1:119:1 | IntArray | | type_constraints.swift:108:7:108:7 | self | | type_constraints.swift:106:1:119:1 | IntArray | @@ -1757,6 +1812,7 @@ inferType | type_constraints.swift:214:7:214:7 | hl | | file://:0:0:0:0 | String | | type_constraints.swift:214:12:214:12 | truck | | type_constraints.swift:186:1:201:1 | Truck | | type_constraints.swift:214:12:214:23 | call to haul() | | file://:0:0:0:0 | String | +| type_constraints.swift:221:15:221:15 | self | | type_constraints.swift:219:1:222:1 | Self [Summable] | | type_constraints.swift:229:7:229:7 | self | | type_constraints.swift:228:1:240:1 | Accumulator | | type_constraints.swift:229:7:229:7 | self | | type_constraints.swift:228:1:240:1 | Accumulator | | type_constraints.swift:229:7:229:7 | self | | type_constraints.swift:228:1:240:1 | Accumulator | diff --git a/swift/ql/test/library-tests/type-inference/type-inference.expected b/swift/ql/test/library-tests/type-inference/type-inference.expected index e69de29bb2d1..46c4d1b6caec 100644 --- a/swift/ql/test/library-tests/type-inference/type-inference.expected +++ b/swift/ql/test/library-tests/type-inference/type-inference.expected @@ -0,0 +1 @@ +| generics.swift:240:23:241:1 | // $ type=v2:Int target=callBaz\n | Missing result: type=v2:Int | From 35ae1fcbb2b5471380aa553fc3eb490be7b9f439 Mon Sep 17 00:00:00 2001 From: Tom Hvitved Date: Tue, 12 May 2026 21:39:38 +0200 Subject: [PATCH 18/18] Method call resolution in QL --- .../swift/typeinference/TypeInference.qll | 80 +++++++- .../swift/typeinference/TypeMention.qll | 17 +- .../library-tests/type-inference/Common.qll | 36 +--- .../type-inference/type-inference-ql.expected | 173 ++++++++++++++++-- .../type-inference/type-inference-ql.ql | 20 ++ .../type-inference/type-inference.ql | 21 +++ 6 files changed, 292 insertions(+), 55 deletions(-) diff --git a/swift/ql/lib/codeql/swift/typeinference/TypeInference.qll b/swift/ql/lib/codeql/swift/typeinference/TypeInference.qll index 9ceef12facc8..f23464fa8e42 100644 --- a/swift/ql/lib/codeql/swift/typeinference/TypeInference.qll +++ b/swift/ql/lib/codeql/swift/typeinference/TypeInference.qll @@ -123,7 +123,7 @@ import M2 private module Input3 implements InputSig3 { private import swift as Swift - predicate cachedStageRevRef() { none() } + predicate cachedStageRevRef() { MethodCallResolution::resolveMethodCall(_, _) implies any() } predicate inferType = M3::inferType/2; @@ -135,6 +135,8 @@ private module Input3 implements InputSig3 { TypeMention getTypeAnnotation(AstNode n) { result.(ParamDeclTypeMention).getDecl() = n + or + result.(TypedPatternTypeMention).getPattern() = n // todo: more cases, like local variables with explicit type annotations and casts } @@ -264,9 +266,8 @@ private module Input3 implements InputSig3 { Callable getTargetCertain() { none() } Callable getTarget(CallResolutionContext ctx) { - // todo: implement in QL - result = this.getStaticTarget() and - exists(ctx) + exists(ctx) and + result = getCallTarget(this) } } @@ -278,8 +279,13 @@ private module Input3 implements InputSig3 { result = M3::inferCallArgumentTypeTopDown(_, _, _, n, path) } - predicate inferStepSymmetric(AstNode n1, TypePath prefix1, AstNode n2, TypePath prefix2) { - none() + predicate inferStepSymmetric(AstNode n1, TypePath path1, AstNode n2, TypePath path2) { + path1.isEmpty() and + path2.isEmpty() and + exists(Variable v | + n1 = v.getParentPattern() and + n2 = v.getDefiningNode() + ) } predicate inferStep(AstNode n1, TypePath path1, AstNode n2, TypePath path2) { @@ -318,6 +324,68 @@ predicate inferType = M3::inferType/2; predicate inferTypeCertain = M3::inferTypeCertain/2; +private module MethodCallResolution { + private class MethodCall extends MethodCallExpr { + pragma[nomagic] + predicate hasInfo(string name, int arity) { + name = + this.getFunction() + .(MethodLookupExpr) + .getMethodRef() + .(DeclRefExpr) + .getDecl() + .(Method) + .getName() and + arity = this.getNumberOfArguments() + } + + Type getTypeAt(TypePath path) { result = inferType(this.getQualifier(), path) } + } + + pragma[nomagic] + private predicate methodInfo(Method m, string name, int arity, ParamDeclTypeMention selfType) { + name = m.getName() and + arity = m.getNumberOfParams() and + selfType.getDecl() = m.getSelfParam() + } + + private module MethodCallQualifierSatisfiesSelfTypeInput implements + SatisfiesConstraintInputSig + { + pragma[nomagic] + predicate relevantConstraint(MethodCall call, TypeMention constraint) { + exists(string name, int arity | + call.hasInfo(name, arity) and + methodInfo(_, name, arity, constraint) + ) + } + } + + private module MethodCallQualifierSatisfiesSelfType = + SatisfiesConstraint; + + cached + predicate resolveMethodCall(MethodCallExpr call, Method m) { + M3::CachedStage::ref() and + exists(ParamDeclTypeMention selfType | + MethodCallQualifierSatisfiesSelfType::satisfiesConstraint(call, selfType, _, _) and + methodInfo(m, _, _, selfType) + ) + } +} + +Callable getCallTarget(CallExpr call) { + MethodCallResolution::resolveMethodCall(call, result) + or + // todo: implement in QL + result = call.getStaticTarget() and + ( + not call instanceof MethodCallExpr + or + result instanceof Initializer + ) +} + module Consistency { import M2::Consistency } diff --git a/swift/ql/lib/codeql/swift/typeinference/TypeMention.qll b/swift/ql/lib/codeql/swift/typeinference/TypeMention.qll index aaaa075f59a9..adb2ee15e9df 100644 --- a/swift/ql/lib/codeql/swift/typeinference/TypeMention.qll +++ b/swift/ql/lib/codeql/swift/typeinference/TypeMention.qll @@ -45,7 +45,8 @@ newtype TTypeMention = TTypeDeclBaseTypeMention(TypeDecl decl, int i) { exists(decl.getInheritedType(i)) } or TExtensionDeclBaseTypeMention(ExtensionDecl decl, int i) { exists(decl.getProtocol(i)) } or TParamDeclTypeMention(ParamDecl decl) or - TCallableReturnTypeMention(Callable c) + TCallableReturnTypeMention(Callable c) or + TTypedPatternTypeMention(TypedPattern p) /** An AST node that may mention a type. */ abstract private class TypeMentionImpl extends TTypeMention { @@ -164,3 +165,17 @@ class CallableReturnTypeMention extends TypeMentionImpl, TCallableReturnTypeMent override Location getLocation() { result = c.getLocation() } } + +class TypedPatternTypeMention extends TypeMentionImpl, TTypedPatternTypeMention { + TypedPattern p; + + TypedPatternTypeMention() { this = TTypedPatternTypeMention(p) } + + TypedPattern getPattern() { result = p } + + override Type getTypeAt(TypePath path) { result = getTypeAt(p.getType(), path) } + + override string toString() { result = p.toString() + " [pattern type]" } + + override Location getLocation() { result = p.getLocation() } +} diff --git a/swift/ql/test/library-tests/type-inference/Common.qll b/swift/ql/test/library-tests/type-inference/Common.qll index a194c50d818b..7b242e818793 100644 --- a/swift/ql/test/library-tests/type-inference/Common.qll +++ b/swift/ql/test/library-tests/type-inference/Common.qll @@ -20,32 +20,12 @@ private SingleLineComment getPrecedingComment(Decl d) { ) } -module ResolveTest implements TestSig { - string getARelevantTag() { result = "target" } - - private predicate declHasName(Decl c, string value) { - exists(string s | - s = getPrecedingComment(c).getText() and - value = s.substring(3, s.length() - 1) - ) - or - not exists(getPrecedingComment(c)) and - value = [c.(EnumElementDecl).getName(), c.(Function).getName()] - } - - predicate hasActualResult(Location location, string element, string tag, string value) { - exists(AstNode source, Decl target | - location = source.getLocation() and - element = source.toString() and - target = - [ - source.(CallExpr).getStaticTarget().(Decl), source.(MethodLookupExpr).getMember(), - source.(EnumElementPattern).getElement() - ] and - declHasName(target, value) and - tag = "target" and - toBeTested(source) and - toBeTested(target) - ) - } +predicate declHasName(Decl c, string value) { + exists(string s | + s = getPrecedingComment(c).getText() and + value = s.substring(3, s.length() - 1) + ) + or + not exists(getPrecedingComment(c)) and + value = [c.(EnumElementDecl).getName(), c.(Function).getName()] } diff --git a/swift/ql/test/library-tests/type-inference/type-inference-ql.expected b/swift/ql/test/library-tests/type-inference/type-inference-ql.expected index 06dc246e3168..09adad61fe83 100644 --- a/swift/ql/test/library-tests/type-inference/type-inference-ql.expected +++ b/swift/ql/test/library-tests/type-inference/type-inference-ql.expected @@ -4,27 +4,42 @@ testFailures | basics.swift:40:18:41:1 | // $ type=.value:T\n | Missing result: type=.value:T | | basics.swift:47:22:48:1 | // $ type=super@Generic:Int target=Generic.init\n | Missing result: type=super@Generic:Int | | basics.swift:64:22:65:1 | // $ type=.myInt:Int\n | Missing result: type=.myInt:Int | +| classes.swift:16:34:17:1 | // $ type=s:Int target=MathUtils.square\n | Missing result: target=MathUtils.square | +| classes.swift:16:34:17:1 | // $ type=s:Int target=MathUtils.square\n | Missing result: type=s:Int | +| classes.swift:17:33:18:1 | // $ type=cu:Int target=MathUtils.cube\n | Missing result: target=MathUtils.cube | +| classes.swift:17:33:18:1 | // $ type=cu:Int target=MathUtils.cube\n | Missing result: type=cu:Int | +| classes.swift:36:12:36:24 | call to process(_:) | Unexpected result: target=Overloaded.process(_:String) | +| classes.swift:37:12:37:29 | call to process(_:) | Unexpected result: target=Overloaded.process(_:Int) | | classes.swift:74:25:75:1 | // $ type=.value:Int\n | Missing result: type=.value:Int | | classes.swift:106:18:107:1 | // $ type=.value:Int\n | Missing result: type=.value:Int | | classes.swift:146:20:147:1 | // $ type=.celsius:Double\n | Missing result: type=.celsius:Double | | classes.swift:151:39:152:1 | // $ type=.celsius:Double\n | Missing result: type=.celsius:Double | +| classes.swift:204:12:204:20 | call to speak() | Unexpected result: target=Animal.speak | +| classes.swift:208:12:208:21 | call to speak() | Unexpected result: target=Animal.speak | | classes.swift:223:18:224:1 | // $ type=.count:Int\n | Missing result: type=.count:Int | | generics.swift:14:24:15:1 | // $ type=i:Int target=identity\n | Missing result: type=i:Int | | generics.swift:15:29:16:1 | // $ type=s:String target=identity\n | Missing result: type=s:String | | generics.swift:33:18:34:1 | // $ type=.first:A\n | Missing result: type=.first:A | | generics.swift:38:19:39:1 | // $ type=.second:B\n | Missing result: type=.second:B | | generics.swift:60:16:61:1 | // $ type=v:T\n | Missing result: type=v:T | +| generics.swift:68:30:69:1 | // $ type=r@Result:Int target=Result.success\n | Missing result: target=Result.success | | generics.swift:68:30:69:1 | // $ type=r@Result:Int target=Result.success\n | Missing result: type=r@Result:Int | -| generics.swift:71:39:72:1 | // $ type=r2@Result:Int target=Result.success\n | Missing result: type=r2@Result:Int | +| generics.swift:69:24:70:1 | // $ target=Result.getValue\n | Missing result: target=Result.getValue | +| generics.swift:71:39:72:1 | // $ type=r2@Result:Int target=Result.success\n | Missing result: target=Result.success | | generics.swift:83:50:84:1 | // $ target=applyTransform type=result:Int\n | Missing result: type=result:Int | | generics.swift:84:56:85:1 | // $ target=applyTransform type=strings:String\n | Missing result: type=strings:String | | generics.swift:109:18:110:1 | // $ type=.inner:T\n | Missing result: type=.inner:T | +| generics.swift:114:26:115:1 | // $ type=x:T target=MyProtocol.foo\n | Missing result: target=MyProtocol.foo | | generics.swift:114:26:115:1 | // $ type=x:T target=MyProtocol.foo\n | Missing result: type=x:T | +| generics.swift:120:26:121:1 | // $ type=x:MyType target=MyProtocol.bar\n | Missing result: target=MyProtocol.bar | | generics.swift:120:26:121:1 | // $ type=x:MyType target=MyProtocol.bar\n | Missing result: type=x:MyType | | generics.swift:159:19:160:1 | // $ type=.value1:T1\n | Missing result: type=.value1:T1 | | generics.swift:164:19:165:1 | // $ type=.value2:T2\n | Missing result: type=.value2:T2 | | generics.swift:204:17:205:1 | // $ type=self:Self\n | Missing result: type=self:Self | | generics.swift:222:18:223:1 | // $ type=.value:T\n | Missing result: type=.value:T | +| generics.swift:228:18:229:1 | // $ target=MyProtocol2.foo\n | Missing result: target=MyProtocol2.foo | +| generics.swift:233:18:234:1 | // $ target=MyProtocol2.baz\n | Missing result: target=MyProtocol2.baz | +| generics.swift:238:19:239:1 | // $ type=s@MyClass:Int target=MyProtocol2.foo\n | Missing result: target=MyProtocol2.foo | | generics.swift:238:19:239:1 | // $ type=s@MyClass:Int target=MyProtocol2.foo\n | Missing result: type=s@MyClass:Int | | generics.swift:239:19:240:1 | // $ type=v:Int target=MyClass.baz\n | Missing result: type=v:Int | | generics.swift:240:23:241:1 | // $ type=v2:Int target=callBaz\n | Missing result: type=v2:Int | @@ -48,13 +63,49 @@ testFailures | key_paths.swift:215:37:216:1 | // $ type=first:Int\n | Missing result: type=first:Int | | key_paths.swift:219:34:220:1 | // $ type=val:Int?\n | Missing result: type=val:Int? | | lub.swift:10:25:11:1 | // $ type=x:A\n | Missing result: type=x:A | +| overload_resolution.swift:27:12:27:23 | call to handle(_:) | Unexpected result: target=OverloadByType.handle(_:Bool) | +| overload_resolution.swift:27:12:27:23 | call to handle(_:) | Unexpected result: target=OverloadByType.handle(_:Double) | +| overload_resolution.swift:27:12:27:23 | call to handle(_:) | Unexpected result: target=OverloadByType.handle(_:String) | +| overload_resolution.swift:28:12:28:25 | call to handle(_:) | Unexpected result: target=OverloadByType.handle(_:Bool) | +| overload_resolution.swift:28:12:28:25 | call to handle(_:) | Unexpected result: target=OverloadByType.handle(_:Int) | +| overload_resolution.swift:28:12:28:25 | call to handle(_:) | Unexpected result: target=OverloadByType.handle(_:String) | +| overload_resolution.swift:29:12:29:25 | call to handle(_:) | Unexpected result: target=OverloadByType.handle(_:Bool) | +| overload_resolution.swift:29:12:29:25 | call to handle(_:) | Unexpected result: target=OverloadByType.handle(_:Double) | +| overload_resolution.swift:29:12:29:25 | call to handle(_:) | Unexpected result: target=OverloadByType.handle(_:Int) | +| overload_resolution.swift:30:12:30:25 | call to handle(_:) | Unexpected result: target=OverloadByType.handle(_:Double) | +| overload_resolution.swift:30:12:30:25 | call to handle(_:) | Unexpected result: target=OverloadByType.handle(_:Int) | +| overload_resolution.swift:30:12:30:25 | call to handle(_:) | Unexpected result: target=OverloadByType.handle(_:String) | +| overload_resolution.swift:118:18:118:27 | call to create() | Unexpected result: target=OverloadByReturn.create()->Double | +| overload_resolution.swift:118:18:118:27 | call to create() | Unexpected result: target=OverloadByReturn.create()->String | +| overload_resolution.swift:119:21:119:30 | call to create() | Unexpected result: target=OverloadByReturn.create()->Double | +| overload_resolution.swift:119:21:119:30 | call to create() | Unexpected result: target=OverloadByReturn.create()->Int | +| overload_resolution.swift:120:21:120:30 | call to create() | Unexpected result: target=OverloadByReturn.create()->Int | +| overload_resolution.swift:120:21:120:30 | call to create() | Unexpected result: target=OverloadByReturn.create()->String | +| overload_resolution.swift:139:12:139:24 | call to process(_:) | Unexpected result: target=OverloadGenericVsConcrete.process(_:) | +| overload_resolution.swift:140:12:140:29 | call to process(_:) | Unexpected result: target=OverloadGenericVsConcrete.process(_:Int) | +| overload_resolution.swift:141:12:141:26 | call to process(_:) | Unexpected result: target=OverloadGenericVsConcrete.process(_:Int) | | overload_resolution.swift:162:29:163:1 | // $ type=r1:String target=freeOverload(_:Int)\n | Missing result: type=r1:String | | overload_resolution.swift:163:31:164:1 | // $ type=r2:String target=freeOverload(_:String)\n | Missing result: type=r2:String | | overload_resolution.swift:164:30:165:1 | // $ type=r3:String target=freeOverload(_:Double)\n | Missing result: type=r3:String | | overload_resolution.swift:194:18:195:1 | // $ type=.value:String\n | Missing result: type=.value:String | +| overload_resolution.swift:226:38:227:1 | // $ type=r2:String target=StaticVsInstance.action()->static\n | Missing result: target=StaticVsInstance.action()->static | +| overload_resolution.swift:226:38:227:1 | // $ type=r2:String target=StaticVsInstance.action()->static\n | Missing result: type=r2:String | +| overload_resolution.swift:261:22:262:1 | // $ type=r2:String target=Describable.extra\n | Missing result: target=Describable.extra | +| overload_resolution.swift:261:22:262:1 | // $ type=r2:String target=Describable.extra\n | Missing result: type=r2:String | +| overload_resolution.swift:315:12:315:21 | call to action() | Unexpected result: target=Base.action | +| overload_resolution.swift:320:13:320:23 | call to action() | Unexpected result: target=Base.action | +| overload_resolution.swift:320:13:320:23 | call to action() | Unexpected result: target=Sub.action | | overload_resolution.swift:379:30:380:1 | // $ type=r1:Int target=constrainedId(_:Numeric2)\n | Missing result: type=r1:Int | | overload_resolution.swift:380:35:381:1 | // $ type=r2:String target=constrainedId(_:Equatable)\n | Missing result: type=r2:String | | overload_resolution.swift:395:17:396:1 | // $ type=.item:T\n | Missing result: type=.item:T | +| overload_resolution.swift:466:12:466:34 | call to apply(_:) | Unexpected result: target=Processor.apply(_:(Int,Int)->Int) | +| overload_resolution.swift:466:12:466:34 | call to apply(_:) | Unexpected result: target=Processor.apply(_:(String)->String) | +| overload_resolution.swift:467:12:467:36 | call to apply(_:) | Unexpected result: target=Processor.apply(_:(Int)->Int) | +| overload_resolution.swift:467:12:467:36 | call to apply(_:) | Unexpected result: target=Processor.apply(_:(Int,Int)->Int) | +| overload_resolution.swift:468:12:468:37 | call to apply(_:) | Unexpected result: target=Processor.apply(_:(Int)->Int) | +| overload_resolution.swift:468:12:468:37 | call to apply(_:) | Unexpected result: target=Processor.apply(_:(String)->String) | +| overload_resolution.swift:493:12:493:20 | call to get(_:) | Unexpected result: target=MultiSubscript.get(_:String) | +| overload_resolution.swift:494:12:494:22 | call to get(_:) | Unexpected result: target=MultiSubscript.get(_:) | | protocols.swift:18:38:19:1 | // $ type=.radius:Double\n | Missing result: type=.radius:Double | | protocols.swift:34:27:35:1 | // $ type=.width:Double\n | Missing result: type=.width:Double | | protocols.swift:106:17:107:1 | // $ type=.name:String\n | Missing result: type=.name:String | @@ -65,13 +116,19 @@ testFailures | type_constraints.swift:17:28:18:1 | // $ type=m4:String target=maxOf(_:_:)\n | Missing result: type=m4:String | | type_constraints.swift:44:16:45:1 | // $ type=.tag:String\n | Missing result: type=.tag:String | | type_constraints.swift:49:21:50:1 | // $ type=.priority:Int\n | Missing result: type=.priority:Int | +| type_constraints.swift:55:25:56:1 | // $ target=Displayable2.display\n | Missing result: target=Displayable2.display | | type_constraints.swift:65:29:66:1 | // $ type=s:String target=showAndSort(_:)\n | Missing result: type=s:String | | type_constraints.swift:66:43:67:1 | // $ type=eq:Bool target=showSortAndCompare(_:_:)\n | Missing result: type=eq:Bool | +| type_constraints.swift:138:28:139:1 | // $ target=ElementContainer.first\n | Missing result: target=ElementContainer.first | +| type_constraints.swift:143:28:144:1 | // $ target=ElementContainer.first\n | Missing result: target=ElementContainer.first | | type_constraints.swift:149:35:150:1 | // $ type=r1:Int target=extractFirst(from:Int)\n | Missing result: type=r1:Int | | type_constraints.swift:150:35:151:1 | // $ type=r2:String target=extractFirst(from:String)\n | Missing result: type=r2:String | +| type_constraints.swift:205:23:206:1 | // $ target=Vehicle.describe\n | Missing result: target=Vehicle.describe | | type_constraints.swift:211:33:212:1 | // $ type=d1:String target=describeVehicle(_:)\n | Missing result: type=d1:String | | type_constraints.swift:212:35:213:1 | // $ type=d2:String target=describeVehicle(_:)\n | Missing result: type=d2:String | +| type_constraints.swift:245:34:246:1 | // $ target=Summable.+\n | Missing result: target=Summable.+ | | type_constraints.swift:259:28:260:1 | // $ type=tot:Int target=Accumulator.total\n | Missing result: type=tot:Int | +| type_constraints.swift:271:32:272:1 | // $ target=Summable.+\n | Missing result: target=Summable.+ | | type_constraints.swift:282:35:283:1 | // $ type=r1:Int target=Transformer.transform\n | Missing result: type=r1:Int | | type_constraints.swift:283:41:284:1 | // $ type=r2:String target=Transformer.transform\n | Missing result: type=r2:String | | type_constraints.swift:297:38:298:1 | // $ type=r1:Int target=clamp(_:min:max:)\n | Missing result: type=r1:Int | @@ -85,10 +142,12 @@ inferType | basics.swift:3:1:3:1 | topLevelDecl | | file://:0:0:0:0 | Int | | basics.swift:3:16:3:16 | 1 | | file://:0:0:0:0 | Int | | basics.swift:5:7:5:7 | self | | basics.swift:5:1:16:1 | C | +| basics.swift:6:7:6:7 | myInt | | file://:0:0:0:0 | Int | | basics.swift:6:7:6:7 | self | | basics.swift:5:1:16:1 | C | | basics.swift:6:7:6:7 | self | | basics.swift:5:1:16:1 | C | | basics.swift:6:7:6:7 | self | | basics.swift:5:1:16:1 | C | | basics.swift:6:7:6:7 | value | | file://:0:0:0:0 | Int | +| basics.swift:6:7:6:15 | ... as ... | | file://:0:0:0:0 | Int | | basics.swift:8:3:8:3 | self | | basics.swift:5:1:16:1 | C | | basics.swift:8:8:8:11 | n | | file://:0:0:0:0 | Int | | basics.swift:9:5:9:5 | .myInt | | file://:0:0:0:0 | Int | @@ -117,6 +176,8 @@ inferType | basics.swift:32:7:32:7 | self | T | basics.swift:31:15:31:15 | T | | basics.swift:32:7:32:7 | self | T | basics.swift:31:15:31:15 | T | | basics.swift:32:7:32:7 | value | | basics.swift:31:15:31:15 | T | +| basics.swift:32:7:32:7 | value | | basics.swift:31:15:31:15 | T | +| basics.swift:32:7:32:15 | ... as ... | | basics.swift:31:15:31:15 | T | | basics.swift:34:3:34:3 | self | | basics.swift:31:1:42:1 | Generic | | basics.swift:34:3:34:3 | self | T | basics.swift:31:15:31:15 | T | | basics.swift:34:8:34:11 | v | | basics.swift:31:15:31:15 | T | @@ -174,13 +235,7 @@ inferType | classes.swift:11:12:11:12 | x | | file://:0:0:0:0 | Int | | classes.swift:11:16:11:16 | x | | file://:0:0:0:0 | Int | | classes.swift:11:20:11:20 | x | | file://:0:0:0:0 | Int | -| classes.swift:16:7:16:7 | s | | file://:0:0:0:0 | Int | -| classes.swift:16:7:16:7 | s | | file://:0:0:0:0 | Int | -| classes.swift:16:11:16:32 | call to square(x:) | | file://:0:0:0:0 | Int | | classes.swift:16:31:16:31 | 4 | | file://:0:0:0:0 | Int | -| classes.swift:17:7:17:7 | cu | | file://:0:0:0:0 | Int | -| classes.swift:17:7:17:7 | cu | | file://:0:0:0:0 | Int | -| classes.swift:17:12:17:31 | call to cube(x:) | | file://:0:0:0:0 | Int | | classes.swift:17:30:17:30 | 3 | | file://:0:0:0:0 | Int | | classes.swift:22:7:22:7 | self | | classes.swift:22:1:32:1 | Overloaded | | classes.swift:22:7:22:7 | self | | classes.swift:22:1:32:1 | Overloaded | @@ -196,12 +251,18 @@ inferType | classes.swift:35:11:35:22 | call to Overloaded.init() | | classes.swift:22:1:32:1 | Overloaded | | classes.swift:36:7:36:7 | r1 | | file://:0:0:0:0 | Int | | classes.swift:36:7:36:7 | r1 | | file://:0:0:0:0 | Int | +| classes.swift:36:7:36:7 | r1 | | file://:0:0:0:0 | String | +| classes.swift:36:7:36:7 | r1 | | file://:0:0:0:0 | String | | classes.swift:36:12:36:12 | o | | classes.swift:22:1:32:1 | Overloaded | | classes.swift:36:12:36:24 | call to process(_:) | | file://:0:0:0:0 | Int | +| classes.swift:36:12:36:24 | call to process(_:) | | file://:0:0:0:0 | String | | classes.swift:36:22:36:22 | 42 | | file://:0:0:0:0 | Int | +| classes.swift:37:7:37:7 | r2 | | file://:0:0:0:0 | Int | +| classes.swift:37:7:37:7 | r2 | | file://:0:0:0:0 | Int | | classes.swift:37:7:37:7 | r2 | | file://:0:0:0:0 | String | | classes.swift:37:7:37:7 | r2 | | file://:0:0:0:0 | String | | classes.swift:37:12:37:12 | o | | classes.swift:22:1:32:1 | Overloaded | +| classes.swift:37:12:37:29 | call to process(_:) | | file://:0:0:0:0 | Int | | classes.swift:37:12:37:29 | call to process(_:) | | file://:0:0:0:0 | String | | classes.swift:37:22:37:22 | hello | | file://:0:0:0:0 | String | | classes.swift:43:7:43:7 | self | | classes.swift:42:1:54:1 | Matrix | @@ -229,6 +290,8 @@ inferType | classes.swift:65:9:65:9 | self | | classes.swift:64:3:76:3 | Inner | | classes.swift:65:9:65:9 | self | | classes.swift:64:3:76:3 | Inner | | classes.swift:65:9:65:9 | value | | file://:0:0:0:0 | Int | +| classes.swift:65:9:65:9 | value | | file://:0:0:0:0 | Int | +| classes.swift:65:9:65:17 | ... as ... | | file://:0:0:0:0 | Int | | classes.swift:68:5:68:5 | self | | classes.swift:64:3:76:3 | Inner | | classes.swift:68:10:68:17 | value | | file://:0:0:0:0 | Int | | classes.swift:69:7:69:7 | self | | classes.swift:64:3:76:3 | Inner | @@ -301,10 +364,12 @@ inferType | classes.swift:131:12:131:47 | call to setup(retries:timeout:) | | file://:0:0:0:0 | Int | | classes.swift:131:31:131:31 | 2 | | file://:0:0:0:0 | Int | | classes.swift:136:7:136:7 | self | | classes.swift:136:1:153:1 | Temperature | +| classes.swift:137:7:137:7 | celsius | | file://:0:0:0:0 | Double | | classes.swift:137:7:137:7 | self | | classes.swift:136:1:153:1 | Temperature | | classes.swift:137:7:137:7 | self | | classes.swift:136:1:153:1 | Temperature | | classes.swift:137:7:137:7 | self | | classes.swift:136:1:153:1 | Temperature | | classes.swift:137:7:137:7 | value | | file://:0:0:0:0 | Double | +| classes.swift:137:7:137:17 | ... as ... | | file://:0:0:0:0 | Double | | classes.swift:140:3:140:3 | self | | classes.swift:136:1:153:1 | Temperature | | classes.swift:140:8:140:17 | celsius | | file://:0:0:0:0 | Double | | classes.swift:141:5:141:5 | self | | classes.swift:136:1:153:1 | Temperature | @@ -402,6 +467,7 @@ inferType | generics.swift:16:11:16:28 | call to makePair(_:_:) | element 1 of tuple of arity 2 | file://:0:0:0:0 | String | | generics.swift:16:20:16:20 | 1 | | file://:0:0:0:0 | Int | | generics.swift:16:23:16:23 | two | | file://:0:0:0:0 | String | +| generics.swift:22:7:22:7 | first | | generics.swift:21:13:21:13 | A | | generics.swift:22:7:22:7 | self | | generics.swift:21:1:40:1 | Pair | | generics.swift:22:7:22:7 | self | | generics.swift:21:1:40:1 | Pair | | generics.swift:22:7:22:7 | self | | generics.swift:21:1:40:1 | Pair | @@ -412,6 +478,8 @@ inferType | generics.swift:22:7:22:7 | self | B | generics.swift:21:16:21:16 | B | | generics.swift:22:7:22:7 | self | B | generics.swift:21:16:21:16 | B | | generics.swift:22:7:22:7 | value | | generics.swift:21:13:21:13 | A | +| generics.swift:22:7:22:15 | ... as ... | | generics.swift:21:13:21:13 | A | +| generics.swift:23:7:23:7 | second | | generics.swift:21:16:21:16 | B | | generics.swift:23:7:23:7 | self | | generics.swift:21:1:40:1 | Pair | | generics.swift:23:7:23:7 | self | | generics.swift:21:1:40:1 | Pair | | generics.swift:23:7:23:7 | self | | generics.swift:21:1:40:1 | Pair | @@ -422,6 +490,7 @@ inferType | generics.swift:23:7:23:7 | self | B | generics.swift:21:16:21:16 | B | | generics.swift:23:7:23:7 | self | B | generics.swift:21:16:21:16 | B | | generics.swift:23:7:23:7 | value | | generics.swift:21:16:21:16 | B | +| generics.swift:23:7:23:16 | ... as ... | | generics.swift:21:16:21:16 | B | | generics.swift:26:3:26:3 | self | | generics.swift:21:1:40:1 | Pair | | generics.swift:26:3:26:3 | self | A | generics.swift:21:13:21:13 | A | | generics.swift:26:3:26:3 | self | B | generics.swift:21:16:21:16 | B | @@ -479,7 +548,15 @@ inferType | generics.swift:58:12:58:12 | self | | generics.swift:50:1:65:1 | Result | | generics.swift:58:12:58:12 | self | T | generics.swift:50:13:50:13 | T | | generics.swift:68:26:68:26 | 42 | | file://:0:0:0:0 | Int | +| generics.swift:71:7:71:7 | r2 | | generics.swift:50:1:65:1 | Result | +| generics.swift:71:7:71:7 | r2 | T | file://:0:0:0:0 | Int | +| generics.swift:71:7:71:22 | ... as ... | | generics.swift:50:1:65:1 | Result | +| generics.swift:71:7:71:22 | ... as ... | T | file://:0:0:0:0 | Int | +| generics.swift:71:26:71:37 | call to ... | | generics.swift:50:1:65:1 | Result | +| generics.swift:71:26:71:37 | call to ... | T | file://:0:0:0:0 | Int | | generics.swift:71:35:71:35 | 42 | | file://:0:0:0:0 | Int | +| generics.swift:72:12:72:12 | r2 | | generics.swift:50:1:65:1 | Result | +| generics.swift:72:12:72:12 | r2 | T | file://:0:0:0:0 | Int | | generics.swift:78:27:78:36 | value | | generics.swift:78:21:78:21 | T | | generics.swift:79:20:79:20 | value | | generics.swift:78:21:78:21 | T | | generics.swift:83:31:83:31 | 5 | | file://:0:0:0:0 | Int | @@ -494,6 +571,7 @@ inferType | generics.swift:96:8:96:8 | self | | generics.swift:89:1:97:1 | Self [MyProtocol] | | generics.swift:99:7:99:7 | self | | generics.swift:99:1:123:1 | Wrapper | | generics.swift:99:7:99:7 | self | T | generics.swift:99:15:99:18 | T | +| generics.swift:100:7:100:7 | inner | | generics.swift:99:15:99:18 | T | | generics.swift:100:7:100:7 | self | | generics.swift:99:1:123:1 | Wrapper | | generics.swift:100:7:100:7 | self | | generics.swift:99:1:123:1 | Wrapper | | generics.swift:100:7:100:7 | self | | generics.swift:99:1:123:1 | Wrapper | @@ -501,6 +579,7 @@ inferType | generics.swift:100:7:100:7 | self | T | generics.swift:99:15:99:18 | T | | generics.swift:100:7:100:7 | self | T | generics.swift:99:15:99:18 | T | | generics.swift:100:7:100:7 | value | | generics.swift:99:15:99:18 | T | +| generics.swift:100:7:100:15 | ... as ... | | generics.swift:99:15:99:18 | T | | generics.swift:103:3:103:3 | self | | generics.swift:99:1:123:1 | Wrapper | | generics.swift:103:3:103:3 | self | T | generics.swift:99:15:99:18 | T | | generics.swift:103:8:103:17 | inner | | generics.swift:99:15:99:18 | T | @@ -555,6 +634,8 @@ inferType | generics.swift:148:7:148:7 | self | T2 | generics.swift:147:16:147:16 | T2 | | generics.swift:148:7:148:7 | self | T2 | generics.swift:147:16:147:16 | T2 | | generics.swift:148:7:148:7 | value | | generics.swift:147:12:147:12 | T1 | +| generics.swift:148:7:148:7 | value1 | | generics.swift:147:12:147:12 | T1 | +| generics.swift:148:7:148:16 | ... as ... | | generics.swift:147:12:147:12 | T1 | | generics.swift:149:7:149:7 | self | | generics.swift:147:1:166:1 | Base | | generics.swift:149:7:149:7 | self | | generics.swift:147:1:166:1 | Base | | generics.swift:149:7:149:7 | self | | generics.swift:147:1:166:1 | Base | @@ -565,6 +646,8 @@ inferType | generics.swift:149:7:149:7 | self | T2 | generics.swift:147:16:147:16 | T2 | | generics.swift:149:7:149:7 | self | T2 | generics.swift:147:16:147:16 | T2 | | generics.swift:149:7:149:7 | value | | generics.swift:147:16:147:16 | T2 | +| generics.swift:149:7:149:7 | value2 | | generics.swift:147:16:147:16 | T2 | +| generics.swift:149:7:149:16 | ... as ... | | generics.swift:147:16:147:16 | T2 | | generics.swift:152:3:152:3 | self | | generics.swift:147:1:166:1 | Base | | generics.swift:152:3:152:3 | self | T1 | generics.swift:147:12:147:12 | T1 | | generics.swift:152:3:152:3 | self | T2 | generics.swift:147:16:147:16 | T2 | @@ -670,6 +753,8 @@ inferType | generics.swift:209:7:209:7 | self | T | generics.swift:208:15:208:15 | T | | generics.swift:209:7:209:7 | self | T | generics.swift:208:15:208:15 | T | | generics.swift:209:7:209:7 | value | | generics.swift:208:15:208:15 | T | +| generics.swift:209:7:209:7 | value | | generics.swift:208:15:208:15 | T | +| generics.swift:209:7:209:15 | ... as ... | | generics.swift:208:15:208:15 | T | | generics.swift:212:3:212:3 | self | | generics.swift:208:1:215:1 | MyClass | | generics.swift:212:3:212:3 | self | T | generics.swift:208:15:208:15 | T | | generics.swift:212:8:212:17 | value | | generics.swift:208:15:208:15 | T | @@ -702,10 +787,14 @@ inferType | key_paths.swift:4:7:4:7 | self | | key_paths.swift:3:1:17:1 | Point | | key_paths.swift:4:7:4:7 | self | | key_paths.swift:3:1:17:1 | Point | | key_paths.swift:4:7:4:7 | value | | file://:0:0:0:0 | Double | +| key_paths.swift:4:7:4:7 | x | | file://:0:0:0:0 | Double | +| key_paths.swift:4:7:4:11 | ... as ... | | file://:0:0:0:0 | Double | | key_paths.swift:5:7:5:7 | self | | key_paths.swift:3:1:17:1 | Point | | key_paths.swift:5:7:5:7 | self | | key_paths.swift:3:1:17:1 | Point | | key_paths.swift:5:7:5:7 | self | | key_paths.swift:3:1:17:1 | Point | | key_paths.swift:5:7:5:7 | value | | file://:0:0:0:0 | Double | +| key_paths.swift:5:7:5:7 | y | | file://:0:0:0:0 | Double | +| key_paths.swift:5:7:5:11 | ... as ... | | file://:0:0:0:0 | Double | | key_paths.swift:8:3:8:3 | self | | key_paths.swift:3:1:17:1 | Point | | key_paths.swift:8:8:8:11 | x | | file://:0:0:0:0 | Double | | key_paths.swift:8:19:8:22 | y | | file://:0:0:0:0 | Double | @@ -723,11 +812,15 @@ inferType | key_paths.swift:20:7:20:7 | self | | key_paths.swift:19:1:28:1 | Line | | key_paths.swift:20:7:20:7 | self | | key_paths.swift:19:1:28:1 | Line | | key_paths.swift:20:7:20:7 | self | | key_paths.swift:19:1:28:1 | Line | +| key_paths.swift:20:7:20:7 | start | | key_paths.swift:3:1:17:1 | Point | | key_paths.swift:20:7:20:7 | value | | key_paths.swift:3:1:17:1 | Point | +| key_paths.swift:20:7:20:15 | ... as ... | | key_paths.swift:3:1:17:1 | Point | +| key_paths.swift:21:7:21:7 | end | | key_paths.swift:3:1:17:1 | Point | | key_paths.swift:21:7:21:7 | self | | key_paths.swift:19:1:28:1 | Line | | key_paths.swift:21:7:21:7 | self | | key_paths.swift:19:1:28:1 | Line | | key_paths.swift:21:7:21:7 | self | | key_paths.swift:19:1:28:1 | Line | | key_paths.swift:21:7:21:7 | value | | key_paths.swift:3:1:17:1 | Point | +| key_paths.swift:21:7:21:13 | ... as ... | | key_paths.swift:3:1:17:1 | Point | | key_paths.swift:24:3:24:3 | self | | key_paths.swift:19:1:28:1 | Line | | key_paths.swift:24:8:24:15 | start | | key_paths.swift:3:1:17:1 | Point | | key_paths.swift:24:22:24:27 | end | | key_paths.swift:3:1:17:1 | Point | @@ -756,10 +849,12 @@ inferType | key_paths.swift:48:16:48:16 | line | | key_paths.swift:19:1:28:1 | Line | | key_paths.swift:49:14:49:14 | line | | key_paths.swift:19:1:28:1 | Line | | key_paths.swift:56:13:56:13 | 42 | | file://:0:0:0:0 | Int | +| key_paths.swift:62:7:62:7 | name | | file://:0:0:0:0 | String | | key_paths.swift:62:7:62:7 | self | | key_paths.swift:61:1:70:1 | Person | | key_paths.swift:62:7:62:7 | self | | key_paths.swift:61:1:70:1 | Person | | key_paths.swift:62:7:62:7 | self | | key_paths.swift:61:1:70:1 | Person | | key_paths.swift:62:7:62:7 | value | | file://:0:0:0:0 | String | +| key_paths.swift:62:7:62:14 | ... as ... | | file://:0:0:0:0 | String | | key_paths.swift:63:7:63:7 | self | | key_paths.swift:61:1:70:1 | Person | | key_paths.swift:63:7:63:7 | self | | key_paths.swift:61:1:70:1 | Person | | key_paths.swift:63:7:63:7 | self | | key_paths.swift:61:1:70:1 | Person | @@ -769,14 +864,18 @@ inferType | key_paths.swift:67:5:67:10 | .name | | file://:0:0:0:0 | String | | key_paths.swift:67:17:67:17 | name | | file://:0:0:0:0 | String | | key_paths.swift:68:5:68:5 | self | | key_paths.swift:61:1:70:1 | Person | +| key_paths.swift:73:7:73:7 | city | | file://:0:0:0:0 | String | | key_paths.swift:73:7:73:7 | self | | key_paths.swift:72:1:81:1 | Address | | key_paths.swift:73:7:73:7 | self | | key_paths.swift:72:1:81:1 | Address | | key_paths.swift:73:7:73:7 | self | | key_paths.swift:72:1:81:1 | Address | | key_paths.swift:73:7:73:7 | value | | file://:0:0:0:0 | String | +| key_paths.swift:73:7:73:14 | ... as ... | | file://:0:0:0:0 | String | | key_paths.swift:74:7:74:7 | self | | key_paths.swift:72:1:81:1 | Address | | key_paths.swift:74:7:74:7 | self | | key_paths.swift:72:1:81:1 | Address | | key_paths.swift:74:7:74:7 | self | | key_paths.swift:72:1:81:1 | Address | | key_paths.swift:74:7:74:7 | value | | file://:0:0:0:0 | String | +| key_paths.swift:74:7:74:7 | zip | | file://:0:0:0:0 | String | +| key_paths.swift:74:7:74:13 | ... as ... | | file://:0:0:0:0 | String | | key_paths.swift:77:3:77:3 | self | | key_paths.swift:72:1:81:1 | Address | | key_paths.swift:77:8:77:14 | city | | file://:0:0:0:0 | String | | key_paths.swift:77:22:77:27 | zip | | file://:0:0:0:0 | String | @@ -797,14 +896,18 @@ inferType | key_paths.swift:86:29:86:29 | Alice | | file://:0:0:0:0 | String | | key_paths.swift:86:47:86:47 | addr | | key_paths.swift:72:1:81:1 | Address | | key_paths.swift:88:14:88:14 | person | | key_paths.swift:61:1:70:1 | Person | +| key_paths.swift:94:7:94:7 | name | | file://:0:0:0:0 | String | | key_paths.swift:94:7:94:7 | self | | key_paths.swift:93:1:102:1 | Employee | | key_paths.swift:94:7:94:7 | self | | key_paths.swift:93:1:102:1 | Employee | | key_paths.swift:94:7:94:7 | self | | key_paths.swift:93:1:102:1 | Employee | | key_paths.swift:94:7:94:7 | value | | file://:0:0:0:0 | String | +| key_paths.swift:94:7:94:14 | ... as ... | | file://:0:0:0:0 | String | +| key_paths.swift:95:7:95:7 | salary | | file://:0:0:0:0 | Int | | key_paths.swift:95:7:95:7 | self | | key_paths.swift:93:1:102:1 | Employee | | key_paths.swift:95:7:95:7 | self | | key_paths.swift:93:1:102:1 | Employee | | key_paths.swift:95:7:95:7 | self | | key_paths.swift:93:1:102:1 | Employee | | key_paths.swift:95:7:95:7 | value | | file://:0:0:0:0 | Int | +| key_paths.swift:95:7:95:16 | ... as ... | | file://:0:0:0:0 | Int | | key_paths.swift:98:3:98:3 | self | | key_paths.swift:93:1:102:1 | Employee | | key_paths.swift:98:8:98:14 | name | | file://:0:0:0:0 | String | | key_paths.swift:98:22:98:30 | salary | | file://:0:0:0:0 | Int | @@ -838,6 +941,7 @@ inferType | key_paths.swift:116:25:116:25 | 0 | | file://:0:0:0:0 | Int | | key_paths.swift:121:7:121:7 | self | | key_paths.swift:121:1:128:1 | KPContainer | | key_paths.swift:121:7:121:7 | self | T | key_paths.swift:121:19:121:19 | T | +| key_paths.swift:122:7:122:7 | item | | key_paths.swift:121:19:121:19 | T | | key_paths.swift:122:7:122:7 | self | | key_paths.swift:121:1:128:1 | KPContainer | | key_paths.swift:122:7:122:7 | self | | key_paths.swift:121:1:128:1 | KPContainer | | key_paths.swift:122:7:122:7 | self | | key_paths.swift:121:1:128:1 | KPContainer | @@ -845,6 +949,7 @@ inferType | key_paths.swift:122:7:122:7 | self | T | key_paths.swift:121:19:121:19 | T | | key_paths.swift:122:7:122:7 | self | T | key_paths.swift:121:19:121:19 | T | | key_paths.swift:122:7:122:7 | value | | key_paths.swift:121:19:121:19 | T | +| key_paths.swift:122:7:122:14 | ... as ... | | key_paths.swift:121:19:121:19 | T | | key_paths.swift:125:3:125:3 | self | | key_paths.swift:121:1:128:1 | KPContainer | | key_paths.swift:125:3:125:3 | self | T | key_paths.swift:121:19:121:19 | T | | key_paths.swift:125:8:125:14 | item | | key_paths.swift:121:19:121:19 | T | @@ -866,9 +971,6 @@ inferType | key_paths.swift:139:11:139:31 | call to Point.init(x:y:) | | key_paths.swift:3:1:17:1 | Point | | key_paths.swift:141:3:141:3 | p | | key_paths.swift:3:1:17:1 | Point | | key_paths.swift:142:14:142:14 | p | | key_paths.swift:3:1:17:1 | Point | -| key_paths.swift:150:7:150:7 | kpStartX | | file://:0:0:0:0 | WritableKeyPath | -| key_paths.swift:150:7:150:7 | kpStartX | | file://:0:0:0:0 | WritableKeyPath | -| key_paths.swift:150:18:150:45 | call to appending(path:) | | file://:0:0:0:0 | WritableKeyPath | | key_paths.swift:152:7:152:7 | s | | key_paths.swift:3:1:17:1 | Point | | key_paths.swift:152:7:152:7 | s | | key_paths.swift:3:1:17:1 | Point | | key_paths.swift:152:11:152:31 | call to Point.init(x:y:) | | key_paths.swift:3:1:17:1 | Point | @@ -881,7 +983,6 @@ inferType | key_paths.swift:154:26:154:26 | s | | key_paths.swift:3:1:17:1 | Point | | key_paths.swift:154:34:154:34 | e | | key_paths.swift:3:1:17:1 | Point | | key_paths.swift:155:13:155:13 | line | | key_paths.swift:19:1:28:1 | Line | -| key_paths.swift:155:27:155:27 | kpStartX | | file://:0:0:0:0 | WritableKeyPath | | key_paths.swift:161:7:161:7 | e1 | | key_paths.swift:93:1:102:1 | Employee | | key_paths.swift:161:7:161:7 | e1 | | key_paths.swift:93:1:102:1 | Employee | | key_paths.swift:161:12:161:47 | call to Employee.init(name:salary:) | | key_paths.swift:93:1:102:1 | Employee | @@ -899,10 +1000,12 @@ inferType | key_paths.swift:166:32:166:32 | $0 | | key_paths.swift:93:1:102:1 | Employee | | key_paths.swift:167:25:167:25 | 0 | | file://:0:0:0:0 | Int | | key_paths.swift:172:7:172:7 | self | | key_paths.swift:172:1:179:1 | Shape2 | +| key_paths.swift:173:7:173:7 | color | | file://:0:0:0:0 | String | | key_paths.swift:173:7:173:7 | self | | key_paths.swift:172:1:179:1 | Shape2 | | key_paths.swift:173:7:173:7 | self | | key_paths.swift:172:1:179:1 | Shape2 | | key_paths.swift:173:7:173:7 | self | | key_paths.swift:172:1:179:1 | Shape2 | | key_paths.swift:173:7:173:7 | value | | file://:0:0:0:0 | String | +| key_paths.swift:173:7:173:15 | ... as ... | | file://:0:0:0:0 | String | | key_paths.swift:176:3:176:3 | self | | key_paths.swift:172:1:179:1 | Shape2 | | key_paths.swift:176:8:176:15 | color | | file://:0:0:0:0 | String | | key_paths.swift:177:5:177:5 | self | | key_paths.swift:172:1:179:1 | Shape2 | @@ -911,10 +1014,12 @@ inferType | key_paths.swift:181:7:181:7 | key_paths.Circle2 | | file://:0:0:0:0 | String | | key_paths.swift:181:7:181:7 | self | | key_paths.swift:181:1:189:1 | Circle2 | | key_paths.swift:181:24:181:24 | self | | key_paths.swift:181:1:189:1 | Circle2 | +| key_paths.swift:182:7:182:7 | radius | | file://:0:0:0:0 | Double | | key_paths.swift:182:7:182:7 | self | | key_paths.swift:181:1:189:1 | Circle2 | | key_paths.swift:182:7:182:7 | self | | key_paths.swift:181:1:189:1 | Circle2 | | key_paths.swift:182:7:182:7 | self | | key_paths.swift:181:1:189:1 | Circle2 | | key_paths.swift:182:7:182:7 | value | | file://:0:0:0:0 | Double | +| key_paths.swift:182:7:182:16 | ... as ... | | file://:0:0:0:0 | Double | | key_paths.swift:185:3:185:3 | self | | key_paths.swift:181:1:189:1 | Circle2 | | key_paths.swift:185:8:185:15 | color | | file://:0:0:0:0 | String | | key_paths.swift:185:23:185:31 | radius | | file://:0:0:0:0 | Double | @@ -1140,6 +1245,8 @@ inferType | overload_resolution.swift:170:7:170:7 | self | | overload_resolution.swift:169:1:196:1 | MultiInit | | overload_resolution.swift:170:7:170:7 | self | | overload_resolution.swift:169:1:196:1 | MultiInit | | overload_resolution.swift:170:7:170:7 | value | | file://:0:0:0:0 | String | +| overload_resolution.swift:170:7:170:7 | value | | file://:0:0:0:0 | String | +| overload_resolution.swift:170:7:170:15 | ... as ... | | file://:0:0:0:0 | String | | overload_resolution.swift:173:3:173:3 | self | | overload_resolution.swift:169:1:196:1 | MultiInit | | overload_resolution.swift:174:5:174:5 | .value | | file://:0:0:0:0 | String | | overload_resolution.swift:174:5:174:5 | self | | overload_resolution.swift:169:1:196:1 | MultiInit | @@ -1194,9 +1301,6 @@ inferType | overload_resolution.swift:225:7:225:7 | r1 | | file://:0:0:0:0 | String | | overload_resolution.swift:225:12:225:12 | o | | overload_resolution.swift:208:1:221:1 | StaticVsInstance | | overload_resolution.swift:225:12:225:21 | call to action() | | file://:0:0:0:0 | String | -| overload_resolution.swift:226:7:226:7 | r2 | | file://:0:0:0:0 | String | -| overload_resolution.swift:226:7:226:7 | r2 | | file://:0:0:0:0 | String | -| overload_resolution.swift:226:12:226:36 | call to action() | | file://:0:0:0:0 | String | | overload_resolution.swift:233:8:233:8 | self | | overload_resolution.swift:231:1:234:1 | Self [Describable] | | overload_resolution.swift:239:12:239:12 | default | | file://:0:0:0:0 | String | | overload_resolution.swift:244:12:244:12 | extra | | file://:0:0:0:0 | String | @@ -1211,10 +1315,7 @@ inferType | overload_resolution.swift:260:7:260:7 | r1 | | file://:0:0:0:0 | String | | overload_resolution.swift:260:12:260:12 | d | | overload_resolution.swift:248:1:256:1 | DescribableImpl | | overload_resolution.swift:260:12:260:23 | call to describe() | | file://:0:0:0:0 | String | -| overload_resolution.swift:261:7:261:7 | r2 | | file://:0:0:0:0 | String | -| overload_resolution.swift:261:7:261:7 | r2 | | file://:0:0:0:0 | String | | overload_resolution.swift:261:12:261:12 | d | | overload_resolution.swift:248:1:256:1 | DescribableImpl | -| overload_resolution.swift:261:12:261:20 | call to extra() | | file://:0:0:0:0 | String | | overload_resolution.swift:266:7:266:7 | self | | overload_resolution.swift:266:1:279:1 | Base | | overload_resolution.swift:268:3:268:3 | self | | overload_resolution.swift:266:1:279:1 | Base | | overload_resolution.swift:271:8:271:8 | self | | overload_resolution.swift:266:1:279:1 | Base | @@ -1312,6 +1413,7 @@ inferType | overload_resolution.swift:380:26:380:26 | hello | | file://:0:0:0:0 | String | | overload_resolution.swift:385:7:385:7 | self | | overload_resolution.swift:385:1:402:1 | Box | | overload_resolution.swift:385:7:385:7 | self | T | overload_resolution.swift:385:11:385:11 | T | +| overload_resolution.swift:386:7:386:7 | item | | overload_resolution.swift:385:11:385:11 | T | | overload_resolution.swift:386:7:386:7 | self | | overload_resolution.swift:385:1:402:1 | Box | | overload_resolution.swift:386:7:386:7 | self | | overload_resolution.swift:385:1:402:1 | Box | | overload_resolution.swift:386:7:386:7 | self | | overload_resolution.swift:385:1:402:1 | Box | @@ -1319,6 +1421,7 @@ inferType | overload_resolution.swift:386:7:386:7 | self | T | overload_resolution.swift:385:11:385:11 | T | | overload_resolution.swift:386:7:386:7 | self | T | overload_resolution.swift:385:11:385:11 | T | | overload_resolution.swift:386:7:386:7 | value | | overload_resolution.swift:385:11:385:11 | T | +| overload_resolution.swift:386:7:386:14 | ... as ... | | overload_resolution.swift:385:11:385:11 | T | | overload_resolution.swift:389:3:389:3 | self | | overload_resolution.swift:385:1:402:1 | Box | | overload_resolution.swift:389:3:389:3 | self | T | overload_resolution.swift:385:11:385:11 | T | | overload_resolution.swift:389:8:389:16 | item | | overload_resolution.swift:385:11:385:11 | T | @@ -1370,14 +1473,18 @@ inferType | overload_resolution.swift:410:3:410:23 | call to replace(_:) | | file://:0:0:0:0 | (T_0) | | overload_resolution.swift:410:18:410:18 | bye | | file://:0:0:0:0 | String | | overload_resolution.swift:415:7:415:7 | self | | overload_resolution.swift:415:1:434:1 | Widget | +| overload_resolution.swift:416:7:416:7 | name | | file://:0:0:0:0 | String | | overload_resolution.swift:416:7:416:7 | self | | overload_resolution.swift:415:1:434:1 | Widget | | overload_resolution.swift:416:7:416:7 | self | | overload_resolution.swift:415:1:434:1 | Widget | | overload_resolution.swift:416:7:416:7 | self | | overload_resolution.swift:415:1:434:1 | Widget | | overload_resolution.swift:416:7:416:7 | value | | file://:0:0:0:0 | String | +| overload_resolution.swift:416:7:416:14 | ... as ... | | file://:0:0:0:0 | String | | overload_resolution.swift:417:7:417:7 | self | | overload_resolution.swift:415:1:434:1 | Widget | | overload_resolution.swift:417:7:417:7 | self | | overload_resolution.swift:415:1:434:1 | Widget | | overload_resolution.swift:417:7:417:7 | self | | overload_resolution.swift:415:1:434:1 | Widget | +| overload_resolution.swift:417:7:417:7 | size | | file://:0:0:0:0 | Int | | overload_resolution.swift:417:7:417:7 | value | | file://:0:0:0:0 | Int | +| overload_resolution.swift:417:7:417:14 | ... as ... | | file://:0:0:0:0 | Int | | overload_resolution.swift:420:3:420:3 | self | | overload_resolution.swift:415:1:434:1 | Widget | | overload_resolution.swift:420:8:420:14 | name | | file://:0:0:0:0 | String | | overload_resolution.swift:420:22:420:28 | size | | file://:0:0:0:0 | Int | @@ -1426,22 +1533,31 @@ inferType | overload_resolution.swift:465:11:465:21 | call to Processor.init() | | overload_resolution.swift:444:1:462:1 | Processor | | overload_resolution.swift:466:7:466:7 | r1 | | file://:0:0:0:0 | Int | | overload_resolution.swift:466:7:466:7 | r1 | | file://:0:0:0:0 | Int | +| overload_resolution.swift:466:7:466:7 | r1 | | file://:0:0:0:0 | String | +| overload_resolution.swift:466:7:466:7 | r1 | | file://:0:0:0:0 | String | | overload_resolution.swift:466:12:466:12 | p | | overload_resolution.swift:444:1:462:1 | Processor | | overload_resolution.swift:466:12:466:34 | call to apply(_:) | | file://:0:0:0:0 | Int | +| overload_resolution.swift:466:12:466:34 | call to apply(_:) | | file://:0:0:0:0 | String | | overload_resolution.swift:466:22:466:22 | x | | file://:0:0:0:0 | Int | | overload_resolution.swift:466:27:466:27 | x | | file://:0:0:0:0 | Int | | overload_resolution.swift:466:31:466:31 | 1 | | file://:0:0:0:0 | Int | +| overload_resolution.swift:467:7:467:7 | r2 | | file://:0:0:0:0 | Int | +| overload_resolution.swift:467:7:467:7 | r2 | | file://:0:0:0:0 | Int | | overload_resolution.swift:467:7:467:7 | r2 | | file://:0:0:0:0 | String | | overload_resolution.swift:467:7:467:7 | r2 | | file://:0:0:0:0 | String | | overload_resolution.swift:467:12:467:12 | p | | overload_resolution.swift:444:1:462:1 | Processor | +| overload_resolution.swift:467:12:467:36 | call to apply(_:) | | file://:0:0:0:0 | Int | | overload_resolution.swift:467:12:467:36 | call to apply(_:) | | file://:0:0:0:0 | String | | overload_resolution.swift:467:22:467:22 | s | | file://:0:0:0:0 | String | | overload_resolution.swift:467:27:467:27 | s | | file://:0:0:0:0 | String | | overload_resolution.swift:467:31:467:31 | ! | | file://:0:0:0:0 | String | | overload_resolution.swift:468:7:468:7 | r3 | | file://:0:0:0:0 | Int | | overload_resolution.swift:468:7:468:7 | r3 | | file://:0:0:0:0 | Int | +| overload_resolution.swift:468:7:468:7 | r3 | | file://:0:0:0:0 | String | +| overload_resolution.swift:468:7:468:7 | r3 | | file://:0:0:0:0 | String | | overload_resolution.swift:468:12:468:12 | p | | overload_resolution.swift:444:1:462:1 | Processor | | overload_resolution.swift:468:12:468:37 | call to apply(_:) | | file://:0:0:0:0 | Int | +| overload_resolution.swift:468:12:468:37 | call to apply(_:) | | file://:0:0:0:0 | String | | overload_resolution.swift:468:22:468:22 | x | | file://:0:0:0:0 | Int | | overload_resolution.swift:468:25:468:25 | y | | file://:0:0:0:0 | Int | | overload_resolution.swift:468:30:468:30 | x | | file://:0:0:0:0 | Int | @@ -1482,10 +1598,12 @@ inferType | overload_resolution.swift:494:12:494:22 | call to get(_:) | | file://:0:0:0:0 | Int | | overload_resolution.swift:494:19:494:19 | a | | file://:0:0:0:0 | String | | protocols.swift:5:8:5:8 | self | | protocols.swift:3:1:6:1 | Self [Shape] | +| protocols.swift:9:7:9:7 | radius | | file://:0:0:0:0 | Double | | protocols.swift:9:7:9:7 | self | | protocols.swift:8:1:20:1 | Circle | | protocols.swift:9:7:9:7 | self | | protocols.swift:8:1:20:1 | Circle | | protocols.swift:9:7:9:7 | self | | protocols.swift:8:1:20:1 | Circle | | protocols.swift:9:7:9:7 | value | | file://:0:0:0:0 | Double | +| protocols.swift:9:7:9:16 | ... as ... | | file://:0:0:0:0 | Double | | protocols.swift:12:3:12:3 | self | | protocols.swift:8:1:20:1 | Circle | | protocols.swift:12:8:12:16 | radius | | file://:0:0:0:0 | Double | | protocols.swift:13:5:13:5 | self | | protocols.swift:8:1:20:1 | Circle | @@ -1498,10 +1616,14 @@ inferType | protocols.swift:23:7:23:7 | self | | protocols.swift:22:1:36:1 | Rectangle | | protocols.swift:23:7:23:7 | self | | protocols.swift:22:1:36:1 | Rectangle | | protocols.swift:23:7:23:7 | value | | file://:0:0:0:0 | Double | +| protocols.swift:23:7:23:7 | width | | file://:0:0:0:0 | Double | +| protocols.swift:23:7:23:15 | ... as ... | | file://:0:0:0:0 | Double | +| protocols.swift:24:7:24:7 | height | | file://:0:0:0:0 | Double | | protocols.swift:24:7:24:7 | self | | protocols.swift:22:1:36:1 | Rectangle | | protocols.swift:24:7:24:7 | self | | protocols.swift:22:1:36:1 | Rectangle | | protocols.swift:24:7:24:7 | self | | protocols.swift:22:1:36:1 | Rectangle | | protocols.swift:24:7:24:7 | value | | file://:0:0:0:0 | Double | +| protocols.swift:24:7:24:16 | ... as ... | | file://:0:0:0:0 | Double | | protocols.swift:27:3:27:3 | self | | protocols.swift:22:1:36:1 | Rectangle | | protocols.swift:27:8:27:15 | width | | file://:0:0:0:0 | Double | | protocols.swift:27:23:27:31 | height | | file://:0:0:0:0 | Double | @@ -1557,14 +1679,18 @@ inferType | protocols.swift:86:8:86:8 | self | | protocols.swift:84:1:87:1 | Self [Printable] | | protocols.swift:91:8:91:8 | self | | protocols.swift:89:1:92:1 | Self [Identifiable] | | protocols.swift:94:7:94:7 | self | | protocols.swift:94:1:113:1 | Entity | +| protocols.swift:95:7:95:7 | name | | file://:0:0:0:0 | String | | protocols.swift:95:7:95:7 | self | | protocols.swift:94:1:113:1 | Entity | | protocols.swift:95:7:95:7 | self | | protocols.swift:94:1:113:1 | Entity | | protocols.swift:95:7:95:7 | self | | protocols.swift:94:1:113:1 | Entity | | protocols.swift:95:7:95:7 | value | | file://:0:0:0:0 | String | +| protocols.swift:95:7:95:14 | ... as ... | | file://:0:0:0:0 | String | +| protocols.swift:96:7:96:7 | entityId | | file://:0:0:0:0 | Int | | protocols.swift:96:7:96:7 | self | | protocols.swift:94:1:113:1 | Entity | | protocols.swift:96:7:96:7 | self | | protocols.swift:94:1:113:1 | Entity | | protocols.swift:96:7:96:7 | self | | protocols.swift:94:1:113:1 | Entity | | protocols.swift:96:7:96:7 | value | | file://:0:0:0:0 | Int | +| protocols.swift:96:7:96:18 | ... as ... | | file://:0:0:0:0 | Int | | protocols.swift:99:3:99:3 | self | | protocols.swift:94:1:113:1 | Entity | | protocols.swift:99:8:99:14 | name | | file://:0:0:0:0 | String | | protocols.swift:99:22:99:32 | entityId | | file://:0:0:0:0 | Int | @@ -1616,11 +1742,15 @@ inferType | type_constraints.swift:33:7:33:7 | self | | type_constraints.swift:32:1:51:1 | TaggedItem | | type_constraints.swift:33:7:33:7 | self | | type_constraints.swift:32:1:51:1 | TaggedItem | | type_constraints.swift:33:7:33:7 | self | | type_constraints.swift:32:1:51:1 | TaggedItem | +| type_constraints.swift:33:7:33:7 | tag | | file://:0:0:0:0 | String | | type_constraints.swift:33:7:33:7 | value | | file://:0:0:0:0 | String | +| type_constraints.swift:33:7:33:13 | ... as ... | | file://:0:0:0:0 | String | +| type_constraints.swift:34:7:34:7 | priority | | file://:0:0:0:0 | Int | | type_constraints.swift:34:7:34:7 | self | | type_constraints.swift:32:1:51:1 | TaggedItem | | type_constraints.swift:34:7:34:7 | self | | type_constraints.swift:32:1:51:1 | TaggedItem | | type_constraints.swift:34:7:34:7 | self | | type_constraints.swift:32:1:51:1 | TaggedItem | | type_constraints.swift:34:7:34:7 | value | | file://:0:0:0:0 | Int | +| type_constraints.swift:34:7:34:18 | ... as ... | | file://:0:0:0:0 | Int | | type_constraints.swift:37:3:37:3 | self | | type_constraints.swift:32:1:51:1 | TaggedItem | | type_constraints.swift:37:8:37:13 | tag | | file://:0:0:0:0 | String | | type_constraints.swift:37:21:37:31 | priority | | file://:0:0:0:0 | Int | @@ -1636,7 +1766,6 @@ inferType | type_constraints.swift:49:12:49:12 | self | | type_constraints.swift:32:1:51:1 | TaggedItem | | type_constraints.swift:54:46:54:54 | item | | type_constraints.swift:54:18:54:36 | T | | type_constraints.swift:55:10:55:10 | item | | type_constraints.swift:54:18:54:36 | T | -| type_constraints.swift:55:10:55:23 | call to display() | | file://:0:0:0:0 | String | | type_constraints.swift:59:28:59:33 | a | | type_constraints.swift:59:25:59:25 | T | | type_constraints.swift:59:36:59:41 | b | | type_constraints.swift:59:25:59:25 | T | | type_constraints.swift:60:10:60:10 | a | | type_constraints.swift:59:25:59:25 | T | @@ -1652,6 +1781,7 @@ inferType | type_constraints.swift:71:7:71:7 | self | | type_constraints.swift:71:1:90:1 | SortedPair | | type_constraints.swift:71:7:71:7 | self | T | type_constraints.swift:71:18:71:21 | T | | type_constraints.swift:71:7:71:7 | self | U | type_constraints.swift:71:33:71:36 | U | +| type_constraints.swift:72:7:72:7 | first | | type_constraints.swift:71:18:71:21 | T | | type_constraints.swift:72:7:72:7 | self | | type_constraints.swift:71:1:90:1 | SortedPair | | type_constraints.swift:72:7:72:7 | self | | type_constraints.swift:71:1:90:1 | SortedPair | | type_constraints.swift:72:7:72:7 | self | | type_constraints.swift:71:1:90:1 | SortedPair | @@ -1662,6 +1792,8 @@ inferType | type_constraints.swift:72:7:72:7 | self | U | type_constraints.swift:71:33:71:36 | U | | type_constraints.swift:72:7:72:7 | self | U | type_constraints.swift:71:33:71:36 | U | | type_constraints.swift:72:7:72:7 | value | | type_constraints.swift:71:18:71:21 | T | +| type_constraints.swift:72:7:72:15 | ... as ... | | type_constraints.swift:71:18:71:21 | T | +| type_constraints.swift:73:7:73:7 | second | | type_constraints.swift:71:33:71:36 | U | | type_constraints.swift:73:7:73:7 | self | | type_constraints.swift:71:1:90:1 | SortedPair | | type_constraints.swift:73:7:73:7 | self | | type_constraints.swift:71:1:90:1 | SortedPair | | type_constraints.swift:73:7:73:7 | self | | type_constraints.swift:71:1:90:1 | SortedPair | @@ -1672,6 +1804,7 @@ inferType | type_constraints.swift:73:7:73:7 | self | U | type_constraints.swift:71:33:71:36 | U | | type_constraints.swift:73:7:73:7 | self | U | type_constraints.swift:71:33:71:36 | U | | type_constraints.swift:73:7:73:7 | value | | type_constraints.swift:71:33:71:36 | U | +| type_constraints.swift:73:7:73:16 | ... as ... | | type_constraints.swift:71:33:71:36 | U | | type_constraints.swift:76:3:76:3 | self | | type_constraints.swift:71:1:90:1 | SortedPair | | type_constraints.swift:76:3:76:3 | self | T | type_constraints.swift:71:18:71:21 | T | | type_constraints.swift:76:3:76:3 | self | U | type_constraints.swift:71:33:71:36 | U | @@ -1765,7 +1898,9 @@ inferType | type_constraints.swift:156:7:156:7 | self | | type_constraints.swift:155:1:167:1 | Vehicle | | type_constraints.swift:156:7:156:7 | self | | type_constraints.swift:155:1:167:1 | Vehicle | | type_constraints.swift:156:7:156:7 | self | | type_constraints.swift:155:1:167:1 | Vehicle | +| type_constraints.swift:156:7:156:7 | speed | | file://:0:0:0:0 | Int | | type_constraints.swift:156:7:156:7 | value | | file://:0:0:0:0 | Int | +| type_constraints.swift:156:7:156:15 | ... as ... | | file://:0:0:0:0 | Int | | type_constraints.swift:159:3:159:3 | self | | type_constraints.swift:155:1:167:1 | Vehicle | | type_constraints.swift:159:8:159:15 | speed | | file://:0:0:0:0 | Int | | type_constraints.swift:160:5:160:5 | self | | type_constraints.swift:155:1:167:1 | Vehicle | @@ -1793,7 +1928,6 @@ inferType | type_constraints.swift:199:12:199:12 | hauling | | file://:0:0:0:0 | String | | type_constraints.swift:204:34:204:39 | v | | type_constraints.swift:204:22:204:25 | T | | type_constraints.swift:205:10:205:10 | v | | type_constraints.swift:204:22:204:25 | T | -| type_constraints.swift:205:10:205:21 | call to describe() | | file://:0:0:0:0 | String | | type_constraints.swift:209:7:209:7 | car | | type_constraints.swift:169:1:184:1 | Car | | type_constraints.swift:209:7:209:7 | car | | type_constraints.swift:169:1:184:1 | Car | | type_constraints.swift:209:13:209:27 | call to Car.init(speed:) | | type_constraints.swift:169:1:184:1 | Car | @@ -1840,7 +1974,6 @@ inferType | type_constraints.swift:251:17:251:25 | item | | type_constraints.swift:228:20:228:20 | T | | type_constraints.swift:252:12:252:12 | self | | type_constraints.swift:228:1:240:1 | Accumulator | | type_constraints.swift:252:12:252:12 | self | T | type_constraints.swift:228:20:228:20 | T | -| type_constraints.swift:252:12:252:49 | call to contains(where:) | | file://:0:0:0:0 | Bool | | type_constraints.swift:252:35:252:35 | $0 | | type_constraints.swift:228:20:228:20 | T | | type_constraints.swift:252:37:252:37 | $0 | | type_constraints.swift:228:20:228:20 | T | | type_constraints.swift:252:43:252:43 | item | | type_constraints.swift:228:20:228:20 | T | diff --git a/swift/ql/test/library-tests/type-inference/type-inference-ql.ql b/swift/ql/test/library-tests/type-inference/type-inference-ql.ql index b418e04b2c6b..b4f69cd587b9 100644 --- a/swift/ql/test/library-tests/type-inference/type-inference-ql.ql +++ b/swift/ql/test/library-tests/type-inference/type-inference-ql.ql @@ -9,6 +9,26 @@ query predicate inferType(AstNode n, TypePath path, Type t) { toBeTested(n) } +module ResolveTest implements TestSig { + string getARelevantTag() { result = "target" } + + predicate hasActualResult(Location location, string element, string tag, string value) { + exists(AstNode source, Decl target | + location = source.getLocation() and + element = source.toString() and + target = + [ + TypeInference::getCallTarget(source).(Decl), + source.(EnumElementPattern).getElement() + ] and + declHasName(target, value) and + tag = "target" and + toBeTested(source) and + toBeTested(target) + ) + } +} + module TypeTest implements TestSig { string getARelevantTag() { result = "type" } diff --git a/swift/ql/test/library-tests/type-inference/type-inference.ql b/swift/ql/test/library-tests/type-inference/type-inference.ql index f2ff25b2a767..6b1e4314aed8 100644 --- a/swift/ql/test/library-tests/type-inference/type-inference.ql +++ b/swift/ql/test/library-tests/type-inference/type-inference.ql @@ -1,6 +1,27 @@ import swift import Common +module ResolveTest implements TestSig { + string getARelevantTag() { result = "target" } + + predicate hasActualResult(Location location, string element, string tag, string value) { + exists(AstNode source, Decl target | + location = source.getLocation() and + element = source.toString() and + target = + [ + source.(CallExpr).getStaticTarget().(Decl), + source.(MethodLookupExpr).getMember(), + source.(EnumElementPattern).getElement() + ] and + declHasName(target, value) and + tag = "target" and + toBeTested(source) and + toBeTested(target) + ) + } +} + private Type getTypeAt(Type t, string path) { path = "" and result = t.getUnderlyingType()