As part of the implementation of
https://github.com/dart-lang/language/issues/731 (improved inference
for fold etc.), I expanded the front end's type inference logic so
that instead of just having a downward phase and an upward phase, it
could have 3 or more phases. The function that previously did
downward inference became repurposed to do "partial inference" (which
could either be the first, downward stage, or a later, horizontal
stage). However, I failed to generalize the logic that prevents types
assigned by one inference stage from being refined by later
stages--previously this logic was only needed for upward inference,
but now it's needed for horizontal inference stages as well. (This
logic is needed because of Dart's "runtime checked covariance"
behavior--it means that we want to stick with the type from downward
inference, even if a later horizontal inference stage is able to find
a more precise type, because that more precise type may lead to
runtime failures).
As part of this change I've re-architected the inference methods so
that they are responsible for creating and returning the list of
inferred types. This makes the inference logic more similar between
the front end and analyzer, and is easier to read IMHO. The total
number of list allocations is the same as before.
Change-Id: I19bfcede9c2968e50f110b571164549f16495217
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/243707
Reviewed-by: Chloe Stefantsova <cstefantsova@google.com>
Commit-Queue: Paul Berry <paulberry@google.com>
The front end desugars extension methods by inserting a synthetic
`this` argument before all other arguments. But this synthetic
argument isn't included in the `formalTypes` and `actualTypes` arrays.
So when recording a value into
`_DeferredParamInfo.evaluationOrderIndex` we may need to subtract 1 in
order to ensure that later logic will find the correct argument.
Fixes a corner case of https://github.com/dart-lang/language/issues/731.
Change-Id: Idbf136195e40555199f7c5b61a575a430f6ec6bd
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/243854
Commit-Queue: Paul Berry <paulberry@google.com>
Reviewed-by: Chloe Stefantsova <cstefantsova@google.com>
This change allows function literals in invocations to be inferred in
dependency order. For example, given the following code:
U f<T, U>(T Function() g, U Function(T) h) => h(g());
test() {
var x = f(() => 0, (y) => [y]);
}
The function literal `() => 0` is inferred first, causing the type
parameter `T` to be assigned the type `int`. Then, `(y) => [x]` is
inferred with the benefit of this type assignment, so `y` gets the
type `int`, and consequently, `U` gets assigned the type `List<int>`.
This completes the support for
https://github.com/dart-lang/language/issues/731 (improved inference
for fold etc.)
Change-Id: I48c22693720a1cc8bbf435643e863834e07f02b1
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/243002
Reviewed-by: Chloe Stefantsova <cstefantsova@google.com>
Commit-Queue: Paul Berry <paulberry@google.com>
When a function literal is wrapped in parentheses, it shouldn't affect
how it interacts with type inference. This change ensures that
parenthesized function literals are treated the same as
unparenthesized ones by the logic that supports
https://github.com/dart-lang/language/issues/731 (improved inference
for fold etc.)
Change-Id: I672787a31addbfe3f3282b6e638e00b693eea46f
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/243000
Reviewed-by: Chloe Stefantsova <cstefantsova@google.com>
Commit-Queue: Paul Berry <paulberry@google.com>
In the definition of `_computeExplicitlyTypedParameterSet`, I
accidentally nested the declaration of `unnamedParameterIndex` inside
the `for` loop, defeating the increment and causing all parameters to
be considered to have index 0.
I've included a test case that would have caught the mistake.
Bug: https://github.com/dart-lang/language/issues/731
Change-Id: I0cd0e1e5b481313150e495d370af2477253d6637
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/242741
Commit-Queue: Paul Berry <paulberry@google.com>
Reviewed-by: Konstantin Shcheglov <scheglov@google.com>
Reviewed-by: Samuel Rawlins <srawlins@google.com>
These tests exercise the "deferred type inference of function
literals" part of https://github.com/dart-lang/language/issues/731
(improved inference for fold etc.) for super-constructor invocations
and redirecting constructor invocations, both of which have their own
code paths in the analyzer.
Change-Id: I6877ac3c07a3cca31550ba74d941d250c8410cfd
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/241987
Commit-Queue: Paul Berry <paulberry@google.com>
Reviewed-by: Samuel Rawlins <srawlins@google.com>
Reviewed-by: Chloe Stefantsova <cstefantsova@google.com>
This extends the fix for
https://github.com/dart-lang/language/issues/731 (improved inference
for fold etc.) to cover situations where the function literal passed
to an invocation is enclosed in (unnecessary) parentheses.
Change-Id: I5eb40cf73336612e241a930122f8ae7b1c25bb2a
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/241021
Reviewed-by: Konstantin Shcheglov <scheglov@google.com>
Commit-Queue: Paul Berry <paulberry@google.com>
The purpose of horizontal inference is to allow the types of closure
parameters to be inferred based on the static type of other arguments
in the same invocation. When the closure parameter in question
already has an explicit type, there is no benefit, and there are
potential drawbacks (because horizontal inference could infer too
narrow a type).
This change includes the explicitness/implicitness of closure
parameter types in the dependency analysis for
https://github.com/dart-lang/language/issues/731 (improved inference
for fold etc.) so that we won't do horizontal inference when it's not
needed.
Change-Id: I33781877685867a8fcb40de54fc055f6348c21b2
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/240505
Reviewed-by: Konstantin Shcheglov <scheglov@google.com>
Reviewed-by: Samuel Rawlins <srawlins@google.com>
Commit-Queue: Paul Berry <paulberry@google.com>
This change addresses a corner case discovered during internal testing
of the fix for https://github.com/dart-lang/language/issues/731
(improved inference for fold etc.): if there is no order dependency
forcing us to do a round of horizontal inference between visiting
non-closure arguments and closure arguments, then it's important that
we *don't* do a round of horizontal inference before visiting the
closure, because there is a risk of inferring too narrow a type.
The new algorithm includes all the invocation arguments in dependency
analysis, and the dependency rules are structured such that
non-closure arguments always wind up in stage 1. If there is no
dependency between non-closure arguments and closure arguments, then
the closure arguments also wind up in stage 1, and no horizontal
inference occurs. If there is a dependency, then closure arguments
wind up in stage 2 or later, and horizontal inference occurs between
stages.
Change-Id: Ida0b28da211f63191c9c9c39e6004893617507bf
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/240442
Reviewed-by: Konstantin Shcheglov <scheglov@google.com>
Commit-Queue: Paul Berry <paulberry@google.com>
Reviewed-by: Samuel Rawlins <srawlins@google.com>
This change enhances support for
https://github.com/dart-lang/language/issues/731 (improved inference
for fold etc.) by allowing closures in invocations to be inferred in
dependency order. For example, given the following code:
U f<T, U>(T Function() g, U Function(T) h) => h(g());
test() {
var x = f(() => 0, (y) => [y]);
}
The closure `() => 0` is inferred first, causing the type parameter
`T` to be assigned the type `int`. Then, `(y) => [x]` is inferred
with the benefit of this type assignment, so `y` gets the type `int`,
and consequently, `U` gets assigned the type `List<int>`.
Change-Id: Ia0028a7f3fc8cdc78fbdf2c10c3b8d7b82a9006a
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/239461
Reviewed-by: Samuel Rawlins <srawlins@google.com>
Reviewed-by: Konstantin Shcheglov <scheglov@google.com>
Commit-Queue: Paul Berry <paulberry@google.com>
The order of operations for type inference of a generic invocation is
now:
1. Create some constraints on type parameters by trying to match the
return type of the invocation target as a subtype of the incoming
type context. (For a constructor invocation, the return type of
the invocation target is considered the raw uninstantiated type of
the class enclosing the constructor declaration.)
2. Downwards inference: partially solve the set of type constraints
accumulated in step 1, to produce a preliminary mapping of type
parameters to type schemas.
3. Recursively infer all arguments to the invocation, except that if
experimental feature `inference-update-1` is enabled, skip any
arguments that are function literals (a.k.a. "closures"). Obtain
the type contexts for the recursive inference by substituting the
preliminary mapping (from step 2) into the corresponding parameter
types of the invocation target. For each argument that is
recursively inferred, create additional constraints on type
parameters using the resulting static type.
4. If no arguments were skipped during step 3, go to step 7 (this
always happens if `inference-update-1` is disabled).
5. Horizontal inference: partially solve the set of type constraints
accumulated so far, to produce an updated preliminary mapping of
type parameters to type schemas.
6. Recursively infer all of the invocation arguments that were
previously skipped. As in step 3, obtain the type contexts for the
recursive inference by substituting the preliminary mapping (this
time from step 5) into the corresponding parameter types of the
invocation target. Again, for each argument that is recursively
inferred, create additional constraints on type parameters using
the resulting static type.
7. Upwards inference: solve the set of type constraints accumulated so
far, to produce a final mapping of type parameters to types. Check
that each type is a subtype of the bound of its corresponding type
parameter.
8. Check that the static type of each argument is assignable to the
type obtained by substituting the final mapping (from step 7) into
the corresponding parameter type of the invocation target.
9. Finally, obtain the static type of the invocation by substituting
the final mapping (from step 7) into the return type of the
invocation target.
This addresses simpler cases of
https://github.com/dart-lang/language/issues/731. Note that if
experimental flag `inference-update-1` is disabled, the behavior is
unchanged.
Note that steps 2 and 5 use the same algorithm as each other (they
only differ in how many type constraints have been accumulated so
far), so I've renamed the function that performs it from
`downwardsInfer` to `partialInfer`.
Change-Id: I10d3288d4f4ba9e2b6bc18409186ddc67ca2ee9d
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/238881
Reviewed-by: Samuel Rawlins <srawlins@google.com>
Commit-Queue: Paul Berry <paulberry@google.com>
In order to address https://github.com/dart-lang/language/issues/731
(improved type inference for `fold` etc.) we're going to need to
sometimes defer analysis of invocation arguments that are closures, so
that closure parameters can have their types inferred based on other
parameters. To avoid annoying the user with inconsistent behaviors,
we defer analysis of closures in all circumstances, even if it's not
necessary to do so for type inference purposes.
This has a minor user-visible effect: if an invocation contains some
closures and some non-closures, any demotions that happen due to write
captures in the closures are postponed until the end of the
invocation; this means that the write-captured variables remain
promoted for other invocation arguments, even if those arguments
appear after the closure. This is safe because there is no way for
the closure to be called until after all of the other invocation
arguments are evaluated. See the language tests in this CL for
details.
Note that this change only has an effect when the experimental feature
`inference-update-1` is enabled.
Change-Id: I283fc5eb07af2aeca0a06d523011d8c4617fbad7
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/237720
Reviewed-by: Samuel Rawlins <srawlins@google.com>
Commit-Queue: Paul Berry <paulberry@google.com>