RFR: 8367530: The exhaustiveness errors could be improved [v8]
David Alayachew
duke at openjdk.org
Thu Dec 4 22:24:44 UTC 2025
On Thu, 4 Dec 2025 14:40:48 GMT, Jan Lahoda <jlahoda at openjdk.org> wrote:
>> While I agree that having a timeout in weird/non-standard in javac, I believe it is the first time when we have a process that a) can run for a very long time; b) is not required for correctness. In all other cases I can recall, if some process is slow (including e.g. verifying exhaustiveness), then it is required for correctness. And so we cannot skip it based on criteria like time.
>>
>> The timeout here provides a way to say how much real-world time is the user willing to invest to get the outcome - if more time is invested, more detailed missing patterns may possibly be computed. It is a somewhat weird approach for javac, but it is a limit that (I think) the user and javac can agree on.
>>
>> We could introduce a limit on e.g. the depth to which we expand, but adding one level of nesting may affect performance significantly or almost not at all, depending on e.g. the record type form/shape.
>>
>> E.g. having a record with many components, where each component is a sealed type with many permitted subtypes, one level of nesting may lead to a high number of newly generated patterns possibly taking a lot of time to go through them. But having a record that has a single component that is not a sealed type should only generate one pattern, and so have minimal impact.
>
> As an example, if I have:
>
> package exhaustiveness.test;
>
> public class Deep {
>
> public record Box0(Box1 v) {}
> public record Box1(Box2 v) {}
> public record Box2(Box3 v) {}
> public record Box3(Box4 v) {}
> public record Box4(Box5 v) {}
> public record Box5(Box6 v) {}
> public record Box6(Box7 v) {}
> public record Box7(Box8 v) {}
> public record Box8(Box9 v) {}
> public record Box9(I v) {}
> public sealed interface I {}
> public final class C1 implements I {}
> public final class C2 implements I {}
>
> public int test(Box0 box0) {
> return switch (box0) {
> case Box0(Box1(Box2(Box3(Box4(Box5(Box6(Box7(Box8(Box9(C1 _)))))))))) -> 0;
> };
> }
> }
>
>
> this runs very quickly on my laptop <1s in total, despite recursing quite deep (and it finds the one missing pattern). If I have something like:
>
> package exhaustiveness.test;
>
> public class Shallow {
>
> public sealed interface Base {}
>
> public sealed interface I0 extends Base {}
> public non-sealed interface L0 extends Base {}
>
> public non-sealed interface L1 extends I0 {}
> public non-sealed interface L2 extends I0 {}
> public non-sealed interface L3 extends I0 {}
> // public non-sealed interface L4 extends I0 {}
> // public non-sealed interface L5 extends I0 {}
> // public non-sealed interface L6 extends I0 {}
> // public non-sealed interface L7 extends I0 {}
> // public non-sealed interface L8 extends I0 {}
> // public non-sealed interface L9 extends I0 {}
> public record Data(Base b1, Base b2, Base b3, Base b4) {}
> public int test(Data d) {
> return switch (d) {
> case Data(I0 _, I0 _, I0 _, I0 _) -> 0;
> case Data(L0 _, L0 _, L0 _, L0 _) -> 0;
> case Data(I0 _, _, _, _) -> 0;
> };
> }
> }
>
>
> the current run time on my laptop is ~25s; if I un-comment L4, the time is ~2m15s. Also, in this case it is relatively difficult to reduce details, as if the record pattern is not expanded, the less-detailed version is simply `Data _`.
>
> So, while there may be a way to estimate complexity, it depends on many factors - the depth, the number of sealed types, but also the user-written patterns (as those may make exhaustiveness computation faster or slower). And, to me, it feels like a heuristic to the real factor, which is what duration can be reasonably spent by this computation.
Hello. I have multiple very deep and wide real-world hierarchies that I could run this against as a simple test. They mix sealed interfaces, enums, and records. In case we want to see which commandline option is a better fit. For example, one project in particular that very much needed this example generator went up to about L6, but only 3 Data slots, and not quite as wide. I have multiple that are ready to test right now. The data might be useful.
-------------
PR Review Comment: https://git.openjdk.org/jdk/pull/27256#discussion_r2590781125
More information about the compiler-dev
mailing list