Checks whether the type of a switch expression is assignable to the
types of its case expressions.

Resolves the first part of
https://github.com/dart-lang/sdk/issues/34207. According to the
language team the second part may soon become irrelevant.

Change-Id: Ifce38f2a62c293156dafa94f16799d5a126c6b9c
Reviewed-on: https://dart-review.googlesource.com/71981
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 1e4d3a4..7855f44 100644
--- a/pkg/front_end/lib/src/fasta/fasta_codes_generated.dart
+++ b/pkg/front_end/lib/src/fasta/fasta_codes_generated.dart
@@ -6747,6 +6747,54 @@
     message: r"""Switch case may fall through to the next case.""");
 
 // DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+const Template<
+    Message Function(
+        DartType _type,
+        DartType
+            _type2)> templateSwitchExpressionNotAssignable = const Template<
+        Message Function(DartType _type, DartType _type2)>(
+    messageTemplate:
+        r"""Type '#type' of the switch expression isn't assignable to the type '#type2' of this case expression.""",
+    withArguments: _withArgumentsSwitchExpressionNotAssignable);
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+const Code<Message Function(DartType _type, DartType _type2)>
+    codeSwitchExpressionNotAssignable =
+    const Code<Message Function(DartType _type, DartType _type2)>(
+        "SwitchExpressionNotAssignable", templateSwitchExpressionNotAssignable,
+        analyzerCode: "SWITCH_EXPRESSION_NOT_ASSIGNABLE",
+        severity: Severity.error);
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+Message _withArgumentsSwitchExpressionNotAssignable(
+    DartType _type, DartType _type2) {
+  NameSystem nameSystem = new NameSystem();
+  StringBuffer buffer;
+  buffer = new StringBuffer();
+  new Printer(buffer, syntheticNames: nameSystem).writeNode(_type);
+  String type = '$buffer';
+
+  buffer = new StringBuffer();
+  new Printer(buffer, syntheticNames: nameSystem).writeNode(_type2);
+  String type2 = '$buffer';
+
+  return new Message(codeSwitchExpressionNotAssignable,
+      message:
+          """Type '${type}' of the switch expression isn't assignable to the type '${type2}' of this case expression.""",
+      arguments: {'type': _type, 'type2': _type2});
+}
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+const Code<Null> codeSwitchExpressionNotAssignableCause =
+    messageSwitchExpressionNotAssignableCause;
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+const MessageCode messageSwitchExpressionNotAssignableCause = const MessageCode(
+    "SwitchExpressionNotAssignableCause",
+    severity: Severity.context,
+    message: r"""The switch expression is here.""");
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
 const Code<Null> codeSwitchHasCaseAfterDefault =
     messageSwitchHasCaseAfterDefault;
 
diff --git a/pkg/front_end/lib/src/fasta/kernel/expression_generator_helper.dart b/pkg/front_end/lib/src/fasta/kernel/expression_generator_helper.dart
index a1ca4c0..af00914 100644
--- a/pkg/front_end/lib/src/fasta/kernel/expression_generator_helper.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/expression_generator_helper.dart
@@ -158,7 +158,8 @@
   void addCompileTimeError(Message message, int charOffset, int length,
       {List<LocatedMessage> context});
 
-  void addProblem(Message message, int charOffset, int length);
+  void addProblem(Message message, int charOffset, int length,
+      {List<LocatedMessage> context});
 
   void addProblemErrorIfConst(Message message, int charOffset, int length);
 
diff --git a/pkg/front_end/lib/src/fasta/kernel/kernel_shadow_ast.dart b/pkg/front_end/lib/src/fasta/kernel/kernel_shadow_ast.dart
index 88b7b7e..c6ab5e4 100644
--- a/pkg/front_end/lib/src/fasta/kernel/kernel_shadow_ast.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/kernel_shadow_ast.dart
@@ -37,12 +37,14 @@
 
 import '../fasta_codes.dart'
     show
+        messageSwitchExpressionNotAssignableCause,
         messageVoidExpression,
         noLength,
         templateCantInferTypeDueToCircularity,
         templateCantUseSuperBoundedTypeForInstanceCreation,
         templateForInLoopElementTypeNotAssignable,
