Skip to content

The Dart Analyzer hangs when destructuring a sealed type variant with a large number of fields inside a switch statement #59927

@bubnov

Description

@bubnov

Let's say we have the following sealed class:

sealed class State {
   const State._();
}

final class StateWithBunchOfFields extends State {
   const StateWithBunchOfFields({
      this.value1,
      this.value2,
      this.value3,
      this.value4,
      this.value5,
      this.value6,
      this.value7,
      this.value8,
      this.value9,
      this.value10,
      this.value11,
      this.value12,
      this.value13,
      this.value14,
      this.value15,
      this.value16,
      this.value17,
      this.value18,
      this.value19,
      this.value20,
      this.value21,
      this.value22,
      this.value23,
      this.value24,
      this.value25,
      this.value26,
      this.value27,
      this.value28,
      this.value29,
      this.value30,
   }) : super._();
  
   final String? value1;
   final String? value2;
   final String? value3;
   final String? value4;
   final String? value5;
   final String? value6;
   final String? value7;
   final String? value8;
   final String? value9;
   final String? value10;
   final String? value11;
   final String? value12;
   final String? value13;
   final String? value14;
   final String? value15;
   final String? value16;
   final String? value17;
   final String? value18;
   final String? value19;
   final String? value20;
   final String? value21;
   final String? value22;
   final String? value23;
   final String? value24;
   final String? value25;
   final String? value26;
   final String? value27;
   final String? value28;
   final String? value29;
   final String? value30;
}

If we try to use it inside a switch statement, the Dart analyzer will likely hang:

final State state; // some state initialized somewhere

switch (state) {
   case StateWithBunchOfFields(
         :var value1,
         :var value2,
         :var value3,
         :var value4,
         :var value5,
         :var value6,
         :var value7,
         :var value8,
         :var value9,
         :var value10,
         :var value11,
         :var value12,
         :var value13,
         :var value14,
         :var value15,
         :var value16,
         :var value17,
         :var value18,
         :var value19,
         :var value20,
         :var value21,
         :var value22,
         :var value23,
         :var value24,
         :var value25,
         :var value26,
         :var value27,
         :var value28,
         :var value29,
         :var value30,
      ): 
      print("too many fields");
}

One possible workaround for this issue is to use an intermediate fields container like this:

sealed class State {
   const State._();
}

final class StateWithFieldsContainer extends State {
  const StateWithFieldsContainer({
    required this.fields,
  }) : super._();

  final FieldsContainer fields;
}

final class FieldsContainer {
  const FieldsContainer({
    this.value1,
    this.value2,
    this.value3,
    this.value4,
    this.value5,
    this.value6,
    this.value7,
    this.value8,
    this.value9,
    this.value10,
    this.value11,
    this.value12,
    this.value13,
    this.value14,
    this.value15,
    this.value16,
    this.value17,
    this.value18,
    this.value19,
    this.value20,
    this.value21,
    this.value22,
    this.value23,
    this.value24,
    this.value25,
    this.value26,
    this.value27,
    this.value28,
    this.value29,
    this.value30,
  });

  final String? value1;
  final String? value2;
  final String? value3;
  final String? value4;
  final String? value5;
  final String? value6;
  final String? value7;
  final String? value8;
  final String? value9;
  final String? value10;
  final String? value11;
  final String? value12;
  final String? value13;
  final String? value14;
  final String? value15;
  final String? value16;
  final String? value17;
  final String? value18;
  final String? value19;
  final String? value20;
  final String? value21;
  final String? value22;
  final String? value23;
  final String? value24;
  final String? value25;
  final String? value26;
  final String? value27;
  final String? value28;
  final String? value29;
  final String? value30;
}

Actual behavior

The Dart Analyzer hangs for a very long time (10+ minutes in my case) when attempting to analyze the switch statement with pattern matching on a class containing numerous fields.

Expected behavior

The Dart Analyzer should either:

  • Complete the analysis in a reasonable time frame
  • Fail gracefully with a clear error message indicating the complexity issue
  • Provide a warning if the pattern matching operation exceeds recommended complexity

Repro

demo.zip

For additional context about this issue, please refer to Dart-Code/Dart-Code#5392

Dart SDK version

Dart SDK version: 3.6.1 (stable) (Tue Jan 7 09:50:00 2025 -0800) on "macos_arm64"

Metadata

Metadata

Assignees

Labels

P2A bug or feature request we're likely to work onlegacy-area-analyzerUse area-devexp instead.type-bugIncorrect behavior (everything from a crash to more subtle misbehavior)

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions