|
3 | 3 | * |
4 | 4 | * The library is initialized in three phases: |
5 | 5 | * |
6 | | - * 1. `Make1`, which takes as input a definition of types (including type parameters) |
7 | | - * and constructs the `TypePath` type used to represent paths into compound types. |
| 6 | + * 1. `Make1`, which takes as input a definition of atomic types (including type |
| 7 | + * parameters) and constructs the `TypePath` type used to represent paths into |
| 8 | + * compound types. |
8 | 9 | * |
9 | 10 | * 2. `Make2`, which takes as input a definition of type mentions (using the `TypePath` |
10 | 11 | * type) as well as the type hierarchy and type constraints, and constructs the |
|
18 | 19 | * nodes. |
19 | 20 | * |
20 | 21 | * Unlike unification-based type inference, this library does directed/bottom-up type |
21 | | - * inference by default, but allowing for contextual/top-down type inference only when |
| 22 | + * inference by default, but allowing for contextual/top-down type inference when |
22 | 23 | * explicitly needed. |
23 | 24 | * |
24 | 25 | * For example, in order to infer the type of a conditional expression, |
25 | | - * `if cond { e1 } else { e2 }`, we first infer the types of `e1` and `e2` and then |
26 | | - * apply their types to the conditional expression (not taking least-upper-bound or |
27 | | - * similar into account). This corresponds to the type inference rules |
| 26 | + * `if cond { e1 } else { e2 }`, we propagate type information from either of the |
| 27 | + * branches `e1` and `e2` into the conditional expression (for simplicity, we do not |
| 28 | + * attempt to calculate least-upper-bound types or similar). This corresponds to the |
| 29 | + * two bottom-up type inference rules: |
28 | 30 | * |
29 | 31 | * ```text |
30 | 32 | * e1: T |
|
44 | 46 | * ``` |
45 | 47 | * |
46 | 48 | * where the type of `Default::default()` needs to be inferred from the context, we |
47 | | - * first assign `Default::default()` the special `UnknownType`, then using the |
48 | | - * `cond-then` rule we conclude that the conditional has type `i64`, and then since |
49 | | - * the `else` branch has `UnknownType`, we can apply the `cond-else` rule _backwards_ |
50 | | - * to infer that `Default::default()` has type `i64`. |
| 49 | + * |
| 50 | + * 1. assign `Default::default()` the special `UnknownType`, |
| 51 | + * 2. using the `cond-then` rule we conclude that the conditional has type `i64`, and |
| 52 | + * 3. since the `else` branch has `UnknownType`, we apply the `cond-else` rule _backwards_ |
| 53 | + * to infer that `Default::default()` has type `i64`. |
51 | 54 | * |
52 | 55 | * Note that `UnknownType` can propagate bottom-up like any other type, which is needed |
53 | 56 | * in cases like for example |
@@ -2612,18 +2615,18 @@ module Make1<LocationSig Location, InputSig1<Location> Input1> { |
2612 | 2615 | * context, and also implements logic for performing contextual inference. |
2613 | 2616 | */ |
2614 | 2617 | private module ContextualTyping { |
| 2618 | + pragma[nomagic] |
| 2619 | + private TypeParameter getAConstrained(TypeParameter tp) { |
| 2620 | + result = getATypeParameterConstraint(tp).getTypeAt(_) |
| 2621 | + } |
| 2622 | + |
2615 | 2623 | /** |
2616 | 2624 | * Holds if parameterizable `p` mentions type parameter `tp` at some parameter, |
2617 | 2625 | * possibly via a constraint on another mentioned type parameter. |
2618 | 2626 | */ |
2619 | 2627 | pragma[nomagic] |
2620 | 2628 | private predicate mentionsTypeParameterAtParameter(Parameterizable p, TypeParameter tp) { |
2621 | | - tp = p.getParameter(_).getType().getTypeAt(_) |
2622 | | - or |
2623 | | - exists(TypeParameter mid | |
2624 | | - mentionsTypeParameterAtParameter(p, mid) and |
2625 | | - tp = getATypeParameterConstraint(mid).getTypeAt(_) |
2626 | | - ) |
| 2629 | + tp = getAConstrained*(p.getParameter(_).getType().getTypeAt(_)) |
2627 | 2630 | } |
2628 | 2631 |
|
2629 | 2632 | /** |
@@ -2674,9 +2677,12 @@ module Make1<LocationSig Location, InputSig1<Location> Input1> { |
2674 | 2677 | target = invocation.(InvocationMatchingGetTypeArgumentInput::Access).getTarget() and |
2675 | 2678 | parameterizableReturnContextTypedAt(target, path, tp) and |
2676 | 2679 | tp = target.getTypeParameter(_) and |
2677 | | - // check that no explicit type arguments have been supplied for `tp` |
2678 | | - not exists( |
2679 | | - InvocationMatchingGetTypeArgument::getTypeArgument(invocation, target, tp, _) |
| 2680 | + // check that no explicit type arguments have been supplied which bind `tp` |
| 2681 | + not exists(TypeParameter supplied | |
| 2682 | + tp = getAConstrained*(supplied) and |
| 2683 | + exists( |
| 2684 | + InvocationMatchingGetTypeArgument::getTypeArgument(invocation, target, supplied, _) |
| 2685 | + ) |
2680 | 2686 | ) |
2681 | 2687 | ) |
2682 | 2688 | } |
|
0 commit comments