Skip to content

Commit 4e6a1ef

Browse files
committed
wip
1 parent 80c4bfb commit 4e6a1ef

5 files changed

Lines changed: 140 additions & 75 deletions

File tree

‎shared/typeinference/codeql/typeinference/internal/TypeInference.qll‎

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -2685,7 +2685,12 @@ module Make1<LocationSig Location, InputSig1<Location> Input1> {
26852685
*/
26862686
default Type inferTypeCertainLanguageSpecific(AstNode n, TypePath path) { none() }
26872687

2688-
default predicate allowContextualInference(AstNode n, TypePath path) { none() }
2688+
/**
2689+
* Holds if full contextual inference is allowed for the node `n` at the
2690+
* type path `path`. This means that `n` does not need to be assigned
2691+
* `UnknownType` at (a prefix of) `path`.
2692+
*/
2693+
default predicate allowFullContextualInference(AstNode n, TypePath path) { none() }
26892694
}
26902695

26912696
module Make3<InputSig3 Input3> {
@@ -3036,7 +3041,7 @@ module Make1<LocationSig Location, InputSig1<Location> Input1> {
30363041
// `inferTypeContextualCand2` performs the proper check for contextual
30373042
// typing, but we can already rule out cases where receivers don't have
30383043
// an unknown type anywhere
3039-
ContextualTyping::hasUnknownType(receiver, _)
3044+
ContextualTyping::hasUnknownType(receiver)
30403045
)
30413046
}
30423047

@@ -3225,7 +3230,7 @@ module Make1<LocationSig Location, InputSig1<Location> Input1> {
32253230
// `inferTypeContextualCand2` performs the proper check for contextual
32263231
// typing, but we can already rule out cases where arguments don't have
32273232
// an unknown type anywhere
3228-
ContextualTyping::hasUnknownType(arg, _)
3233+
ContextualTyping::hasUnknownType(arg)
32293234
}
32303235

