Fix slow compilation of programs using recursive union types#4920
Draft
SeanTAllen wants to merge 1 commit intomainfrom
Draft
Fix slow compilation of programs using recursive union types#4920SeanTAllen wants to merge 1 commit intomainfrom
SeanTAllen wants to merge 1 commit intomainfrom
Conversation
f716ddb to
b52d138
Compare
2aeb937 to
8fd4acf
Compare
The subtype checker's assumption stack (which breaks cycles during recursive type checks) only operated on nominal types. When a recursive union type appeared as a field in its own members, the compiler would re-expand the full union membership at every level of recursion, causing exponential work in the expression pass. Three changes fix this: 1. Generalize `exact_nominal` to `exact_type`, which can structurally compare any type AST node (unions, intersections, tuples, arrows, type params, capabilities, ephemeral markers) — not just nominals. This lets the assumption stack recognize compound type pairs. Importantly, `exact_type` compares nominal typeargs structurally rather than via `is_eq_typeargs` (which calls `is_eqtype` and re-enters the subtype checker, causing infinite recursion when called from `check_assume` during union subtype checks). 2. Add assumption stack check/push/pop to `is_union_sub_x`, so that when a union-as-subtype check recurs back to the same pair, it short-circuits instead of re-expanding. 3. Rewrite `check_assume` to scan the assumption list directly instead of using a push-then-pop probe pattern, eliminating unnecessary ast_dup/ast_free allocations on every check. Benchmarks on projects using recursive union types (json-ng): - http_server expr pass: 25.7s -> 13.1s (-49%) - http_server full build: 31.7s -> 15.2s (-52%) - No measurable regression on stdlib or other projects.
8fd4acf to
4f4f1db
Compare
Member
Author
|
The numbers here are from before I had to change the approach to being more targeted as the first pass caused a crash. I will revisit this. |
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
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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.
The subtype checker's assumption stack (which breaks cycles during recursive type checks) only operated on nominal types. When a recursive union type appeared as a field in its own members, the compiler would re-expand the full union membership at every level of recursion, causing exponential work in the expression pass.
Three changes fix this:
Generalize
exact_nominaltoexact_type, which can structurally compare any type AST node (unions, intersections, tuples, arrows, type params, capabilities, ephemeral markers) — not just nominals. This lets the assumption stack recognize compound type pairs. Importantly,exact_typecompares nominal typeargs structurally rather than viais_eq_typeargs(which callsis_eqtypeand re-enters the subtype checker, causing infinite recursion when called fromcheck_assumeduring union subtype checks).Add assumption stack check/push/pop to
is_union_sub_x, so that when a union-as-subtype check recurs back to the same pair, it short-circuits instead of re-expanding.Rewrite
check_assumeto scan the assumption list directly instead of using a push-then-pop probe pattern, eliminating unnecessaryast_dup/ast_freeallocations on every check.Benchmarks on projects using recursive union types (json-ng):