Land new subtyping algorithm; more tests but still not comprehensive.

Includes changes to implicit covariance test.

To redescribe this, for some tear-off C<T>.f, the member type may be
contravariant if T is referenced by a parameter of C<T>.f (such as if it
is of type void Function(T)). This must be a runtime failure if C<T> is
a reference which has been covariantly upcast at runtime. For example,
List<Object>.add, where the list is at runtime an instance of List<int>,
results in a static type of void Function(Object), but a runtime type of
void Function(int), which is unsound.

We omit these checks, however, when it is trivially sound for all cases
Previously, this was the case for tearing off `List<Null>.add`. This is
no longer a valid test case for NNBD where the runtime type of that list
could actually be `List<Never>`.

Therefore the test has been changed to expect `List<Null>.add` to result
in a cast, and a new NNBD test case has been added to test `List<Never>`
cases don't get a cast.

Change-Id: I61d024a249e2c2a249ad5a8037cbe3d103e3ea8b
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/104361
Commit-Queue: Mike Fairhurst <mfairhurst@google.com>
Reviewed-by: Paul Berry <paulberry@google.com>
diff --git a/pkg/analyzer/lib/src/generated/type_system.dart b/pkg/analyzer/lib/src/generated/type_system.dart
index 7b41701..29716e8 100644
--- a/pkg/analyzer/lib/src/generated/type_system.dart
+++ b/pkg/analyzer/lib/src/generated/type_system.dart
@@ -33,7 +33,8 @@
  * This is expressed by their topiness (higher = more toppy).
  */
 int _getTopiness(DartType t) {
-  assert(_isTop(t), 'only Top types have a topiness');
+  // TODO(mfairhurst): switch legacy Top checks to true Top checks
+  assert(_isLegacyTop(t, orTrueTop: true), 'only Top types have a topiness');
 
   // Highest top
   if (t.isVoid) return 3;
@@ -50,13 +51,26 @@
 }
 
 bool _isBottom(DartType t) {
-  return t.isBottom ||
-      // TODO(mfairhurst): Remove the exception treating Null* as Top.
-      t.isDartCoreNull &&
-          (t as TypeImpl).nullabilitySuffix == NullabilitySuffix.star ||
+  return (t.isBottom &&
+          (t as TypeImpl).nullabilitySuffix != NullabilitySuffix.question) ||
       identical(t, UnknownInferredType.instance);
 }
 
