[cfe] If statements for const functions.

Change-Id: If4449eb6613c7a2b53119ed2e8d3f3042624ccd1
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/190800
Commit-Queue: Kallen Tu <kallentu@google.com>
Reviewed-by: Jake Macdonald <jakemac@google.com>
Reviewed-by: Dmitry Stefantsov <dmitryas@google.com>
diff --git a/pkg/front_end/lib/src/fasta/kernel/constant_evaluator.dart b/pkg/front_end/lib/src/fasta/kernel/constant_evaluator.dart
index 1089515..ec0a9bb 100644
--- a/pkg/front_end/lib/src/fasta/kernel/constant_evaluator.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/constant_evaluator.dart
@@ -3340,6 +3340,29 @@
   }
 
   @override
+  ExecutionStatus visitIfStatement(IfStatement node) {
+    Constant condition = evaluate(node.condition);
+    if (condition is AbortConstant) return new ReturnStatus(condition);
+    if (condition is BoolConstant) {
+      if (condition.value) {
+        return node.then.accept(this);
+      } else if (node.otherwise != null) {
+        return node.otherwise.accept(this);
+      } else {
+        return const ProceedStatus();
+      }
+    } else {
+      return new ReturnStatus(exprEvaluator.createErrorConstant(
+          node.condition,
+          templateConstEvalInvalidType.withArguments(
+              condition,
+              exprEvaluator.typeEnvironment.coreTypes.boolLegacyRawType,
+              condition.getType(exprEvaluator._staticTypeContext),
+              exprEvaluator.isNonNullableByDefault)));
+    }
+  }
+
+  @override
   ExecutionStatus visitExpressionStatement(ExpressionStatement node) {
     Constant value = evaluate(node.expression);
     if (value is AbortConstant) return new ReturnStatus(value);
@@ -3436,7 +3459,12 @@
   EvaluationEnvironment.withParent(this._parent);
 
   /// Whether the current environment is empty.
-  bool get isEmpty => _typeVariables.isEmpty && _variables.isEmpty;
+  bool get isEmpty {
+    // Since we look up variables in enclosing environment, the environment
+    // is not empty if its parent is not empty.
+    if (_parent != null && !_parent.isEmpty) return false;
+    return _typeVariables.isEmpty && _variables.isEmpty;
+  }
 
   void addTypeParameterValue(TypeParameter parameter, DartType value) {
     assert(!_typeVariables.containsKey(parameter));
diff --git a/pkg/front_end/testcases/const_functions/const_functions_if_statements.dart b/pkg/front_end/testcases/const_functions/const_functions_if_statements.dart
new file mode 100644
index 0000000..a4118a3
--- /dev/null
+++ b/pkg/front_end/testcases/const_functions/const_functions_if_statements.dart
@@ -0,0 +1,75 @@
+// Copyright (c) 2021, 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.
+
+// Tests if statements for const functions.
+
+import "package:expect/expect.dart";
+
+const var1 = ifTest(1);
+const var2 = ifTest(2);
+const var3 = ifTest(3);
+int ifTest(int a) {
+  if (a == 1) {
+    return 100;
+  } else if (a == 2) {
+    return 200;
+  } else {
+    return 300;
+  }
+}
+
+const one = 1;
+const var4 = ifTest2(1);
+const var5 = ifTest2(2);
+int ifTest2(int a) {
+  if (a == one) {
+    return 100;
+  } else {
+    return 200;
+  }
+}
+
+const var6 = ifTest3(1);
+const var6_1 = ifTest3(2);
+const var6_2 = ifTest3(0);
+int ifTest3(int a) {
+  if (a > 0) {
+    if (a == 1) return 100;
+    return 200;
+  }
+  return 300;
+}
+
+const var7 = ifTest4(1);
+int ifTest4(int a) {
+  int b = a;
+  if (a == 1) {
+    b += a;
+    if (a % 2 == 1) {
+      b += a;
+    }
+  } else if (a == 2) {
+    b -= a;
+  }
+
+  return b;
+}
+
+const var8 = ifTest5();
+int ifTest5() {
+  var x = 10;
+  if (true) var x = 20;
+  return x;
+}
+
+void main() {
+  Expect.equals(var1, 100);
+  Expect.equals(var2, 200);
+  Expect.equals(var3, 300);
+  Expect.equals(var4, 100);
+  Expect.equals(var5, 200);
+  Expect.equals(var6, 100);
+  Expect.equals(var7, 3);
+  Expect.equals(var8, 10);
+}
diff --git a/pkg/front_end/testcases/const_functions/const_functions_if_statements.dart.strong.expect b/pkg/front_end/testcases/const_functions/const_functions_if_statements.dart.strong.expect
new file mode 100644
index 0000000..8d0f813
--- /dev/null
+++ b/pkg/front_end/testcases/const_functions/const_functions_if_statements.dart.strong.expect
@@ -0,0 +1,86 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+import "package:expect/expect.dart" as exp;
+
+import "package:expect/expect.dart";
+
+static const field core::int var1 = #C1;
+static const field core::int var2 = #C2;
+static const field core::int var3 = #C3;
+static const field core::int one = #C4;
+static const field core::int var4 = #C1;
+static const field core::int var5 = #C2;
+static const field core::int var6 = #C1;
+static const field core::int var6_1 = #C2;
+static const field core::int var6_2 = #C3;
+static const field core::int var7 = #C5;
+static const field core::int var8 = #C6;
+static method ifTest(core::int a) → core::int {
+  if(a.{core::num::==}(1)) {
+    return 100;
+  }
+  else
+    if(a.{core::num::==}(2)) {
+      return 200;
+    }
+    else {
+      return 300;
+    }
+}
+static method ifTest2(core::int a) → core::int {
+  if(a.{core::num::==}(#C4)) {
+    return 100;
+  }
+  else {
+    return 200;
+  }
+}
+static method ifTest3(core::int a) → core::int {
+  if(a.{core::num::>}(0)) {
+    if(a.{core::num::==}(1))
+      return 100;
+    return 200;
+  }
+  return 300;
+}
+static method ifTest4(core::int a) → core::int {
+  core::int b = a;
+  if(a.{core::num::==}(1)) {
+    b = b.{core::num::+}(a);
+    if(a.{core::num::%}(2).{core::num::==}(1)) {
+      b = b.{core::num::+}(a);
+    }
+  }
+  else
+    if(a.{core::num::==}(2)) {
+      b = b.{core::num::-}(a);
+    }
+  return b;
+}
+static method ifTest5() → core::int {
+  core::int x = 10;
+  if(true) {
+    core::int x = 20;
+  }
+  return x;
+}
+static method main() → void {
+  exp::Expect::equals(#C1, 100);
+  exp::Expect::equals(#C2, 200);
+  exp::Expect::equals(#C3, 300);
+  exp::Expect::equals(#C1, 100);
+  exp::Expect::equals(#C2, 200);
+  exp::Expect::equals(#C1, 100);
+  exp::Expect::equals(#C5, 3);
+  exp::Expect::equals(#C6, 10);
+}
+
+constants  {
+  #C1 = 100
+  #C2 = 200
+  #C3 = 300
+  #C4 = 1
+  #C5 = 3
+  #C6 = 10
+}
diff --git a/pkg/front_end/testcases/const_functions/const_functions_if_statements.dart.strong.transformed.expect b/pkg/front_end/testcases/const_functions/const_functions_if_statements.dart.strong.transformed.expect
new file mode 100644
index 0000000..8d0f813
--- /dev/null
+++ b/pkg/front_end/testcases/const_functions/const_functions_if_statements.dart.strong.transformed.expect
@@ -0,0 +1,86 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+import "package:expect/expect.dart" as exp;
+
+import "package:expect/expect.dart";
+
+static const field core::int var1 = #C1;
+static const field core::int var2 = #C2;
+static const field core::int var3 = #C3;
+static const field core::int one = #C4;
+static const field core::int var4 = #C1;
+static const field core::int var5 = #C2;
+static const field core::int var6 = #C1;
+static const field core::int var6_1 = #C2;
+static const field core::int var6_2 = #C3;
+static const field core::int var7 = #C5;
+static const field core::int var8 = #C6;
+static method ifTest(core::int a) → core::int {
+  if(a.{core::num::==}(1)) {
+    return 100;
+  }
+  else
+    if(a.{core::num::==}(2)) {
+      return 200;
+    }
+    else {
+      return 300;
+    }
+}
+static method ifTest2(core::int a) → core::int {
+  if(a.{core::num::==}(#C4)) {
+    return 100;
+  }
+  else {
+    return 200;
+  }
+}
+static method ifTest3(core::int a) → core::int {
+  if(a.{core::num::>}(0)) {
+    if(a.{core::num::==}(1))
+      return 100;
+    return 200;
+  }
+  return 300;
+}
+static method ifTest4(core::int a) → core::int {
+  core::int b = a;
+  if(a.{core::num::==}(1)) {
+    b = b.{core::num::+}(a);
+    if(a.{core::num::%}(2).{core::num::==}(1)) {
+      b = b.{core::num::+}(a);
+    }
+  }
+  else
+    if(a.{core::num::==}(2)) {
+      b = b.{core::num::-}(a);
+    }
+  return b;
+}
+static method ifTest5() → core::int {
+  core::int x = 10;
+  if(true) {
+    core::int x = 20;
+  }
+  return x;
+}
+static method main() → void {
+  exp::Expect::equals(#C1, 100);
+  exp::Expect::equals(#C2, 200);
+  exp::Expect::equals(#C3, 300);
+  exp::Expect::equals(#C1, 100);
+  exp::Expect::equals(#C2, 200);
+  exp::Expect::equals(#C1, 100);
+  exp::Expect::equals(#C5, 3);
+  exp::Expect::equals(#C6, 10);
+}
+
+constants  {
+  #C1 = 100
+  #C2 = 200
+  #C3 = 300
+  #C4 = 1
+  #C5 = 3
+  #C6 = 10
+}
diff --git a/pkg/front_end/testcases/const_functions/const_functions_if_statements.dart.textual_outline.expect b/pkg/front_end/testcases/const_functions/const_functions_if_statements.dart.textual_outline.expect
new file mode 100644
index 0000000..0af1a72
--- /dev/null
+++ b/pkg/front_end/testcases/const_functions/const_functions_if_statements.dart.textual_outline.expect
@@ -0,0 +1,19 @@
+import "package:expect/expect.dart";
+
+const var1 = ifTest(1);
+const var2 = ifTest(2);
+const var3 = ifTest(3);
+int ifTest(int a) {}
+const one = 1;
+const var4 = ifTest2(1);
+const var5 = ifTest2(2);
+int ifTest2(int a) {}
+const var6 = ifTest3(1);
+const var6_1 = ifTest3(2);
+const var6_2 = ifTest3(0);
+int ifTest3(int a) {}
+const var7 = ifTest4(1);
+int ifTest4(int a) {}
+const var8 = ifTest5();
+int ifTest5() {}
+void main() {}
diff --git a/pkg/front_end/testcases/const_functions/const_functions_if_statements.dart.textual_outline_modelled.expect b/pkg/front_end/testcases/const_functions/const_functions_if_statements.dart.textual_outline_modelled.expect
new file mode 100644
index 0000000..5f5b560
--- /dev/null
+++ b/pkg/front_end/testcases/const_functions/const_functions_if_statements.dart.textual_outline_modelled.expect
@@ -0,0 +1,19 @@
+import "package:expect/expect.dart";
+
+const one = 1;
+const var1 = ifTest(1);
+const var2 = ifTest(2);
+const var3 = ifTest(3);
+const var4 = ifTest2(1);
+const var5 = ifTest2(2);
+const var6 = ifTest3(1);
+const var6_1 = ifTest3(2);
+const var6_2 = ifTest3(0);
+const var7 = ifTest4(1);
+const var8 = ifTest5();
+int ifTest(int a) {}
+int ifTest2(int a) {}
+int ifTest3(int a) {}
+int ifTest4(int a) {}
+int ifTest5() {}
+void main() {}
diff --git a/pkg/front_end/testcases/const_functions/const_functions_if_statements.dart.weak.expect b/pkg/front_end/testcases/const_functions/const_functions_if_statements.dart.weak.expect
new file mode 100644
index 0000000..8d0f813
--- /dev/null
+++ b/pkg/front_end/testcases/const_functions/const_functions_if_statements.dart.weak.expect
@@ -0,0 +1,86 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+import "package:expect/expect.dart" as exp;
+
+import "package:expect/expect.dart";
+
+static const field core::int var1 = #C1;
+static const field core::int var2 = #C2;
+static const field core::int var3 = #C3;
+static const field core::int one = #C4;
+static const field core::int var4 = #C1;
+static const field core::int var5 = #C2;
+static const field core::int var6 = #C1;
+static const field core::int var6_1 = #C2;
+static const field core::int var6_2 = #C3;
+static const field core::int var7 = #C5;
+static const field core::int var8 = #C6;
+static method ifTest(core::int a) → core::int {
+  if(a.{core::num::==}(1)) {
+    return 100;
+  }
+  else
+    if(a.{core::num::==}(2)) {
+      return 200;
+    }
+    else {
+      return 300;
+    }
+}
+static method ifTest2(core::int a) → core::int {
+  if(a.{core::num::==}(#C4)) {
+    return 100;
+  }
+  else {
+    return 200;
+  }
+}
+static method ifTest3(core::int a) → core::int {
+  if(a.{core::num::>}(0)) {
+    if(a.{core::num::==}(1))
+      return 100;
+    return 200;
+  }
+  return 300;
+}
+static method ifTest4(core::int a) → core::int {
+  core::int b = a;
+  if(a.{core::num::==}(1)) {
+    b = b.{core::num::+}(a);
+    if(a.{core::num::%}(2).{core::num::==}(1)) {
+      b = b.{core::num::+}(a);
+    }
+  }
+  else
+    if(a.{core::num::==}(2)) {
+      b = b.{core::num::-}(a);
+    }
+  return b;
+}
+static method ifTest5() → core::int {
+  core::int x = 10;
+  if(true) {
+    core::int x = 20;
+  }
+  return x;
+}
+static method main() → void {
+  exp::Expect::equals(#C1, 100);
+  exp::Expect::equals(#C2, 200);
+  exp::Expect::equals(#C3, 300);
+  exp::Expect::equals(#C1, 100);
+  exp::Expect::equals(#C2, 200);
+  exp::Expect::equals(#C1, 100);
+  exp::Expect::equals(#C5, 3);
+  exp::Expect::equals(#C6, 10);
+}
+
+constants  {
+  #C1 = 100
+  #C2 = 200
+  #C3 = 300
+  #C4 = 1
+  #C5 = 3
+  #C6 = 10
+}
diff --git a/pkg/front_end/testcases/const_functions/const_functions_if_statements.dart.weak.outline.expect b/pkg/front_end/testcases/const_functions/const_functions_if_statements.dart.weak.outline.expect
new file mode 100644
index 0000000..ee08b4b
--- /dev/null
+++ b/pkg/front_end/testcases/const_functions/const_functions_if_statements.dart.weak.outline.expect
@@ -0,0 +1,29 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+
+import "package:expect/expect.dart";
+
+static const field core::int var1 = self::ifTest(1);
+static const field core::int var2 = self::ifTest(2);
+static const field core::int var3 = self::ifTest(3);
+static const field core::int one = 1;
+static const field core::int var4 = self::ifTest2(1);
+static const field core::int var5 = self::ifTest2(2);
+static const field core::int var6 = self::ifTest3(1);
+static const field core::int var6_1 = self::ifTest3(2);
+static const field core::int var6_2 = self::ifTest3(0);
+static const field core::int var7 = self::ifTest4(1);
+static const field core::int var8 = self::ifTest5();
+static method ifTest(core::int a) → core::int
+  ;
+static method ifTest2(core::int a) → core::int
+  ;
+static method ifTest3(core::int a) → core::int
+  ;
+static method ifTest4(core::int a) → core::int
+  ;
+static method ifTest5() → core::int
+  ;
+static method main() → void
+  ;
diff --git a/pkg/front_end/testcases/const_functions/const_functions_if_statements.dart.weak.transformed.expect b/pkg/front_end/testcases/const_functions/const_functions_if_statements.dart.weak.transformed.expect
new file mode 100644
index 0000000..8d0f813
--- /dev/null
+++ b/pkg/front_end/testcases/const_functions/const_functions_if_statements.dart.weak.transformed.expect
@@ -0,0 +1,86 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+import "package:expect/expect.dart" as exp;
+
+import "package:expect/expect.dart";
+
+static const field core::int var1 = #C1;
+static const field core::int var2 = #C2;
+static const field core::int var3 = #C3;
+static const field core::int one = #C4;
+static const field core::int var4 = #C1;
+static const field core::int var5 = #C2;
+static const field core::int var6 = #C1;
+static const field core::int var6_1 = #C2;
+static const field core::int var6_2 = #C3;
+static const field core::int var7 = #C5;
+static const field core::int var8 = #C6;
+static method ifTest(core::int a) → core::int {
+  if(a.{core::num::==}(1)) {
+    return 100;
+  }
+  else
+    if(a.{core::num::==}(2)) {
+      return 200;
+    }
+    else {
+      return 300;
+    }
+}
+static method ifTest2(core::int a) → core::int {
+  if(a.{core::num::==}(#C4)) {
+    return 100;
+  }
+  else {
+    return 200;
+  }
+}
+static method ifTest3(core::int a) → core::int {
+  if(a.{core::num::>}(0)) {
+    if(a.{core::num::==}(1))
+      return 100;
+    return 200;
+  }
+  return 300;
+}
+static method ifTest4(core::int a) → core::int {
+  core::int b = a;
+  if(a.{core::num::==}(1)) {
+    b = b.{core::num::+}(a);
+    if(a.{core::num::%}(2).{core::num::==}(1)) {
+      b = b.{core::num::+}(a);
+    }
+  }
+  else
+    if(a.{core::num::==}(2)) {
+      b = b.{core::num::-}(a);
+    }
+  return b;
+}
+static method ifTest5() → core::int {
+  core::int x = 10;
+  if(true) {
+    core::int x = 20;
+  }
+  return x;
+}
+static method main() → void {
+  exp::Expect::equals(#C1, 100);
+  exp::Expect::equals(#C2, 200);
+  exp::Expect::equals(#C3, 300);
+  exp::Expect::equals(#C1, 100);
+  exp::Expect::equals(#C2, 200);
+  exp::Expect::equals(#C1, 100);
+  exp::Expect::equals(#C5, 3);
+  exp::Expect::equals(#C6, 10);
+}
+
+constants  {
+  #C1 = 100
+  #C2 = 200
+  #C3 = 300
+  #C4 = 1
+  #C5 = 3
+  #C6 = 10
+}
diff --git a/tests/language/const_functions/const_functions_if_statements_test.dart b/tests/language/const_functions/const_functions_if_statements_test.dart
new file mode 100644
index 0000000..e4998ee
--- /dev/null
+++ b/tests/language/const_functions/const_functions_if_statements_test.dart
@@ -0,0 +1,97 @@
+// Copyright (c) 2021, 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.
+
+// Tests if statements for const functions.
+
+// SharedOptions=--enable-experiment=const-functions
+
+import "package:expect/expect.dart";
+
+const var1 = ifTest(1);
+//           ^^^^^^^^^
+// [analyzer] COMPILE_TIME_ERROR.CONST_INITIALIZED_WITH_NON_CONSTANT_VALUE
+const var2 = ifTest(2);
+//           ^^^^^^^^^
+// [analyzer] COMPILE_TIME_ERROR.CONST_INITIALIZED_WITH_NON_CONSTANT_VALUE
+const var3 = ifTest(3);
+//           ^^^^^^^^^
+// [analyzer] COMPILE_TIME_ERROR.CONST_INITIALIZED_WITH_NON_CONSTANT_VALUE
+int ifTest(int a) {
+  if (a == 1) {
+    return 100;
+  } else if (a == 2) {
+    return 200;
+  } else {
+    return 300;
+  }
+}
+
+const one = 1;
+const var4 = ifTest2(1);
+//           ^^^^^^^^^^
+// [analyzer] COMPILE_TIME_ERROR.CONST_INITIALIZED_WITH_NON_CONSTANT_VALUE
+const var5 = ifTest2(2);
+//           ^^^^^^^^^^
+// [analyzer] COMPILE_TIME_ERROR.CONST_INITIALIZED_WITH_NON_CONSTANT_VALUE
+int ifTest2(int a) {
+  if (a == one) {
+    return 100;
+  } else {
+    return 200;
+  }
+}
+
+const var6 = ifTest3(1);
+//           ^^^^^^^^^^
+// [analyzer] COMPILE_TIME_ERROR.CONST_INITIALIZED_WITH_NON_CONSTANT_VALUE
+const var6_1 = ifTest3(2);
+//             ^^^^^^^^^^
+// [analyzer] COMPILE_TIME_ERROR.CONST_INITIALIZED_WITH_NON_CONSTANT_VALUE
+const var6_2 = ifTest3(0);
+//             ^^^^^^^^^^
+// [analyzer] COMPILE_TIME_ERROR.CONST_INITIALIZED_WITH_NON_CONSTANT_VALUE
+int ifTest3(int a) {
+  if (a > 0) {
+    if (a == 1) return 100;
+    return 200;
+  }
+  return 300;
+}
+
+const var7 = ifTest4(1);
+//           ^^^^^^^^^^
+// [analyzer] COMPILE_TIME_ERROR.CONST_INITIALIZED_WITH_NON_CONSTANT_VALUE
+int ifTest4(int a) {
+  int b = a;
+  if (a == 1) {
+    b += a;
+    if (a % 2 == 1) {
+      b += a;
+    }
+  } else if (a == 2) {
+    b -= a;
+  }
+
+  return b;
+}
+
+const var8 = ifTest5();
+//           ^^^^^^^^^
+// [analyzer] COMPILE_TIME_ERROR.CONST_INITIALIZED_WITH_NON_CONSTANT_VALUE
+int ifTest5() {
+  var x = 10;
+  if (true) var x = 20;
+  return x;
+}
+
+void main() {
+  Expect.equals(var1, 100);
+  Expect.equals(var2, 200);
+  Expect.equals(var3, 300);
+  Expect.equals(var4, 100);
+  Expect.equals(var5, 200);
+  Expect.equals(var6, 100);
+  Expect.equals(var7, 3);
+  Expect.equals(var8, 10);
+}