Suppress NO_DEFAULT_BOUND errors.

This suppresses the emission of NO_DEFAULT_BOUND errors, and makes the
default instantation for recursive bounds backwards (and possibly
forwards) compatible.  Also adds a couple of currently failing tests
for cases that should be made erroneous regardless of whether we
choose to support malbounded type constructor applications.

BUG=
R=scheglov@google.com

Review-Url: https://codereview.chromium.org/2662973005 .
diff --git a/pkg/analyzer/lib/src/generated/resolver.dart b/pkg/analyzer/lib/src/generated/resolver.dart
index 32d24bf..973ef70 100644
--- a/pkg/analyzer/lib/src/generated/resolver.dart
+++ b/pkg/analyzer/lib/src/generated/resolver.dart
@@ -8370,12 +8370,7 @@
       }
       type = typeSystem.instantiateType(type, typeArguments);
     } else {
-      List<bool> hasError = [false];
-      type = typeSystem.instantiateToBounds(type, hasError: hasError);
-      if (hasError[0]) {
-        errorListener.onError(new AnalysisError(source, node.offset,
-            node.length, StrongModeCode.NO_DEFAULT_BOUNDS));
-      }
+      type = typeSystem.instantiateToBounds(type);
     }
     typeName.staticType = type;
     node.type = type;
diff --git a/pkg/analyzer/lib/src/generated/type_system.dart b/pkg/analyzer/lib/src/generated/type_system.dart
index b951f4f..bcf8ad5 100644
--- a/pkg/analyzer/lib/src/generated/type_system.dart
+++ b/pkg/analyzer/lib/src/generated/type_system.dart
@@ -58,14 +58,14 @@
       {this.implicitCasts: true,
       this.nonnullableTypes: AnalysisOptionsImpl.NONNULLABLE_TYPES});
 
+  @override
+  bool get isStrong => true;
+
   bool anyParameterType(FunctionType ft, bool predicate(DartType t)) {
     return ft.parameters.any((p) => predicate(p.type));
   }
 
   @override
-  bool get isStrong => true;
-
-  @override
   FunctionType functionTypeToConcreteType(FunctionType t) {
     // TODO(jmesserly): should we use a real "fuzzyArrow" bit on the function
     // type? That would allow us to implement this in the subtype relation.
@@ -371,13 +371,26 @@
     }
 
     // If we stopped making progress, and not all types are ground,
-    // then the whole type is malbounded and an error should be reported.
+    // then the whole type is malbounded and an error should be reported
+    // if errors are requested, and a partially completed type should
+    // be returned.
     if (partials.isNotEmpty) {
       if (hasError != null) {
         hasError[0] = true;
       }
-      return instantiateType(
-          type, new List<DartType>.filled(count, DynamicTypeImpl.instance));
+      var domain = defaults.keys.toList();
+      var range = defaults.values.toList();
+      // Build a substitution Phi mapping each uncompleted type variable to
+      // dynamic, and each completed type variable to its default.
+      for (TypeParameterType parameter in partials.keys) {
+        domain.add(parameter);
+        range.add(DynamicTypeImpl.instance);
+      }
+      // Set the default for an uncompleted type variable (T extends B)
+      // to be Phi(B)
+      for (TypeParameterType parameter in partials.keys) {
+        defaults[parameter] = partials[parameter].substitute2(range, domain);
+      }
     }
 
     List<DartType> orderedArguments =
diff --git a/pkg/analyzer/test/generated/strong_mode_test.dart b/pkg/analyzer/test/generated/strong_mode_test.dart
index 364bcf5..bc5bd88 100644
--- a/pkg/analyzer/test/generated/strong_mode_test.dart
+++ b/pkg/analyzer/test/generated/strong_mode_test.dart
@@ -955,6 +955,51 @@
     expect(functionReturnValue(4).staticType, typeProvider.stringType);
   }
 
