Skip to content

Exhaustivity fails on Result<T, E> with Ok<T> and Err<E> where the other generic is Never #60260

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
TekExplorer opened this issue Mar 6, 2025 · 4 comments
Labels
area-dart-model For issues related to conformance to the language spec in the parser, compilers or the CLI analyzer. model-exhaustiveness Implementation of exhaustiveness checking

Comments

@TekExplorer
Copy link

TekExplorer commented Mar 6, 2025

Using the following sealed class

sealed class Result<T, E extends Object> {}

final class Ok<T> implements Result<T, Never> {
  const Ok(this.value);
  final T value;
}

final class Err<E extends Object> implements Result<Never, E> {
  Err(this.error, this.stackTrace);

  final E error;
  final StackTrace stackTrace;
}

See the following:

// this is ok, but we lose type information in the generics
check(Result<String, Exception> r) {
  return switch (r) {
    Ok<dynamic>() => throw UnimplementedError(),
    Err<Object>() => throw UnimplementedError(),
  };
}
// this SHOULD be ok, but the analyzer complains that we don't handle Ok<dynamic> and Err<Object>
check(Result<String, Exception> r) {
  return switch (r) {
    Ok<String>() => throw UnimplementedError(),
    Err<Exception>() => throw UnimplementedError(),
  };
}

I think there was an issue about this prior, but I can't seem to find it, so I'll make it here.

I noticed this issue as I was messing around with Result<T> and wanted to try specifying the specific error type

@TekExplorer TekExplorer added the legacy-area-analyzer Use area-devexp instead. label Mar 6, 2025
@johnniwinther johnniwinther added area-dart-model For issues related to conformance to the language spec in the parser, compilers or the CLI analyzer. and removed legacy-area-analyzer Use area-devexp instead. labels Mar 6, 2025
@johnniwinther
Copy link
Member

This is a known limitation of the current support for exhaustiveness. We only recognize "trivial" subtype relations, such as class A<T> extends B<T>, where the type arguments to B are exactly the type parameters of A, or class A extends B<int> where the type argument to B is fixed.

@eernstg Do we have a language issue for this?

@eernstg
Copy link
Member

eernstg commented Mar 6, 2025

I don't think so, and I can't find any, so I created dart-lang/language#4286.

@TekExplorer
Copy link
Author

So this has been around for a while.

How come it hasn't been fixed then?

I ask because a language feature is effectively broken, which seems like a serious bug.

I have genuinely no understanding why the algorithm cant handle this case.

Seems simple enough to me:

  • Result is sealed - has 2 subtypes: Ok, Err
  • see Ok<T>
    • check types. T is a supertype of T
    • no other considerations can be made. Ok<T> is self-exhaustive
  • see Err<E>
    • same logic
  • exhaustive
    You just... don't care that one of the generics is hard coded internally because it doesn't matter.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area-dart-model For issues related to conformance to the language spec in the parser, compilers or the CLI analyzer. model-exhaustiveness Implementation of exhaustiveness checking
Projects
None yet
Development

No branches or pull requests

4 participants