Skip to content

Pattern type inference error #52373

Description

@dnys1

I'm facing an error with inference of pattern types when using sealed classes.

Here is the full reproduction: https://github.com/dnys1/sealed_repro

Given the following class hierarchy:

Legend:
  Open Class (o)
  Sealed Class (*)
  Final Class (x)
classDiagram
    class BaseException["BaseException o"]
    class AuthException["AuthException *"]
    class UnknownException["UnknownException o"]
    class AuthServiceException["AuthServiceException o"]
    BaseException <|-- AuthException
    AuthException <|-- UnknownException: implements
    AuthException <|-- AuthServiceException: extends

    class AuthResult["AuthResult *"]
    class AuthSuccessResult["AuthSuccessResult x"]
    class AuthPartialSuccessResult["AuthPartialSuccessResult x"] {
        +step1Exception: Step1Exception
        +step2Exception: Step2Exception
    }
    class AuthFailureResult["AuthFailureResult x"] {
        +exception: AuthException
    }
    class Step1Exception["Step1Exception x"]
    class Step2Exception["Step2Exception x"]
    AuthResult <|-- AuthSuccessResult
    AuthResult <|-- AuthPartialSuccessResult
    AuthResult <|-- AuthFailureResult
    UnknownException <|-- Step1Exception: extends
    AuthServiceException <|-- Step2Exception: extends
Loading

In base_exception.dart we define an exception hiearchy for which all classes inherit BaseException. From this, we derive AuthException which is sealed and has discrete subtypes, namely UnknownException and AuthServiceException.

In auth_result.dart, we define the result of an Auth operation. The result takes one of three discrete states: a successful result (AuthSuccessResult); a partial success (AuthPartialSuccessResult) where either the first or second step of the operation failed with an AuthException; and a complete failure (AuthFailureResult).


Given a method which performs an Auth operation:

Future<AuthResult> performAuthOperation();

We expect the following to work:

1. Inference on exception type

final result = await performAuthOperation();
switch (result) {
case AuthSuccessResult _:
    print('Success');
case AuthPartialSuccessResult(step1Exception: final exception?) ||
        AuthPartialSuccessResult(step2Exception: final exception?) ||
        AuthFailureResult(:final exception):
    print('Partially failed: $exception');
// For exhaustiveness; should never happen.
case AuthPartialSuccessResult(step1Exception: null, step2Exception: null):
    print('Unknown error occurred');
}

In the second branch, step1Exception, step2Exception, and exception are all subclasses of AuthException, yet its type cannot be inferred. The Analyzer presents the error:

The variable 'exception' has a different type and/or finality in this branch of the logical-or pattern.
Try declaring the variable pattern with the same type and finality in both branches.dartinconsistent_pattern_variable_logical_or
inferred_type

2. Explicit exception type

final result = await performAuthOperation();
switch (result) {
  case AuthSuccessResult _:
    print('Success');
  case AuthPartialSuccessResult(step1Exception: final AuthException exception) ||
        AuthPartialSuccessResult(step2Exception: final AuthException exception) ||
        AuthFailureResult(:final AuthException exception):
    print('Partially failed: $exception');
  // For exhaustiveness; should never happen.
  case AuthPartialSuccessResult(step1Exception: null, step2Exception: null):
    print('Unknown error occurred');
}

By explicitly specifying the exception types as AuthException, the analyzer stops complaining.

explicit_type_analysis

However, upon compilation, we get a similar error:

bin/sealed_repro.dart:28:71: Error: Variable pattern 'exception' doesn't have the same type or finality in all cases.
    case AuthPartialSuccessResult(step1Exception: final AuthException exception) ||
                                                                      ^^^^^^^^^
explicit_type_compile

Metadata

Metadata

Assignees

Labels

Type

No type
No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions