From 2af1a3ac323d610dc0f80b952b38baa4fc70d3c7 Mon Sep 17 00:00:00 2001 From: Tom Hvitved Date: Mon, 9 Nov 2026 20:28:56 +0100 Subject: [PATCH 1/4] Share type inference constraint matching through matching positions Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- .../typeinference/internal/TypeInference.qll | 256 ++++++++---------- 1 file changed, 117 insertions(+), 139 deletions(-) diff --git a/shared/typeinference/codeql/typeinference/internal/TypeInference.qll b/shared/typeinference/codeql/typeinference/internal/TypeInference.qll index 1bc9591607e8..1b3a5b53aecd 100644 --- a/shared/typeinference/codeql/typeinference/internal/TypeInference.qll +++ b/shared/typeinference/codeql/typeinference/internal/TypeInference.qll @@ -1357,7 +1357,7 @@ module Make1 Input1> { ) } - private module AccessBaseType { + private module ConstraintMatching { /** * 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 @@ -1381,27 +1381,95 @@ module Make1 Input1> { ) } - private newtype TRelevantTarget = - MkRelevantTarget(Declaration target, AccessPosition apos) { + private newtype TMatchingPosition = + MkArgumentPosition(Declaration target, AccessPosition apos) { argRootTypeSatisfiesTargetTypeCand(_, target, apos, _, _) + } or + MkTypeArgumentPosition( + Declaration target, TypeParameter constrainedTp, TypeMention constraint + ) { + typeParameterHasConstraint(target, constrainedTp, constraint) } - private class RelevantTarget extends MkRelevantTarget { + /** A position whose inferred type should satisfy a declared type or type constraint. */ + abstract private class MatchingPosition extends TMatchingPosition { + abstract Declaration getTarget(); + + abstract Type getTypeAt(TypePath path); + + bindingset[a, e] + abstract Type getInferredType(Access a, AccessEnvironment e, TypePath path); + + predicate relevantAccess(Access a, AccessEnvironment e) { + this.getTarget() = a.getTarget(e) + } + + abstract predicate typeParameterAt(TypePath path, TypeParameter tp); + + abstract string toString(); + + Location getLocation() { result = this.getTarget().getLocation() } + } + + private class ArgumentPosition extends MatchingPosition, MkArgumentPosition { Declaration target; AccessPosition apos; - RelevantTarget() { this = MkRelevantTarget(target, apos) } + ArgumentPosition() { this = MkArgumentPosition(target, apos) } - Type getTypeAt(TypePath path) { + override Declaration getTarget() { result = target } + + override Type getTypeAt(TypePath path) { exists(DeclarationPosition dpos | accessDeclarationPositionMatch(apos, dpos) and result = target.getDeclaredType(dpos, path) ) } - string toString() { result = target.toString() + ", " + apos.toString() } + bindingset[a, e] + override Type getInferredType(Access a, AccessEnvironment e, TypePath path) { + result = a.getInferredType(e, apos, path) + } + + override predicate relevantAccess(Access a, AccessEnvironment e) { + exists(Type t | + accessTargetsWithArgRootType(a, e, target, apos, t) and + argRootTypeSatisfiesTargetTypeCand(t, target, apos, _, _) + ) + } + + override predicate typeParameterAt(TypePath path, TypeParameter tp) { + argRootTypeSatisfiesTargetTypeCand(_, target, apos, tp, path) + } + + override string toString() { result = target.toString() + ", " + apos.toString() } + } + + private class TypeArgumentPosition extends MatchingPosition, MkTypeArgumentPosition { + Declaration target; + TypeParameter constrainedTp; + TypeMention constraint; + + TypeArgumentPosition() { + this = MkTypeArgumentPosition(target, constrainedTp, constraint) + } + + override Declaration getTarget() { result = target } - Location getLocation() { result = target.getLocation() } + override Type getTypeAt(TypePath path) { result = constraint.getTypeAt(path) } + + bindingset[a, e] + override Type getInferredType(Access a, AccessEnvironment e, TypePath path) { + typeMatch(a, e, _, path, result, constrainedTp) + } + + override predicate typeParameterAt(TypePath path, TypeParameter tp) { + typeParameterConstraintHasTypeParameter(target, constrainedTp, constraint, path, tp) + } + + override string toString() { + result = constrainedTp.toString() + ": " + constraint.toString() + } } pragma[nomagic] @@ -1412,57 +1480,56 @@ module Make1 Input1> { t = a.getInferredType(e, apos, TypePath::nil()) } - private predicate relevantAccessTarget( - Access a, AccessPosition apos, AccessEnvironment e, Declaration target - ) { - exists(Type t | - accessTargetsWithArgRootType(a, e, target, apos, t) and - argRootTypeSatisfiesTargetTypeCand(t, target, apos, _, _) - ) - } - private newtype TRelevantAccess = - MkRelevantAccess(Access a, AccessPosition apos, AccessEnvironment e) { - relevantAccessTarget(a, apos, e, _) + MkRelevantAccess(Access a, MatchingPosition pos, AccessEnvironment e) { + pos.relevantAccess(a, e) } private class RelevantAccess extends MkRelevantAccess { Access a; - AccessPosition apos; + MatchingPosition pos; AccessEnvironment e; - RelevantAccess() { this = MkRelevantAccess(a, apos, e) } + RelevantAccess() { this = MkRelevantAccess(a, pos, e) } - RelevantTarget getTarget() { - exists(Declaration target | - relevantAccessTarget(a, apos, e, target) and - result = MkRelevantTarget(target, apos) - ) - } + MatchingPosition getConstraint() { result = pos } pragma[nomagic] - Type getTypeAt(TypePath path) { result = a.getInferredType(e, apos, path) } + Type getTypeAt(TypePath path) { result = pos.getInferredType(a, e, path) } - string toString() { result = a.toString() + ", " + apos.toString() } + string toString() { result = a.toString() + ", " + pos.toString() } Location getLocation() { result = a.getLocation() } } private module SatisfiesParameterConstraintInput implements - SatisfiesConstraintInputSig + SatisfiesConstraintWithTypeMatchingInputSig { - predicate relevantConstraint(RelevantAccess at, RelevantTarget constraint) { - constraint = at.getTarget() + predicate relevantConstraint(RelevantAccess at, MatchingPosition constraint) { + constraint = at.getConstraint() + } + + class TypeMatchingContext = Access; + + TypeMatchingContext getTypeMatchingContext(RelevantAccess at) { + // Only type parameter constraints use inferred type arguments for disambiguation. + at = MkRelevantAccess(result, any(TypeArgumentPosition pos), _) + } + + pragma[nomagic] + predicate typeMatch(TypeMatchingContext ctx, TypeParameter tp, TypePath path, Type t) { + typeMatch(ctx, _, _, path, t, tp) } } private module SatisfiesParameterConstraint = - SatisfiesConstraint; + SatisfiesConstraintWithTypeMatching; /** * 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 some argument or type argument position matches the type parameter + * `tp` in a declared type or type parameter constraint of `target`. * * For example, in * @@ -1495,13 +1562,12 @@ module Make1 Input1> { predicate baseTypeMatch( Access a, AccessEnvironment e, Declaration target, TypePath path, Type t, TypeParameter tp ) { - exists(AccessPosition apos, TypePath pathToTp | - argRootTypeSatisfiesTargetTypeCand(_, target, pragma[only_bind_into](apos), tp, pathToTp) and + hasNotTypeArgument(a, target, tp) and + exists(MatchingPosition pos, TypePath pathToTp | + target = pos.getTarget() and + pos.typeParameterAt(pathToTp, tp) and SatisfiesParameterConstraint::satisfiesConstraint(MkRelevantAccess(a, - pragma[only_bind_into](apos), e), - MkRelevantTarget(target, pragma[only_bind_into](apos)), pathToTp.appendInverse(path), - t) and - hasNotTypeArgument(a, target, tp) + pragma[only_bind_into](pos), e), pos, pathToTp.appendInverse(path), t) ) } @@ -1514,8 +1580,8 @@ module Make1 Input1> { | argRootTypeSatisfiesTargetTypeCand(_, target, pragma[only_bind_into](apos), tp, pathToTp) and SatisfiesParameterConstraint::satisfiesConstraintAtTypeParameter(MkRelevantAccess(a, - pragma[only_bind_into](apos), e), - MkRelevantTarget(target, pragma[only_bind_into](apos)), pathToTypeParamInConstraint, + MkArgumentPosition(target, pragma[only_bind_into](apos)), e), + MkArgumentPosition(target, pragma[only_bind_into](apos)), pathToTypeParamInConstraint, pathToTypeParamInSub) and hasNotTypeArgument(a, target, tp) | @@ -1578,66 +1644,6 @@ module Make1 Input1> { ) ) } - } - - private module AccessConstraint { - private predicate relevantAccessConstraint( - Access a, AccessEnvironment e, Declaration target, TypeParameter constrainedTp, - TypeMention constraint - ) { - target = a.getTarget(e) and - typeParameterHasConstraint(target, constrainedTp, constraint) - } - - private newtype TRelevantAccess = - MkRelevantAccess(Access a, AccessEnvironment e, TypeParameter constrainedTp) { - relevantAccessConstraint(a, e, _, constrainedTp, _) - } - - private class RelevantAccess extends MkRelevantAccess { - Access a; - AccessEnvironment e; - TypeParameter constrainedTp; - - RelevantAccess() { this = MkRelevantAccess(a, e, constrainedTp) } - - pragma[nomagic] - Type getTypeAt(TypePath path) { typeMatch(a, e, _, path, result, constrainedTp) } - - /** Gets the constraint that this relevant access should satisfy. */ - TypeMention getConstraint(Declaration target) { - relevantAccessConstraint(a, e, target, constrainedTp, result) - } - - string toString() { - result = a.toString() + ", " + e.toString() + ", " + constrainedTp.toString() - } - - Location getLocation() { result = a.getLocation() } - } - - private module SatisfiesTypeParameterConstraintInput implements - SatisfiesConstraintWithTypeMatchingInputSig - { - predicate relevantConstraint(RelevantAccess at, TypeMention constraint) { - constraint = at.getConstraint(_) - } - - 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 SatisfiesTypeParameterConstraint = - SatisfiesConstraintWithTypeMatching; pragma[nomagic] predicate argSatisfiesConstraintAtTypeParameter( @@ -1645,29 +1651,16 @@ module Make1 Input1> { TypeMention constraint, TypePath pathToTypeParamInConstraint, TypePath pathToTypeParamInSub ) { - exists(RelevantAccess ra, TypeParameter constrainedTp | - ra = MkRelevantAccess(a, e, constrainedTp) and - relevantAccessConstraint(a, e, target, constrainedTp, constraint) and - SatisfiesTypeParameterConstraint::satisfiesConstraintAtTypeParameter(ra, constraint, - pathToTypeParamInConstraint, pathToTypeParamInSub) and + exists(TypeParameter constrainedTp, MatchingPosition pos | + pos = MkTypeArgumentPosition(target, constrainedTp, constraint) and + SatisfiesParameterConstraint::satisfiesConstraintAtTypeParameter(MkRelevantAccess(a, + pos, e), pos, pathToTypeParamInConstraint, pathToTypeParamInSub) and exists(DeclarationPosition dpos | accessDeclarationPositionMatch(apos, dpos) and constrainedTp = target.getDeclaredType(dpos, prefix) ) ) } - - pragma[nomagic] - predicate satisfiesConstraint( - Access a, AccessEnvironment e, Declaration target, TypeParameter constrainedTp, - TypeMention constraint, TypePath path, Type t - ) { - exists(RelevantAccess ra | - ra = MkRelevantAccess(a, e, constrainedTp) and - relevantAccessConstraint(a, e, target, constrainedTp, constraint) and - SatisfiesTypeParameterConstraint::satisfiesConstraint(ra, constraint, path, t) - ) - } } /** @@ -1729,18 +1722,6 @@ module Make1 Input1> { constrainedTp != tp } - pragma[nomagic] - private predicate typeConstraintBaseTypeMatch( - Access a, AccessEnvironment e, Declaration target, TypePath path, Type t, TypeParameter tp - ) { - hasNotTypeArgument(a, target, tp) and - exists(TypeMention constraint, TypeParameter constrainedTp, TypePath pathToTp | - typeParameterConstraintHasTypeParameter(target, constrainedTp, constraint, pathToTp, tp) and - AccessConstraint::satisfiesConstraint(a, e, target, constrainedTp, constraint, - pathToTp.appendInverse(path), t) - ) - } - /** * Holds if type parameter `tp`, which is in scope in `target`, can be matched * to have type `t` at `path` via the inferred argument types of `a`. @@ -1756,11 +1737,8 @@ 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 hierarchy - AccessBaseType::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) + // We can infer the type of `tp` via the type hierarchy or a type constraint + ConstraintMatching::baseTypeMatch(a, e, target, path, t, tp) } /** @@ -1823,7 +1801,7 @@ module Make1 Input1> { Declaration target, TypePath prefix, TypePath requiredPrefix, TypePath suffix, TypeParameter tp | - AccessBaseType::baseTypeMatchAtTypeParameter(a, e, apos, target, tp, prefix, + ConstraintMatching::baseTypeMatchAtTypeParameter(a, e, apos, target, tp, prefix, requiredPrefix) and typeMatch(a, e, target, requiredPrefix.appendInverse(suffix), result, tp) and path = prefix.append(suffix) @@ -1833,7 +1811,7 @@ module Make1 Input1> { Declaration target, TypePath prefix, TypeMention constraint, TypePath pathToTypeParamInConstraint, TypePath pathToTypeParamInSub | - AccessConstraint::argSatisfiesConstraintAtTypeParameter(a, e, target, apos, prefix, + ConstraintMatching::argSatisfiesConstraintAtTypeParameter(a, e, target, apos, prefix, constraint, pathToTypeParamInConstraint, pathToTypeParamInSub) | exists(TypePath suffix | From a331d8aa9a43e9a382a167cab1c876ae1130f296 Mon Sep 17 00:00:00 2001 From: Tom Hvitved Date: Mon, 9 Nov 2026 20:35:35 +0100 Subject: [PATCH 2/4] Share contextual constraint matching and path alignment Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- .../typeinference/internal/TypeInference.qll | 161 ++++++------------ 1 file changed, 52 insertions(+), 109 deletions(-) diff --git a/shared/typeinference/codeql/typeinference/internal/TypeInference.qll b/shared/typeinference/codeql/typeinference/internal/TypeInference.qll index 1b3a5b53aecd..7130085d0679 100644 --- a/shared/typeinference/codeql/typeinference/internal/TypeInference.qll +++ b/shared/typeinference/codeql/typeinference/internal/TypeInference.qll @@ -1392,7 +1392,7 @@ module Make1 Input1> { } /** A position whose inferred type should satisfy a declared type or type constraint. */ - abstract private class MatchingPosition extends TMatchingPosition { + abstract class MatchingPosition extends TMatchingPosition { abstract Declaration getTarget(); abstract Type getTypeAt(TypePath path); @@ -1406,6 +1406,18 @@ module Make1 Input1> { abstract predicate typeParameterAt(TypePath path, TypeParameter tp); + /** Holds if `prefix` leads from `apos` to the inferred type being constrained. */ + abstract predicate accessPath(AccessPosition apos, TypePath prefix); + + /** Gets a concrete type that can be propagated from this constraint to the access. */ + Type getConcreteTypeAt(TypePath path) { none() } + + /** Holds if the type parameter at `path` can supply contextual types for `a`. */ + predicate contextualTypeParameterAt(Access a, TypePath path, TypeParameter tp) { + this.getTarget() = a.getTarget(_) and + this.typeParameterAt(path, tp) + } + abstract string toString(); Location getLocation() { result = this.getTarget().getLocation() } @@ -1442,6 +1454,16 @@ module Make1 Input1> { argRootTypeSatisfiesTargetTypeCand(_, target, apos, tp, path) } + override predicate accessPath(AccessPosition accessPos, TypePath prefix) { + accessPos = apos and + prefix = TypePath::nil() + } + + override predicate contextualTypeParameterAt(Access a, TypePath path, TypeParameter tp) { + this.typeParameterAt(path, tp) and + hasNotTypeArgument(a, target, tp) + } + override string toString() { result = target.toString() + ", " + apos.toString() } } @@ -1467,6 +1489,18 @@ module Make1 Input1> { typeParameterConstraintHasTypeParameter(target, constrainedTp, constraint, path, tp) } + override predicate accessPath(AccessPosition apos, TypePath prefix) { + exists(DeclarationPosition dpos | + accessDeclarationPositionMatch(apos, dpos) and + constrainedTp = target.getDeclaredType(dpos, prefix) + ) + } + + override Type getConcreteTypeAt(TypePath path) { + result = this.getTypeAt(path) and + not result instanceof TypeParameter + } + override string toString() { result = constrainedTp.toString() + ": " + constraint.toString() } @@ -1571,95 +1605,20 @@ module Make1 Input1> { ) } - predicate baseTypeMatchAtTypeParameter( - Access a, AccessEnvironment e, AccessPosition apos, Declaration target, TypeParameter tp, - TypePath prefix, TypePath requiredPrefix - ) { - exists( - TypePath pathToTypeParamInConstraint, TypePath pathToTp, TypePath pathToTypeParamInSub - | - argRootTypeSatisfiesTargetTypeCand(_, target, pragma[only_bind_into](apos), tp, pathToTp) and - SatisfiesParameterConstraint::satisfiesConstraintAtTypeParameter(MkRelevantAccess(a, - MkArgumentPosition(target, pragma[only_bind_into](apos)), e), - MkArgumentPosition(target, pragma[only_bind_into](apos)), pathToTypeParamInConstraint, - pathToTypeParamInSub) and - hasNotTypeArgument(a, target, tp) - | - /* - * Example: - * - * ```swift - * class Base { - * init(_ value: B) {} - * } - * - * class Derived: Base<[D]> { - * init(_ value: D) { super.init([value]) } - * } - * - * func foo(_ value: T, _ base: Base) { } - * - * foo([2], Derived()) - * ``` - * - * - tp = T (bound by `foo`) - * - prefix = pathToTypeParamInSub = "D" - * - requiredPrefix = "Element" - * - pathToTypeParamInConstraint = "B.Element" - * - pathToTp = "B" - */ - - pathToTypeParamInConstraint = pathToTp.appendInverse(requiredPrefix) and - prefix = pathToTypeParamInSub - or - /* - * Example: - * - * ```swift - * class Base { - * init(_ value: B) {} - * } - * - * class Derived: Base { - * override init(_ value: D) { super.init(value) } - * } - * - * func foo(_ value: T, _ base: Base) {} - * - * foo(2, Derived(Optional.none)) - * ``` - * - * - tp = T (bound by `foo`) - * - prefix = "D.Wrapped" - * - pathToTypeParamInSub = "D" - * - requiredPrefix = "" - * - pathToTypeParamInConstraint = "B" - * - pathToTp = "B.Wrapped" - */ - - exists(TypePath path0 | - pathToTp = pathToTypeParamInConstraint.appendInverse(path0) and - prefix = pathToTypeParamInSub.append(path0) and - requiredPrefix = TypePath::nil() - ) - ) - } - + /** + * Holds if a constraint on an argument or type argument reaches a type + * parameter in the inferred type at `pathToTypeParamInSub`. `prefix` is + * the path from `apos` to the inferred type being constrained. + */ pragma[nomagic] predicate argSatisfiesConstraintAtTypeParameter( - Access a, AccessEnvironment e, Declaration target, AccessPosition apos, TypePath prefix, - TypeMention constraint, TypePath pathToTypeParamInConstraint, + Access a, AccessEnvironment e, AccessPosition apos, TypePath prefix, + MatchingPosition constraint, TypePath pathToTypeParamInConstraint, TypePath pathToTypeParamInSub ) { - exists(TypeParameter constrainedTp, MatchingPosition pos | - pos = MkTypeArgumentPosition(target, constrainedTp, constraint) and - SatisfiesParameterConstraint::satisfiesConstraintAtTypeParameter(MkRelevantAccess(a, - pos, e), pos, pathToTypeParamInConstraint, pathToTypeParamInSub) and - exists(DeclarationPosition dpos | - accessDeclarationPositionMatch(apos, dpos) and - constrainedTp = target.getDeclaredType(dpos, prefix) - ) - ) + constraint.accessPath(apos, prefix) and + SatisfiesParameterConstraint::satisfiesConstraintAtTypeParameter(MkRelevantAccess(a, + constraint, e), constraint, pathToTypeParamInConstraint, pathToTypeParamInSub) } } @@ -1798,21 +1757,11 @@ module Make1 Input1> { ) or exists( - Declaration target, TypePath prefix, TypePath requiredPrefix, TypePath suffix, - TypeParameter tp - | - ConstraintMatching::baseTypeMatchAtTypeParameter(a, e, apos, target, tp, prefix, - requiredPrefix) and - typeMatch(a, e, target, requiredPrefix.appendInverse(suffix), result, tp) and - path = prefix.append(suffix) - ) - or - exists( - Declaration target, TypePath prefix, TypeMention constraint, + TypePath prefix, ConstraintMatching::MatchingPosition constraint, TypePath pathToTypeParamInConstraint, TypePath pathToTypeParamInSub | - ConstraintMatching::argSatisfiesConstraintAtTypeParameter(a, e, target, apos, prefix, - constraint, pathToTypeParamInConstraint, pathToTypeParamInSub) + ConstraintMatching::argSatisfiesConstraintAtTypeParameter(a, e, apos, prefix, constraint, + pathToTypeParamInConstraint, pathToTypeParamInSub) | exists(TypePath suffix | /* @@ -1841,18 +1790,12 @@ module Make1 Input1> { * That is, it allows us to infer that the type of `x` is `MyThing`. */ - result = constraint.getTypeAt(pathToTypeParamInConstraint.appendInverse(suffix)) and - not result instanceof TypeParameter and + result = constraint.getConcreteTypeAt(pathToTypeParamInConstraint.appendInverse(suffix)) and path = prefix.append(pathToTypeParamInSub.append(suffix)) ) or exists(TypeParameter tp, TypePath suffix, TypePath mid, TypePath pathToTp | - exists(TypeParameter constrainedTp, DeclarationPosition dpos | - typeParameterConstraintHasTypeParameter(target, constrainedTp, constraint, pathToTp, - tp) and - accessDeclarationPositionMatch(apos, dpos) and - constrainedTp = target.getDeclaredType(dpos, _) - ) + constraint.contextualTypeParameterAt(a, pathToTp, tp) | /* * Example: @@ -1887,7 +1830,7 @@ module Make1 Input1> { * That is, it allows us to infer that the type of `y` is `MyThing>`. */ - typeMatch(a, e, target, suffix, result, tp) and + typeMatch(a, e, constraint.getTarget(), suffix, result, tp) and pathToTp = pathToTypeParamInConstraint.appendInverse(mid) and path = prefix.append(pathToTypeParamInSub.append(mid).append(suffix)) or @@ -1925,7 +1868,7 @@ module Make1 Input1> { */ pathToTypeParamInConstraint = pathToTp.appendInverse(mid) and - typeMatch(a, e, target, mid.appendInverse(suffix), result, tp) and + typeMatch(a, e, constraint.getTarget(), mid.appendInverse(suffix), result, tp) and path = prefix.append(pathToTypeParamInSub.append(suffix)) ) ) From 542097d97691a860ec35256217f2d67e7a8c8a38 Mon Sep 17 00:00:00 2001 From: Tom Hvitved Date: Mon, 9 Nov 2026 21:24:17 +0100 Subject: [PATCH 3/4] Add recursive variance inference test cases Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- .../type-inference/variance.swift | 69 ++++++++++++++++++- 1 file changed, 68 insertions(+), 1 deletion(-) diff --git a/unified/ql/test/library-tests/type-inference/variance.swift b/unified/ql/test/library-tests/type-inference/variance.swift index 11d19d3b6054..831da050ced5 100644 --- a/unified/ql/test/library-tests/type-inference/variance.swift +++ b/unified/ql/test/library-tests/type-inference/variance.swift @@ -1,4 +1,4 @@ -// variance is not currently supported +// Recursive variance through function inputs, outputs, and generic constructors. class A {} @@ -17,3 +17,70 @@ func bar() -> B { B() } // $ target=A.init func baz() { let x: A = bar() // $ target=bar $ MISSING: type=bar(...)@B:Int } + +func boundLower(_ value: [T]) -> T { fatalError() } +let boundLowerActual = [1] +let lower = boundLower(boundLowerActual) // $ target=boundLower type=lower:Int + +func boundUpper(_ callback: (T) -> Int) -> T { fatalError() } +let boundUpperActual = { (value: Int) -> Int in value } // $ type=value:Int +let upper = boundUpper(boundUpperActual) // $ target=boundUpper type=upper:Int + +func boundDouble(_ callback: ((T) -> Int) -> Int) -> T { fatalError() } +let boundDoubleActual = { (callback: (Int) -> Int) -> Int in callback(1) } // $ target=Function.callAsFunction +let double = boundDouble(boundDoubleActual) // $ target=boundDouble type=double:Int + +func boundInvariant(_ value: A) -> T { fatalError() } +let boundInvariantActual = A() // $ target=A.init +let invariant = boundInvariant(boundInvariantActual) // $ target=boundInvariant type=invariant:Int + +func boundNested(_ value: [A]) -> T { fatalError() } +let boundNestedActual = [B()] // $ target=A.init +let nested = boundNested(boundNestedActual) // $ target=boundNested $ MISSING: type=nested:Int + +// Neither reversing a covariant conversion nor converting through an invariant +// parameter can establish a bound. These calls intentionally have incompatible types. +func boundWrongDirection(_ value: [B]) -> T { fatalError() } +let boundWrongDirectionActual = [A()] // $ target=A.init +let wrongDirection = boundWrongDirection(boundWrongDirectionActual) // $ target=boundWrongDirection + +func boundInvariantConversion(_ value: A>) -> T { fatalError() } +let boundInvariantConversionActual = A>() // $ target=A.init +let invariantConversion = boundInvariantConversion(boundInvariantConversionActual) // $ target=boundInvariantConversion + +func boundContext(_ first: T, _ values: [A]) {} +boundContext(1, [B()]) // $ target=boundContext target=A.init $ MISSING: type=B(...)@B:Int + +func boundAlternatives(_ first: [T], _ second: [T]) -> T { fatalError() } +let alternatives = boundAlternatives([1], ["x"]) // $ target=boundAlternatives type=alternatives:Int type=alternatives:String + +// Keep lower and upper bounds distinct, without solving their inconsistency. +func boundMixed(_ callback: (T) -> T) -> T { fatalError() } +let boundMixedActual = { (value: Int) -> String in "x" } +let mixed = boundMixed(boundMixedActual) // $ target=boundMixed type=mixed:Int type=mixed:String + +class Shift: A<[C]> { + override init() { super.init() } // $ target=A.init +} +func boundShift(_ value: [A<[T]>]) -> T { fatalError() } +let boundShiftActual = [Shift()] // $ target=Shift.init +let shifted = boundShift(boundShiftActual) // $ target=boundShift $ MISSING: type=shifted:Int + +func shiftedContext(_ first: T, _ values: [A<[T]>]) {} +shiftedContext(1, [Shift()]) // $ target=shiftedContext target=Shift.init $ MISSING: type=Shift(...)@Shift:Int + +func boundContraReverse(_ callback: (A) -> Int) -> T { fatalError() } +let boundContraReverseActual = { (value: B) -> Int in 1 } +let contraReverse = boundContraReverse(boundContraReverseActual) // $ target=boundContraReverse + +func boundDoubleNested(_ callback: ((A) -> Int) -> Int) -> T { fatalError() } +let boundDoubleNestedActual = { (callback: (B) -> Int) -> Int in 1 } +let doubleNested = boundDoubleNested(boundDoubleNestedActual) // $ target=boundDoubleNested $ MISSING: type=doubleNested:Int + +func boundInvariantFunction(_ value: A<(A) -> Int>) -> T { fatalError() } +let boundInvariantFunctionActual = A<(B) -> Int>() // $ target=A.init +let invariantFunction = boundInvariantFunction(boundInvariantFunctionActual) // $ target=boundInvariantFunction + +func boundOptional(_ value: A?) -> T { fatalError() } +let boundOptionalActual: B? = B() // $ target=A.init +let optional = boundOptional(boundOptionalActual) // $ target=boundOptional $ MISSING: type=optional:Int From 861cd998c85e51cf7646598aacde3fa588151186 Mon Sep 17 00:00:00 2001 From: Tom Hvitved Date: Mon, 9 Nov 2026 21:24:20 +0100 Subject: [PATCH 4/4] Implement recursive variance-aware type matching Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- .../typeinference/internal/TypeInference.qll | 406 ++++++++++++- .../internal/typeinference/TypeInference.qll | 4 + .../typeinference/TypeInferencePlugin.qll | 10 + .../TypeInferencePluginSwift.qll | 21 + .../internal/typeinference/TypeMention.qll | 2 + .../type-inference/type-inference.expected | 533 ++++++++++++++++++ .../type-inference/variance-bounds.expected | 16 + .../type-inference/variance-bounds.ql | 39 ++ .../type-inference/variance.swift | 16 +- 9 files changed, 1035 insertions(+), 12 deletions(-) create mode 100644 unified/ql/test/library-tests/type-inference/variance-bounds.expected create mode 100644 unified/ql/test/library-tests/type-inference/variance-bounds.ql diff --git a/shared/typeinference/codeql/typeinference/internal/TypeInference.qll b/shared/typeinference/codeql/typeinference/internal/TypeInference.qll index 7130085d0679..3a31ac1ff794 100644 --- a/shared/typeinference/codeql/typeinference/internal/TypeInference.qll +++ b/shared/typeinference/codeql/typeinference/internal/TypeInference.qll @@ -105,6 +105,15 @@ signature module InputSig1 { /** A type parameter. */ class TypeParameter extends Type; + /** + * Gets the variance of `tp`: 1 for covariance, -1 for contravariance, and 0 + * for invariance. This describes the parameter's position in its constructor, + * not the variance of an inference variable substituted at that position. + * Clients with no variant parameters retain the existing non-recursive + * `typeMatch` behavior; `typeMatchBound` always uses recursive matching. + */ + default int getTypeParameterVariance(TypeParameter tp) { exists(tp) and result = 0 } + /** * A type abstraction. I.e., a place in the program where type variables may * be introduced. @@ -365,6 +374,10 @@ module Make1 Input1> { module Make2 Input2> { private import Input2 + private predicate varianceEnabled() { + exists(TypeParameter tp | getTypeParameterVariance(tp) != 0) + } + /** * Gets the non-pseudo root type mentioned at `tm`. * @@ -1178,6 +1191,277 @@ module Make1 Input1> { import SatisfiesConstraint } + /** Provides the relevant pairs of type trees for `VarianceMatching`. */ + signature module VarianceMatchingInputSig { + predicate relevantConstraint(Term term, Constraint constraint); + + /** Gets the initial relation: 1 for subtype, -1 for supertype, or 0 for equality. */ + default int getRelation(Term term, Constraint constraint) { + relevantConstraint(term, constraint) and result = 1 + } + + /** Gets an inferred instantiation for contextual propagation into `term`. */ + default Type getTypeArgument(Term term, Constraint constraint, TypeParameter tp, TypePath path) { + none() + } + } + + /** + * Recursively matches type trees, projecting subtrees through the nominal + * hierarchy only in the direction permitted by their enclosing parameters. + * Bounds are possible types, not solutions of a system of inequalities. + * Views are created only for relevant pairs, and their child/projection + * depth is bounded by `getTypePathLimit`, when configured. + */ + module VarianceMatching< + HasTypeTreeSig Term, HasTypeTreeSig Constraint, + VarianceMatchingInputSig Input> + { + private newtype TTree = + MkTerm(Term term) { Input::relevantConstraint(term, _) } or + MkConstraint(Constraint constraint) { Input::relevantConstraint(_, constraint) } or + MkChild(Tree parent, TypeParameter tp) { child(parent, tp) } or + MkBase(Tree tree, Type root) { projection(tree, root) } + + private class Tree extends TTree { + int getDepth() { + (this instanceof MkTerm or this instanceof MkConstraint) and + result = 0 + or + exists(Tree parent | + (this = MkChild(parent, _) or this = MkBase(parent, _)) and + result = parent.getDepth() + 1 + ) + } + + Type getTypeAt(TypePath path) { + exists(Term term | this = MkTerm(term) | result = term.getTypeAt(path)) + or + exists(Constraint constraint | this = MkConstraint(constraint) | + result = constraint.getTypeAt(path) + ) + or + exists(Tree parent, TypeParameter tp, TypePath parentPath | + this = MkChild(parent, tp) and + result = parent.getTypeAt(parentPath) and + parentPath.isCons(tp, path) + ) + or + exists(Tree parent, Type root | + this = MkBase(parent, root) and + Projection::satisfiesConstraint(parent, MkRoot(root), path, result) + ) + } + + /** Maps a projected path back to a path in the original term. */ + pragma[nomagic] + predicate originPath(TypePath path, TypePath original) { + relevantOrigin(this, path) and + ( + this instanceof MkTerm and original = path + or + exists(Tree parent, TypeParameter tp | + this = MkChild(parent, tp) and + parent.originPath(TypePath::cons(tp, path), original) + ) + or + exists(Tree parent, Type root, TypePath prefix, TypePath source, TypePath suffix | + this = MkBase(parent, root) and + Projection::satisfiesConstraintAtTypeParameter(parent, MkRoot(root), prefix, source) and + path = prefix.appendInverse(suffix) and + parent.originPath(source.append(suffix), original) + ) + ) + } + + string toString() { result = "variance matching subtree" } + + Location getLocation() { + exists(Term term | this = MkTerm(term) | result = term.getLocation()) + or + exists(Constraint constraint | this = MkConstraint(constraint) | + result = constraint.getLocation() + ) + or + exists(Tree parent | + (this = MkChild(parent, _) or this = MkBase(parent, _)) and + result = parent.getLocation() + ) + } + } + + private predicate canDescend(Tree tree) { + not exists(getTypePathLimit()) or tree.getDepth() < getTypePathLimit() + } + + private int childRelation(int relation, TypeParameter tp) { + relation in [-1, 0, 1] and + result = relation * getTypeParameterVariance(tp) + } + + pragma[nomagic] + private predicate aligned( + Term term, Constraint constraint, Tree actual, Tree expected, int relation + ) { + Input::relevantConstraint(term, constraint) and + actual = MkTerm(term) and + expected = MkConstraint(constraint) and + relation = Input::getRelation(term, constraint) + or + exists( + Tree actualParent, Tree expectedParent, int parentRelation, Type root, TypeParameter tp + | + aligned(term, constraint, actualParent, expectedParent, parentRelation) and + root = actualParent.getTypeAt(TypePath::nil()) and + root = expectedParent.getTypeAt(TypePath::nil()) and + tp = root.getATypeParameter() and + canDescend(actualParent) and + canDescend(expectedParent) and + actual = MkChild(actualParent, tp) and + expected = MkChild(expectedParent, tp) and + relation = childRelation(parentRelation, tp) + ) + or + exists(Tree previous, Type root | + aligned(term, constraint, previous, expected, relation) and + relation = 1 and + root = expected.getTypeAt(TypePath::nil()) and + projection(previous, root) and + actual = MkBase(previous, root) + ) + or + exists(Tree previous, Type root | + aligned(term, constraint, actual, previous, relation) and + relation = -1 and + root = actual.getTypeAt(TypePath::nil()) and + projection(previous, root) and + expected = MkBase(previous, root) + ) + } + + private predicate child(Tree parent, TypeParameter tp) { + exists( + Term term, Constraint constraint, Tree actual, Tree expected, int relation, Type root + | + aligned(term, constraint, actual, expected, relation) and + root = actual.getTypeAt(TypePath::nil()) and + root = expected.getTypeAt(TypePath::nil()) and + tp = root.getATypeParameter() and + (parent = actual or parent = expected) and + canDescend(actual) and + canDescend(expected) + ) + } + + private predicate projection(Tree tree, Type root) { + exists( + Term term, Constraint constraint, Tree actual, Tree expected, int relation, Type source + | + aligned(term, constraint, actual, expected, relation) and + ( + relation = 1 and tree = actual and root = expected.getTypeAt(TypePath::nil()) + or + relation = -1 and tree = expected and root = actual.getTypeAt(TypePath::nil()) + ) and + source = tree.getTypeAt(TypePath::nil()) and + source != root and + rootTypesSatisfaction(source, root, _, _, _) and + canDescend(tree) + ) + } + + private newtype TRoot = MkRoot(Type root) { projection(_, root) } + + private class Root extends MkRoot { + Type root; + + Root() { this = MkRoot(root) } + + Type getTypeAt(TypePath path) { result = root and path.isEmpty() } + + string toString() { result = root.toString() } + + Location getLocation() { result = root.getLocation() } + } + + private module ProjectionInput implements SatisfiesConstraintInputSig { + predicate relevantConstraint(Tree term, Root constraint) { + exists(Type type | constraint = MkRoot(type) | projection(term, type)) + } + } + + private module Projection = SatisfiesConstraint; + + pragma[nomagic] + private predicate relevantOrigin(Tree tree, TypePath path) { + exists(Term term, Constraint constraint, Tree expected, int relation | + aligned(term, constraint, tree, expected, relation) and + tree.getTypeAt(TypePath::nil()) instanceof PseudoType and + ( + exists(expected.getTypeAt(path)) + or + exists(TypeParameter tp | + tp = expected.getTypeAt(TypePath::nil()) and + exists(Input::getTypeArgument(term, constraint, tp, path)) + ) + ) + ) + or + exists(Tree child, TypeParameter tp, TypePath suffix | + relevantOrigin(child, suffix) and + child = MkChild(tree, tp) and + path = TypePath::cons(tp, suffix) + ) + or + exists( + Tree base, Type root, TypePath prefix, TypePath source, TypePath suffix, TypePath basePath + | + relevantOrigin(base, basePath) and + base = MkBase(tree, root) and + Projection::satisfiesConstraintAtTypeParameter(tree, MkRoot(root), prefix, source) and + basePath = prefix.appendInverse(suffix) and + path = source.append(suffix) + ) + } + + /** + * Gets a possible bound on `tp`. `relation` is 1 for a lower bound + * (`t <: tp`), -1 for an upper bound (`tp <: t`), or 0 for equality. + * All paths in a bound refer to the same projected subtree. + */ + pragma[nomagic] + predicate typeBound( + Term term, Constraint constraint, TypeParameter tp, int relation, TypePath path, Type t + ) { + exists(Tree actual, Tree expected | + aligned(term, constraint, actual, expected, relation) and + tp = expected.getTypeAt(TypePath::nil()) and + tp = constraint.getTypeAt(_) and + t = actual.getTypeAt(path) + ) + } + + /** Gets a contextual type for an unknown subtree of the original term. */ + pragma[nomagic] + Type contextualType(Term term, Constraint constraint, TypePath path) { + exists(Tree actual, Tree expected, int relation, TypePath suffix | + aligned(term, constraint, actual, expected, relation) and + actual.getTypeAt(TypePath::nil()) instanceof PseudoType and + ( + result = expected.getTypeAt(suffix) + or + exists(TypeParameter tp | + tp = expected.getTypeAt(TypePath::nil()) and + result = Input::getTypeArgument(term, constraint, tp, suffix) + ) + ) and + not result instanceof TypeParameter and + not result instanceof PseudoType and + actual.originPath(suffix, path) + ) + } + } + /** Provides the input to `MatchingWithEnvironment`. */ signature module MatchingWithEnvironmentInputSig { /** @@ -1275,6 +1559,13 @@ module Make1 Input1> { bindingset[apos] bindingset[dpos] predicate accessDeclarationPositionMatch(AccessPosition apos, DeclarationPosition dpos); + + /** + * Gets the relation of an inferred access type to its declared type. + * Arguments are subtypes (1); contextual return types are supertypes (-1). + */ + bindingset[apos] + default int getAccessTypeRelation(AccessPosition apos) { exists(apos) and result = 1 } } /** @@ -1383,7 +1674,10 @@ module Make1 Input1> { private newtype TMatchingPosition = MkArgumentPosition(Declaration target, AccessPosition apos) { - argRootTypeSatisfiesTargetTypeCand(_, target, apos, _, _) + exists(DeclarationPosition dpos | + accessDeclarationPositionMatch(apos, dpos) and + exists(target.getDeclaredType(dpos, TypePath::nil())) + ) } or MkTypeArgumentPosition( Declaration target, TypeParameter constrainedTp, TypeMention constraint @@ -1397,6 +1691,8 @@ module Make1 Input1> { abstract Type getTypeAt(TypePath path); + int getRelation() { result = 1 } + bindingset[a, e] abstract Type getInferredType(Access a, AccessEnvironment e, TypePath path); @@ -1431,6 +1727,8 @@ module Make1 Input1> { override Declaration getTarget() { result = target } + override int getRelation() { result = getAccessTypeRelation(apos) } + override Type getTypeAt(TypePath path) { exists(DeclarationPosition dpos | accessDeclarationPositionMatch(apos, dpos) and @@ -1446,7 +1744,16 @@ module Make1 Input1> { override predicate relevantAccess(Access a, AccessEnvironment e) { exists(Type t | accessTargetsWithArgRootType(a, e, target, apos, t) and - argRootTypeSatisfiesTargetTypeCand(t, target, apos, _, _) + ( + argRootTypeSatisfiesTargetTypeCand(t, target, apos, _, _) + or + t = this.getTypeAt(TypePath::nil()) + or + this.getTypeAt(TypePath::nil()) instanceof TypeParameter + or + this.getRelation() = -1 and + rootTypesSatisfaction(this.getTypeAt(TypePath::nil()), t, _, _, _) + ) ) } @@ -1560,6 +1867,52 @@ module Make1 Input1> { SatisfiesConstraintWithTypeMatching; + private module VarianceInput implements + VarianceMatchingInputSig + { + predicate relevantConstraint(RelevantAccess term, MatchingPosition constraint) { + constraint = term.getConstraint() + } + + int getRelation(RelevantAccess term, MatchingPosition constraint) { + constraint = term.getConstraint() and + result = constraint.getRelation() + } + + Type getTypeArgument( + RelevantAccess term, MatchingPosition constraint, TypeParameter tp, TypePath path + ) { + exists(Access a, AccessEnvironment e | + term = MkRelevantAccess(a, constraint, e) and + typeMatch(a, e, constraint.getTarget(), path, result, tp) + ) + } + } + + private module RecursiveMatching = + VarianceMatching; + + pragma[nomagic] + predicate typeBound( + Access a, AccessEnvironment e, Declaration target, TypeParameter tp, int relation, + TypePath path, Type t + ) { + hasNotTypeArgument(a, target, tp) and + exists(MatchingPosition pos | + target = pos.getTarget() and + RecursiveMatching::typeBound(MkRelevantAccess(a, pos, e), pos, tp, relation, path, t) + ) + } + + pragma[nomagic] + Type contextualType(Access a, AccessEnvironment e, AccessPosition apos, TypePath path) { + exists(MatchingPosition pos, TypePath prefix, TypePath suffix | + pos.accessPath(apos, prefix) and + result = RecursiveMatching::contextualType(MkRelevantAccess(a, pos, e), pos, suffix) and + path = prefix.append(suffix) + ) + } + /** * Holds if the (transitive) base type `t` at `path` of `a` in environment `e` * for some argument or type argument position matches the type parameter @@ -1698,6 +2051,23 @@ module Make1 Input1> { or // We can infer the type of `tp` via the type hierarchy or a type constraint ConstraintMatching::baseTypeMatch(a, e, target, path, t, tp) + or + varianceEnabled() and typeMatchBound(a, e, target, tp, _, path, t) + } + + /** + * Holds if `t` at `path` is a possible directional bound on `tp`. + * A relation of 1 denotes a lower bound, -1 an upper bound, and 0 equality. + * Like `typeMatch`, this preserves possible alternatives rather than + * selecting a least upper bound or rejecting inconsistent combinations. + */ + predicate typeMatchBound( + Access a, AccessEnvironment e, Declaration target, TypeParameter tp, int relation, + TypePath path, Type t + ) { + explicitTypeMatch(a, e, target, path, t, tp) and relation = 0 + or + ConstraintMatching::typeBound(a, e, target, tp, relation, path, t) } /** @@ -1740,6 +2110,8 @@ module Make1 Input1> { */ pragma[nomagic] Type inferAccessType(Access a, AccessEnvironment e, AccessPosition apos, TypePath path) { + varianceEnabled() and result = ConstraintMatching::contextualType(a, e, apos, path) + or exists(DeclarationPosition dpos | accessDeclarationPositionMatch(apos, dpos) | // A suffix of `path` leads to a type parameter in the target exists(Declaration target, TypePath prefix, TypeParameter tp, TypePath suffix | @@ -1962,6 +2334,10 @@ module Make1 Input1> { bindingset[apos] bindingset[dpos] predicate accessDeclarationPositionMatch(AccessPosition apos, DeclarationPosition dpos); + + /** Gets the relation of an inferred access type to its declared type. */ + bindingset[apos] + default int getAccessTypeRelation(AccessPosition apos) { exists(apos) and result = 1 } } /** @@ -1977,6 +2353,8 @@ module Make1 Input1> { predicate getATypeParameterConstraint = Input::getATypeParameterConstraint/2; + predicate getAccessTypeRelation = Input::getAccessTypeRelation/1; + class AccessEnvironment = Unit; final private class AccessFinal = Input::Access; @@ -3057,6 +3435,11 @@ module Make1 Input1> { class AccessPosition = DeclarationPosition; + bindingset[apos] + int getAccessTypeRelation(AccessPosition apos) { + if apos = getReturnPosition() then result = -1 else result = 1 + } + bindingset[apos] bindingset[dpos] predicate accessDeclarationPositionMatch(AccessPosition apos, DeclarationPosition dpos) { @@ -3121,8 +3504,12 @@ module Make1 Input1> { pragma[nomagic] private Type getInferredReturnType(AccessPosition apos, TypePath path) { - result = inferInvocationTypeContextual(this, path) and - apos = getReturnPosition() + apos = getReturnPosition() and + ( + result = inferInvocationTypeContextual(this, path) + or + varianceEnabled() and result = ContextualTyping::inferTypeForMatching(this, path) + ) } bindingset[e] @@ -3292,6 +3679,17 @@ module Make1 Input1> { ) } + /** + * Gets an expected type tree before filtering paths against the inferred + * constructor. Matching must first project through the hierarchy: an + * expected `Base` and an inferred `Derived` use different paths. + */ + pragma[nomagic] + Type inferTypeForMatching(AstNode n, TypePath path) { + hasUnknownType(n) and + result = inferTypeContextualCand0(n, path) + } + pragma[nomagic] private Type inferTypeContextualCand1(AstNode n, TypePath prefix, TypePath path) { result = inferTypeContextualCand0(n, path) and diff --git a/unified/ql/lib/codeql/unified/internal/typeinference/TypeInference.qll b/unified/ql/lib/codeql/unified/internal/typeinference/TypeInference.qll index 34b6c34dc47f..7cea68fbf2e8 100644 --- a/unified/ql/lib/codeql/unified/internal/typeinference/TypeInference.qll +++ b/unified/ql/lib/codeql/unified/internal/typeinference/TypeInference.qll @@ -46,6 +46,10 @@ private module Input1 implements InputSig1 { class TypeAbstraction = TA::TypeAbstraction; + int getTypeParameterVariance(TypeParameter tp) { + result = any(Plugin::TypeInferencePlugin pl).getTypeParameterVariance(tp) + } + int getTypeParameterId(TypeParameter tp) { CachedStage::ref() and tp = diff --git a/unified/ql/lib/codeql/unified/internal/typeinference/TypeInferencePlugin.qll b/unified/ql/lib/codeql/unified/internal/typeinference/TypeInferencePlugin.qll index 0680c6914ada..c03827ebef70 100644 --- a/unified/ql/lib/codeql/unified/internal/typeinference/TypeInferencePlugin.qll +++ b/unified/ql/lib/codeql/unified/internal/typeinference/TypeInferencePlugin.qll @@ -28,6 +28,9 @@ class TypeInferencePlugin extends Unit { /** Gets the type parameter representing the element type of an array. */ abstract TypeParameter getArrayElementTypeParameter(); + /** Gets parameter variance: 1 for covariance, -1 for contravariance, or 0 for invariance. */ + int getTypeParameterVariance(TypeParameter tp) { exists(tp) and result = 0 } + /** * Gets the type of the given function expression. * @@ -36,6 +39,9 @@ class TypeInferencePlugin extends Unit { bindingset[fe] abstract Type getFunctionExprType(FunctionExpr fe); + /** Gets an intermediate constructor in the type tree of a function signature. */ + Type getFunctionExprIntermediateType(FunctionExpr fe, TypePath path) { none() } + /** * Gets the type path for the `i`th parameter of the given function type * when the function type has the given arity. @@ -127,6 +133,10 @@ TypePath getFunctionExprReturnTypePath(Type t) { result = any(TypeInferencePlugin pl).getFunctionExprReturnTypePath(t) } +Type getFunctionExprIntermediateType(FunctionExpr fe, TypePath path) { + result = any(TypeInferencePlugin pl).getFunctionExprIntermediateType(fe, path) +} + class TupleType extends Type { int arity; diff --git a/unified/ql/lib/codeql/unified/internal/typeinference/TypeInferencePluginSwift.qll b/unified/ql/lib/codeql/unified/internal/typeinference/TypeInferencePluginSwift.qll index 2ea9ccd4afdc..435b667726cd 100644 --- a/unified/ql/lib/codeql/unified/internal/typeinference/TypeInferencePluginSwift.qll +++ b/unified/ql/lib/codeql/unified/internal/typeinference/TypeInferencePluginSwift.qll @@ -122,6 +122,22 @@ private class EnumField extends VariableDeclaration { private predicate inferTypeAlias = inferType/2; private class SwiftTypeInferencePlugin extends Plugin::TypeInferencePlugin { + override int getTypeParameterVariance(TypeParameter tp) { + if tp = getFunctionArgsTypeParameter() + then result = -1 + else + if + tp = getFunctionReturnTypeParameter() + or + tp = any(TupleType t).getATypeParameter() + or + tp = any(ArrayType t).getATypeParameter() + or + tp = any(OptionalType t).getATypeParameter() + then result = 1 + else result = 0 + } + override Type getBoolType() { result instanceof BoolType } override Type getIntType() { result instanceof IntType } @@ -140,6 +156,11 @@ private class SwiftTypeInferencePlugin extends Plugin::TypeInferencePlugin { exists(fe) } + override Type getFunctionExprIntermediateType(FunctionExpr fe, TypePath path) { + result = this.getTupleType(fe.getNumberOfParameters()) and + path = TypePath::singleton(getFunctionArgsTypeParameter()) + } + bindingset[t] override TypePath getFunctionExprParameterTypePath(Type t, int arity, int i) { result = functionParameterPath(arity, i) and exists(t) diff --git a/unified/ql/lib/codeql/unified/internal/typeinference/TypeMention.qll b/unified/ql/lib/codeql/unified/internal/typeinference/TypeMention.qll index baef8f970e26..c1e7671a4501 100644 --- a/unified/ql/lib/codeql/unified/internal/typeinference/TypeMention.qll +++ b/unified/ql/lib/codeql/unified/internal/typeinference/TypeMention.qll @@ -102,6 +102,8 @@ private class NonAliasExprTypeMention extends ExprTypeMentionImpl { result = this.getRootType() and path.isEmpty() or + result = Plugin::getFunctionExprIntermediateType(this.(FunctionExpr), path) + or exists( ClassLikeDeclaration c, AssociatedTypeDeclaration a, ClassLikeDeclaration base, AssociatedTypeParameterType baseTp, string name diff --git a/unified/ql/test/library-tests/type-inference/type-inference.expected b/unified/ql/test/library-tests/type-inference/type-inference.expected index 4a87e1942d3b..00ec8bb3f0ad 100644 --- a/unified/ql/test/library-tests/type-inference/type-inference.expected +++ b/unified/ql/test/library-tests/type-inference/type-inference.expected @@ -780,6 +780,198 @@ inferCertainType | variance.swift:13:16:13:18 | lam | Return.Tb | {EXTERNAL LOCATION} | String | | variance.swift:18:7:18:7 | x | | variance.swift:3:1:3:14 | A | | variance.swift:18:7:18:7 | x | Ta | {EXTERNAL LOCATION} | Int | +| variance.swift:21:22:21:26 | value | | {EXTERNAL LOCATION} | Array | +| variance.swift:21:22:21:26 | value | Element | variance.swift:21:17:21:17 | T | +| variance.swift:22:5:22:20 | boundLowerActual | | {EXTERNAL LOCATION} | Array | +| variance.swift:22:24:22:26 | ArrayLiteral | | {EXTERNAL LOCATION} | Array | +| variance.swift:23:24:23:39 | boundLowerActual | | {EXTERNAL LOCATION} | Array | +| variance.swift:25:22:25:29 | callback | | {EXTERNAL LOCATION} | Function | +| variance.swift:25:22:25:29 | callback | Args | {EXTERNAL LOCATION} | Tuple1 | +| variance.swift:25:22:25:29 | callback | Args.T0 | variance.swift:25:17:25:17 | T | +| variance.swift:25:22:25:29 | callback | Return | {EXTERNAL LOCATION} | Int | +| variance.swift:26:5:26:20 | boundUpperActual | | {EXTERNAL LOCATION} | Function | +| variance.swift:26:5:26:20 | boundUpperActual | Args | {EXTERNAL LOCATION} | Tuple1 | +| variance.swift:26:5:26:20 | boundUpperActual | Args.T0 | {EXTERNAL LOCATION} | Int | +| variance.swift:26:5:26:20 | boundUpperActual | Return | {EXTERNAL LOCATION} | Int | +| variance.swift:26:24:26:55 | FunctionExpr | | {EXTERNAL LOCATION} | Function | +| variance.swift:26:24:26:55 | FunctionExpr | Args | {EXTERNAL LOCATION} | Tuple1 | +| variance.swift:26:24:26:55 | FunctionExpr | Args.T0 | {EXTERNAL LOCATION} | Int | +| variance.swift:26:24:26:55 | FunctionExpr | Return | {EXTERNAL LOCATION} | Int | +| variance.swift:26:27:26:31 | value | | {EXTERNAL LOCATION} | Int | +| variance.swift:26:49:26:53 | Block | | {EXTERNAL LOCATION} | Int | +| variance.swift:26:49:26:53 | value | | {EXTERNAL LOCATION} | Int | +| variance.swift:27:24:27:39 | boundUpperActual | | {EXTERNAL LOCATION} | Function | +| variance.swift:27:24:27:39 | boundUpperActual | Args | {EXTERNAL LOCATION} | Tuple1 | +| variance.swift:27:24:27:39 | boundUpperActual | Args.T0 | {EXTERNAL LOCATION} | Int | +| variance.swift:27:24:27:39 | boundUpperActual | Return | {EXTERNAL LOCATION} | Int | +| variance.swift:29:23:29:30 | callback | | {EXTERNAL LOCATION} | Function | +| variance.swift:29:23:29:30 | callback | Args | {EXTERNAL LOCATION} | Tuple1 | +| variance.swift:29:23:29:30 | callback | Args.T0 | {EXTERNAL LOCATION} | Function | +| variance.swift:29:23:29:30 | callback | Args.T0.Args | {EXTERNAL LOCATION} | Tuple1 | +| variance.swift:29:23:29:30 | callback | Args.T0.Args.T0 | variance.swift:29:18:29:18 | T | +| variance.swift:29:23:29:30 | callback | Args.T0.Return | {EXTERNAL LOCATION} | Int | +| variance.swift:29:23:29:30 | callback | Return | {EXTERNAL LOCATION} | Int | +| variance.swift:30:5:30:21 | boundDoubleActual | | {EXTERNAL LOCATION} | Function | +| variance.swift:30:5:30:21 | boundDoubleActual | Args | {EXTERNAL LOCATION} | Tuple1 | +| variance.swift:30:5:30:21 | boundDoubleActual | Args.T0 | {EXTERNAL LOCATION} | Function | +| variance.swift:30:5:30:21 | boundDoubleActual | Args.T0.Args | {EXTERNAL LOCATION} | Tuple1 | +| variance.swift:30:5:30:21 | boundDoubleActual | Args.T0.Args.T0 | {EXTERNAL LOCATION} | Int | +| variance.swift:30:5:30:21 | boundDoubleActual | Args.T0.Return | {EXTERNAL LOCATION} | Int | +| variance.swift:30:5:30:21 | boundDoubleActual | Return | {EXTERNAL LOCATION} | Int | +| variance.swift:30:25:30:74 | FunctionExpr | | {EXTERNAL LOCATION} | Function | +| variance.swift:30:25:30:74 | FunctionExpr | Args | {EXTERNAL LOCATION} | Tuple1 | +| variance.swift:30:25:30:74 | FunctionExpr | Args.T0 | {EXTERNAL LOCATION} | Function | +| variance.swift:30:25:30:74 | FunctionExpr | Args.T0.Args | {EXTERNAL LOCATION} | Tuple1 | +| variance.swift:30:25:30:74 | FunctionExpr | Args.T0.Args.T0 | {EXTERNAL LOCATION} | Int | +| variance.swift:30:25:30:74 | FunctionExpr | Args.T0.Return | {EXTERNAL LOCATION} | Int | +| variance.swift:30:25:30:74 | FunctionExpr | Return | {EXTERNAL LOCATION} | Int | +| variance.swift:30:28:30:35 | callback | | {EXTERNAL LOCATION} | Function | +| variance.swift:30:28:30:35 | callback | Args | {EXTERNAL LOCATION} | Tuple1 | +| variance.swift:30:28:30:35 | callback | Args.T0 | {EXTERNAL LOCATION} | Int | +| variance.swift:30:28:30:35 | callback | Return | {EXTERNAL LOCATION} | Int | +| variance.swift:30:62:30:69 | callback | | {EXTERNAL LOCATION} | Function | +| variance.swift:30:62:30:69 | callback | Args | {EXTERNAL LOCATION} | Tuple1 | +| variance.swift:30:62:30:69 | callback | Args.T0 | {EXTERNAL LOCATION} | Int | +| variance.swift:30:62:30:69 | callback | Return | {EXTERNAL LOCATION} | Int | +| variance.swift:31:26:31:42 | boundDoubleActual | | {EXTERNAL LOCATION} | Function | +| variance.swift:31:26:31:42 | boundDoubleActual | Args | {EXTERNAL LOCATION} | Tuple1 | +| variance.swift:31:26:31:42 | boundDoubleActual | Args.T0 | {EXTERNAL LOCATION} | Function | +| variance.swift:31:26:31:42 | boundDoubleActual | Args.T0.Args | {EXTERNAL LOCATION} | Tuple1 | +| variance.swift:31:26:31:42 | boundDoubleActual | Args.T0.Args.T0 | {EXTERNAL LOCATION} | Int | +| variance.swift:31:26:31:42 | boundDoubleActual | Args.T0.Return | {EXTERNAL LOCATION} | Int | +| variance.swift:31:26:31:42 | boundDoubleActual | Return | {EXTERNAL LOCATION} | Int | +| variance.swift:33:26:33:30 | value | | variance.swift:3:1:3:14 | A | +| variance.swift:33:26:33:30 | value | Ta | variance.swift:33:21:33:21 | T | +| variance.swift:37:23:37:27 | value | | {EXTERNAL LOCATION} | Array | +| variance.swift:37:23:37:27 | value | Element | variance.swift:3:1:3:14 | A | +| variance.swift:37:23:37:27 | value | Element.Ta | variance.swift:37:18:37:18 | T | +| variance.swift:38:5:38:21 | boundNestedActual | | {EXTERNAL LOCATION} | Array | +| variance.swift:38:25:38:34 | ArrayLiteral | | {EXTERNAL LOCATION} | Array | +| variance.swift:39:26:39:42 | boundNestedActual | | {EXTERNAL LOCATION} | Array | +| variance.swift:43:31:43:35 | value | | {EXTERNAL LOCATION} | Array | +| variance.swift:43:31:43:35 | value | Element | variance.swift:5:1:5:21 | B | +| variance.swift:43:31:43:35 | value | Element.Tb | variance.swift:43:26:43:26 | T | +| variance.swift:44:5:44:29 | boundWrongDirectionActual | | {EXTERNAL LOCATION} | Array | +| variance.swift:44:33:44:42 | ArrayLiteral | | {EXTERNAL LOCATION} | Array | +| variance.swift:45:42:45:66 | boundWrongDirectionActual | | {EXTERNAL LOCATION} | Array | +| variance.swift:47:36:47:40 | value | | variance.swift:3:1:3:14 | A | +| variance.swift:47:36:47:40 | value | Ta | variance.swift:3:1:3:14 | A | +| variance.swift:47:36:47:40 | value | Ta.Ta | variance.swift:47:31:47:31 | T | +| variance.swift:51:24:51:28 | first | | variance.swift:51:19:51:19 | T | +| variance.swift:51:36:51:41 | values | | {EXTERNAL LOCATION} | Array | +| variance.swift:51:36:51:41 | values | Element | variance.swift:3:1:3:14 | A | +| variance.swift:51:36:51:41 | values | Element.Ta | variance.swift:51:19:51:19 | T | +| variance.swift:52:17:52:21 | ArrayLiteral | | {EXTERNAL LOCATION} | Array | +| variance.swift:54:29:54:33 | first | | {EXTERNAL LOCATION} | Array | +| variance.swift:54:29:54:33 | first | Element | variance.swift:54:24:54:24 | T | +| variance.swift:54:43:54:48 | second | | {EXTERNAL LOCATION} | Array | +| variance.swift:54:43:54:48 | second | Element | variance.swift:54:24:54:24 | T | +| variance.swift:55:38:55:40 | ArrayLiteral | | {EXTERNAL LOCATION} | Array | +| variance.swift:55:43:55:47 | ArrayLiteral | | {EXTERNAL LOCATION} | Array | +| variance.swift:58:22:58:29 | callback | | {EXTERNAL LOCATION} | Function | +| variance.swift:58:22:58:29 | callback | Args | {EXTERNAL LOCATION} | Tuple1 | +| variance.swift:58:22:58:29 | callback | Args.T0 | variance.swift:58:17:58:17 | T | +| variance.swift:58:22:58:29 | callback | Return | variance.swift:58:17:58:17 | T | +| variance.swift:59:5:59:20 | boundMixedActual | | {EXTERNAL LOCATION} | Function | +| variance.swift:59:5:59:20 | boundMixedActual | Args | {EXTERNAL LOCATION} | Tuple1 | +| variance.swift:59:5:59:20 | boundMixedActual | Args.T0 | {EXTERNAL LOCATION} | Int | +| variance.swift:59:5:59:20 | boundMixedActual | Return | {EXTERNAL LOCATION} | String | +| variance.swift:59:24:59:56 | FunctionExpr | | {EXTERNAL LOCATION} | Function | +| variance.swift:59:24:59:56 | FunctionExpr | Args | {EXTERNAL LOCATION} | Tuple1 | +| variance.swift:59:24:59:56 | FunctionExpr | Args.T0 | {EXTERNAL LOCATION} | Int | +| variance.swift:59:24:59:56 | FunctionExpr | Return | {EXTERNAL LOCATION} | String | +| variance.swift:59:27:59:31 | value | | {EXTERNAL LOCATION} | Int | +| variance.swift:60:24:60:39 | boundMixedActual | | {EXTERNAL LOCATION} | Function | +| variance.swift:60:24:60:39 | boundMixedActual | Args | {EXTERNAL LOCATION} | Tuple1 | +| variance.swift:60:24:60:39 | boundMixedActual | Args.T0 | {EXTERNAL LOCATION} | Int | +| variance.swift:60:24:60:39 | boundMixedActual | Return | {EXTERNAL LOCATION} | String | +| variance.swift:65:22:65:26 | value | | {EXTERNAL LOCATION} | Array | +| variance.swift:65:22:65:26 | value | Element | variance.swift:3:1:3:14 | A | +| variance.swift:65:22:65:26 | value | Element.Ta | {EXTERNAL LOCATION} | Array | +| variance.swift:65:22:65:26 | value | Element.Ta.Element | variance.swift:65:17:65:17 | T | +| variance.swift:66:5:66:20 | boundShiftActual | | {EXTERNAL LOCATION} | Array | +| variance.swift:66:24:66:37 | ArrayLiteral | | {EXTERNAL LOCATION} | Array | +| variance.swift:67:26:67:41 | boundShiftActual | | {EXTERNAL LOCATION} | Array | +| variance.swift:69:26:69:30 | first | | variance.swift:69:21:69:21 | T | +| variance.swift:69:38:69:43 | values | | {EXTERNAL LOCATION} | Array | +| variance.swift:69:38:69:43 | values | Element | variance.swift:3:1:3:14 | A | +| variance.swift:69:38:69:43 | values | Element.Ta | {EXTERNAL LOCATION} | Array | +| variance.swift:69:38:69:43 | values | Element.Ta.Element | variance.swift:69:21:69:21 | T | +| variance.swift:70:19:70:27 | ArrayLiteral | | {EXTERNAL LOCATION} | Array | +| variance.swift:72:30:72:37 | callback | | {EXTERNAL LOCATION} | Function | +| variance.swift:72:30:72:37 | callback | Args | {EXTERNAL LOCATION} | Tuple1 | +| variance.swift:72:30:72:37 | callback | Args.T0 | variance.swift:3:1:3:14 | A | +| variance.swift:72:30:72:37 | callback | Args.T0.Ta | variance.swift:72:25:72:25 | T | +| variance.swift:72:30:72:37 | callback | Return | {EXTERNAL LOCATION} | Int | +| variance.swift:73:5:73:28 | boundContraReverseActual | | {EXTERNAL LOCATION} | Function | +| variance.swift:73:5:73:28 | boundContraReverseActual | Args | {EXTERNAL LOCATION} | Tuple1 | +| variance.swift:73:5:73:28 | boundContraReverseActual | Args.T0 | variance.swift:5:1:5:21 | B | +| variance.swift:73:5:73:28 | boundContraReverseActual | Args.T0.Tb | {EXTERNAL LOCATION} | Int | +| variance.swift:73:5:73:28 | boundContraReverseActual | Return | {EXTERNAL LOCATION} | Int | +| variance.swift:73:32:73:62 | FunctionExpr | | {EXTERNAL LOCATION} | Function | +| variance.swift:73:32:73:62 | FunctionExpr | Args | {EXTERNAL LOCATION} | Tuple1 | +| variance.swift:73:32:73:62 | FunctionExpr | Args.T0 | variance.swift:5:1:5:21 | B | +| variance.swift:73:32:73:62 | FunctionExpr | Args.T0.Tb | {EXTERNAL LOCATION} | Int | +| variance.swift:73:32:73:62 | FunctionExpr | Return | {EXTERNAL LOCATION} | Int | +| variance.swift:73:35:73:39 | value | | variance.swift:5:1:5:21 | B | +| variance.swift:73:35:73:39 | value | Tb | {EXTERNAL LOCATION} | Int | +| variance.swift:74:40:74:63 | boundContraReverseActual | | {EXTERNAL LOCATION} | Function | +| variance.swift:74:40:74:63 | boundContraReverseActual | Args | {EXTERNAL LOCATION} | Tuple1 | +| variance.swift:74:40:74:63 | boundContraReverseActual | Args.T0 | variance.swift:5:1:5:21 | B | +| variance.swift:74:40:74:63 | boundContraReverseActual | Args.T0.Tb | {EXTERNAL LOCATION} | Int | +| variance.swift:74:40:74:63 | boundContraReverseActual | Return | {EXTERNAL LOCATION} | Int | +| variance.swift:76:29:76:36 | callback | | {EXTERNAL LOCATION} | Function | +| variance.swift:76:29:76:36 | callback | Args | {EXTERNAL LOCATION} | Tuple1 | +| variance.swift:76:29:76:36 | callback | Args.T0 | {EXTERNAL LOCATION} | Function | +| variance.swift:76:29:76:36 | callback | Args.T0.Args | {EXTERNAL LOCATION} | Tuple1 | +| variance.swift:76:29:76:36 | callback | Args.T0.Args.T0 | variance.swift:3:1:3:14 | A | +| variance.swift:76:29:76:36 | callback | Args.T0.Args.T0.Ta | variance.swift:76:24:76:24 | T | +| variance.swift:76:29:76:36 | callback | Args.T0.Return | {EXTERNAL LOCATION} | Int | +| variance.swift:76:29:76:36 | callback | Return | {EXTERNAL LOCATION} | Int | +| variance.swift:77:5:77:27 | boundDoubleNestedActual | | {EXTERNAL LOCATION} | Function | +| variance.swift:77:5:77:27 | boundDoubleNestedActual | Args | {EXTERNAL LOCATION} | Tuple1 | +| variance.swift:77:5:77:27 | boundDoubleNestedActual | Args.T0 | {EXTERNAL LOCATION} | Function | +| variance.swift:77:5:77:27 | boundDoubleNestedActual | Args.T0.Args | {EXTERNAL LOCATION} | Tuple1 | +| variance.swift:77:5:77:27 | boundDoubleNestedActual | Args.T0.Args.T0 | variance.swift:5:1:5:21 | B | +| variance.swift:77:5:77:27 | boundDoubleNestedActual | Args.T0.Args.T0.Tb | {EXTERNAL LOCATION} | Int | +| variance.swift:77:5:77:27 | boundDoubleNestedActual | Args.T0.Return | {EXTERNAL LOCATION} | Int | +| variance.swift:77:5:77:27 | boundDoubleNestedActual | Return | {EXTERNAL LOCATION} | Int | +| variance.swift:77:31:77:73 | FunctionExpr | | {EXTERNAL LOCATION} | Function | +| variance.swift:77:31:77:73 | FunctionExpr | Args | {EXTERNAL LOCATION} | Tuple1 | +| variance.swift:77:31:77:73 | FunctionExpr | Args.T0 | {EXTERNAL LOCATION} | Function | +| variance.swift:77:31:77:73 | FunctionExpr | Args.T0.Args | {EXTERNAL LOCATION} | Tuple1 | +| variance.swift:77:31:77:73 | FunctionExpr | Args.T0.Args.T0 | variance.swift:5:1:5:21 | B | +| variance.swift:77:31:77:73 | FunctionExpr | Args.T0.Args.T0.Tb | {EXTERNAL LOCATION} | Int | +| variance.swift:77:31:77:73 | FunctionExpr | Args.T0.Return | {EXTERNAL LOCATION} | Int | +| variance.swift:77:31:77:73 | FunctionExpr | Return | {EXTERNAL LOCATION} | Int | +| variance.swift:77:34:77:41 | callback | | {EXTERNAL LOCATION} | Function | +| variance.swift:77:34:77:41 | callback | Args | {EXTERNAL LOCATION} | Tuple1 | +| variance.swift:77:34:77:41 | callback | Args.T0 | variance.swift:5:1:5:21 | B | +| variance.swift:77:34:77:41 | callback | Args.T0.Tb | {EXTERNAL LOCATION} | Int | +| variance.swift:77:34:77:41 | callback | Return | {EXTERNAL LOCATION} | Int | +| variance.swift:78:38:78:60 | boundDoubleNestedActual | | {EXTERNAL LOCATION} | Function | +| variance.swift:78:38:78:60 | boundDoubleNestedActual | Args | {EXTERNAL LOCATION} | Tuple1 | +| variance.swift:78:38:78:60 | boundDoubleNestedActual | Args.T0 | {EXTERNAL LOCATION} | Function | +| variance.swift:78:38:78:60 | boundDoubleNestedActual | Args.T0.Args | {EXTERNAL LOCATION} | Tuple1 | +| variance.swift:78:38:78:60 | boundDoubleNestedActual | Args.T0.Args.T0 | variance.swift:5:1:5:21 | B | +| variance.swift:78:38:78:60 | boundDoubleNestedActual | Args.T0.Args.T0.Tb | {EXTERNAL LOCATION} | Int | +| variance.swift:78:38:78:60 | boundDoubleNestedActual | Args.T0.Return | {EXTERNAL LOCATION} | Int | +| variance.swift:78:38:78:60 | boundDoubleNestedActual | Return | {EXTERNAL LOCATION} | Int | +| variance.swift:80:34:80:38 | value | | variance.swift:3:1:3:14 | A | +| variance.swift:80:34:80:38 | value | Ta | {EXTERNAL LOCATION} | Function | +| variance.swift:80:34:80:38 | value | Ta.Args | {EXTERNAL LOCATION} | Tuple1 | +| variance.swift:80:34:80:38 | value | Ta.Args.T0 | variance.swift:3:1:3:14 | A | +| variance.swift:80:34:80:38 | value | Ta.Args.T0.Ta | variance.swift:80:29:80:29 | T | +| variance.swift:80:34:80:38 | value | Ta.Return | {EXTERNAL LOCATION} | Int | +| variance.swift:84:25:84:29 | value | | {EXTERNAL LOCATION} | Optional | +| variance.swift:84:25:84:29 | value | Wrapped | variance.swift:3:1:3:14 | A | +| variance.swift:84:25:84:29 | value | Wrapped.Ta | variance.swift:84:20:84:20 | T | +| variance.swift:85:5:85:23 | boundOptionalActual | | {EXTERNAL LOCATION} | Optional | +| variance.swift:85:5:85:23 | boundOptionalActual | Wrapped | variance.swift:5:1:5:21 | B | +| variance.swift:85:5:85:23 | boundOptionalActual | Wrapped.Tb | {EXTERNAL LOCATION} | Int | +| variance.swift:86:30:86:48 | boundOptionalActual | | {EXTERNAL LOCATION} | Optional | +| variance.swift:86:30:86:48 | boundOptionalActual | Wrapped | variance.swift:5:1:5:21 | B | +| variance.swift:86:30:86:48 | boundOptionalActual | Wrapped.Tb | {EXTERNAL LOCATION} | Int | inferType | accessors.swift:4:7:4:12 | stored | | {EXTERNAL LOCATION} | Int | | accessors.swift:4:21:4:21 | 0 | | {EXTERNAL LOCATION} | Int | @@ -932,11 +1124,13 @@ inferType | accessors.swift:143:34:143:36 | Block | | {EXTERNAL LOCATION} | String | | accessors.swift:146:9:146:14 | result | | {EXTERNAL LOCATION} | String | | accessors.swift:146:18:146:25 | callback | | {EXTERNAL LOCATION} | Function | +| accessors.swift:146:18:146:25 | callback | Args | {EXTERNAL LOCATION} | Tuple0 | | accessors.swift:146:18:146:25 | callback | Return | {EXTERNAL LOCATION} | String | | accessors.swift:146:18:146:27 | callback(...) | | {EXTERNAL LOCATION} | String | | accessors.swift:147:9:147:15 | result2 | | {EXTERNAL LOCATION} | String | | accessors.swift:147:19:147:22 | self | | accessors.swift:142:1:149:1 | C | | accessors.swift:147:19:147:31 | ... .callback | | {EXTERNAL LOCATION} | Function | +| accessors.swift:147:19:147:31 | ... .callback | Args | {EXTERNAL LOCATION} | Tuple0 | | accessors.swift:147:19:147:31 | ... .callback | Return | {EXTERNAL LOCATION} | String | | accessors.swift:147:19:147:33 | ... .callback(...) | | {EXTERNAL LOCATION} | String | | accessors.swift:154:14:154:19 | stored | | {EXTERNAL LOCATION} | Int | @@ -3453,7 +3647,11 @@ inferType | variance.swift:11:41:11:51 | Block | | variance.swift:5:1:5:21 | B | | variance.swift:11:41:11:51 | Block | Tb | {EXTERNAL LOCATION} | String | | variance.swift:13:5:13:5 | x | | {EXTERNAL LOCATION} | Tuple2 | +| variance.swift:13:5:13:5 | x | T0 | {EXTERNAL LOCATION} | Int | +| variance.swift:13:5:13:5 | x | T1 | {EXTERNAL LOCATION} | String | | variance.swift:13:9:13:19 | foo(...) | | {EXTERNAL LOCATION} | Tuple2 | +| variance.swift:13:9:13:19 | foo(...) | T0 | {EXTERNAL LOCATION} | Int | +| variance.swift:13:9:13:19 | foo(...) | T1 | {EXTERNAL LOCATION} | String | | variance.swift:13:16:13:18 | lam | | {EXTERNAL LOCATION} | Function | | variance.swift:13:16:13:18 | lam | Args | {EXTERNAL LOCATION} | Tuple1 | | variance.swift:13:16:13:18 | lam | Args.T0 | variance.swift:3:1:3:14 | A | @@ -3469,4 +3667,339 @@ inferType | variance.swift:18:7:18:7 | x | | variance.swift:3:1:3:14 | A | | variance.swift:18:7:18:7 | x | Ta | {EXTERNAL LOCATION} | Int | | variance.swift:18:19:18:23 | bar(...) | | variance.swift:5:1:5:21 | B | +| variance.swift:18:19:18:23 | bar(...) | Tb | {EXTERNAL LOCATION} | Int | +| variance.swift:21:22:21:26 | value | | {EXTERNAL LOCATION} | Array | +| variance.swift:21:22:21:26 | value | Element | variance.swift:21:17:21:17 | T | +| variance.swift:22:5:22:20 | boundLowerActual | | {EXTERNAL LOCATION} | Array | +| variance.swift:22:5:22:20 | boundLowerActual | Element | {EXTERNAL LOCATION} | Int | +| variance.swift:22:24:22:26 | ArrayLiteral | | {EXTERNAL LOCATION} | Array | +| variance.swift:22:24:22:26 | ArrayLiteral | Element | {EXTERNAL LOCATION} | Int | +| variance.swift:22:25:22:25 | 1 | | {EXTERNAL LOCATION} | Int | +| variance.swift:23:5:23:9 | lower | | {EXTERNAL LOCATION} | Int | +| variance.swift:23:13:23:40 | boundLower(...) | | {EXTERNAL LOCATION} | Int | +| variance.swift:23:24:23:39 | boundLowerActual | | {EXTERNAL LOCATION} | Array | +| variance.swift:23:24:23:39 | boundLowerActual | Element | {EXTERNAL LOCATION} | Int | +| variance.swift:25:22:25:29 | callback | | {EXTERNAL LOCATION} | Function | +| variance.swift:25:22:25:29 | callback | Args | {EXTERNAL LOCATION} | Tuple1 | +| variance.swift:25:22:25:29 | callback | Args.T0 | variance.swift:25:17:25:17 | T | +| variance.swift:25:22:25:29 | callback | Return | {EXTERNAL LOCATION} | Int | +| variance.swift:26:5:26:20 | boundUpperActual | | {EXTERNAL LOCATION} | Function | +| variance.swift:26:5:26:20 | boundUpperActual | Args | {EXTERNAL LOCATION} | Tuple1 | +| variance.swift:26:5:26:20 | boundUpperActual | Args.T0 | {EXTERNAL LOCATION} | Int | +| variance.swift:26:5:26:20 | boundUpperActual | Return | {EXTERNAL LOCATION} | Int | +| variance.swift:26:24:26:55 | FunctionExpr | | {EXTERNAL LOCATION} | Function | +| variance.swift:26:24:26:55 | FunctionExpr | Args | {EXTERNAL LOCATION} | Tuple1 | +| variance.swift:26:24:26:55 | FunctionExpr | Args.T0 | {EXTERNAL LOCATION} | Int | +| variance.swift:26:24:26:55 | FunctionExpr | Return | {EXTERNAL LOCATION} | Int | +| variance.swift:26:27:26:31 | value | | {EXTERNAL LOCATION} | Int | +| variance.swift:26:49:26:53 | Block | | {EXTERNAL LOCATION} | Int | +| variance.swift:26:49:26:53 | value | | {EXTERNAL LOCATION} | Int | +| variance.swift:27:5:27:9 | upper | | {EXTERNAL LOCATION} | Int | +| variance.swift:27:13:27:40 | boundUpper(...) | | {EXTERNAL LOCATION} | Int | +| variance.swift:27:24:27:39 | boundUpperActual | | {EXTERNAL LOCATION} | Function | +| variance.swift:27:24:27:39 | boundUpperActual | Args | {EXTERNAL LOCATION} | Tuple1 | +| variance.swift:27:24:27:39 | boundUpperActual | Args.T0 | {EXTERNAL LOCATION} | Int | +| variance.swift:27:24:27:39 | boundUpperActual | Return | {EXTERNAL LOCATION} | Int | +| variance.swift:29:23:29:30 | callback | | {EXTERNAL LOCATION} | Function | +| variance.swift:29:23:29:30 | callback | Args | {EXTERNAL LOCATION} | Tuple1 | +| variance.swift:29:23:29:30 | callback | Args.T0 | {EXTERNAL LOCATION} | Function | +| variance.swift:29:23:29:30 | callback | Args.T0.Args | {EXTERNAL LOCATION} | Tuple1 | +| variance.swift:29:23:29:30 | callback | Args.T0.Args.T0 | variance.swift:29:18:29:18 | T | +| variance.swift:29:23:29:30 | callback | Args.T0.Return | {EXTERNAL LOCATION} | Int | +| variance.swift:29:23:29:30 | callback | Return | {EXTERNAL LOCATION} | Int | +| variance.swift:30:5:30:21 | boundDoubleActual | | {EXTERNAL LOCATION} | Function | +| variance.swift:30:5:30:21 | boundDoubleActual | Args | {EXTERNAL LOCATION} | Tuple1 | +| variance.swift:30:5:30:21 | boundDoubleActual | Args.T0 | {EXTERNAL LOCATION} | Function | +| variance.swift:30:5:30:21 | boundDoubleActual | Args.T0.Args | {EXTERNAL LOCATION} | Tuple1 | +| variance.swift:30:5:30:21 | boundDoubleActual | Args.T0.Args.T0 | {EXTERNAL LOCATION} | Int | +| variance.swift:30:5:30:21 | boundDoubleActual | Args.T0.Return | {EXTERNAL LOCATION} | Int | +| variance.swift:30:5:30:21 | boundDoubleActual | Return | {EXTERNAL LOCATION} | Int | +| variance.swift:30:25:30:74 | FunctionExpr | | {EXTERNAL LOCATION} | Function | +| variance.swift:30:25:30:74 | FunctionExpr | Args | {EXTERNAL LOCATION} | Tuple1 | +| variance.swift:30:25:30:74 | FunctionExpr | Args.T0 | {EXTERNAL LOCATION} | Function | +| variance.swift:30:25:30:74 | FunctionExpr | Args.T0.Args | {EXTERNAL LOCATION} | Tuple1 | +| variance.swift:30:25:30:74 | FunctionExpr | Args.T0.Args.T0 | {EXTERNAL LOCATION} | Int | +| variance.swift:30:25:30:74 | FunctionExpr | Args.T0.Return | {EXTERNAL LOCATION} | Int | +| variance.swift:30:25:30:74 | FunctionExpr | Return | {EXTERNAL LOCATION} | Int | +| variance.swift:30:28:30:35 | callback | | {EXTERNAL LOCATION} | Function | +| variance.swift:30:28:30:35 | callback | Args | {EXTERNAL LOCATION} | Tuple1 | +| variance.swift:30:28:30:35 | callback | Args.T0 | {EXTERNAL LOCATION} | Int | +| variance.swift:30:28:30:35 | callback | Return | {EXTERNAL LOCATION} | Int | +| variance.swift:30:62:30:69 | callback | | {EXTERNAL LOCATION} | Function | +| variance.swift:30:62:30:69 | callback | Args | {EXTERNAL LOCATION} | Tuple1 | +| variance.swift:30:62:30:69 | callback | Args.T0 | {EXTERNAL LOCATION} | Int | +| variance.swift:30:62:30:69 | callback | Return | {EXTERNAL LOCATION} | Int | +| variance.swift:30:62:30:72 | Block | | {EXTERNAL LOCATION} | Int | +| variance.swift:30:62:30:72 | callback(...) | | {EXTERNAL LOCATION} | Int | +| variance.swift:30:71:30:71 | 1 | | {EXTERNAL LOCATION} | Int | +| variance.swift:31:5:31:10 | double | | {EXTERNAL LOCATION} | Int | +| variance.swift:31:14:31:43 | boundDouble(...) | | {EXTERNAL LOCATION} | Int | +| variance.swift:31:26:31:42 | boundDoubleActual | | {EXTERNAL LOCATION} | Function | +| variance.swift:31:26:31:42 | boundDoubleActual | Args | {EXTERNAL LOCATION} | Tuple1 | +| variance.swift:31:26:31:42 | boundDoubleActual | Args.T0 | {EXTERNAL LOCATION} | Function | +| variance.swift:31:26:31:42 | boundDoubleActual | Args.T0.Args | {EXTERNAL LOCATION} | Tuple1 | +| variance.swift:31:26:31:42 | boundDoubleActual | Args.T0.Args.T0 | {EXTERNAL LOCATION} | Int | +| variance.swift:31:26:31:42 | boundDoubleActual | Args.T0.Return | {EXTERNAL LOCATION} | Int | +| variance.swift:31:26:31:42 | boundDoubleActual | Return | {EXTERNAL LOCATION} | Int | +| variance.swift:33:26:33:30 | value | | variance.swift:3:1:3:14 | A | +| variance.swift:33:26:33:30 | value | Ta | variance.swift:33:21:33:21 | T | +| variance.swift:34:5:34:24 | boundInvariantActual | | variance.swift:3:1:3:14 | A | +| variance.swift:34:5:34:24 | boundInvariantActual | Ta | {EXTERNAL LOCATION} | Int | +| variance.swift:34:28:34:33 | GenericTypeExpr | | variance.swift:3:1:3:14 | A | +| variance.swift:34:28:34:33 | GenericTypeExpr | Ta | {EXTERNAL LOCATION} | Int | +| variance.swift:34:28:34:35 | ...(...) | | variance.swift:3:1:3:14 | A | +| variance.swift:34:28:34:35 | ...(...) | Ta | {EXTERNAL LOCATION} | Int | +| variance.swift:35:5:35:13 | invariant | | {EXTERNAL LOCATION} | Int | +| variance.swift:35:17:35:52 | boundInvariant(...) | | {EXTERNAL LOCATION} | Int | +| variance.swift:35:32:35:51 | boundInvariantActual | | variance.swift:3:1:3:14 | A | +| variance.swift:35:32:35:51 | boundInvariantActual | Ta | {EXTERNAL LOCATION} | Int | +| variance.swift:37:23:37:27 | value | | {EXTERNAL LOCATION} | Array | +| variance.swift:37:23:37:27 | value | Element | variance.swift:3:1:3:14 | A | +| variance.swift:37:23:37:27 | value | Element.Ta | variance.swift:37:18:37:18 | T | +| variance.swift:38:5:38:21 | boundNestedActual | | {EXTERNAL LOCATION} | Array | +| variance.swift:38:5:38:21 | boundNestedActual | Element | variance.swift:5:1:5:21 | B | +| variance.swift:38:5:38:21 | boundNestedActual | Element.Tb | {EXTERNAL LOCATION} | Int | +| variance.swift:38:25:38:34 | ArrayLiteral | | {EXTERNAL LOCATION} | Array | +| variance.swift:38:25:38:34 | ArrayLiteral | Element | variance.swift:5:1:5:21 | B | +| variance.swift:38:25:38:34 | ArrayLiteral | Element.Tb | {EXTERNAL LOCATION} | Int | +| variance.swift:38:26:38:31 | GenericTypeExpr | | variance.swift:5:1:5:21 | B | +| variance.swift:38:26:38:31 | GenericTypeExpr | Tb | {EXTERNAL LOCATION} | Int | +| variance.swift:38:26:38:33 | ...(...) | | variance.swift:5:1:5:21 | B | +| variance.swift:38:26:38:33 | ...(...) | Tb | {EXTERNAL LOCATION} | Int | +| variance.swift:39:5:39:10 | nested | | {EXTERNAL LOCATION} | Int | +| variance.swift:39:14:39:43 | boundNested(...) | | {EXTERNAL LOCATION} | Int | +| variance.swift:39:26:39:42 | boundNestedActual | | {EXTERNAL LOCATION} | Array | +| variance.swift:39:26:39:42 | boundNestedActual | Element | variance.swift:5:1:5:21 | B | +| variance.swift:39:26:39:42 | boundNestedActual | Element.Tb | {EXTERNAL LOCATION} | Int | +| variance.swift:43:31:43:35 | value | | {EXTERNAL LOCATION} | Array | +| variance.swift:43:31:43:35 | value | Element | variance.swift:5:1:5:21 | B | +| variance.swift:43:31:43:35 | value | Element.Tb | variance.swift:43:26:43:26 | T | +| variance.swift:44:5:44:29 | boundWrongDirectionActual | | {EXTERNAL LOCATION} | Array | +| variance.swift:44:5:44:29 | boundWrongDirectionActual | Element | variance.swift:3:1:3:14 | A | +| variance.swift:44:5:44:29 | boundWrongDirectionActual | Element.Ta | {EXTERNAL LOCATION} | Int | +| variance.swift:44:33:44:42 | ArrayLiteral | | {EXTERNAL LOCATION} | Array | +| variance.swift:44:33:44:42 | ArrayLiteral | Element | variance.swift:3:1:3:14 | A | +| variance.swift:44:33:44:42 | ArrayLiteral | Element.Ta | {EXTERNAL LOCATION} | Int | +| variance.swift:44:34:44:39 | GenericTypeExpr | | variance.swift:3:1:3:14 | A | +| variance.swift:44:34:44:39 | GenericTypeExpr | Ta | {EXTERNAL LOCATION} | Int | +| variance.swift:44:34:44:41 | ...(...) | | variance.swift:3:1:3:14 | A | +| variance.swift:44:34:44:41 | ...(...) | Ta | {EXTERNAL LOCATION} | Int | +| variance.swift:45:42:45:66 | boundWrongDirectionActual | | {EXTERNAL LOCATION} | Array | +| variance.swift:45:42:45:66 | boundWrongDirectionActual | Element | variance.swift:3:1:3:14 | A | +| variance.swift:45:42:45:66 | boundWrongDirectionActual | Element.Ta | {EXTERNAL LOCATION} | Int | +| variance.swift:47:36:47:40 | value | | variance.swift:3:1:3:14 | A | +| variance.swift:47:36:47:40 | value | Ta | variance.swift:3:1:3:14 | A | +| variance.swift:47:36:47:40 | value | Ta.Ta | variance.swift:47:31:47:31 | T | +| variance.swift:48:5:48:34 | boundInvariantConversionActual | | variance.swift:3:1:3:14 | A | +| variance.swift:48:5:48:34 | boundInvariantConversionActual | Ta | variance.swift:5:1:5:21 | B | +| variance.swift:48:5:48:34 | boundInvariantConversionActual | Ta.Tb | {EXTERNAL LOCATION} | Int | +| variance.swift:48:38:48:46 | GenericTypeExpr | | variance.swift:3:1:3:14 | A | +| variance.swift:48:38:48:46 | GenericTypeExpr | Ta | variance.swift:5:1:5:21 | B | +| variance.swift:48:38:48:46 | GenericTypeExpr | Ta.Tb | {EXTERNAL LOCATION} | Int | +| variance.swift:48:38:48:48 | ...(...) | | variance.swift:3:1:3:14 | A | +| variance.swift:48:38:48:48 | ...(...) | Ta | variance.swift:5:1:5:21 | B | +| variance.swift:48:38:48:48 | ...(...) | Ta.Tb | {EXTERNAL LOCATION} | Int | +| variance.swift:49:52:49:81 | boundInvariantConversionActual | | variance.swift:3:1:3:14 | A | +| variance.swift:49:52:49:81 | boundInvariantConversionActual | Ta | variance.swift:5:1:5:21 | B | +| variance.swift:49:52:49:81 | boundInvariantConversionActual | Ta.Tb | {EXTERNAL LOCATION} | Int | +| variance.swift:51:24:51:28 | first | | variance.swift:51:19:51:19 | T | +| variance.swift:51:36:51:41 | values | | {EXTERNAL LOCATION} | Array | +| variance.swift:51:36:51:41 | values | Element | variance.swift:3:1:3:14 | A | +| variance.swift:51:36:51:41 | values | Element.Ta | variance.swift:51:19:51:19 | T | +| variance.swift:52:14:52:14 | 1 | | {EXTERNAL LOCATION} | Int | +| variance.swift:52:17:52:21 | ArrayLiteral | | {EXTERNAL LOCATION} | Array | +| variance.swift:52:17:52:21 | ArrayLiteral | Element | variance.swift:5:1:5:21 | B | +| variance.swift:52:17:52:21 | ArrayLiteral | Element.Tb | {EXTERNAL LOCATION} | Int | +| variance.swift:52:18:52:18 | B | | variance.swift:5:1:5:21 | B | +| variance.swift:52:18:52:18 | B | Tb | {EXTERNAL LOCATION} | Int | +| variance.swift:52:18:52:20 | B(...) | | variance.swift:5:1:5:21 | B | +| variance.swift:52:18:52:20 | B(...) | Tb | {EXTERNAL LOCATION} | Int | +| variance.swift:54:29:54:33 | first | | {EXTERNAL LOCATION} | Array | +| variance.swift:54:29:54:33 | first | Element | variance.swift:54:24:54:24 | T | +| variance.swift:54:43:54:48 | second | | {EXTERNAL LOCATION} | Array | +| variance.swift:54:43:54:48 | second | Element | variance.swift:54:24:54:24 | T | +| variance.swift:55:5:55:16 | alternatives | | {EXTERNAL LOCATION} | Int | +| variance.swift:55:5:55:16 | alternatives | | {EXTERNAL LOCATION} | String | +| variance.swift:55:20:55:48 | boundAlternatives(...) | | {EXTERNAL LOCATION} | Int | +| variance.swift:55:20:55:48 | boundAlternatives(...) | | {EXTERNAL LOCATION} | String | +| variance.swift:55:38:55:40 | ArrayLiteral | | {EXTERNAL LOCATION} | Array | +| variance.swift:55:38:55:40 | ArrayLiteral | Element | {EXTERNAL LOCATION} | Int | +| variance.swift:55:39:55:39 | 1 | | {EXTERNAL LOCATION} | Int | +| variance.swift:55:43:55:47 | ArrayLiteral | | {EXTERNAL LOCATION} | Array | +| variance.swift:55:43:55:47 | ArrayLiteral | Element | {EXTERNAL LOCATION} | String | +| variance.swift:55:44:55:46 | "x" | | {EXTERNAL LOCATION} | String | +| variance.swift:58:22:58:29 | callback | | {EXTERNAL LOCATION} | Function | +| variance.swift:58:22:58:29 | callback | Args | {EXTERNAL LOCATION} | Tuple1 | +| variance.swift:58:22:58:29 | callback | Args.T0 | variance.swift:58:17:58:17 | T | +| variance.swift:58:22:58:29 | callback | Return | variance.swift:58:17:58:17 | T | +| variance.swift:59:5:59:20 | boundMixedActual | | {EXTERNAL LOCATION} | Function | +| variance.swift:59:5:59:20 | boundMixedActual | Args | {EXTERNAL LOCATION} | Tuple1 | +| variance.swift:59:5:59:20 | boundMixedActual | Args.T0 | {EXTERNAL LOCATION} | Int | +| variance.swift:59:5:59:20 | boundMixedActual | Return | {EXTERNAL LOCATION} | String | +| variance.swift:59:24:59:56 | FunctionExpr | | {EXTERNAL LOCATION} | Function | +| variance.swift:59:24:59:56 | FunctionExpr | Args | {EXTERNAL LOCATION} | Tuple1 | +| variance.swift:59:24:59:56 | FunctionExpr | Args.T0 | {EXTERNAL LOCATION} | Int | +| variance.swift:59:24:59:56 | FunctionExpr | Return | {EXTERNAL LOCATION} | String | +| variance.swift:59:27:59:31 | value | | {EXTERNAL LOCATION} | Int | +| variance.swift:59:52:59:54 | "x" | | {EXTERNAL LOCATION} | String | +| variance.swift:59:52:59:54 | Block | | {EXTERNAL LOCATION} | String | +| variance.swift:60:5:60:9 | mixed | | {EXTERNAL LOCATION} | Int | +| variance.swift:60:5:60:9 | mixed | | {EXTERNAL LOCATION} | String | +| variance.swift:60:13:60:40 | boundMixed(...) | | {EXTERNAL LOCATION} | Int | +| variance.swift:60:13:60:40 | boundMixed(...) | | {EXTERNAL LOCATION} | String | +| variance.swift:60:24:60:39 | boundMixedActual | | {EXTERNAL LOCATION} | Function | +| variance.swift:60:24:60:39 | boundMixedActual | Args | {EXTERNAL LOCATION} | Tuple1 | +| variance.swift:60:24:60:39 | boundMixedActual | Args.T0 | {EXTERNAL LOCATION} | Int | +| variance.swift:60:24:60:39 | boundMixedActual | Return | {EXTERNAL LOCATION} | String | +| variance.swift:63:19:63:34 | Block | | variance.swift:3:1:3:14 | A | +| variance.swift:63:19:63:34 | Block | Ta | {EXTERNAL LOCATION} | Array | +| variance.swift:63:19:63:34 | Block | Ta.Element | variance.swift:62:13:62:13 | C | +| variance.swift:63:21:63:25 | super | | variance.swift:3:1:3:14 | A | +| variance.swift:63:21:63:25 | super | Ta | {EXTERNAL LOCATION} | Array | +| variance.swift:63:21:63:25 | super | Ta.Element | variance.swift:62:13:62:13 | C | +| variance.swift:63:21:63:32 | ... .init(...) | | variance.swift:3:1:3:14 | A | +| variance.swift:63:21:63:32 | ... .init(...) | Ta | {EXTERNAL LOCATION} | Array | +| variance.swift:63:21:63:32 | ... .init(...) | Ta.Element | variance.swift:62:13:62:13 | C | +| variance.swift:65:22:65:26 | value | | {EXTERNAL LOCATION} | Array | +| variance.swift:65:22:65:26 | value | Element | variance.swift:3:1:3:14 | A | +| variance.swift:65:22:65:26 | value | Element.Ta | {EXTERNAL LOCATION} | Array | +| variance.swift:65:22:65:26 | value | Element.Ta.Element | variance.swift:65:17:65:17 | T | +| variance.swift:66:5:66:20 | boundShiftActual | | {EXTERNAL LOCATION} | Array | +| variance.swift:66:5:66:20 | boundShiftActual | Element | variance.swift:62:1:64:1 | Shift | +| variance.swift:66:5:66:20 | boundShiftActual | Element.C | {EXTERNAL LOCATION} | Int | +| variance.swift:66:24:66:37 | ArrayLiteral | | {EXTERNAL LOCATION} | Array | +| variance.swift:66:24:66:37 | ArrayLiteral | Element | variance.swift:62:1:64:1 | Shift | +| variance.swift:66:24:66:37 | ArrayLiteral | Element.C | {EXTERNAL LOCATION} | Int | +| variance.swift:66:25:66:34 | GenericTypeExpr | | variance.swift:62:1:64:1 | Shift | +| variance.swift:66:25:66:34 | GenericTypeExpr | C | {EXTERNAL LOCATION} | Int | +| variance.swift:66:25:66:36 | ...(...) | | variance.swift:62:1:64:1 | Shift | +| variance.swift:66:25:66:36 | ...(...) | C | {EXTERNAL LOCATION} | Int | +| variance.swift:67:5:67:11 | shifted | | {EXTERNAL LOCATION} | Int | +| variance.swift:67:15:67:42 | boundShift(...) | | {EXTERNAL LOCATION} | Int | +| variance.swift:67:26:67:41 | boundShiftActual | | {EXTERNAL LOCATION} | Array | +| variance.swift:67:26:67:41 | boundShiftActual | Element | variance.swift:62:1:64:1 | Shift | +| variance.swift:67:26:67:41 | boundShiftActual | Element.C | {EXTERNAL LOCATION} | Int | +| variance.swift:69:26:69:30 | first | | variance.swift:69:21:69:21 | T | +| variance.swift:69:38:69:43 | values | | {EXTERNAL LOCATION} | Array | +| variance.swift:69:38:69:43 | values | Element | variance.swift:3:1:3:14 | A | +| variance.swift:69:38:69:43 | values | Element.Ta | {EXTERNAL LOCATION} | Array | +| variance.swift:69:38:69:43 | values | Element.Ta.Element | variance.swift:69:21:69:21 | T | +| variance.swift:70:16:70:16 | 1 | | {EXTERNAL LOCATION} | Int | +| variance.swift:70:19:70:27 | ArrayLiteral | | {EXTERNAL LOCATION} | Array | +| variance.swift:70:19:70:27 | ArrayLiteral | Element | variance.swift:62:1:64:1 | Shift | +| variance.swift:70:19:70:27 | ArrayLiteral | Element.C | {EXTERNAL LOCATION} | Int | +| variance.swift:70:20:70:24 | Shift | | variance.swift:62:1:64:1 | Shift | +| variance.swift:70:20:70:24 | Shift | C | {EXTERNAL LOCATION} | Int | +| variance.swift:70:20:70:26 | Shift(...) | | variance.swift:62:1:64:1 | Shift | +| variance.swift:70:20:70:26 | Shift(...) | C | {EXTERNAL LOCATION} | Int | +| variance.swift:72:30:72:37 | callback | | {EXTERNAL LOCATION} | Function | +| variance.swift:72:30:72:37 | callback | Args | {EXTERNAL LOCATION} | Tuple1 | +| variance.swift:72:30:72:37 | callback | Args.T0 | variance.swift:3:1:3:14 | A | +| variance.swift:72:30:72:37 | callback | Args.T0.Ta | variance.swift:72:25:72:25 | T | +| variance.swift:72:30:72:37 | callback | Return | {EXTERNAL LOCATION} | Int | +| variance.swift:73:5:73:28 | boundContraReverseActual | | {EXTERNAL LOCATION} | Function | +| variance.swift:73:5:73:28 | boundContraReverseActual | Args | {EXTERNAL LOCATION} | Tuple1 | +| variance.swift:73:5:73:28 | boundContraReverseActual | Args.T0 | variance.swift:5:1:5:21 | B | +| variance.swift:73:5:73:28 | boundContraReverseActual | Args.T0.Tb | {EXTERNAL LOCATION} | Int | +| variance.swift:73:5:73:28 | boundContraReverseActual | Return | {EXTERNAL LOCATION} | Int | +| variance.swift:73:32:73:62 | FunctionExpr | | {EXTERNAL LOCATION} | Function | +| variance.swift:73:32:73:62 | FunctionExpr | Args | {EXTERNAL LOCATION} | Tuple1 | +| variance.swift:73:32:73:62 | FunctionExpr | Args.T0 | variance.swift:5:1:5:21 | B | +| variance.swift:73:32:73:62 | FunctionExpr | Args.T0.Tb | {EXTERNAL LOCATION} | Int | +| variance.swift:73:32:73:62 | FunctionExpr | Return | {EXTERNAL LOCATION} | Int | +| variance.swift:73:35:73:39 | value | | variance.swift:5:1:5:21 | B | +| variance.swift:73:35:73:39 | value | Tb | {EXTERNAL LOCATION} | Int | +| variance.swift:73:60:73:60 | 1 | | {EXTERNAL LOCATION} | Int | +| variance.swift:73:60:73:60 | Block | | {EXTERNAL LOCATION} | Int | +| variance.swift:74:40:74:63 | boundContraReverseActual | | {EXTERNAL LOCATION} | Function | +| variance.swift:74:40:74:63 | boundContraReverseActual | Args | {EXTERNAL LOCATION} | Tuple1 | +| variance.swift:74:40:74:63 | boundContraReverseActual | Args.T0 | variance.swift:5:1:5:21 | B | +| variance.swift:74:40:74:63 | boundContraReverseActual | Args.T0.Tb | {EXTERNAL LOCATION} | Int | +| variance.swift:74:40:74:63 | boundContraReverseActual | Return | {EXTERNAL LOCATION} | Int | +| variance.swift:76:29:76:36 | callback | | {EXTERNAL LOCATION} | Function | +| variance.swift:76:29:76:36 | callback | Args | {EXTERNAL LOCATION} | Tuple1 | +| variance.swift:76:29:76:36 | callback | Args.T0 | {EXTERNAL LOCATION} | Function | +| variance.swift:76:29:76:36 | callback | Args.T0.Args | {EXTERNAL LOCATION} | Tuple1 | +| variance.swift:76:29:76:36 | callback | Args.T0.Args.T0 | variance.swift:3:1:3:14 | A | +| variance.swift:76:29:76:36 | callback | Args.T0.Args.T0.Ta | variance.swift:76:24:76:24 | T | +| variance.swift:76:29:76:36 | callback | Args.T0.Return | {EXTERNAL LOCATION} | Int | +| variance.swift:76:29:76:36 | callback | Return | {EXTERNAL LOCATION} | Int | +| variance.swift:77:5:77:27 | boundDoubleNestedActual | | {EXTERNAL LOCATION} | Function | +| variance.swift:77:5:77:27 | boundDoubleNestedActual | Args | {EXTERNAL LOCATION} | Tuple1 | +| variance.swift:77:5:77:27 | boundDoubleNestedActual | Args.T0 | {EXTERNAL LOCATION} | Function | +| variance.swift:77:5:77:27 | boundDoubleNestedActual | Args.T0.Args | {EXTERNAL LOCATION} | Tuple1 | +| variance.swift:77:5:77:27 | boundDoubleNestedActual | Args.T0.Args.T0 | variance.swift:5:1:5:21 | B | +| variance.swift:77:5:77:27 | boundDoubleNestedActual | Args.T0.Args.T0.Tb | {EXTERNAL LOCATION} | Int | +| variance.swift:77:5:77:27 | boundDoubleNestedActual | Args.T0.Return | {EXTERNAL LOCATION} | Int | +| variance.swift:77:5:77:27 | boundDoubleNestedActual | Return | {EXTERNAL LOCATION} | Int | +| variance.swift:77:31:77:73 | FunctionExpr | | {EXTERNAL LOCATION} | Function | +| variance.swift:77:31:77:73 | FunctionExpr | Args | {EXTERNAL LOCATION} | Tuple1 | +| variance.swift:77:31:77:73 | FunctionExpr | Args.T0 | {EXTERNAL LOCATION} | Function | +| variance.swift:77:31:77:73 | FunctionExpr | Args.T0.Args | {EXTERNAL LOCATION} | Tuple1 | +| variance.swift:77:31:77:73 | FunctionExpr | Args.T0.Args.T0 | variance.swift:5:1:5:21 | B | +| variance.swift:77:31:77:73 | FunctionExpr | Args.T0.Args.T0.Tb | {EXTERNAL LOCATION} | Int | +| variance.swift:77:31:77:73 | FunctionExpr | Args.T0.Return | {EXTERNAL LOCATION} | Int | +| variance.swift:77:31:77:73 | FunctionExpr | Return | {EXTERNAL LOCATION} | Int | +| variance.swift:77:34:77:41 | callback | | {EXTERNAL LOCATION} | Function | +| variance.swift:77:34:77:41 | callback | Args | {EXTERNAL LOCATION} | Tuple1 | +| variance.swift:77:34:77:41 | callback | Args.T0 | variance.swift:5:1:5:21 | B | +| variance.swift:77:34:77:41 | callback | Args.T0.Tb | {EXTERNAL LOCATION} | Int | +| variance.swift:77:34:77:41 | callback | Return | {EXTERNAL LOCATION} | Int | +| variance.swift:77:71:77:71 | 1 | | {EXTERNAL LOCATION} | Int | +| variance.swift:77:71:77:71 | Block | | {EXTERNAL LOCATION} | Int | +| variance.swift:78:5:78:16 | doubleNested | | {EXTERNAL LOCATION} | Int | +| variance.swift:78:20:78:61 | boundDoubleNested(...) | | {EXTERNAL LOCATION} | Int | +| variance.swift:78:38:78:60 | boundDoubleNestedActual | | {EXTERNAL LOCATION} | Function | +| variance.swift:78:38:78:60 | boundDoubleNestedActual | Args | {EXTERNAL LOCATION} | Tuple1 | +| variance.swift:78:38:78:60 | boundDoubleNestedActual | Args.T0 | {EXTERNAL LOCATION} | Function | +| variance.swift:78:38:78:60 | boundDoubleNestedActual | Args.T0.Args | {EXTERNAL LOCATION} | Tuple1 | +| variance.swift:78:38:78:60 | boundDoubleNestedActual | Args.T0.Args.T0 | variance.swift:5:1:5:21 | B | +| variance.swift:78:38:78:60 | boundDoubleNestedActual | Args.T0.Args.T0.Tb | {EXTERNAL LOCATION} | Int | +| variance.swift:78:38:78:60 | boundDoubleNestedActual | Args.T0.Return | {EXTERNAL LOCATION} | Int | +| variance.swift:78:38:78:60 | boundDoubleNestedActual | Return | {EXTERNAL LOCATION} | Int | +| variance.swift:80:34:80:38 | value | | variance.swift:3:1:3:14 | A | +| variance.swift:80:34:80:38 | value | Ta | {EXTERNAL LOCATION} | Function | +| variance.swift:80:34:80:38 | value | Ta.Args | {EXTERNAL LOCATION} | Tuple1 | +| variance.swift:80:34:80:38 | value | Ta.Args.T0 | variance.swift:3:1:3:14 | A | +| variance.swift:80:34:80:38 | value | Ta.Args.T0.Ta | variance.swift:80:29:80:29 | T | +| variance.swift:80:34:80:38 | value | Ta.Return | {EXTERNAL LOCATION} | Int | +| variance.swift:81:5:81:32 | boundInvariantFunctionActual | | variance.swift:3:1:3:14 | A | +| variance.swift:81:5:81:32 | boundInvariantFunctionActual | Ta | {EXTERNAL LOCATION} | Function | +| variance.swift:81:5:81:32 | boundInvariantFunctionActual | Ta.Args | {EXTERNAL LOCATION} | Tuple1 | +| variance.swift:81:5:81:32 | boundInvariantFunctionActual | Ta.Args.T0 | variance.swift:5:1:5:21 | B | +| variance.swift:81:5:81:32 | boundInvariantFunctionActual | Ta.Args.T0.Tb | {EXTERNAL LOCATION} | Int | +| variance.swift:81:5:81:32 | boundInvariantFunctionActual | Ta.Return | {EXTERNAL LOCATION} | Int | +| variance.swift:81:36:81:53 | GenericTypeExpr | | variance.swift:3:1:3:14 | A | +| variance.swift:81:36:81:53 | GenericTypeExpr | Ta | {EXTERNAL LOCATION} | Function | +| variance.swift:81:36:81:53 | GenericTypeExpr | Ta.Args | {EXTERNAL LOCATION} | Tuple1 | +| variance.swift:81:36:81:53 | GenericTypeExpr | Ta.Args.T0 | variance.swift:5:1:5:21 | B | +| variance.swift:81:36:81:53 | GenericTypeExpr | Ta.Args.T0.Tb | {EXTERNAL LOCATION} | Int | +| variance.swift:81:36:81:53 | GenericTypeExpr | Ta.Return | {EXTERNAL LOCATION} | Int | +| variance.swift:81:36:81:55 | ...(...) | | variance.swift:3:1:3:14 | A | +| variance.swift:81:36:81:55 | ...(...) | Ta | {EXTERNAL LOCATION} | Function | +| variance.swift:81:36:81:55 | ...(...) | Ta.Args | {EXTERNAL LOCATION} | Tuple1 | +| variance.swift:81:36:81:55 | ...(...) | Ta.Args.T0 | variance.swift:5:1:5:21 | B | +| variance.swift:81:36:81:55 | ...(...) | Ta.Args.T0.Tb | {EXTERNAL LOCATION} | Int | +| variance.swift:81:36:81:55 | ...(...) | Ta.Return | {EXTERNAL LOCATION} | Int | +| variance.swift:82:48:82:75 | boundInvariantFunctionActual | | variance.swift:3:1:3:14 | A | +| variance.swift:82:48:82:75 | boundInvariantFunctionActual | Ta | {EXTERNAL LOCATION} | Function | +| variance.swift:82:48:82:75 | boundInvariantFunctionActual | Ta.Args | {EXTERNAL LOCATION} | Tuple1 | +| variance.swift:82:48:82:75 | boundInvariantFunctionActual | Ta.Args.T0 | variance.swift:5:1:5:21 | B | +| variance.swift:82:48:82:75 | boundInvariantFunctionActual | Ta.Args.T0.Tb | {EXTERNAL LOCATION} | Int | +| variance.swift:82:48:82:75 | boundInvariantFunctionActual | Ta.Return | {EXTERNAL LOCATION} | Int | +| variance.swift:84:25:84:29 | value | | {EXTERNAL LOCATION} | Optional | +| variance.swift:84:25:84:29 | value | Wrapped | variance.swift:3:1:3:14 | A | +| variance.swift:84:25:84:29 | value | Wrapped.Ta | variance.swift:84:20:84:20 | T | +| variance.swift:85:5:85:23 | boundOptionalActual | | {EXTERNAL LOCATION} | Optional | +| variance.swift:85:5:85:23 | boundOptionalActual | Wrapped | variance.swift:5:1:5:21 | B | +| variance.swift:85:5:85:23 | boundOptionalActual | Wrapped.Tb | {EXTERNAL LOCATION} | Int | +| variance.swift:85:36:85:41 | GenericTypeExpr | | variance.swift:5:1:5:21 | B | +| variance.swift:85:36:85:41 | GenericTypeExpr | Tb | {EXTERNAL LOCATION} | Int | +| variance.swift:85:36:85:43 | ...(...) | | variance.swift:5:1:5:21 | B | +| variance.swift:85:36:85:43 | ...(...) | Tb | {EXTERNAL LOCATION} | Int | +| variance.swift:86:5:86:12 | optional | | {EXTERNAL LOCATION} | Int | +| variance.swift:86:16:86:49 | boundOptional(...) | | {EXTERNAL LOCATION} | Int | +| variance.swift:86:30:86:48 | boundOptionalActual | | {EXTERNAL LOCATION} | Optional | +| variance.swift:86:30:86:48 | boundOptionalActual | Wrapped | variance.swift:5:1:5:21 | B | +| variance.swift:86:30:86:48 | boundOptionalActual | Wrapped.Tb | {EXTERNAL LOCATION} | Int | testFailures diff --git a/unified/ql/test/library-tests/type-inference/variance-bounds.expected b/unified/ql/test/library-tests/type-inference/variance-bounds.expected new file mode 100644 index 000000000000..ea0560eb9657 --- /dev/null +++ b/unified/ql/test/library-tests/type-inference/variance-bounds.expected @@ -0,0 +1,16 @@ +bounds +| boundDoubleActual | T | 1 | | Int | +| boundDoubleNestedActual | T | 0 | | Int | +| boundInvariantActual | T | 0 | | Int | +| boundLowerActual | T | 1 | | Int | +| boundMixedActual | T | 1 | | String | +| boundMixedActual | T | -1 | | Int | +| boundNestedActual | T | 0 | | Int | +| boundOptionalActual | T | 0 | | Int | +| boundShiftActual | T | 0 | | Int | +| boundUpperActual | T | -1 | | Int | +noBounds +| boundContraReverseActual | +| boundInvariantConversionActual | +| boundInvariantFunctionActual | +| boundWrongDirectionActual | diff --git a/unified/ql/test/library-tests/type-inference/variance-bounds.ql b/unified/ql/test/library-tests/type-inference/variance-bounds.ql new file mode 100644 index 000000000000..ebe637cc97b2 --- /dev/null +++ b/unified/ql/test/library-tests/type-inference/variance-bounds.ql @@ -0,0 +1,39 @@ +import codeql.unified.internal.typeinference.Type +import codeql.unified.internal.typeinference.TypeMention +import codeql.unified.internal.typeinference.TypeInference + +private class Actual extends Identifier { + Actual() { + this.getValue().matches("bound%Actual") and + this.getFile().getBaseName() = "variance.swift" and + exists(inferType(this, TypePath::nil())) + } + + Type getTypeAt(TypePath path) { result = inferType(this, path) } +} + +private module Input implements VarianceMatchingInputSig { + predicate relevantConstraint(Actual actual, TypeMention constraint) { + exists(actual) and + constraint = + any(FunctionDeclaration f | + actual.getValue() = f.getName() + "Actual" and f.getFile().getBaseName() = "variance.swift" + ).getParameter(0).getType() + } +} + +private module Match = VarianceMatching; + +query predicate bounds(string name, string parameter, int relation, string path, string type) { + exists(Actual actual, TypeMention constraint, TypeParameter tp, TypePath p, Type t | + Match::typeBound(actual, constraint, tp, relation, p, t) and + name = actual.getValue() and + parameter = tp.toString() and + path = TypePath::printTypePathVerbose(p) and + type = t.toString() + ) +} + +query predicate noBounds(string name) { + exists(Actual actual | name = actual.getValue() and not Match::typeBound(actual, _, _, _, _, _)) +} diff --git a/unified/ql/test/library-tests/type-inference/variance.swift b/unified/ql/test/library-tests/type-inference/variance.swift index 831da050ced5..ca2e6e2c6fcd 100644 --- a/unified/ql/test/library-tests/type-inference/variance.swift +++ b/unified/ql/test/library-tests/type-inference/variance.swift @@ -10,12 +10,12 @@ func foo(f: (B) -> A) -> (T1, T2) { let lam = { (a: A) -> B in B() } // $ target=A.init -let x = foo(f: lam) // $ target=foo $ MISSING: type=x@Tuple2:Int type=x@Tuple2:String +let x = foo(f: lam) // $ target=foo type=x@Tuple2:Int type=x@Tuple2:String func bar() -> B { B() } // $ target=A.init func baz() { - let x: A = bar() // $ target=bar $ MISSING: type=bar(...)@B:Int + let x: A = bar() // $ target=bar type=bar(...)@B:Int } func boundLower(_ value: [T]) -> T { fatalError() } @@ -36,7 +36,7 @@ let invariant = boundInvariant(boundInvariantActual) // $ target=boundInvariant func boundNested(_ value: [A]) -> T { fatalError() } let boundNestedActual = [B()] // $ target=A.init -let nested = boundNested(boundNestedActual) // $ target=boundNested $ MISSING: type=nested:Int +let nested = boundNested(boundNestedActual) // $ target=boundNested type=nested:Int // Neither reversing a covariant conversion nor converting through an invariant // parameter can establish a bound. These calls intentionally have incompatible types. @@ -49,7 +49,7 @@ let boundInvariantConversionActual = A>() // $ target=A.init let invariantConversion = boundInvariantConversion(boundInvariantConversionActual) // $ target=boundInvariantConversion func boundContext(_ first: T, _ values: [A]) {} -boundContext(1, [B()]) // $ target=boundContext target=A.init $ MISSING: type=B(...)@B:Int +boundContext(1, [B()]) // $ target=boundContext target=A.init type=B(...)@B:Int func boundAlternatives(_ first: [T], _ second: [T]) -> T { fatalError() } let alternatives = boundAlternatives([1], ["x"]) // $ target=boundAlternatives type=alternatives:Int type=alternatives:String @@ -64,10 +64,10 @@ class Shift: A<[C]> { } func boundShift(_ value: [A<[T]>]) -> T { fatalError() } let boundShiftActual = [Shift()] // $ target=Shift.init -let shifted = boundShift(boundShiftActual) // $ target=boundShift $ MISSING: type=shifted:Int +let shifted = boundShift(boundShiftActual) // $ target=boundShift type=shifted:Int func shiftedContext(_ first: T, _ values: [A<[T]>]) {} -shiftedContext(1, [Shift()]) // $ target=shiftedContext target=Shift.init $ MISSING: type=Shift(...)@Shift:Int +shiftedContext(1, [Shift()]) // $ target=shiftedContext target=Shift.init type=Shift(...)@Shift:Int func boundContraReverse(_ callback: (A) -> Int) -> T { fatalError() } let boundContraReverseActual = { (value: B) -> Int in 1 } @@ -75,7 +75,7 @@ let contraReverse = boundContraReverse(boundContraReverseActual) // $ target=bo func boundDoubleNested(_ callback: ((A) -> Int) -> Int) -> T { fatalError() } let boundDoubleNestedActual = { (callback: (B) -> Int) -> Int in 1 } -let doubleNested = boundDoubleNested(boundDoubleNestedActual) // $ target=boundDoubleNested $ MISSING: type=doubleNested:Int +let doubleNested = boundDoubleNested(boundDoubleNestedActual) // $ target=boundDoubleNested type=doubleNested:Int func boundInvariantFunction(_ value: A<(A) -> Int>) -> T { fatalError() } let boundInvariantFunctionActual = A<(B) -> Int>() // $ target=A.init @@ -83,4 +83,4 @@ let invariantFunction = boundInvariantFunction(boundInvariantFunctionActual) // func boundOptional(_ value: A?) -> T { fatalError() } let boundOptionalActual: B? = B() // $ target=A.init -let optional = boundOptional(boundOptionalActual) // $ target=boundOptional $ MISSING: type=optional:Int +let optional = boundOptional(boundOptionalActual) // $ target=boundOptional type=optional:Int