-        templateForInLoopTypeNotIterable;
+        templateForInLoopTypeNotIterable,
+        templateSwitchExpressionNotAssignable;
 
 import '../problems.dart' show unhandled, unsupported;
 
@@ -2710,14 +2712,30 @@
     var expressionJudgment = this.expressionJudgment;
     inferrer.inferExpression(expressionJudgment, const UnknownType(), true);
     var expressionType = expressionJudgment.inferredType;
+
     for (var switchCase in caseJudgments) {
       for (var caseExpression in switchCase.expressionJudgments) {
-        inferrer.inferExpression(caseExpression, expressionType, false);
+        DartType caseExpressionType =
+            inferrer.inferExpression(caseExpression, expressionType, true);
+
+        // Check whether the expression type is assignable to the case expression type.
+        if (!inferrer.isAssignable(expressionType, caseExpressionType)) {
+          inferrer.helper.addProblem(
+              templateSwitchExpressionNotAssignable.withArguments(
+                  expressionType, caseExpressionType),
+              caseExpression.fileOffset,
+              noLength,
+              context: [
+                messageSwitchExpressionNotAssignableCause.withLocation(
+                    inferrer.uri, expressionJudgment.fileOffset, noLength)
+              ]);
+        }
       }
       inferrer.inferStatement(switchCase.bodyJudgment);
       // TODO(paulberry): support labels.
       inferrer.listener.switchCase(switchCase, null, null, null, null, null);
     }
+
     inferrer.listener
         .switchStatement(this, fileOffset, tokens, expression, cases);
   }
diff --git a/pkg/front_end/lib/src/fasta/type_inference/inference_helper.dart b/pkg/front_end/lib/src/fasta/type_inference/inference_helper.dart
index 38505c3..1a05738 100644
--- a/pkg/front_end/lib/src/fasta/type_inference/inference_helper.dart
+++ b/pkg/front_end/lib/src/fasta/type_inference/inference_helper.dart
@@ -16,7 +16,8 @@
   LocatedMessage checkArgumentsForType(
       FunctionType function, ArgumentsJudgment arguments, int offset);
 
-  void addProblem(Message message, int charOffset, int length);
+  void addProblem(Message message, int charOffset, int length,
+      {List<LocatedMessage> context});
 
   Expression wrapInProblem(Expression expression, Message message, int length,
       {List<LocatedMessage> context});
diff --git a/pkg/front_end/messages.yaml b/pkg/front_end/messages.yaml
index fe4e2fb..a1be91c 100644
--- a/pkg/front_end/messages.yaml
+++ b/pkg/front_end/messages.yaml
@@ -2152,6 +2152,22 @@
   tip: "To delegate a constructor to a super constructor, put the super call as an initializer."
   analyzerCode: SUPER_AS_EXPRESSION
 
+SwitchExpressionNotAssignable:
+  template: "Type '#type' of the switch expression isn't assignable to the type '#type2' of this case expression."
+  severity: ERROR
+  analyzerCode: SWITCH_EXPRESSION_NOT_ASSIGNABLE
+  script:
+    - >-
+      void f() {
+        switch (42) {
+          case "foo": break;
+        }
+      }
+
+SwitchExpressionNotAssignableCause:
+  template: "The switch expression is here."
+  severity: CONTEXT
+
 SwitchHasCaseAfterDefault:
   template: "The default case should be the last case in a switch statement."
   tip: "Try moving the default case after the other case clauses."
diff --git a/tests/co19/co19-kernel.status b/tests/co19/co19-kernel.status
index 594f662..3eab189 100644
--- a/tests/co19/co19-kernel.status
+++ b/tests/co19/co19-kernel.status
@@ -1049,9 +1049,6 @@
 Language/Statements/Switch/execution_case_no_default_t02: CompileTimeError
 Language/Statements/Switch/execution_case_t02: CompileTimeError
 Language/Statements/Switch/execution_t01: CompileTimeError
-Language/Statements/Switch/expressions_t01: MissingCompileTimeError
-Language/Statements/Switch/expressions_t02: MissingCompileTimeError
-Language/Statements/Switch/expressions_t04: MissingCompileTimeError
 Language/Statements/Switch/last_statement_t01: CompileTimeError
 Language/Statements/Switch/scope_t02: CompileTimeError
 Language/Statements/Switch/type_t01: CompileTimeError
