-
Notifications
You must be signed in to change notification settings - Fork 12.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Skip nested references in relation checking #17947
Conversation
commit 6ab553c Author: Nathan Shively-Sanders <nathansa@microsoft.com> Date: Mon Aug 21 08:38:43 2017 -0700 Use Map instead of array. More refactoring/renaming. commit 1e8f244 Author: Nathan Shively-Sanders <nathansa@microsoft.com> Date: Fri Aug 18 16:30:05 2017 -0700 Refactor change to be readable (ish?) commit 2bd0749 Author: Nathan Shively-Sanders <nathansa@microsoft.com> Date: Fri Aug 18 15:24:33 2017 -0700 Revert "cache relationships, needs work on preserving diagnostic output" This reverts commit e7f5c96. commit e7f5c96 Author: Wesley Wigham <wewigham@microsoft.com> Date: Fri Aug 18 14:04:46 2017 -0700 cache relationships, needs work on preserving diagnostic output commit 3eaadc6 Author: Nathan Shively-Sanders <nathansa@microsoft.com> Date: Fri Aug 18 14:06:08 2017 -0700 in isRelatedTo, skip repeated comparisons of generic types
This change makes relating the following types faster: ```ts interface B<T> { contents: T[] map<U>(f: (t: T) => U): B<U> } interface D<T> extends B<T> { contents: List<T> map<U>(f: (t: T) => U): D<U> } ``` Previously, relating the return types of map, `D<U> ===> B<U>`, would cause a recursive check. This recursion would continue until the depth limit was hit. However, no new information will result from relating `D<U> ==> B<U>` vs `D<T> ==> B<T>` — one type parameter is interchangeable with any other in terms of relatability (with the exception of constrained type parameters). The resulting rule is as follows: return Ternary.Maybe when relating 1. Two type references 2. With identical type arguments 3. Where the two type references are identical to a pair that is already being compared in an outer recursion. 4. And where the constraints of the currently-compared type arguments are identical to the constraints of the outer-compared of type arguments. For example, if relation checking has already started comparing `A<T> ==> B<T>`, then if it checks `A<U> ==> B<U>` in a recursive call to `isRelatedTo`, the previous rules will immediately return `Ternary.Maybe`, causing relation checking to move on to the rest of the type for checking relatibility. Here are some counter examples: 1. `A<U> ==> B<V>`; type arguments are not identical. 2. `A<U> ==> B<number>`; type arguments are not identical. 3. `A<U> ==> C<U>`; the pair `A,C` is not currently being compared. 4. `A<U extends V> ==> B<U extends V>`; U's constraint `V` does not match T's constraint `undefined`. Note condition (2) happens when a generic signature is instantiated with the type arguments of another signature when checking assignability. This is a common check, so skipping it should provide a decent speed improvement.
src/compiler/checker.ts
Outdated
@@ -9230,6 +9263,10 @@ namespace ts { | |||
return Ternary.False; | |||
} | |||
} | |||
if (alreadyComparingTypeReferences(source, target)) { | |||
return Ternary.Maybe; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should this be cached in maybeKeys
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
After discussing it with you: probably yes, as long as we only cache non-constrained type parameters. With the combined maybeKeys, when checking A<T> ==> B<T>
and then A<U> ==> B<U>
, when we skip A<U>===>B<U>
with Ternary.Maybe and later find that they are assignable, we'll also mark A<T>==>B<T>
as assignable. Which is what we want.
Some updates:
|
After the latest commit, the angular sample now just has a different elaboration, like the diffs for RxJs et al. So I think switching to use the shared maybe stack improves the error reporting as well. |
I think this can be simplified and optimized a bit. I just pushed a branch named "typeReferenceRelations" that demonstrates what I mean. It constructs special keys for type references containing unconstrained type parameter arguments wherein such type arguments are just given simple numerical indices. Beyond key construction, no other logic is changed. This scheme should work better for type references with non-constrained type arguments or unequal numbers of arguments. For example, Haven't had a chance to test it much yet, but perhaps you can. |
I like the better integration with the existing caching, but I don't know whether the extra work to handle type arguments besides unconstrained type variables will apply often enough to save much. On the other hand, it's only a little more work, so why not? I'll test both to see whether I can find a performance difference. |
I tried making that simpler check I referenced, but the resulting code wasn't that much simpler and the performance wasn't any better either. So far, Immutable and immview compile about 10% faster with |
This PR is obsoleted by #17984, which does the same thing but slightly better. |
Fixes #17097
Fixes #17070
This change makes relating the following types faster:
Previously, relating the return types of map,
D<U> ===> B<U>
, would cause a recursive check. This recursion would continue until the depth limit was hit. However, no new information will result from relatingD<U> ===> B<U>
vsD<T> ===> B<T>
— one type parameter is interchangeable with any other in terms of relatability (with the exception of constrained type parameters).The resulting rule is as follows: return Ternary.Maybe when relating
already being compared in an outer recursion.
are identical to the constraints of the outer-compared of type
arguments.
For example, if relation checking has already started comparing
A<T> ===> B<T>
, then if it checksA<U> ===> B<U>
in a recursive call toisRelatedTo
, the previous rules will immediately returnTernary.Maybe
, causing relation checking to move on to the rest of the type for checking relatibility. Here are some counter examples:A<U> ===> B<V>
; type arguments are not identical.A<U> ===> B<number>
; type arguments are not identical.A<U> ===> C<U>
; the pairA,C
is not currently being compared.A<U extends V> ===> B<U extends V>
; U's constraintV
does notmatch T's constraint
undefined
.Note condition (2) happens when a generic signature is instantiated with the type arguments of another signature when checking assignability. This is a common check, so skipping it should provide a decent speed improvement.
Next steps: