Skip to content

Commit fb0f153

Browse files
committed
Unified: Improve type inference for constructor calls
1 parent 5880968 commit fb0f153

6 files changed

Lines changed: 390 additions & 141 deletions

File tree

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

Lines changed: 95 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -2684,6 +2684,8 @@ module Make1<LocationSig Location, InputSig1<Location> Input1> {
26842684
* in `inferTypeLanguageSpecific`; if in doubt, use `inferTypeLanguageSpecific` instead.
26852685
*/
26862686
default Type inferTypeCertainLanguageSpecific(AstNode n, TypePath path) { none() }
2687+
2688+
default predicate allowContextualInference(AstNode n, TypePath path) { none() }
26872689
}
26882690

26892691
module Make3<InputSig3 Input3> {
@@ -3034,7 +3036,7 @@ module Make1<LocationSig Location, InputSig1<Location> Input1> {
30343036
// `inferTypeContextualCand2` performs the proper check for contextual
30353037
// typing, but we can already rule out cases where receivers don't have
30363038
// an unknown type anywhere
3037-
ContextualTyping::hasUnknownType(receiver)
3039+
ContextualTyping::hasUnknownType(receiver, _)
30383040
)
30393041
}
30403042

@@ -3223,7 +3225,7 @@ module Make1<LocationSig Location, InputSig1<Location> Input1> {
32233225
// `inferTypeContextualCand2` performs the proper check for contextual
32243226
// typing, but we can already rule out cases where arguments don't have
32253227
// an unknown type anywhere
3226-
ContextualTyping::hasUnknownType(arg)
3228+
ContextualTyping::hasUnknownType(arg, _)
32273229
}
32283230

32293231
/**
@@ -3234,74 +3236,101 @@ module Make1<LocationSig Location, InputSig1<Location> Input1> {
32343236
* This module identifies calls where the return type may need to be inferred from the
32353237
* context, and also implements logic for performing contextual inference.
32363238
*/
3237-
private module ContextualTyping {
3238-
pragma[nomagic]
3239-
private TypeParameter getAConstrained(TypeParameter tp) {
3240-
result = getATypeParameterConstraint(tp).getTypeAt(_)
3241-
}
3242-
3239+
module ContextualTyping {
32433240
/**
3244-
* Holds if callable `c` mentions type parameter `tp` at some parameter,
3245-
* possibly via a constraint on another mentioned type parameter.
3241+
* Provides the input to `ContextualReturnType`.
32463242
*/
3247-
pragma[nomagic]
3248-
private predicate mentionsTypeParameterAtParameter(Callable c, TypeParameter tp) {
3249-
tp = getAConstrained*(c.getParameter(_).getType().getTypeAt(_))
3243+
signature module ContextualReturnTypeInputSig {
3244+
/** Holds if `c` has the type parameter `tp` at its return type at `path`. */
3245+
default predicate callableHasTypeParameterAtReturnType(
3246+
Callable c, TypePath path, TypeParameter tp
3247+
) {
3248+
tp = getCallableReturnType(c, path)
3249+
}
3250+
3251+
/** Gets a parameter of `c`. */
3252+
default Parameter getACallableParameter(Callable c) { result = c.getParameter(_) }
32503253
}
32513254

32523255
/**
3253-
* Holds if the return type of the callable `c` at `path` is type parameter
3254-
* `tp`, and `tp` does not appear in the type of any parameter of `c`.
3255-
*
3256-
* In this case, the context in which `p` is called may be needed to infer
3257-
* the instantiation of `tp`.
3258-
*
3259-
* This covers functions like `Default::default` and `Vec::new` in Rust.
3256+
* Provide logic for identifying calls where the return type may need to be inferred from the context.
32603257
*/
3261-
pragma[nomagic]
3262-
private predicate callableReturnContextTypedAt(Callable c, TypePath path, TypeParameter tp) {
3263-
tp = getCallableReturnType(c, path) and
3264-
not mentionsTypeParameterAtParameter(c, tp)
3265-
}
3258+
module ContextualReturnType<ContextualReturnTypeInputSig Input> {
3259+
pragma[nomagic]
3260+
private TypeParameter getAConstrained(TypeParameter tp) {
3261+
result = getATypeParameterConstraint(tp).getTypeAt(_)
3262+
}
32663263

3267-
bindingset[invocation, target]
3268-
pragma[inline_late]
3269-
private predicate hasTypeArgument(Invocation invocation, Callable target, TypeParameter tp) {
3270-
exists(Type t |
3271-
InvocationTypeQualifierMatching::typeMatch(invocation, _, _, _, t, tp) and
3272-
not t instanceof PseudoType
3273-
)
3274-
or
3275-
exists(InvocationMatching::getTypeArgument(invocation, target, tp, _))
3276-
}
3264+
/**
3265+
* Holds if callable `c` mentions type parameter `tp` at some parameter,
3266+
* possibly via a constraint on another mentioned type parameter.
3267+
*/
3268+
pragma[nomagic]
3269+
private predicate mentionsTypeParameterAtParameter(Callable c, TypeParameter tp) {
3270+
tp = getAConstrained*(Input::getACallableParameter(c).getType().getTypeAt(_))
3271+
}
32773272

3278-
/**
3279-
* Holds if `invocation` resolves to some target where the return type at `path`
3280-
* may have to be inferred from the context.
3281-
*/
3282-
pragma[nomagic]
3283-
predicate needsContextualTyping(Invocation invocation, TypePath path) {
3284-
exists(Callable target, TypeParameter tp |
3285-
target = invocation.getATargetForTypeQualifierMatching()
3273+
/**
3274+
* Holds if the return type of the callable `c` at `path` is type parameter
3275+
* `tp`, and `tp` does not appear in the type of any parameter of `c`.
3276+
*
3277+
* In this case, the context in which `p` is called may be needed to infer
3278+
* the instantiation of `tp`.
3279+
*
3280+
* This covers functions like `Default::default` and `Vec::new` in Rust.
3281+
*/
3282+
pragma[nomagic]
3283+
private predicate callableReturnContextTypedAt(Callable c, TypePath path, TypeParameter tp) {
3284+
Input::callableHasTypeParameterAtReturnType(c, path, tp) and
3285+
not mentionsTypeParameterAtParameter(c, tp)
3286+
}
3287+
3288+
bindingset[invocation, target]
3289+
pragma[inline_late]
3290+
private predicate hasTypeArgument(Invocation invocation, Callable target, TypeParameter tp) {
3291+
exists(Type t |
3292+
InvocationTypeQualifierMatching::typeMatch(invocation, _, _, _, t, tp) and
3293+
not t instanceof PseudoType
3294+
)
32863295
or
3287-
target = invocation.getTarget(_)
3288-
|
3289-
callableReturnContextTypedAt(target, path, tp) and
3290-
// check that no explicit type arguments have been supplied that bind `tp`
3291-
not exists(TypeParameter supplied |
3292-
tp = getAConstrained*(supplied) and
3293-
hasTypeArgument(invocation, target, supplied)
3296+
exists(InvocationMatching::getTypeArgument(invocation, target, tp, _))
3297+
}
3298+
3299+
/**
3300+
* Holds if `invocation` resolves to some target where the return type at `path`
3301+
* may have to be inferred from the context.
3302+
*/
3303+
pragma[nomagic]
3304+
predicate needsContextualTyping(Invocation invocation, TypePath path) {
3305+
exists(Callable target, TypeParameter tp |
3306+
target = invocation.getATargetForTypeQualifierMatching()
3307+
or
3308+
target = invocation.getTarget(_)
3309+
|
3310+
callableReturnContextTypedAt(target, path, tp) and
3311+
// check that no explicit type arguments have been supplied that bind `tp`
3312+
not exists(TypeParameter supplied |
3313+
tp = getAConstrained*(supplied) and
3314+
hasTypeArgument(invocation, target, supplied)
3315+
)
32943316
)
3295-
)
3317+
}
32963318
}
32973319

3320+
private module DefaultContextualReturnTypeInput implements ContextualReturnTypeInputSig { }
3321+
3322+
predicate needsContextualTyping =
3323+
ContextualReturnType<DefaultContextualReturnTypeInput>::needsContextualTyping/2;
3324+
32983325
pragma[nomagic]
3299-
predicate hasUnknownTypeAt(AstNode n, TypePath path) {
3300-
inferType(n, path) instanceof UnknownType
3326+
predicate hasUnknownTypeAt(AstNode n, TypePath path, boolean actual) {
3327+
inferType(n, path) instanceof UnknownType and actual = true
3328+
or
3329+
allowContextualInference(n, path) and actual = false
33013330
}
33023331

33033332
pragma[nomagic]
3304-
predicate hasUnknownType(AstNode n) { hasUnknownTypeAt(n, _) }
3333+
predicate hasUnknownType(AstNode n, boolean actual) { hasUnknownTypeAt(n, _, actual) }
33053334

33063335
pragma[nomagic]
33073336
private Type inferTypeContextualCand0(AstNode n, TypePath path) {
@@ -3325,18 +3354,20 @@ module Make1<LocationSig Location, InputSig1<Location> Input1> {
33253354
pragma[nomagic]
33263355
private Type inferTypeContextualCand1(AstNode n, TypePath prefix, TypePath path) {
33273356
result = inferTypeContextualCand0(n, path) and
3328-
hasUnknownType(n) and
33293357
prefix = path.getAPrefix() and
3330-
// no need to propagate `UnknownType`s contextually; `n` must already have an
3331-
// `UnknownType` at some prefix of `path`
3332-
not result instanceof UnknownType
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+
)
33333364
}
33343365

33353366
pragma[nomagic]
33363367
private Type inferTypeContextualCand2(AstNode n, TypePath path) {
33373368
exists(TypePath prefix |
33383369
result = inferTypeContextualCand1(n, prefix, path) and
3339-
hasUnknownTypeAt(n, prefix)
3370+
hasUnknownTypeAt(n, prefix, _)
33403371
)
33413372
}
33423373

@@ -3347,7 +3378,7 @@ module Make1<LocationSig Location, InputSig1<Location> Input1> {
33473378
*/
33483379
pragma[nomagic]
33493380
private predicate isValidContextualNonEmptyPath(AstNode n, TypePath path) {
3350-
hasUnknownType(n) and
3381+
hasUnknownType(n, _) and
33513382
exists(TypePath prefix, TypeParameter tp |
33523383
tp = inferType(n, prefix).getATypeParameter() and
33533384
path = TypePath::snoc(prefix, tp)
@@ -3367,6 +3398,8 @@ module Make1<LocationSig Location, InputSig1<Location> Input1> {
33673398
path.isEmpty()
33683399
or
33693400
isValidContextualNonEmptyPath(n, path)
3401+
or
3402+
allowContextualInference(n, path.getAPrefix())
33703403
)
33713404
}
33723405
}
@@ -3487,7 +3520,7 @@ module Make1<LocationSig Location, InputSig1<Location> Input1> {
34873520
AstNode n, TypePath prefix, int i, TypePath suffix, Type t
34883521
) {
34893522
exists(TypeParameter tp, TypePath suffix0 |
3490-
ContextualTyping::hasUnknownTypeAt(n, prefix) and
3523+
ContextualTyping::hasUnknownTypeAt(n, prefix, _) and
34913524
suffix0.isCons(tp, suffix) and
34923525
tp = any(UnknownType ut).getPositionalTypeParameter(i) and
34933526
t = inferType(n, prefix.appendInverse(suffix0)) and
@@ -3497,7 +3530,7 @@ module Make1<LocationSig Location, InputSig1<Location> Input1> {
34973530

34983531
pragma[nomagic]
34993532
private predicate infersKnownAndUnknownType(AstNode n, TypePath path, int i, TypeParameter tp) {
3500-
ContextualTyping::hasUnknownTypeAt(n, path) and
3533+
ContextualTyping::hasUnknownTypeAt(n, path, _) and
35013534
exists(Type t |
35023535
t = inferType(n, path) and
35033536
not t instanceof UnknownType and

0 commit comments

Comments
 (0)