[cfe] Add implicit cast to relational pattern

Closes #51789

Change-Id: I6c3e512b58f0216e0ba1c01812c92c2d1d9dbeaa
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/289824
Commit-Queue: Johnni Winther <johnniwinther@google.com>
Reviewed-by: Chloe Stefantsova <cstefantsova@google.com>
diff --git a/pkg/front_end/lib/src/fasta/type_inference/delayed_expressions.dart b/pkg/front_end/lib/src/fasta/type_inference/delayed_expressions.dart
index 56fc31e..af3cc55 100644
--- a/pkg/front_end/lib/src/fasta/type_inference/delayed_expressions.dart
+++ b/pkg/front_end/lib/src/fasta/type_inference/delayed_expressions.dart
@@ -346,14 +346,25 @@
   final DelayedExpression _operand;
   final DartType _type;
   final bool isUnchecked;
+  final bool isImplicit;
   final int fileOffset;
 
   DelayedAsExpression(this._operand, this._type,
-      {this.isUnchecked = false, required this.fileOffset});
+      {this.isUnchecked = false,
+      this.isImplicit = false,
+      required this.fileOffset});
 
   @override
   Expression createExpression(TypeEnvironment typeEnvironment) {
-    return createAsExpression(_operand.createExpression(typeEnvironment), _type,
+    Expression operand = _operand.createExpression(typeEnvironment);
+    if (isImplicit) {
+      DartType operandType = _operand.getType(typeEnvironment);
+      if (typeEnvironment.isSubtypeOf(
+          operandType, _type, SubtypeCheckMode.withNullabilities)) {
+        return operand;
+      }
+    }
+    return createAsExpression(operand, _type,
         forNonNullableByDefault: true,
         isUnchecked: isUnchecked,
         fileOffset: fileOffset);
diff --git a/pkg/front_end/lib/src/fasta/type_inference/matching_expressions.dart b/pkg/front_end/lib/src/fasta/type_inference/matching_expressions.dart
index f6ff5a9..070f6c5 100644
--- a/pkg/front_end/lib/src/fasta/type_inference/matching_expressions.dart
+++ b/pkg/front_end/lib/src/fasta/type_inference/matching_expressions.dart
@@ -596,16 +596,30 @@
         Member? staticTarget;
         switch (node.accessKind) {
           case RelationalAccessKind.Instance:
+            FunctionType functionType = node.functionType!;
+            DartType argumentType = functionType.positionalParameters.single;
             expression = new DelayedInstanceInvocation(
-                matchedExpression, node.target!, node.functionType!, [constant],
+                matchedExpression,
+                node.target!,
+                functionType,
+                [
+                  new DelayedAsExpression(constant, argumentType,
+                      isImplicit: true, fileOffset: node.fileOffset)
+                ],
                 fileOffset: node.fileOffset);
             break;
           case RelationalAccessKind.Static:
+            FunctionType functionType = node.functionType!;
+            DartType argumentType = functionType.positionalParameters[1];
             expression = new DelayedExtensionInvocation(
                 node.target!,
-                [matchedExpression, constant],
+                [
+                  matchedExpression,
+                  new DelayedAsExpression(constant, argumentType,
+                      isImplicit: true, fileOffset: node.fileOffset)
+                ],
                 node.typeArguments!,
-                node.functionType!,
+                functionType,
                 fileOffset: node.fileOffset);
             staticTarget = node.target;
             break;
diff --git a/pkg/front_end/testcases/patterns/pattern_types.dart.strong.expect b/pkg/front_end/testcases/patterns/pattern_types.dart.strong.expect
index b019bfa..a5ee8e4 100644
--- a/pkg/front_end/testcases/patterns/pattern_types.dart.strong.expect
+++ b/pkg/front_end/testcases/patterns/pattern_types.dart.strong.expect
@@ -388,7 +388,7 @@
   }
   {
     final synthesized self::Class #17#0 = cls;
-    if(self::_extension#0|<=(#17#0, #C2)) {
+    if(self::_extension#0|<=(#17#0, #C2 as{ForNonNullableByDefault} core::int)) {
     }
   }
   {
diff --git a/pkg/front_end/testcases/patterns/pattern_types.dart.weak.expect b/pkg/front_end/testcases/patterns/pattern_types.dart.weak.expect
index f29e4d0..42ddd8d 100644
--- a/pkg/front_end/testcases/patterns/pattern_types.dart.weak.expect
+++ b/pkg/front_end/testcases/patterns/pattern_types.dart.weak.expect
@@ -389,7 +389,7 @@
   }
   {
     final synthesized self::Class #17#0 = cls;
-    if(self::_extension#0|<=(#17#0, #C2)) {
+    if(self::_extension#0|<=(#17#0, #C2 as{ForNonNullableByDefault} core::int)) {
     }
   }
   {
diff --git a/pkg/front_end/testcases/patterns/pattern_types.dart.weak.modular.expect b/pkg/front_end/testcases/patterns/pattern_types.dart.weak.modular.expect
index f29e4d0..42ddd8d 100644
--- a/pkg/front_end/testcases/patterns/pattern_types.dart.weak.modular.expect
+++ b/pkg/front_end/testcases/patterns/pattern_types.dart.weak.modular.expect
@@ -389,7 +389,7 @@
   }
   {
     final synthesized self::Class #17#0 = cls;
-    if(self::_extension#0|<=(#17#0, #C2)) {
+    if(self::_extension#0|<=(#17#0, #C2 as{ForNonNullableByDefault} core::int)) {
     }
   }
   {
diff --git a/pkg/front_end/testcases/patterns/relational_assignable.dart b/pkg/front_end/testcases/patterns/relational_assignable.dart
new file mode 100644
index 0000000..7453c61
--- /dev/null
+++ b/pkg/front_end/testcases/patterns/relational_assignable.dart
@@ -0,0 +1,13 @@
+// Copyright (c) 2023, 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.
+
+method(int i) {
+  const dynamic d = 0;
+  const dynamic s = '';
+  return switch (i) {
+    < d => 0,
+    > s => 1,
+    _ => 2,
+  };
+}
diff --git a/pkg/front_end/testcases/patterns/relational_assignable.dart.strong.expect b/pkg/front_end/testcases/patterns/relational_assignable.dart.strong.expect
new file mode 100644
index 0000000..03a8a31
--- /dev/null
+++ b/pkg/front_end/testcases/patterns/relational_assignable.dart.strong.expect
@@ -0,0 +1,36 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+
+static method method(core::int i) → dynamic {
+  return block {
+    core::int #t1;
+    final synthesized core::int #0#0 = i;
+    #L1:
+    {
+      {
+        if(#0#0.{core::num::<}(#C1 as{ForNonNullableByDefault} core::num){(core::num) → core::bool}) {
+          #t1 = 0;
+          break #L1;
+        }
+      }
+      {
+        if(#0#0.{core::num::>}(#C2 as{ForNonNullableByDefault} core::num){(core::num) → core::bool}) {
+          #t1 = 1;
+          break #L1;
+        }
+      }
+      {
+        if(true) {
+          #t1 = 2;
+          break #L1;
+        }
+      }
+    }
+  } =>#t1;
+}
+
+constants  {
+  #C1 = 0
+  #C2 = ""
+}
diff --git a/pkg/front_end/testcases/patterns/relational_assignable.dart.strong.transformed.expect b/pkg/front_end/testcases/patterns/relational_assignable.dart.strong.transformed.expect
new file mode 100644
index 0000000..8da7e0a
--- /dev/null
+++ b/pkg/front_end/testcases/patterns/relational_assignable.dart.strong.transformed.expect
@@ -0,0 +1,40 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+
+static method method(core::int i) → dynamic {
+  return block {
+    core::int #t1;
+    final synthesized core::int #0#0 = i;
+    #L1:
+    {
+      {
+        if(#0#0.{core::num::<}(#C1 as{ForNonNullableByDefault} core::num){(core::num) → core::bool}) {
+          #t1 = 0;
+          break #L1;
+        }
+      }
+      {
+        if(#0#0.{core::num::>}(#C2 as{ForNonNullableByDefault} core::num){(core::num) → core::bool}) {
+          #t1 = 1;
+          break #L1;
+        }
+      }
+      {
+        if(true) {
+          #t1 = 2;
+          break #L1;
+        }
+      }
+    }
+  } =>#t1;
+}
+
+constants  {
+  #C1 = 0
+  #C2 = ""
+}
+
+Extra constant evaluation status:
+Evaluated: AsExpression @ org-dartlang-testcase:///relational_assignable.dart:9:5 -> IntConstant(0)
+Extra constant evaluation: evaluated: 12, effectively constant: 1
diff --git a/pkg/front_end/testcases/patterns/relational_assignable.dart.textual_outline.expect b/pkg/front_end/testcases/patterns/relational_assignable.dart.textual_outline.expect
new file mode 100644
index 0000000..63be012
--- /dev/null
+++ b/pkg/front_end/testcases/patterns/relational_assignable.dart.textual_outline.expect
@@ -0,0 +1 @@
+method(int i) {}
diff --git a/pkg/front_end/testcases/patterns/relational_assignable.dart.textual_outline_modelled.expect b/pkg/front_end/testcases/patterns/relational_assignable.dart.textual_outline_modelled.expect
new file mode 100644
index 0000000..63be012
--- /dev/null
+++ b/pkg/front_end/testcases/patterns/relational_assignable.dart.textual_outline_modelled.expect
@@ -0,0 +1 @@
+method(int i) {}
diff --git a/pkg/front_end/testcases/patterns/relational_assignable.dart.weak.expect b/pkg/front_end/testcases/patterns/relational_assignable.dart.weak.expect
new file mode 100644
index 0000000..03a8a31
--- /dev/null
+++ b/pkg/front_end/testcases/patterns/relational_assignable.dart.weak.expect
@@ -0,0 +1,36 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+
+static method method(core::int i) → dynamic {
+  return block {
+    core::int #t1;
+    final synthesized core::int #0#0 = i;
+    #L1:
+    {
+      {
+        if(#0#0.{core::num::<}(#C1 as{ForNonNullableByDefault} core::num){(core::num) → core::bool}) {
+          #t1 = 0;
+          break #L1;
+        }
+      }
+      {
+        if(#0#0.{core::num::>}(#C2 as{ForNonNullableByDefault} core::num){(core::num) → core::bool}) {
+          #t1 = 1;
+          break #L1;
+        }
+      }
+      {
+        if(true) {
+          #t1 = 2;
+          break #L1;
+        }
+      }
+    }
+  } =>#t1;
+}
+
+constants  {
+  #C1 = 0
+  #C2 = ""
+}
diff --git a/pkg/front_end/testcases/patterns/relational_assignable.dart.weak.modular.expect b/pkg/front_end/testcases/patterns/relational_assignable.dart.weak.modular.expect
new file mode 100644
index 0000000..03a8a31
--- /dev/null
+++ b/pkg/front_end/testcases/patterns/relational_assignable.dart.weak.modular.expect
@@ -0,0 +1,36 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+
+static method method(core::int i) → dynamic {
+  return block {
+    core::int #t1;
+    final synthesized core::int #0#0 = i;
+    #L1:
+    {
+      {
+        if(#0#0.{core::num::<}(#C1 as{ForNonNullableByDefault} core::num){(core::num) → core::bool}) {
+          #t1 = 0;
+          break #L1;
+        }
+      }
+      {
+        if(#0#0.{core::num::>}(#C2 as{ForNonNullableByDefault} core::num){(core::num) → core::bool}) {
+          #t1 = 1;
+          break #L1;
+        }
+      }
+      {
+        if(true) {
+          #t1 = 2;
+          break #L1;
+        }
+      }
+    }
+  } =>#t1;
+}
+
+constants  {
+  #C1 = 0
+  #C2 = ""
+}
diff --git a/pkg/front_end/testcases/patterns/relational_assignable.dart.weak.outline.expect b/pkg/front_end/testcases/patterns/relational_assignable.dart.weak.outline.expect
new file mode 100644
index 0000000..eb9ee10
--- /dev/null
+++ b/pkg/front_end/testcases/patterns/relational_assignable.dart.weak.outline.expect
@@ -0,0 +1,6 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+
+static method method(core::int i) → dynamic
+  ;
diff --git a/pkg/front_end/testcases/patterns/relational_assignable.dart.weak.transformed.expect b/pkg/front_end/testcases/patterns/relational_assignable.dart.weak.transformed.expect
new file mode 100644
index 0000000..8da7e0a
--- /dev/null
+++ b/pkg/front_end/testcases/patterns/relational_assignable.dart.weak.transformed.expect
@@ -0,0 +1,40 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+
+static method method(core::int i) → dynamic {
+  return block {
+    core::int #t1;
+    final synthesized core::int #0#0 = i;
+    #L1:
+    {
+      {
+        if(#0#0.{core::num::<}(#C1 as{ForNonNullableByDefault} core::num){(core::num) → core::bool}) {
+          #t1 = 0;
+          break #L1;
+        }
+      }
+      {
+        if(#0#0.{core::num::>}(#C2 as{ForNonNullableByDefault} core::num){(core::num) → core::bool}) {
+          #t1 = 1;
+          break #L1;
+        }
+      }
+      {
+        if(true) {
+          #t1 = 2;
+          break #L1;
+        }
+      }
+    }
+  } =>#t1;
+}
+
+constants  {
+  #C1 = 0
+  #C2 = ""
+}
+
+Extra constant evaluation status:
+Evaluated: AsExpression @ org-dartlang-testcase:///relational_assignable.dart:9:5 -> IntConstant(0)
+Extra constant evaluation: evaluated: 12, effectively constant: 1