+/// Is [t] the bottom of the legacy type hierarchy.
+bool _isLegacyBottom(DartType t, {@required bool orTrueBottom}) {
+  return (t.isBottom &&
+          (t as TypeImpl).nullabilitySuffix == NullabilitySuffix.question) ||
+      t.isDartCoreNull ||
+      (orTrueBottom ? _isBottom(t) : false);
+}
+
+/// Is [t] the top of the legacy type hierarch.
+bool _isLegacyTop(DartType t, {@required bool orTrueTop}) =>
+// TODO(mfairhurst): handle FutureOr<LegacyTop> cases, with tests.
+    (t.isObject &&
+        (t as TypeImpl).nullabilitySuffix == NullabilitySuffix.none) ||
+    (orTrueTop ? _isTop(t) : false);
+
 bool _isTop(DartType t) {
   if (t.isDartAsyncFutureOr) {
     return _isTop((t as InterfaceType).typeArguments[0]);
@@ -141,16 +155,23 @@
 
     // For the purpose of GLB, we say some Tops are subtypes (less toppy) than
     // the others. Return the least toppy.
-    if (_isTop(type1) && _isTop(type2)) {
+    // TODO(mfairhurst): switch legacy Top checks to true Top checks
+    if (_isLegacyTop(type1, orTrueTop: true) &&
+        _isLegacyTop(type2, orTrueTop: true)) {
       return _getTopiness(type1) < _getTopiness(type2) ? type1 : type2;
     }
 
     // The GLB of top and any type is just that type.
     // Also GLB of bottom and any type is bottom.
-    if (_isTop(type1) || _isBottom(type2)) {
+    // TODO(mfairhurst): switch legacy Top checks to true Top checks
+    // TODO(mfairhurst): switch legacy Bottom checks to true Bottom checks.
+    if (_isLegacyTop(type1, orTrueTop: true) ||
+        _isLegacyBottom(type2, orTrueBottom: true)) {
       return type2;
     }
-    if (_isTop(type2) || _isBottom(type1)) {
+    // TODO(mfairhurst): switch legacy Bottom checks to true Bottom checks
+    if (_isLegacyTop(type2, orTrueTop: true) ||
+        _isLegacyBottom(type1, orTrueBottom: true)) {
       return type1;
     }
 
@@ -515,13 +536,17 @@
     if (t is TypeParameterType) {
       return false;
     }
-    if (_isTop(t)) {
+    // TODO(mfairhurst): switch legacy Top checks to true Top checks
+    if (_isLegacyTop(t, orTrueTop: true)) {
       return true;
     }
 
     if (t is FunctionType) {
-      if (!_isTop(t.returnType) ||
-          anyParameterType(t, (pt) => !_isBottom(pt))) {
+      // TODO(mfairhurst): switch legacy Top checks to true Top checks
+      // TODO(mfairhurst): switch legacy Bottom checks to true Bottom checks
+      if (!_isLegacyTop(t.returnType, orTrueTop: true) ||
+          anyParameterType(
+              t, (pt) => !_isLegacyBottom(pt, orTrueBottom: true))) {
         return false;
       } else {
         return true;
@@ -531,7 +556,8 @@
     if (t is InterfaceType) {
       List<DartType> typeArguments = t.typeArguments;
       for (DartType typeArgument in typeArguments) {
-        if (!_isTop(typeArgument)) return false;
+        // TODO(mfairhurst): switch legacy Top checks to true Top checks
+        if (!_isLegacyTop(typeArgument, orTrueTop: true)) return false;
       }
       return true;
     }
@@ -577,6 +603,14 @@
     var t1 = _t1 as TypeImpl;
     var t2 = _t2 as TypeImpl;
 
+    // Convert Null to Never? so that NullabilitySuffix can handle more cases.
+    if (t1.isDartCoreNull) {
+      t1 = BottomTypeImpl.instanceNullable;
+    }
+    if (t2.isDartCoreNull) {
+      t2 = BottomTypeImpl.instanceNullable;
+    }
+
     if (identical(t1, t2)) {
       return true;
     }
@@ -585,25 +619,26 @@
     //
     // We proceed by eliminating these different classes from consideration.
 
-    // Trivially true.
-    //
-    // Note that `?` is treated as a top and a bottom type during inference,
-    // so it's also covered here.
-    if (_isTop(t2) || _isBottom(t1)) {
+    // `?` is treated as a top and a bottom type during inference.
+    if (identical(t1, UnknownInferredType.instance) ||
+        identical(t2, UnknownInferredType.instance)) {
       return true;
     }
 
-    // Trivially false.
-    if (_isTop(t1) || _isBottom(t2)) {
-      return false;
+    // Trivial top case.
+    if (_isTop(t2)) {
+      return true;
     }
 
-    // TODO(mfairhurst): Convert Null to Never?, to simplify the algorithm, and
-    // remove this check.
-    if (t1.isDartCoreNull) {
-      if (t2.nullabilitySuffix != NullabilitySuffix.none) {
-        return true;
-      }
+    // Legacy top case. Must be done now to find Object* <: Object.
+    if (t1.nullabilitySuffix == NullabilitySuffix.star &&
+        _isLegacyTop(t2, orTrueTop: false)) {
+      return true;
+    }
+
+    // Having excluded RHS top, this now must be false.
+    if (_isTop(t1)) {
+      return false;
     }
 
     // Handle T1? <: T2
@@ -614,16 +649,23 @@
           return false;
         }
 
-        // T1? <: FutureOr<T2> is true if T2 is S2?.
-        // TODO(mfairhurst): handle T1? <: FutureOr<dynamic>, etc.
-        if (t2 is InterfaceTypeImpl &&
-            (t2.typeArguments[0] as TypeImpl).nullabilitySuffix ==
-                NullabilitySuffix.none) {
+        // T1? <: FutureOr<S2> is true if S2 is nullable.
+        final s2 = (t2 as InterfaceType).typeArguments[0];
+        if (!isNullable(s2)) {
           return false;
         }
       }
     }
 
+    // Legacy bottom cases
+    if (_isLegacyBottom(t1, orTrueBottom: true)) {
+      return true;
+    }
+
+    if (_isLegacyBottom(t2, orTrueBottom: true)) {
+      return false;
+    }
+
     // Handle FutureOr<T> union type.
     if (t1 is InterfaceTypeImpl && t1.isDartAsyncFutureOr) {
       var t1TypeArg = t1.typeArguments[0];
@@ -1775,7 +1817,10 @@
       return false;
     }
 
-    if (_isBottom(t1) || _isTop(t2)) return true;
+    // TODO(mfairhurst): switch legacy Bottom checks to true Bottom checks
+    // TODO(mfairhurst): switch legacy Top checks to true Top checks
+    if (_isLegacyBottom(t1, orTrueBottom: true) ||
+        _isLegacyTop(t2, orTrueTop: true)) return true;
 
     if (t1 is InterfaceType && t2 is InterfaceType) {
       return _matchInterfaceSubtypeOf(t1, t2, visited, origin,
@@ -1955,16 +2000,24 @@
 
     // For the purpose of LUB, we say some Tops are subtypes (less toppy) than
     // the others. Return the most toppy.
-    if (_isTop(type1) && _isTop(type2)) {
+    // TODO(mfairhurst): switch legacy Top checks to true Top checks
+    if (_isLegacyTop(type1, orTrueTop: true) &&
+        _isLegacyTop(type2, orTrueTop: true)) {
       return _getTopiness(type1) > _getTopiness(type2) ? type1 : type2;
     }
 
     // The least upper bound of top and any type T is top.
     // The least upper bound of bottom and any type T is T.
-    if (_isTop(type1) || _isBottom(type2)) {
+    // TODO(mfairhurst): switch legacy Top checks to true Top checks
+    // TODO(mfairhurst): switch legacy Bottom checks to true Bottom checks
+    if (_isLegacyTop(type1, orTrueTop: true) ||
+        _isLegacyBottom(type2, orTrueBottom: true)) {
       return type1;
     }
-    if (_isTop(type2) || _isBottom(type1)) {
+    // TODO(mfairhurst): switch legacy Top checks to true Top checks
+    // TODO(mfairhurst): switch legacy Bottom checks to true Bottom checks
+    if (_isLegacyTop(type2, orTrueTop: true) ||
+        _isLegacyBottom(type1, orTrueBottom: true)) {
       return type2;
     }
 
diff --git a/pkg/analyzer/lib/src/task/strong/checker.dart b/pkg/analyzer/lib/src/task/strong/checker.dart
index 7725c55..b286b97 100644
--- a/pkg/analyzer/lib/src/task/strong/checker.dart
+++ b/pkg/analyzer/lib/src/task/strong/checker.dart
@@ -896,7 +896,7 @@
       // parameters are properly substituted.
       var classType = targetType.element.type;
       var classLowerBound = classType.instantiate(new List.filled(
-          classType.typeParameters.length, typeProvider.bottomType));
+          classType.typeParameters.length, BottomTypeImpl.instance));
       var memberLowerBound = inheritance.getMember(
           classLowerBound, Name(element.librarySource.uri, element.name));
       var expectedType = invokeType.returnType;
diff --git a/pkg/analyzer/test/generated/strong_mode_test.dart b/pkg/analyzer/test/generated/strong_mode_test.dart
index 1f4358e..176d24f 100644
--- a/pkg/analyzer/test/generated/strong_mode_test.dart
+++ b/pkg/analyzer/test/generated/strong_mode_test.dart
@@ -4,6 +4,7 @@
 
 import 'dart:async';
 
+import 'package:analyzer/dart/analysis/features.dart';
 import 'package:analyzer/dart/ast/ast.dart';
 import 'package:analyzer/dart/ast/standard_resolution_map.dart';
 import 'package:analyzer/dart/element/element.dart';
@@ -26,6 +27,7 @@
   defineReflectiveSuite(() {
     defineReflectiveTests(StrongModeCastsTest);
     defineReflectiveTests(StrongModeLocalInferenceTest);
+    defineReflectiveTests(StrongModeLocalInferenceTest_NNBD);
     defineReflectiveTests(StrongModeStaticTypeAnalyzer2Test);
     defineReflectiveTests(StrongModeTypePropagationTest);
   });
@@ -1708,6 +1710,12 @@
   cD.m1;
   cD.m1();
   cD.m2();
+
+  cN.f;
+  cN.g;
+  cN.m1;
+  cN.m1();
+  cN.m2();
 }
 
 noCasts() {
@@ -1723,12 +1731,6 @@
   (d.g)(42);
   d.m1()(42);
   d.m2()()(42);
-
-  cN.f;
-  cN.g;
-  cN.m1;
-  cN.m1();
-  cN.m2();
 }
 ''');
     var unit = (await computeAnalysisResult(source)).unit;
@@ -3907,6 +3909,233 @@
 }
 
 @reflectiveTest
+class StrongModeLocalInferenceTest_NNBD extends ResolverTestCase {
+  @override
+  AnalysisOptions get analysisOptions => new AnalysisOptionsImpl()
+    ..contextFeatures =
+        new FeatureSet.forTesting(additionalFeatures: [Feature.non_nullable]);
+
+  @override
+  void setUp() {
+    //TODO(mfairhurst): why is this override required?
+    super.setUp();
+    AnalysisOptionsImpl options = analysisOptions;
+    resetWith(options: options);
+  }
+
+  test_covarianceChecks_returnFunction() async {
+    // test Never cases.
+    var source = addSource(r'''
+typedef F<T>(T t);
+typedef T R<T>();
+class C<T> {
+  F<T> f = throw '';
+
+  C();
+  factory C.fact() => new C<Never>();
+
+  F<T> get g => throw '';
+  F<T> m1() => throw '';
+  R<F<T>> m2() => throw '';
+
+  casts(C<T> other, T t) {
+    other.f;
+    other.g(t);
+    other.m1();
+    other.m2;
+
+    new C<T>.fact().f(t);
+    new C<int>.fact().g;
+    new C<int>.fact().m1;
+    new C<T>.fact().m2();
+
+    new C<Object>.fact().f(42);
+    new C<Object>.fact().g;
+    new C<Object>.fact().m1;
+    new C<Object>.fact().m2();
+
+    new C.fact().f(42);
+    new C.fact().g;
+    new C.fact().m1;
+    new C.fact().m2();
+  }
+
+  noCasts(T t) {
+    f;
+    g;
+    m1();
+    m2();
+
+    f(t);
+    g(t);
+    (f)(t);
+    (g)(t);
+    m1;
+    m2;
+
+    this.f;
+    this.g;
+    this.m1();
+    this.m2();
+    this.m1;
+    this.m2;
+    (this.m1)();
+    (this.m2)();
+    this.f(t);
+    this.g(t);
+    (this.f)(t);
+    (this.g)(t);
+
+    new C<int>().f;
+    new C<T>().g;
+    new C<int>().m1();
+    new C().m2();
+
+    new D().f;
+    new D().g;
+    new D().m1();
+    new D().m2();
+  }
+}
+class D extends C<num> {
+  noCasts(t) {
+    f;
+    this.g;
+    this.m1();
+    m2;
+
+    super.f;
+    super.g;
+    super.m1;
+    super.m2();
+  }
+}
+
+D d = throw '';
+C<Object> c = throw '';
+C cD = throw '';
+C<Null> cNu = throw '';
+C<Never> cN = throw '';
+F<Object> f = throw '';
+F<Never> fN = throw '';
+R<F<Object>> rf = throw '';
+R<F<Never>> rfN = throw '';
+R<R<F<Object>>> rrf = throw '';
+R<R<F<Never>>> rrfN = throw '';
+Object obj = throw '';
+F<int> fi = throw '';
+R<F<int>> rfi = throw '';
+R<R<F<int>>> rrfi = throw '';
+
+casts() {
+  c.f;
+  c.g;
+  c.m1;
+  c.m1();
+  c.m2();
+
+  fN = c.f;
+  fN = c.g;
+  rfN = c.m1;
+  rrfN = c.m2;
+  fN = c.m1();
+  rfN = c.m2();
+
+  f = c.f;
+  f = c.g;
+  rf = c.m1;
+  rrf = c.m2;
+  f = c.m1();
+  rf = c.m2();
+  c.m2()();
+
+  c.f(obj);
+  c.g(obj);
+  (c.f)(obj);
+  (c.g)(obj);
+  (c.m1)();
+  c.m1()(obj);
+  (c.m2)();
+
+  cD.f;
+  cD.g;
+  cD.m1;
+  cD.m1();
+  cD.m2();
+
+  cNu.f;
+  cNu.g;
+  cNu.m1;
+  cNu.m1();
+  cNu.m2();
+}
+
+noCasts() {
+  fi = d.f;
+  fi = d.g;
+  rfi = d.m1;
+  fi = d.m1();
+  rrfi = d.m2;
+  rfi = d.m2();
+  d.f(42);
+  d.g(42);
+  (d.f)(42);
+  (d.g)(42);
+  d.m1()(42);
+  d.m2()()(42);
+
+  cN.f;
+  cN.g;
+  cN.m1;
+  cN.m1();
+  cN.m2();
+}
+''');
+    var unit = (await computeAnalysisResult(source)).unit;
+    assertNoErrors(source);
+
+    void expectCast(Statement statement, bool hasCast) {
+      var value = (statement as ExpressionStatement).expression;
+      if (value is AssignmentExpression) {
+        value = (value as AssignmentExpression).rightHandSide;
+      }
+      while (value is FunctionExpressionInvocation) {
+        value = (value as FunctionExpressionInvocation).function;
+      }
+      while (value is ParenthesizedExpression) {
+        value = (value as ParenthesizedExpression).expression;
+      }
+      var isCallingGetter =
+          value is MethodInvocation && !value.methodName.name.startsWith('m');
+      var cast = isCallingGetter
+          ? getImplicitOperationCast(value)
+          : getImplicitCast(value);
+      var castKind = isCallingGetter ? 'special cast' : 'cast';
+      expect(cast, hasCast ? isNotNull : isNull,
+          reason: '`$statement` should ' +
+              (hasCast ? '' : 'not ') +
+              'have a $castKind on `$value`.');
+    }
+
+    for (var s in AstFinder.getStatementsInMethod(unit, 'C', 'noCasts')) {
+      expectCast(s, false);
+    }
+    for (var s in AstFinder.getStatementsInMethod(unit, 'C', 'casts')) {
+      expectCast(s, true);
+    }
+    for (var s in AstFinder.getStatementsInMethod(unit, 'D', 'noCasts')) {
+      expectCast(s, false);
+    }
+    for (var s in AstFinder.getStatementsInTopLevelFunction(unit, 'noCasts')) {
+      expectCast(s, false);
+    }
+    for (var s in AstFinder.getStatementsInTopLevelFunction(unit, 'casts')) {
+      expectCast(s, true);
+    }
+  }
+}
+
+@reflectiveTest
 class StrongModeStaticTypeAnalyzer2Test extends StaticTypeAnalyzer2TestShared {
   void expectStaticInvokeType(String search, String type) {
     var invocation = findIdentifier(search).parent as MethodInvocation;
diff --git a/pkg/analyzer/test/generated/type_system_test.dart b/pkg/analyzer/test/generated/type_system_test.dart
index 964112f..c3df650 100644
--- a/pkg/analyzer/test/generated/type_system_test.dart
+++ b/pkg/analyzer/test/generated/type_system_test.dart
@@ -1867,19 +1867,41 @@
     typeProvider = new NonNullableTypeProvider(coreLibrary, asyncLibrary);
   }
 
+  void test_dynamicType() {
+    List<DartType> equivalents = <DartType>[
+      voidType,
+      _question(objectType),
+      _star(objectType),
+    ];
+    List<DartType> subtypes = <DartType>[bottomType, nullType, objectType];
+    _checkGroups(dynamicType, equivalents: equivalents, subtypes: subtypes);
+  }
+
   void test_int_nullableTypes() {
     List<DartType> equivalents = <DartType>[
       intType,
       _star(intType),
     ];
+    List<DartType> subtypes = <DartType>[
+      bottomType,
+    ];
     List<DartType> supertypes = <DartType>[
       _question(intType),
       objectType,
       _question(objectType),
     ];
-    List<DartType> unrelated = <DartType>[doubleType, nullType];
+    List<DartType> unrelated = <DartType>[
+      doubleType,
+      nullType,
+      _star(nullType),
+      _question(nullType),
+      _question(bottomType),
+    ];
     _checkGroups(intType,
-        equivalents: equivalents, supertypes: supertypes, unrelated: unrelated);
+        equivalents: equivalents,
+        supertypes: supertypes,
+        unrelated: unrelated,
+        subtypes: subtypes);
   }
 
   void test_intQuestion_nullableTypes() {
@@ -1890,6 +1912,11 @@
     List<DartType> subtypes = <DartType>[
       intType,
       nullType,
+      _question(nullType),
+      _star(nullType),
+      bottomType,
+      _question(bottomType),
+      _star(bottomType),
     ];
     List<DartType> supertypes = <DartType>[
       _question(numType),
@@ -1911,7 +1938,14 @@
       _question(intType),
       _star(intType),
     ];
-    List<DartType> subtypes = <DartType>[nullType];
+    List<DartType> subtypes = <DartType>[
+      nullType,
+      _star(nullType),
+      _question(nullType),
+      bottomType,
+      _star(bottomType),
+      _question(bottomType),
+    ];
     List<DartType> supertypes = <DartType>[
       numType,
       _question(numType),
@@ -1927,6 +1961,61 @@
         subtypes: subtypes);
   }
 
+  void test_nullType() {
+    List<DartType> equivalents = <DartType>[
+      nullType,
+      _question(nullType),
+      _star(nullType),
+      _question(bottomType),
+    ];
+    List<DartType> supertypes = <DartType>[
+      _question(intType),
+      _star(intType),
+      _question(objectType),
+      _star(objectType),
+      dynamicType,
+      voidType,
+    ];
+    List<DartType> subtypes = <DartType>[bottomType];
+    List<DartType> unrelated = <DartType>[
+      doubleType,
+      intType,
+      numType,
+      objectType
+    ];
+
+    for (final formOfNull in equivalents) {
+      _checkGroups(formOfNull,
+          equivalents: equivalents,
+          supertypes: supertypes,
+          unrelated: unrelated,
+          subtypes: subtypes);
+    }
+  }
+
+  void test_objectType() {
+    List<DartType> equivalents = <DartType>[
+      _star(objectType),
+    ];
+    List<DartType> supertypes = <DartType>[
+      _question(objectType),
+      dynamicType,
+      voidType,
+    ];
+    List<DartType> subtypes = <DartType>[bottomType];
+    List<DartType> unrelated = <DartType>[
+      _question(doubleType),
+      _question(numType),
+      _question(intType),
+      nullType
+    ];
+    _checkGroups(objectType,
+        equivalents: equivalents,
+        supertypes: supertypes,
+        unrelated: unrelated,
+        subtypes: subtypes);
+  }
+
   DartType _question(DartType dartType) =>
       (dartType as TypeImpl).withNullability(NullabilitySuffix.question);