fix[next]: empty range is absorbing for domain intersection#2702
Merged
tehrengruber merged 3 commits intoJul 14, 2026
Conversation
`_reduce_ranges` is shared by `domain_union` and `domain_intersection`. GridTools#2677 taught it to drop statically empty ranges before folding. That is correct for the union, where an empty range is the identity element, but wrong for the intersection, where it is the absorbing element. Dropping an empty range from an intersection returns the remaining range, e.g. `[1, 1[ & [0, inf[` yields `[0, inf[` instead of staying empty. Since `promote_domain` fills non-condition dimensions with `[-inf, inf[`, the surviving range can be unbounded, so an `InfinityLiteral` leaks into the inferred domain and reaches the backends. Split the two operations: the union keeps dropping empty ranges, the intersection returns the empty range directly. Also restore the constant folding that GridTools#2677 skipped when a single range survived the reduction. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Cover both the finite case (`[1, 1[ & [0, 10[`), which silently produced a non-empty domain, and the half-infinite case (`[1, 1[ & [0, inf[`), which leaked an `InfinityLiteral` into the result. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
tehrengruber
approved these changes
Jul 14, 2026
tehrengruber
marked this pull request as ready for review
July 14, 2026 10:52
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.
Problem
_reduce_rangesindomain_utils.pyis shared bydomain_unionanddomain_intersection:#2677 taught it to drop statically empty ranges before folding. That is correct for the union, where an empty range is the identity element, but wrong for the intersection, where an empty range is the absorbing element.
Dropping the empty range from an intersection returns the remaining range instead of staying empty:
The second form is reached in practice:
promote_domainfills non-condition dimensions with[-inf, inf[, and_infer_concat_whereintersects that with the target domain. If the target range is statically empty, the half-infinite range survives verbatim and anInfinityLiteralleaks into the inferred domain.Symptoms
Both show up in ICON4Py with
concat_whereover a statically-known-empty vertical range (a Rayleigh damping layer of zero thickness, where the layer bound is bound as a compile-time static arg):gtfn — the leaked
InfinityLiteralreachesitir_to_gtfn_ir._make_domain:dace —
gtir_to_sdfg_concat_wherecallsdomain_intersection(source_domain, output_domain)directly at lowering time, with finite bounds. No infinity, no crash: a scalarconcat_wherebranch whose range is empty ([1, 1[) is widened to the full output domain ([1, 10[), so the constant branch is broadcast over the whole domain and silently overwrites the real values.The dace case is a silent wrong-results bug, so it is worth backporting alongside the crash.
Fix
Split the two operations. The union keeps dropping empty ranges; the intersection returns the empty range directly. Emptiness remains decidable only for static ranges — non-static ranges may not be empty, as before.
Also restores the constant folding that #2677 skipped in the single-surviving-range case.
Testing
test_domain_utils.pycovering the finite and the half-infinite case. Both fail onmain.tests/next_tests/unit_tests/A/B'd with and without the change: no new failures.TypeErrorand the dacediff_multfac_n2wmismatch both disappear, and the previously-failing GAUSS3D driver integration test passes.Follow-up on #2677, related to #2205. The symbolic (non-static) case is still left to #2673.
🤖 Generated with Claude Code