32313236
/**
@@ -3323,14 +3328,14 @@ module Make1<LocationSig Location, InputSig1<Location> Input1> {
33233328
ContextualReturnType<DefaultContextualReturnTypeInput>::needsContextualTyping/2;
33243329

33253330
pragma[nomagic]
3326-
predicate hasUnknownTypeAt(AstNode n, TypePath path, boolean actual) {
3327-
inferType(n, path) instanceof UnknownType and actual = true
3331+
predicate hasUnknownTypeAt(AstNode n, TypePath path) {
3332+
inferType(n, path) instanceof UnknownType
33283333
or
3329-
allowContextualInference(n, path) and actual = false
3334+
allowFullContextualInference(n, path)
33303335
}
33313336

33323337
pragma[nomagic]
3333-
predicate hasUnknownType(AstNode n, boolean actual) { hasUnknownTypeAt(n, _, actual) }
3338+
predicate hasUnknownType(AstNode n) { hasUnknownTypeAt(n, _) }
33343339

33353340
pragma[nomagic]
33363341
private Type inferTypeContextualCand0(AstNode n, TypePath path) {
@@ -3355,19 +3360,14 @@ module Make1<LocationSig Location, InputSig1<Location> Input1> {
33553360
private Type inferTypeContextualCand1(AstNode n, TypePath prefix, TypePath path) {
33563361
result = inferTypeContextualCand0(n, path) and
33573362
prefix = path.getAPrefix() and
3358-
exists(boolean actual |
3359-
hasUnknownType(n, actual) and
3360-
// no need to propagate `UnknownType`s contextually; `n` must already have an
3361-
// `UnknownType` at some prefix of `path`
3362-
if result instanceof UnknownType then actual = false else any()
3363-
)
3363+
hasUnknownType(n)
33643364
}
33653365

33663366
pragma[nomagic]
33673367
private Type inferTypeContextualCand2(AstNode n, TypePath path) {
33683368
exists(TypePath prefix |
33693369
result = inferTypeContextualCand1(n, prefix, path) and
3370-
hasUnknownTypeAt(n, prefix, _)
3370+
hasUnknownTypeAt(n, prefix)
33713371
)
33723372
}
33733373

@@ -3378,7 +3378,7 @@ module Make1<LocationSig Location, InputSig1<Location> Input1> {
33783378
*/
33793379
pragma[nomagic]
33803380
private predicate isValidContextualNonEmptyPath(AstNode n, TypePath path) {
3381-
hasUnknownType(n, _) and
3381+
hasUnknownType(n) and
33823382
exists(TypePath prefix, TypeParameter tp |
33833383
tp = inferType(n, prefix).getATypeParameter() and
33843384
path = TypePath::snoc(prefix, tp)
@@ -3399,7 +3399,7 @@ module Make1<LocationSig Location, InputSig1<Location> Input1> {
33993399
or
34003400
isValidContextualNonEmptyPath(n, path)
34013401
or
3402-
allowContextualInference(n, path.getAPrefix())
3402+
allowFullContextualInference(n, path.getAPrefix())
34033403
)
34043404
}
34053405
}
@@ -3520,7 +3520,7 @@ module Make1<LocationSig Location, InputSig1<Location> Input1> {
35203520
AstNode n, TypePath prefix, int i, TypePath suffix, Type t
35213521
) {
35223522
exists(TypeParameter tp, TypePath suffix0 |
3523-
ContextualTyping::hasUnknownTypeAt(n, prefix, _) and
3523+
ContextualTyping::hasUnknownTypeAt(n, prefix) and
35243524
suffix0.isCons(tp, suffix) and
35253525
tp = any(UnknownType ut).getPositionalTypeParameter(i) and
35263526
t = inferType(n, prefix.appendInverse(suffix0)) and
@@ -3530,7 +3530,7 @@ module Make1<LocationSig Location, InputSig1<Location> Input1> {
35303530

35313531
pragma[nomagic]
35323532
private predicate infersKnownAndUnknownType(AstNode n, TypePath path, int i, TypeParameter tp) {
3533-
ContextualTyping::hasUnknownTypeAt(n, path, _) and
3533+
ContextualTyping::hasUnknownTypeAt(n, path) and
35343534
exists(Type t |
35353535
t = inferType(n, path) and
35363536
not t instanceof UnknownType and

‎unified/ql/lib/codeql/unified/internal/ExprPositions.qll‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ predicate isInTypeContext(Expr expr) {
3737
expr.getParent() instanceof TypeConstraint
3838
or
3939
exists(Identifier id | id = NameBinding::getStaticBindingTargetFromRef(expr) |
40-
id = any(ClassLikeDeclaration c).getNameNode()
40+
id = any(ClassLikeDeclaration c | not c instanceof EnumCaseClass).getNameNode()
4141
or
4242
id = any(TypeAliasDeclaration t).getNameNode()
4343
or

‎unified/ql/lib/codeql/unified/internal/typeinference/TypeInference.qll‎

Lines changed: 119 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -503,7 +503,8 @@ module Input3 implements InputSig3 {
503503
)
504504
or
505505
// call without explicit receiver: `foo(42)`
506-
result.getNameNode() = b
506+
result.getNameNode() = b and
507+
kind = Ordinary()
507508
)
508509
}
509510

@@ -619,34 +620,130 @@ module Input3 implements InputSig3 {
619620
}
620621

621622
/**
622-
* Since we handle constructors as instance methods without return types,
623-
* we need to explicitly handle constructors where (parts of) the type of
624-
* the constructed object must be inferred from the context.
623+
* Provides logic for resolving the types of constructor invocations.
625624
*
626-
* Example:
625+
* Constructors are treated as instance methods without return types, in order
626+
* to be able to handle inherited constructors similar to ordinary instance
627+
* methods:
627628
*
628629
* ```swift
629-
* class C<T1, T2> {
630-
* init(x : T1) { ... } // `T2` must be inferred from the context
630+
* class A<Ta> {
631+
* init(x : Ta) { ... }
631632
* }
633+
*
634+
* class B<Tb> : A<Tb?> { }
635+
*
636+
* A.init(0); // (1) has type `A<Int>`
637+
* B.init(.some(0)); // (2) has type `B<Int>`
638+
* B<Int>(.none); // (3) has type `B<Int>`
632639
* ```
640+
*
641+
* Because the constructor calls are treated as normal instance method calls,
642+
* we use the type qualifier to represent the receiver, and then allow for
643+
* contextual type information to flow into the receiver:
644+
*
645+
* (1) The receiver has type `A`, `Ta` is inferred to be instantiated to `Int`,
646+
* which is propagated directly back into the receiver.
647+
* (2) The receiver has type `B`, `Ta` is inferred to be instantiated to `Int?`
648+
* which in turn means `Tb` is inferred to be instantiated to `Int`, which
649+
* is then propagated back into the receiver.
650+
* (3) The receiver has type `B<Int>`, and no further type inference is needed.
633651
*/
634-
private module ContextualConstructorReturnTypeInput implements
635-
M3::ContextualTyping::ContextualReturnTypeInputSig
636-
{
637-
predicate callableHasTypeParameterAtReturnType(Callable c, TypePath path, TypeParameter tp) {
638-
c.asCallable() instanceof ConstructorDeclaration and
639-
tp = c.getDeclaringType().getTypeAt(path)
652+
private module Construction {
653+
private TypeMention getAReceiverTypeMention() {
654+
exists(Invocation invocation |
655+
invocation.getTargetViaStaticNameBinding(_).asCallable() instanceof ConstructorDeclaration and
656+
result = invocation.getReceiverTypeMention()
657+
)
640658
}
641659

642-
Parameter getACallableParameter(Callable c) {
643-
result = c.getParameter(_) and
644-
not result.getParameterEx().isImplicitReceiverParameter(_)
660+
/**
661+
* Since we handle constructors as instance methods without return types,
662+
* we need to explicitly handle constructors where (parts of) the type of
663+
* the constructed object must be inferred from the context.
664+
*
665+
* Example:
666+
*
667+
* ```swift
668+
* class C<T1, T2> {
669+
* init(x : T1) { ... } // `T2` must be inferred from the context
670+
* }
671+
* ```
672+
*/
673+
private module ContextualConstructorReturnTypeInput implements
674+
M3::ContextualTyping::ContextualReturnTypeInputSig
675+
{
676+
predicate callableHasTypeParameterAtReturnType(Callable c, TypePath path, TypeParameter tp) {
677+
c.asCallable() instanceof ConstructorDeclaration and
678+
tp = c.getDeclaringType().getTypeAt(path)
679+
}
680+
681+
Parameter getACallableParameter(Callable c) {
682+
result = c.getParameter(_) and
683+
not result.getParameterEx().isImplicitReceiverParameter(_)
684+
}
645685
}
646-
}
647686

648-
private module ContextualConstructorReturnType =
649-
M3::ContextualTyping::ContextualReturnType<ContextualConstructorReturnTypeInput>;
687+
private module ContextualConstructorReturnType =
688+
M3::ContextualTyping::ContextualReturnType<ContextualConstructorReturnTypeInput>;
689+
690+
/**
691+
* Gets the inferred type at `path` of the object constructed at `i` using constructor
692+
* `cd`. Because of how we handle constructors as instance methods without return types,
693+
* this is simply the inferred type of the receiver.
694+
*/
695+
private Type inferConstructionType(Invocation i, ConstructorDeclaration cd, TypePath path) {
696+
cd = i.getTargetImpl(_).asCallable() and
697+
result = inferType(i.getArgument(0), path)
698+
}
699+
700+
private predicate needsContextualTyping(Invocation i, TypePath path) {
701+
exists(ConstructorDeclaration cd, ClassLikeDeclaration declType, TypePath path0 |
702+
declType =
703+
inferConstructionType(i, cd, TypePath::nil())
704+
.(ClassLikeDeclarationType)
705+
.getClassLikeDeclaration() and
706+
ContextualConstructorReturnType::needsContextualTyping(i, path0)
707+
|
708+
declType.getAMember() = cd and
709+
path = path0
710+
or
711+
// inherited constructor
712+
exists(TypeAbstraction abs, TypeMention constraint, TypeParameter tp |
713+
BaseTypes::rootTypesSatisfaction(_, TClassLikeDeclarationType(declType), abs, _,
714+
constraint) and
715+
BaseTypes::conditionSatisfiesConstraintTypeAt(abs, _, constraint, path0, tp) and
716+
path = TypePath::singleton(tp)
717+
)
718+
)
719+
}
720+
721+
Type inferConstructionType(AstNode n, TypePath path) {
722+
exists(TypeMention tm |
723+
n = tm and
724+
tm = getAReceiverTypeMention() and
725+
result = tm.getTypeAt(path)
726+
)
727+
or
728+
result = inferConstructionType(n, _, path)
729+
or
730+
needsContextualTyping(n, path) and
731+
result instanceof UnknownType
732+
}
733+
734+
predicate allowFullContextualInference(TypeMention tm, TypePath path) {
735+
// For a construction such as `Optional.some(0)`, we need contextual inference
736+
// to infer the type of the wrapped object
737+
tm = getAReceiverTypeMention() and
738+
exists(TypePath prefix, Type t, TypeParameter tp |
739+
t = tm.getTypeAt(prefix) and
740+
not t instanceof UnknownType and
741+
tp = t.getATypeParameter() and
742+
path = TypePath::snoc(prefix, tp) and
743+
not exists(tm.getTypeAt(path))
744+
)
745+
}
746+
}
650747

651748
Type inferTypeLanguageSpecific(AstNode n, TypePath path) {
652749
result = Plugin::inferType(n, path)
@@ -687,39 +784,14 @@ module Input3 implements InputSig3 {
687784
path = TypePath::singleton(getArrayElementTypeParameter()) and
688785
result instanceof UnknownType
689786
or
690-
exists(ConstructorDeclaration cd |
691-
cd = n.(Invocation).getTargetImpl(_).asCallable() and
692-
result = inferType(n.(Invocation).getArgument(0), path)
693-
)
694-
or
695-
ContextualConstructorReturnType::needsContextualTyping(n, path) and
696-
result instanceof UnknownType
697-
or
698-
exists(TypeMention tm, Invocation invocation |
699-
n = invocation.getArgument(0) and
700-
invocation.getTargetViaStaticNameBinding(_).asCallable() instanceof ConstructorDeclaration and
701-
tm = invocation.getReceiverTypeMention() and
702-
result = tm.getTypeAt(path)
703-
)
787+
result = Construction::inferConstructionType(n, path)
704788
}
705789

706790
pragma[nomagic]
707791
Type inferTypeCertainLanguageSpecific(AstNode n, TypePath path) { none() }
708792

709-
predicate allowContextualInference(AstNode n, TypePath path) {
710-
exists(TypeMention tm, Invocation invocation | n = tm |
711-
// or
712-
// n = invocation and
713-
// tm = invocation.getCallee()
714-
tm = invocation.getReceiverTypeMention() and
715-
exists(TypePath prefix, Type t, TypeParameter tp |
716-
t = tm.getTypeAt(prefix) and
717-
not t instanceof UnknownType and
718-
tp = t.getATypeParameter() and
719-
path = TypePath::snoc(prefix, tp) and
720-
not exists(tm.getTypeAt(path))
721-
)
722-
)
793+
predicate allowFullContextualInference(AstNode n, TypePath path) {
794+
Construction::allowFullContextualInference(n, path)
723795
}
724796
}
725797

‎unified/ql/lib/codeql/unified/internal/typeinference/TypeMention.qll‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,8 @@ private Type resolveType(Identifier access) {
2626
b = result.(ClassLikeDeclarationType).getClassLikeDeclaration().getNameNode()
2727
or
2828
b = result.(TypeParameterType).getTypeParameter().getNameNode()
29-
)
29+
) and
30+
not result.(ClassLikeDeclarationType).getClassLikeDeclaration() instanceof EnumCaseClass
3031
}
3132

3233
final class ExprTypeMention = ExprTypeMentionImpl;

‎unified/ql/test/library-tests/type-inference/type-inference.expected‎

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1130,14 +1130,12 @@ inferType
11301130
| constructors.swift:9:11:9:14 | C1(...) | | constructors.swift:1:1:7:1 | C1 |
11311131
| constructors.swift:10:5:10:7 | c12 | | constructors.swift:1:1:7:1 | C1 |
11321132
| constructors.swift:10:11:10:12 | C1 | | constructors.swift:1:1:7:1 | C1 |
1133-
| constructors.swift:10:11:10:17 | ... .init | | constructors.swift:1:1:7:1 | C1 |
11341133
| constructors.swift:10:11:10:19 | ... .init(...) | | constructors.swift:1:1:7:1 | C1 |
11351134
| constructors.swift:14:5:14:7 | c21 | | constructors.swift:12:1:12:15 | C2 |
11361135
| constructors.swift:14:11:14:12 | C2 | | constructors.swift:12:1:12:15 | C2 |
11371136
| constructors.swift:14:11:14:14 | C2(...) | | constructors.swift:12:1:12:15 | C2 |
11381137
| constructors.swift:15:5:15:7 | c22 | | constructors.swift:12:1:12:15 | C2 |
11391138
| constructors.swift:15:11:15:12 | C2 | | constructors.swift:12:1:12:15 | C2 |
1140-
| constructors.swift:15:11:15:17 | ... .init | | constructors.swift:12:1:12:15 | C2 |
11411139
| constructors.swift:15:11:15:19 | ... .init(...) | | constructors.swift:12:1:12:15 | C2 |
11421140
| constructors.swift:21:5:21:6 | c3 | | constructors.swift:17:1:19:1 | C3 |
11431141
| constructors.swift:21:10:21:11 | C3 | | constructors.swift:17:1:19:1 | C3 |
@@ -1162,14 +1160,12 @@ inferType
11621160
| constructors.swift:41:18:41:18 | s | | {EXTERNAL LOCATION} | String |
11631161
| constructors.swift:43:22:43:22 | x | | constructors.swift:38:10:38:11 | T1 |
11641162
| constructors.swift:44:20:44:60 | "Convenience initializer not implemented" | | {EXTERNAL LOCATION} | String |
1165-
| constructors.swift:50:5:50:7 | c71 | | constructors.swift:38:1:46:1 | C6 |
11661163
| constructors.swift:50:5:50:7 | c71 | | constructors.swift:48:1:48:31 | C7 |
11671164
| constructors.swift:50:5:50:7 | c71 | T3 | {EXTERNAL LOCATION} | Bool |
11681165
| constructors.swift:50:5:50:7 | c71 | T4 | {EXTERNAL LOCATION} | String |
11691166
| constructors.swift:50:11:50:12 | C7 | | constructors.swift:48:1:48:31 | C7 |
11701167
| constructors.swift:50:11:50:12 | C7 | T3 | {EXTERNAL LOCATION} | Bool |
11711168
| constructors.swift:50:11:50:12 | C7 | T4 | {EXTERNAL LOCATION} | String |
1172-
| constructors.swift:50:11:50:34 | C7(...) | | constructors.swift:38:1:46:1 | C6 |
11731169
| constructors.swift:50:11:50:34 | C7(...) | | constructors.swift:48:1:48:31 | C7 |
11741170
| constructors.swift:50:11:50:34 | C7(...) | T3 | {EXTERNAL LOCATION} | Bool |
11751171
| constructors.swift:50:11:50:34 | C7(...) | T4 | {EXTERNAL LOCATION} | String |
@@ -1368,8 +1364,6 @@ inferType
13681364
| generics.swift:60:7:60:7 | r | T | {EXTERNAL LOCATION} | Int |
13691365
| generics.swift:60:11:60:16 | Result | | generics.swift:45:1:57:1 | Result |
13701366
| generics.swift:60:11:60:16 | Result | T | {EXTERNAL LOCATION} | Int |
1371-
| generics.swift:60:11:60:24 | ... .success | | generics.swift:45:1:57:1 | Result |
1372-
| generics.swift:60:11:60:24 | ... .success | T | {EXTERNAL LOCATION} | Int |
13731367
| generics.swift:60:11:60:28 | ... .success(...) | | generics.swift:45:1:57:1 | Result |
13741368
| generics.swift:60:11:60:28 | ... .success(...) | T | {EXTERNAL LOCATION} | Int |
13751369
| generics.swift:60:26:60:27 | 42 | | {EXTERNAL LOCATION} | Int |
@@ -2540,8 +2534,6 @@ inferType
25402534
| pattern_matching.swift:30:7:30:7 | x | T | {EXTERNAL LOCATION} | Int |
25412535
| pattern_matching.swift:30:11:30:22 | PatternMatch | | pattern_matching.swift:7:1:11:1 | PatternMatch |
25422536
| pattern_matching.swift:30:11:30:22 | PatternMatch | T | {EXTERNAL LOCATION} | Int |
2543-
| pattern_matching.swift:30:11:30:28 | ... .value | | pattern_matching.swift:7:1:11:1 | PatternMatch |
2544-
| pattern_matching.swift:30:11:30:28 | ... .value | T | {EXTERNAL LOCATION} | Int |
25452537
| pattern_matching.swift:30:11:30:32 | ... .value(...) | | pattern_matching.swift:7:1:11:1 | PatternMatch |
25462538
| pattern_matching.swift:30:11:30:32 | ... .value(...) | T | {EXTERNAL LOCATION} | Int |
25472539
| pattern_matching.swift:30:30:30:31 | 42 | | {EXTERNAL LOCATION} | Int |

0 commit comments

Comments
 (0)