+  test_futureOr_assignFromFuture() async {
+    // Test a Future<T> can be assigned to FutureOr<T>.
+    MethodInvocation invoke = await _testFutureOr(r'''
+    FutureOr<T> mk<T>(Future<T> x) => x;
+    test() => mk(new Future<int>.value(42));
+    ''');
+    _isFutureOrOfInt(invoke.staticType);
+  }
+
+  test_futureOr_assignFromValue() async {
+    // Test a T can be assigned to FutureOr<T>.
+    MethodInvocation invoke = await _testFutureOr(r'''
+    FutureOr<T> mk<T>(T x) => x;
+    test() => mk(42);
+    ''');
+    _isFutureOrOfInt(invoke.staticType);
+  }
+
+  test_futureOr_asyncExpressionBody() async {
+    // A FutureOr<T> can be used as the expression body for an async function
+    MethodInvocation invoke = await _testFutureOr(r'''
+    Future<T> mk<T>(FutureOr<T> x) async => x;
+    test() => mk(42);
+    ''');
+    _isFutureOfInt(invoke.staticType);
+  }
+
+  test_futureOr_asyncReturn() async {
+    // A FutureOr<T> can be used as the return value for an async function
+    MethodInvocation invoke = await _testFutureOr(r'''
+    Future<T> mk<T>(FutureOr<T> x) async { return x; }
+    test() => mk(42);
+    ''');
+    _isFutureOfInt(invoke.staticType);
+  }
+
+  test_futureOr_await() async {
+    // Test a FutureOr<T> can be awaited.
+    MethodInvocation invoke = await _testFutureOr(r'''
+    Future<T> mk<T>(FutureOr<T> x) async => await x;
+    test() => mk(42);
+    ''');
+    _isFutureOfInt(invoke.staticType);
+  }
+
   test_futureOr_downwards1() async {
     // Test that downwards inference interacts correctly with FutureOr
     // parameters.
@@ -1090,51 +1135,6 @@
     _isFutureOf([_isObject])(invoke.staticType);
   }
 
-  test_futureOr_assignFromValue() async {
-    // Test a T can be assigned to FutureOr<T>.
-    MethodInvocation invoke = await _testFutureOr(r'''
-    FutureOr<T> mk<T>(T x) => x;
-    test() => mk(42);
-    ''');
-    _isFutureOrOfInt(invoke.staticType);
-  }
-
-  test_futureOr_assignFromFuture() async {
-    // Test a Future<T> can be assigned to FutureOr<T>.
-    MethodInvocation invoke = await _testFutureOr(r'''
-    FutureOr<T> mk<T>(Future<T> x) => x;
-    test() => mk(new Future<int>.value(42));
-    ''');
-    _isFutureOrOfInt(invoke.staticType);
-  }
-
-  test_futureOr_await() async {
-    // Test a FutureOr<T> can be awaited.
-    MethodInvocation invoke = await _testFutureOr(r'''
-    Future<T> mk<T>(FutureOr<T> x) async => await x;
-    test() => mk(42);
-    ''');
-    _isFutureOfInt(invoke.staticType);
-  }
-
-  test_futureOr_asyncExpressionBody() async {
-    // A FutureOr<T> can be used as the expression body for an async function
-    MethodInvocation invoke = await _testFutureOr(r'''
-    Future<T> mk<T>(FutureOr<T> x) async => x;
-    test() => mk(42);
-    ''');
-    _isFutureOfInt(invoke.staticType);
-  }
-
-  test_futureOr_asyncReturn() async {
-    // A FutureOr<T> can be used as the return value for an async function
-    MethodInvocation invoke = await _testFutureOr(r'''
-    Future<T> mk<T>(FutureOr<T> x) async { return x; }
-    test() => mk(42);
-    ''');
-    _isFutureOfInt(invoke.staticType);
-  }
-
   test_inference_hints() async {
     Source source = addSource(r'''
       void main () {
@@ -2738,14 +2738,41 @@
     expectIdentifierType('cc', "C<int, B<int>, A<dynamic>>");
   }
 
+  @failingTest
+  test_instantiateToBounds_class_error_extension_malbounded() async {
+    // Test that superclasses are strictly checked for malbounded default
+    // types
+    String code = r'''
+class C<T0 extends List<T1>, T1 extends List<T0>> {}
+class D extends C {}
+''';
+    await resolveTestUnit(code, noErrors: false);
+    assertErrors(testSource, [StrongModeCode.NO_DEFAULT_BOUNDS]);
+  }
+
+  @failingTest
+  test_instantiateToBounds_class_error_instantiation_malbounded() async {
+    // Test that instance creations are strictly checked for malbounded default
+    // types
+    String code = r'''
+class C<T0 extends List<T1>, T1 extends List<T0>> {}
+void test() {
+  var c = new C();
+}
+''';
+    await resolveTestUnit(code, noErrors: false);
+    assertErrors(testSource, [StrongModeCode.NO_DEFAULT_BOUNDS]);
+    expectIdentifierType('c;', 'C<List<dynamic>, List<dynamic>>');
+  }
+
   test_instantiateToBounds_class_error_recursion() async {
     String code = r'''
 class C<T0 extends List<T1>, T1 extends List<T0>> {}
 C c;
 ''';
     await resolveTestUnit(code, noErrors: false);
-    assertErrors(testSource, [StrongModeCode.NO_DEFAULT_BOUNDS]);
-    expectIdentifierType('c;', 'C<dynamic, dynamic>');
+    assertNoErrors(testSource);
+    expectIdentifierType('c;', 'C<List<dynamic>, List<dynamic>>');
   }
 
   test_instantiateToBounds_class_error_recursion_self() async {
@@ -2754,8 +2781,8 @@
 C c;
 ''';
     await resolveTestUnit(code, noErrors: false);
-    assertErrors(testSource, [StrongModeCode.NO_DEFAULT_BOUNDS]);
-    expectIdentifierType('c;', 'C<dynamic>');
+    assertNoErrors(testSource);
+    expectIdentifierType('c;', 'C<C<dynamic>>');
   }
 
   test_instantiateToBounds_class_error_recursion_self2() async {
@@ -2765,8 +2792,8 @@
 C c;
 ''';
     await resolveTestUnit(code, noErrors: false);
-    assertErrors(testSource, [StrongModeCode.NO_DEFAULT_BOUNDS]);
-    expectIdentifierType('c;', 'C<dynamic>');
+    assertNoErrors(testSource);
+    expectIdentifierType('c;', 'C<A<dynamic>>');
   }
 
   test_instantiateToBounds_class_error_typedef() async {
@@ -2776,8 +2803,8 @@
 C c;
 ''';
     await resolveTestUnit(code, noErrors: false);
-    assertErrors(testSource, [StrongModeCode.NO_DEFAULT_BOUNDS]);
-    expectIdentifierType('c;', 'C<dynamic>');
+    assertNoErrors(testSource);
+    expectIdentifierType('c;', 'C<(dynamic) → dynamic>');
   }
 
   test_instantiateToBounds_class_ok_implicitDynamic_multi() async {
@@ -2852,6 +2879,22 @@
     expectIdentifierType('d;', 'D<A<dynamic>>');
   }
 
+  @failingTest
+  test_instantiateToBounds_generic_function_error_malbounded() async {
+    // Test that generic methods are strictly checked for malbounded default
+    // types
+    String code = r'''
+T0 f<T0 extends List<T1>, T1 extends List<T0>>() {}
+void g() {
+  var c = f();
+  return;
+}
+''';
+    await resolveTestUnit(code, noErrors: false);
+    assertErrors(testSource, [StrongModeCode.NO_DEFAULT_BOUNDS]);
+    expectIdentifierType('c;', 'List<dynamic>');
+  }
+
   test_instantiateToBounds_method_ok_referenceOther_before() async {
     String code = r'''
 class C<T> {
diff --git a/pkg/analyzer/test/src/summary/resynthesize_ast_test.dart b/pkg/analyzer/test/src/summary/resynthesize_ast_test.dart
index be79415..e185566 100644
--- a/pkg/analyzer/test/src/summary/resynthesize_ast_test.dart
+++ b/pkg/analyzer/test/src/summary/resynthesize_ast_test.dart
@@ -720,7 +720,6 @@
       super.createOptions()..strongMode = true;
 
   @override
-  @failingTest
   test_instantiateToBounds_boundRefersToItself() {
     super.test_instantiateToBounds_boundRefersToItself();
   }
diff --git a/pkg/analyzer/test/src/task/strong/inferred_type_test.dart b/pkg/analyzer/test/src/task/strong/inferred_type_test.dart
index 541e75f..6077b7c 100644
--- a/pkg/analyzer/test/src/task/strong/inferred_type_test.dart
+++ b/pkg/analyzer/test/src/task/strong/inferred_type_test.dart
@@ -644,7 +644,6 @@
     ''');
   }
 
