You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
In #45025 we increased the type instantiation depth limit from 50 to 500. We've subsequently seen multiple examples of stack overflows occurring during compilation of programs containing non-terminating types, particularly when the compiler is used in browser hosted environments (where, apparently, in some cases the call stack limit is lower than in Node.js). It is clear that 500 is too high of a limit, so in this PR we lower the maximum to 100.
There are legitimate cases where evaluation of recursive conditional types needs more than 100 iterations to complete. For that reason, this PR implements tail recursive evaluation of conditional types. Specifically, when a conditional type ends in another (or, through recursion, another instantiation of the same) conditional type, the compiler now performs type resolution in a loop that consumes no extra call stack. We permit evaluation of such types to loop 1000 times before we consider the type non-terminating and issue an error.
Previously, resolution of instantiations of this type would error for string literals containing more than 25 blanks. We now permit strings with up to 1000 blanks.
Conditional types that aren't tail recursive generally can be written in a tail recursive form by introducing an "accumulator". For example, this type, which extracts a union of the characters in a string, isn't tail recursive (because it recurs through a union type)
The reason will be displayed to describe this comment to others. Learn more.
It'd be nice if we could kinda automatically do the "extract to an accumulator" thing you suggest in the OP, so people didn't need to write their conditionals with such structure in mind; but this seems good.
Changed the tail recursion logic to bypass caching since it is effectively just computing temporary results. There's still a slight slowdown in material-ui (<1%), but generally I think not caching in the tail recursion loop is the best approach.
@ahejlsberg Thanks for this work! Using 4.4.3, I just hit the instantiationDepth === 50 limit (literally just 50, nothing above that) on a project I am working on.
Because it is possible / even likely to hit this depth limit of 50 with tuple types and template literal types in 4.4, I'm wondering if it would be possible to backport the instantiationDepth === 100 limit to a new patch release of 4.4 without including this tail recursive evaluation strategy This would mitigate the issue until 4.5 is released with this new (awesome) work. Let me know if you think this would be possible / a good idea. Thanks!
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
In #45025 we increased the type instantiation depth limit from 50 to 500. We've subsequently seen multiple examples of stack overflows occurring during compilation of programs containing non-terminating types, particularly when the compiler is used in browser hosted environments (where, apparently, in some cases the call stack limit is lower than in Node.js). It is clear that 500 is too high of a limit, so in this PR we lower the maximum to 100.
There are legitimate cases where evaluation of recursive conditional types needs more than 100 iterations to complete. For that reason, this PR implements tail recursive evaluation of conditional types. Specifically, when a conditional type ends in another (or, through recursion, another instantiation of the same) conditional type, the compiler now performs type resolution in a loop that consumes no extra call stack. We permit evaluation of such types to loop 1000 times before we consider the type non-terminating and issue an error.
The following conditional type is tail recursive:
Previously, resolution of instantiations of this type would error for string literals containing more than 25 blanks. We now permit strings with up to 1000 blanks.
Conditional types that aren't tail recursive generally can be written in a tail recursive form by introducing an "accumulator". For example, this type, which extracts a union of the characters in a string, isn't tail recursive (because it recurs through a union type)
but this rewritten form is
Some additional examples of tail recursive conditional types:
The examples above previously would error in "Type instantiation is excessively deep and possibly infinite", but now succeed.