dart-lang / sdk

The Dart SDK, including the VM, JS and Wasm compilers, analysis, core libraries, and more.
https://dart.dev
BSD 3-Clause "New" or "Revised" License
10.06k stars 1.56k forks source link

bug: dart analyzer does not catch the error `FutureOr<Object>? Function()' can't be assigned to the parameter type 'FutureOr<Object> Function()` #56256

Open Diaga opened 1 month ago

Diaga commented 1 month ago

Consider the following code:

import 'dart:async';

FutureOr<bool> returnFutureOrBool() {
  return true;
}

class Test {
  void then<T2>(FutureOr<T2> Function() fn) {
  }
}

void main() {  
  Test().then(() {
    final futureOrBool = returnFutureOrBool();
    if (futureOrBool is Future) {
      return futureOrBool;
    }

     return null;
  });  
}

There is no error by the analyzer, however, on running the code, there is an error:

compileDDC
main.dart:13:15: Error: The argument type 'FutureOr<bool>? Function()' can't be assigned to the parameter type 'FutureOr<bool> Function()' because 'FutureOr<bool>?' is nullable and 'FutureOr<bool>' isn't.
  Test().then(() {
              ^

Environment: The above error exists on current main (Dart SDK 3.5.0-315.0.dev) and stable (Dart SDK 3.4.3) channels of dartpad.

Expected behaviour: The analyzer should be consistent with the compiler at least. Not sure if the error is as intended.

dart-github-bot commented 1 month ago

Summary: The Dart analyzer fails to detect an error when assigning a nullable FutureOr<Object>? Function() to a non-nullable FutureOr<Object> Function() parameter, leading to a runtime error. The analyzer should consistently identify this type mismatch.

eernstg commented 1 month ago

Good catch! The analyzer and the common front end should not disagree on the inferred types here.

However, if a tool infers the actual type argument FutureOr<bool>? for the invocation of then then an actual argument of type FutureOr<bool>? Function() is perfectly fine.

So we'd need to consider both (1) what is the inferred type of the function literal which is given as an argument to then? .. and (2) what's the inferred type argument which is passed to then?

The type of the function literal is pretty much evident because it takes no arguments and returns an expression of type FutureOr<bool> as well as an expression of type Null. So there's basically no way a tool could infer any other type than FutureOr<bool>? Function() for that function literal.

The type of Test.then is void Function<T2>(FutureOr<T2> Function()).

So we're inferring a T2 such that FutureOr<bool>? Function() is assignable to FutureOr<T2> Function(), which would require FutureOr<bool>? to be a subtype of FutureOr<T2> (that is FutureOr<bool>? <: FutureOr<T2>), which is only true if Null <: T2 and bool <: T2. This yields bool? as a solution, along with all supertypes thereof.

The analyzer does not report any errors, and it actually infers FutureOr<bool>? as the actual type argument for the invocation of Test.then.

The common front end infers FutureOr<bool> (which is not a solution to the constraints), and then reports that the given type argument doesn't satisfy the constraints. ;-)

So something went wrong during type inference in the CFE. I'll adjust the labels to match this analysis.