Checks whether a class extends or implements FutureOr.

According to the specification (§10.9) it is a compile-time error to
extend, implement, or use FutureOr as a mixin.

Expands the "multiple implements" check to include a test for whether
a class attempts to extend or implement the 'FutureOr' class.

The mixin case is indirectly caught by another syntactic check for
mixed in types (c.f. checkSemantics method in source_loader.dart). It
verifies whether a mixin has zero constructors. Since FutureOr has a
constructor the check fails. This is technically enough to meet the
specification, but it leads to a confusing error message for this
particular case. To improve the quality of the error message, we may
want to merge some of the mixed in check into checkSupertypes in
kernel_class_builder.dart.

Closes https://github.com/dart-lang/sdk/issues/33744

Change-Id: I887ea9431fd50059399937f4236523ed917e3673
Reviewed-on: https://dart-review.googlesource.com/71141
Commit-Queue: Daniel Hillerström <hillerstrom@google.com>
Reviewed-by: Aske Simon Christensen <askesc@google.com>
diff --git a/pkg/front_end/lib/src/fasta/fasta_codes_generated.dart b/pkg/front_end/lib/src/fasta/fasta_codes_generated.dart
index d46ccad..a695393 100644
--- a/pkg/front_end/lib/src/fasta/fasta_codes_generated.dart
+++ b/pkg/front_end/lib/src/fasta/fasta_codes_generated.dart
@@ -3502,6 +3502,16 @@
     tip: r"""Try moving the with clause before the implements clause.""");
 
 // DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+const Code<Null> codeImplementsFutureOr = messageImplementsFutureOr;
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+const MessageCode messageImplementsFutureOr = const MessageCode(
+    "ImplementsFutureOr",
+    dart2jsCode: "*fatal*",
+    severity: Severity.error,
+    message: r"""'FutureOr' can't be used in an 'implements' clause.""");
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
 const Template<Message Function(String name, int count)>
     templateImplementsRepeated =
     const Template<Message Function(String name, int count)>(
diff --git a/pkg/front_end/lib/src/fasta/kernel/kernel_class_builder.dart b/pkg/front_end/lib/src/fasta/kernel/kernel_class_builder.dart
index 875bc466..d46c89c 100644
--- a/pkg/front_end/lib/src/fasta/kernel/kernel_class_builder.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/kernel_class_builder.dart
@@ -47,6 +47,7 @@
     show
         LocatedMessage,
         Message,
+        messageImplementsFutureOr,
         messagePatchClassOrigin,
         messagePatchClassTypeVariablesMismatch,
         messagePatchDeclarationMismatch,
@@ -197,7 +198,7 @@
     }
   }
 
