Skip to content
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

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

Open
TekExplorer opened this issue Mar 6, 2025 · 4 comments
Labels
area-dart-model Use area-dart-model for issues related to packages analyzer, front_end, and kernel. 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 area-analyzer Use area-analyzer for Dart analyzer issues, including the analysis server and code completion. label Mar 6, 2025
@johnniwinther johnniwinther added area-dart-model Use area-dart-model for issues related to packages analyzer, front_end, and kernel. and removed area-analyzer Use area-analyzer for Dart analyzer issues, including the analysis server and code completion. 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 Use area-dart-model for issues related to packages analyzer, front_end, and kernel. model-exhaustiveness Implementation of exhaustiveness checking
Projects
None yet
Development

No branches or pull requests

4 participants