diff --git a/tests/co19_2/co19_2-kernel.status b/tests/co19_2/co19_2-kernel.status
index 140c019..b78c3e4 100644
--- a/tests/co19_2/co19_2-kernel.status
+++ b/tests/co19_2/co19_2-kernel.status
@@ -155,12 +155,6 @@
 Language/Statements/Continue/label_t07: MissingCompileTimeError # Issue 34206
 Language/Statements/Switch/equal_operator_t01: MissingCompileTimeError # Issue 32557
 Language/Statements/Switch/equal_operator_t02: MissingCompileTimeError # Issue 32557
-Language/Statements/Switch/execution_t01: MissingCompileTimeError # Issue 34207
-Language/Statements/Switch/expressions_t01: MissingCompileTimeError # Issue 34207
-Language/Statements/Switch/expressions_t02: MissingCompileTimeError # Issue 34207
-Language/Statements/Switch/expressions_t04: MissingCompileTimeError # Issue 34207
-Language/Statements/Switch/type_t01: MissingCompileTimeError # Issue 34207
-Language/Statements/Switch/type_t02: MissingCompileTimeError # Issue 34207
 Language/Statements/Try/catch_scope_t01: CompileTimeError
 Language/Types/Dynamic_Type_System/malbounded_type_error_t01: MissingCompileTimeError # Issue 33308
 Language/Types/Interface_Types/subtype_t30: CompileTimeError
diff --git a/tests/language_2/language_2_dart2js.status b/tests/language_2/language_2_dart2js.status
index a079eb5..98d11b6 100644
--- a/tests/language_2/language_2_dart2js.status
+++ b/tests/language_2/language_2_dart2js.status
@@ -219,7 +219,6 @@
 const_init2_test/02: MissingCompileTimeError
 const_map2_test/00: MissingCompileTimeError
 const_map3_test/00: MissingCompileTimeError
-const_switch2_test/01: MissingCompileTimeError
 const_switch_test/02: RuntimeError, OK # constant identity based on JS constants
 const_switch_test/04: RuntimeError, OK # constant identity based on JS constants
 const_types_test/01: MissingCompileTimeError
@@ -557,7 +556,6 @@
 const_map2_test/00: MissingCompileTimeError
 const_map3_test/00: MissingCompileTimeError
 const_map4_test: RuntimeError
-const_switch2_test/01: MissingCompileTimeError
 const_switch_test/02: RuntimeError, OK # constant identity based on JS constants
 const_switch_test/04: RuntimeError, OK # constant identity based on JS constants
 constructor12_test: RuntimeError
diff --git a/tests/language_2/language_2_dartdevc.status b/tests/language_2/language_2_dartdevc.status
index cb05f47..6b9305d 100644
--- a/tests/language_2/language_2_dartdevc.status
+++ b/tests/language_2/language_2_dartdevc.status
@@ -263,7 +263,6 @@
 const_map2_test/00: MissingCompileTimeError
 const_map3_test/00: MissingCompileTimeError
 const_optional_args_test/01: MissingCompileTimeError
-const_switch2_test/01: MissingCompileTimeError
 const_syntax_test/05: MissingCompileTimeError
 constants_test/05: MissingCompileTimeError
 covariant_subtyping_test: RuntimeError
diff --git a/tests/language_2/language_2_kernel.status b/tests/language_2/language_2_kernel.status
index ca5235e..ea7e5db 100644
--- a/tests/language_2/language_2_kernel.status
+++ b/tests/language_2/language_2_kernel.status
@@ -355,7 +355,6 @@
 [ $compiler != dart2analyzer && $fasta ]
 const_map2_test/00: MissingCompileTimeError # KernelVM bug: Constant evaluation.
 const_map3_test/00: MissingCompileTimeError # KernelVM bug: Constant evaluation.
-const_switch2_test/01: MissingCompileTimeError # KernelVM bug: Constant evaluation.
 invalid_returns/async_invalid_return_05_test: MissingCompileTimeError
 invalid_returns/async_invalid_return_06_test: MissingCompileTimeError
 invalid_returns/async_invalid_return_07_test: MissingCompileTimeError