-  void checkSupertypes() {
+  void checkSupertypes(CoreTypes coreTypes) {
     // This method determines whether the class (that's being built) its super
     // class appears both in 'extends' and 'implements' clauses and whether any
     // interface appears multiple times in the 'implements' clause.
@@ -235,6 +236,9 @@
 
             problemsOffsets ??= new Map<ClassBuilder, int>();
             problemsOffsets[interface] ??= type.charOffset;
+          } else if (interface.target == coreTypes.futureOrClass) {
+            addCompileTimeError(messageImplementsFutureOr, type.charOffset,
+                interface.target.name.length);
           } else {
             implemented.add(interface);
           }
diff --git a/pkg/front_end/lib/src/fasta/source/source_loader.dart b/pkg/front_end/lib/src/fasta/source/source_loader.dart
index 9c65335..cbe5078 100644
--- a/pkg/front_end/lib/src/fasta/source/source_loader.dart
+++ b/pkg/front_end/lib/src/fasta/source/source_loader.dart
@@ -729,7 +729,7 @@
   void checkSupertypes(List<SourceClassBuilder> sourceClasses) {
     for (SourceClassBuilder builder in sourceClasses) {
       if (builder.library.loader == this) {
-        builder.checkSupertypes();
+        builder.checkSupertypes(coreTypes);
       }
     }
     ticker.logMs("Checked overrides");
diff --git a/pkg/front_end/messages.status b/pkg/front_end/messages.status
index 0c946ee..0c4ff07 100644
--- a/pkg/front_end/messages.status
+++ b/pkg/front_end/messages.status
@@ -195,6 +195,7 @@
 IllegalSyncGeneratorVoidReturnType/analyzerCode: Fail # The analyzer doesn't report this error.
 ImplementsBeforeExtends/script: Fail
 ImplementsBeforeWith/script: Fail
+ImplementsFutureOr/analyzerCode: Fail # The analyzer doesn't report this error.
 ImplicitCallOfNonMethod/example: Fail
 ImportAfterPart/script1: Fail
 InitializerForStaticField/example: Fail
diff --git a/pkg/front_end/messages.yaml b/pkg/front_end/messages.yaml
index 97c4778..b428d444 100644
--- a/pkg/front_end/messages.yaml
+++ b/pkg/front_end/messages.yaml
@@ -331,6 +331,15 @@
   dart2jsCode: "GENERIC"
   script: "class A implements B implements C, D {}"
 
+ImplementsFutureOr:
+  template: "'FutureOr' can't be used in an 'implements' clause."
+  severity: ERROR
+  dart2jsCode: "*fatal*"
+  script:
+    - >-
+      import 'dart:async';
+      class A implements FutureOr<int> {}
+
 ExpectedClassOrMixinBody:
   template: "Expected a class or mixin body, but got '#lexeme'."
   analyzerCode: MISSING_CLASS_BODY
diff --git a/tests/language_2/implements_futureor_test.dart b/tests/language_2/implements_futureor_test.dart
new file mode 100644
index 0000000..4e878ff
--- /dev/null
+++ b/tests/language_2/implements_futureor_test.dart
@@ -0,0 +1,13 @@
+// Copyright (c) 2018, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'dart:async';
+
+class A<T>
+  implements FutureOr<T> //# 01: compile-time error
+{}
+
+void main() {
+  A a = new A();
+}
diff --git a/tests/language_2/language_2_analyzer.status b/tests/language_2/language_2_analyzer.status
index fd846a6..83f9f80 100644
--- a/tests/language_2/language_2_analyzer.status
+++ b/tests/language_2/language_2_analyzer.status
@@ -130,6 +130,7 @@
 hidden_import_test/02: MissingStaticWarning
 illegal_initializer_test/02: Crash # Issue #33771 - Error recovery in method body (error in constructor initializer)
 illegal_initializer_test/04: Crash # Issue #33771 - Error recovery in method body (error in constructor initializer)
+implements_futureor_test/01: Crash
 import_nonexisting_dart_uri_test/01: Crash # Issue 33686 - No core library found
 instantiate_tearoff_of_call_test: CompileTimeError # Front end fails to instantiate tear-offs of `call`
 instantiate_type_variable_test/01: Crash # Error recovery in method body (attempt to instantiate type variable)
@@ -434,6 +435,7 @@
 generic_no_such_method_dispatcher_test: CompileTimeError
 generic_tearoff_test: CompileTimeError
 getter_setter_in_lib_test: Fail # Issue 23286
+implements_futureor_test/01: MissingCompileTimeError
 implicit_creation/implicit_const_context_constructor_generic_named_test: CompileTimeError
 implicit_creation/implicit_const_context_constructor_generic_test: CompileTimeError
 implicit_creation/implicit_const_context_prefix_constructor_generic_named_test: CompileTimeError
diff --git a/tests/language_2/language_2_dartdevc.status b/tests/language_2/language_2_dartdevc.status
index b11012f..3098bb5 100644
--- a/tests/language_2/language_2_dartdevc.status
+++ b/tests/language_2/language_2_dartdevc.status
@@ -69,6 +69,7 @@
 generic_no_such_method_dispatcher_test: CompileTimeError
 getter_closure_execution_order_test: RuntimeError # Issue 29920
 getter_setter_in_lib_test: CompileTimeError
+implements_futureor_test/01: MissingCompileTimeError
 implicit_creation/implicit_const_context_constructor_generic_named_test: CompileTimeError
 implicit_creation/implicit_const_context_constructor_generic_test: CompileTimeError
 implicit_creation/implicit_const_context_prefix_constructor_generic_named_test: CompileTimeError