-  @failingTest
   void test_constructors_inferenceFBounded() {
     // Regression for https://github.com/dart-lang/sdk/issues/26990
     var unit = checkFile('''
diff --git a/pkg/dev_compiler/test/not_yet_strong_tests.dart b/pkg/dev_compiler/test/not_yet_strong_tests.dart
index 0d67109..9ca06f3 100644
--- a/pkg/dev_compiler/test/not_yet_strong_tests.dart
+++ b/pkg/dev_compiler/test/not_yet_strong_tests.dart
@@ -930,7 +930,6 @@
   'language/is_not_class2_test',
   'language/is_object_test',
   'language/isnot_malformed_type_test',
-  'language/issue_23914_test', // issue 28478
   'language/issue11724_test',
   'language/issue11793_test',
   'language/issue13474_test',
@@ -1448,8 +1447,6 @@
   'language/proxy_test_02_multi',
   'language/proxy_test_05_multi',
   'language/proxy_test_06_multi',
-  'language/recursive_generic_test', // issue 28478
-  'language/recursive_inheritance_test', // issue 28478
   'language/redirecting_constructor_initializer_test',
   'language/redirecting_factory_default_values_test_01_multi',
   'language/redirecting_factory_default_values_test_02_multi',
@@ -2093,7 +2090,6 @@
   'corelib/symbol_test_none_multi',
   'corelib/uri_path_test',
   'corelib/uri_query_test',
-  'lib/collection/linked_list_test', // issue 28478
   'lib/convert/chunked_conversion1_test',
   'lib/math/min_max_test',
   'lib/typed_data/float32x4_test',