Update UP for FutureOr.

Bug: https://github.com/dart-lang/sdk/issues/43720
Change-Id: I9decd3a3b430f82e3e4bd0c4e6963cd3069f9adf
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/166786
Reviewed-by: Samuel Rawlins <srawlins@google.com>
Commit-Queue: Konstantin Shcheglov <scheglov@google.com>
diff --git a/pkg/analyzer/lib/src/dart/element/least_upper_bound.dart b/pkg/analyzer/lib/src/dart/element/least_upper_bound.dart
index 4bc97f5..2cb1bde 100644
--- a/pkg/analyzer/lib/src/dart/element/least_upper_bound.dart
+++ b/pkg/analyzer/lib/src/dart/element/least_upper_bound.dart
@@ -677,6 +677,11 @@
       return getLeastUpperBound(T1, _typeSystem.objectNone);
     }
 
+    var futureOrResult = _futureOr(T1, T2);
+    if (futureOrResult != null) {
+      return futureOrResult;
+    }
+
     // UP(T1, T2) = T2 if T1 <: T2
     // UP(T1, T2) = T1 if T2 <: T1
     // And other, more complex variants of interface types.
@@ -806,6 +811,56 @@
     );
   }
 
+  DartType _futureOr(DartType T1, DartType T2) {
+    var T1_futureOr = T1 is InterfaceType && T1.isDartAsyncFutureOr
+        ? T1.typeArguments[0]
+        : null;
+
+    var T1_future = T1 is InterfaceType && T1.isDartAsyncFuture
+        ? T1.typeArguments[0]
+        : null;
+
+    var T2_futureOr = T2 is InterfaceType && T2.isDartAsyncFutureOr
+        ? T2.typeArguments[0]
+        : null;
+
+    var T2_future = T2 is InterfaceType && T2.isDartAsyncFuture
+        ? T2.typeArguments[0]
+        : null;
+
+    // UP(FutureOr<T1>, FutureOr<T2>) = FutureOr<T3> where T3 = UP(T1, T2)
+    if (T1_futureOr != null && T2_futureOr != null) {
+      var T3 = getLeastUpperBound(T1_futureOr, T2_futureOr);
+      return _typeSystem.typeProvider.futureOrType2(T3);
+    }
+
+    // UP(Future<T1>, FutureOr<T2>) = FutureOr<T3> where T3 = UP(T1, T2)
+    if (T1_future != null && T2_futureOr != null) {
+      var T3 = getLeastUpperBound(T1_future, T2_futureOr);
+      return _typeSystem.typeProvider.futureOrType2(T3);
+    }
+
+    // UP(FutureOr<T1>, Future<T2>) = FutureOr<T3> where T3 = UP(T1, T2)
+    if (T1_futureOr != null && T2_future != null) {
+      var T3 = getLeastUpperBound(T1_futureOr, T2_future);
+      return _typeSystem.typeProvider.futureOrType2(T3);
+    }
+
+    // UP(T1, FutureOr<T2>) = FutureOr<T3> where T3 = UP(T1, T2)
+    if (T2_futureOr != null) {
+      var T3 = getLeastUpperBound(T1, T2_futureOr);
+      return _typeSystem.typeProvider.futureOrType2(T3);
+    }
+
+    // UP(FutureOr<T1>, T2) = FutureOr<T3> where T3 = UP(T1, T2)
+    if (T1_futureOr != null) {
+      var T3 = getLeastUpperBound(T1_futureOr, T2);
+      return _typeSystem.typeProvider.futureOrType2(T3);
+    }
+
+    return null;
+  }
+
   DartType _parameterType(ParameterElement a, ParameterElement b) {
     return _typeSystem.getGreatestLowerBound(a.type, b.type);
   }
diff --git a/pkg/analyzer/test/src/dart/element/upper_lower_bound_test.dart b/pkg/analyzer/test/src/dart/element/upper_lower_bound_test.dart
index 3c9c633..3372610 100644
--- a/pkg/analyzer/test/src/dart/element/upper_lower_bound_test.dart
+++ b/pkg/analyzer/test/src/dart/element/upper_lower_bound_test.dart
@@ -2608,6 +2608,50 @@
     );
   }
 
+  /// UP(Future<T1>, FutureOr<T2>) = FutureOr<T3> where T3 = UP(T1, T2)
+  /// UP(FutureOr<T1>, Future<T2>) = FutureOr<T3> where T3 = UP(T1, T2)
+  test_futureOr_future() {
+    void check(DartType T1, DartType T2, DartType expected) {
+      _checkLeastUpperBound(
+        futureNone(T1),
+        futureOrNone(T2),
+        futureOrNone(expected),
+      );
+    }
+
+    check(intNone, doubleNone, numNone);
+    check(intNone, stringNone, objectNone);
+  }
+
+  /// UP(FutureOr<T1>, FutureOr<T2>) = FutureOr<T3> where T3 = UP(T1, T2)
+  test_futureOr_futureOr() {
+    void check(DartType T1, DartType T2, DartType expected) {
+      _checkLeastUpperBound(
+        futureOrNone(T1),
+        futureOrNone(T2),
+        futureOrNone(expected),
+      );
+    }
+
+    check(intNone, doubleNone, numNone);
+    check(intNone, stringNone, objectNone);
+  }
+
+  /// UP(T1, FutureOr<T2>) = FutureOr<T3> where T3 = UP(T1, T2)
+  /// UP(FutureOr<T1>, T2) = FutureOr<T3> where T3 = UP(T1, T2)
+  test_futureOr_other() {
+    void check(DartType T1, DartType T2, DartType expected) {
+      _checkLeastUpperBound(
+        futureOrNone(T1),
+        T2,
+        futureOrNone(expected),
+      );
+    }
+
+    check(intNone, doubleNone, numNone);
+    check(intNone, stringNone, objectNone);
+  }
+
   test_identical() {
     void check(DartType type) {
       _checkLeastUpperBound(type, type, type);