[cfe] Closures and environment changes for const functions.
Change-Id: If844a28192c1eef4dcbb62075ae07e86297f2c92
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/193920
Reviewed-by: Dmitry Stefantsov <dmitryas@google.com>
Reviewed-by: Bob Nystrom <rnystrom@google.com>
Reviewed-by: Jake Macdonald <jakemac@google.com>
Commit-Queue: Kallen Tu <kallentu@google.com>
diff --git a/pkg/_fe_analyzer_shared/lib/src/messages/codes_generated.dart b/pkg/_fe_analyzer_shared/lib/src/messages/codes_generated.dart
index 530baf1..f05045e 100644
--- a/pkg/_fe_analyzer_shared/lib/src/messages/codes_generated.dart
+++ b/pkg/_fe_analyzer_shared/lib/src/messages/codes_generated.dart
@@ -1354,6 +1354,28 @@
// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
const Template<Message Function(String nameOKEmpty)>
+ templateConstEvalGetterNotFound =
+ const Template<Message Function(String nameOKEmpty)>(
+ messageTemplate: r"""Variable get not found: '#nameOKEmpty'""",
+ withArguments: _withArgumentsConstEvalGetterNotFound);
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+const Code<Message Function(String nameOKEmpty)> codeConstEvalGetterNotFound =
+ const Code<Message Function(String nameOKEmpty)>(
+ "ConstEvalGetterNotFound",
+);
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+Message _withArgumentsConstEvalGetterNotFound(String nameOKEmpty) {
+ // ignore: unnecessary_null_comparison
+ if (nameOKEmpty == null || nameOKEmpty.isEmpty) nameOKEmpty = '(unnamed)';
+ return new Message(codeConstEvalGetterNotFound,
+ message: """Variable get not found: '${nameOKEmpty}'""",
+ arguments: {'nameOKEmpty': nameOKEmpty});
+}
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+const Template<Message Function(String nameOKEmpty)>
templateConstEvalInvalidStaticInvocation =
const Template<Message Function(String nameOKEmpty)>(
messageTemplate:
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 97f21f3..1a2a02f 100644
--- a/pkg/front_end/lib/src/fasta/kernel/constant_evaluator.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/constant_evaluator.dart
@@ -59,6 +59,7 @@
templateConstEvalElementImplementsEqual,
templateConstEvalFailedAssertionWithMessage,
templateConstEvalFreeTypeParameter,
+ templateConstEvalGetterNotFound,
templateConstEvalInvalidType,
templateConstEvalInvalidBinaryOperandType,
templateConstEvalInvalidEqualsOperandType,
@@ -559,7 +560,7 @@
node.function = transform(node.function)..parent = node;
}
constantEvaluator.env.addVariableValue(
- node.variable, new IntermediateValue(node.function));
+ node.variable, new FunctionValue(node.function, null));
} else {
return super.visitFunctionDeclaration(node, removalSentinel);
}
@@ -1394,6 +1395,9 @@
@override
Constant visitFunctionExpression(FunctionExpression node) {
+ if (enableConstFunctions) {
+ return new FunctionValue(node.function, env);
+ }
return createInvalidExpressionConstant(node, "Function literal");
}
@@ -2165,6 +2169,9 @@
node, "method invocation with named arguments");
}
+ final Constant receiver = _evaluateSubexpression(node.receiver);
+ if (receiver is AbortConstant) return receiver;
+
final List<Constant> arguments =
_evaluatePositionalArguments(node.arguments);
@@ -2176,12 +2183,7 @@
assert(_gotError == null);
assert(arguments != null);
- final Constant receiver = _evaluateSubexpression(node.receiver);
- if (receiver is AbortConstant) {
- return receiver;
- } else if (enableConstFunctions &&
- receiver is IntermediateValue &&
- receiver.value is FunctionNode) {
+ if (enableConstFunctions && receiver is FunctionValue) {
// Evaluate type arguments of the method invoked.
List<DartType> types = _evaluateTypeArguments(node, node.arguments);
if (types == null && _gotError != null) {
@@ -2203,7 +2205,9 @@
assert(_gotError == null);
assert(named != null);
- return _handleFunctionInvocation(receiver.value, types, arguments, named);
+ return _handleFunctionInvocation(
+ receiver.function, types, arguments, named,
+ functionEnvironment: receiver.environment);
}
if (shouldBeUnevaluated) {
@@ -2465,8 +2469,8 @@
final VariableDeclaration variable = node.variable;
if (enableConstFunctions) {
return env.lookupVariable(variable) ??
- createInvalidExpressionConstant(
- node, 'Variable get of an unknown value.');
+ createErrorConstant(node,
+ templateConstEvalGetterNotFound.withArguments(variable.name));
} else {
if (variable.parent is Let || _isFormalParameter(variable)) {
return env.lookupVariable(node.variable) ??
@@ -2815,8 +2819,9 @@
FunctionNode function,
List<DartType> typeArguments,
List<Constant> positionalArguments,
- Map<String, Constant> namedArguments) {
- return withNewEnvironment(() {
+ Map<String, Constant> namedArguments,
+ {EvaluationEnvironment functionEnvironment}) {
+ Constant executeFunction() {
// Map arguments from caller to callee.
for (int i = 0; i < function.typeParameters.length; i++) {
env.addTypeParameterValue(function.typeParameters[i], typeArguments[i]);
@@ -2838,7 +2843,12 @@
env.addVariableValue(parameter, value);
}
return execute(function.body);
- });
+ }
+
+ if (functionEnvironment != null) {
+ return withEnvironment(functionEnvironment, executeFunction);
+ }
+ return withNewEnvironment(executeFunction);
}
@override
@@ -3264,6 +3274,14 @@
return result;
}
+ T withEnvironment<T>(EvaluationEnvironment newEnv, T fn()) {
+ final EvaluationEnvironment oldEnv = env;
+ env = newEnv;
+ T result = fn();
+ env = oldEnv;
+ return result;
+ }
+
/// Binary operation between two operands, at least one of which is a double.
Constant evaluateBinaryNumericOperation(
String op, num a, num b, TreeNode node) {
@@ -3424,6 +3442,17 @@
}
@override
+ ExecutionStatus visitFunctionDeclaration(FunctionDeclaration node) {
+ final EvaluationEnvironment newEnv =
+ new EvaluationEnvironment.withParent(exprEvaluator.env);
+ newEnv.addVariableValue(
+ node.variable, new FunctionValue(node.function, null));
+ final FunctionValue function = new FunctionValue(node.function, newEnv);
+ exprEvaluator.env.addVariableValue(node.variable, function);
+ return const ProceedStatus();
+ }
+
+ @override
ExecutionStatus visitIfStatement(IfStatement node) {
Constant condition = evaluate(node.condition);
if (condition is AbortConstant) return new AbortStatus(condition);
@@ -3740,11 +3769,13 @@
BreakStatus(this.target);
}
-/// An intermediate result that is used within the [ConstantEvaluator].
-class IntermediateValue implements Constant {
- dynamic value;
+/// An intermediate result that is used for invoking function nodes with their
+/// respective environment within the [ConstantEvaluator].
+class FunctionValue implements Constant {
+ final FunctionNode function;
+ final EvaluationEnvironment environment;
- IntermediateValue(this.value);
+ FunctionValue(this.function, this.environment);
@override
R accept<R>(ConstantVisitor<R> v) {
diff --git a/pkg/front_end/messages.status b/pkg/front_end/messages.status
index a8965ac..35e9097 100644
--- a/pkg/front_end/messages.status
+++ b/pkg/front_end/messages.status
@@ -106,6 +106,8 @@
ConstEvalFailedAssertionWithMessage/example: Fail
ConstEvalFreeTypeParameter/analyzerCode: Fail
ConstEvalFreeTypeParameter/example: Fail
+ConstEvalGetterNotFound/analyzerCode: Fail
+ConstEvalGetterNotFound/example: Fail
ConstEvalInvalidBinaryOperandType/analyzerCode: Fail # CONST_EVAL_TYPE_NUM / CONST_EVAL_TYPE_BOOL
ConstEvalInvalidBinaryOperandType/example: Fail
ConstEvalInvalidEqualsOperandType/analyzerCode: Fail
diff --git a/pkg/front_end/messages.yaml b/pkg/front_end/messages.yaml
index 0226a47..540ad5d 100644
--- a/pkg/front_end/messages.yaml
+++ b/pkg/front_end/messages.yaml
@@ -150,6 +150,9 @@
ConstEvalNonNull:
template: "Constant expression must be non-null."
+ConstEvalGetterNotFound:
+ template: "Variable get not found: '#nameOKEmpty'"
+
ConstEvalInvalidMethodInvocation:
template: "The method '#stringOKEmpty' can't be invoked on '#constant' in a constant expression."
analyzerCode: UNDEFINED_OPERATOR
diff --git a/pkg/front_end/testcases/const_functions/const_functions_assert_statements.dart b/pkg/front_end/testcases/const_functions/const_functions_assert_statements.dart
index de66c5b..6be9676 100644
--- a/pkg/front_end/testcases/const_functions/const_functions_assert_statements.dart
+++ b/pkg/front_end/testcases/const_functions/const_functions_assert_statements.dart
@@ -13,6 +13,17 @@
return x;
}
+const var2 = fn2();
+int fn2() {
+ int x = 0;
+ assert(() {
+ var y = x + 1;
+ return y == 1;
+ }());
+ return x;
+}
+
void main() {
Expect.equals(var1, 0);
+ Expect.equals(var2, 0);
}
diff --git a/pkg/front_end/testcases/const_functions/const_functions_assert_statements.dart.strong.expect b/pkg/front_end/testcases/const_functions/const_functions_assert_statements.dart.strong.expect
index 8fa0afb..6f5f7b1 100644
--- a/pkg/front_end/testcases/const_functions/const_functions_assert_statements.dart.strong.expect
+++ b/pkg/front_end/testcases/const_functions/const_functions_assert_statements.dart.strong.expect
@@ -6,13 +6,23 @@
import "package:expect/expect.dart";
static const field core::int var1 = #C1;
+static const field core::int var2 = #C1;
static method fn() → core::int {
core::int x = 0;
assert(x.{core::num::==}(0), "fail");
return x;
}
+static method fn2() → core::int {
+ core::int x = 0;
+ assert((() → core::bool {
+ core::int y = x.{core::num::+}(1);
+ return y.{core::num::==}(1);
+ }).call());
+ return x;
+}
static method main() → void {
exp::Expect::equals(#C1, 0);
+ exp::Expect::equals(#C1, 0);
}
constants {
diff --git a/pkg/front_end/testcases/const_functions/const_functions_assert_statements.dart.strong.transformed.expect b/pkg/front_end/testcases/const_functions/const_functions_assert_statements.dart.strong.transformed.expect
index 8fa0afb..6f5f7b1 100644
--- a/pkg/front_end/testcases/const_functions/const_functions_assert_statements.dart.strong.transformed.expect
+++ b/pkg/front_end/testcases/const_functions/const_functions_assert_statements.dart.strong.transformed.expect
@@ -6,13 +6,23 @@
import "package:expect/expect.dart";
static const field core::int var1 = #C1;
+static const field core::int var2 = #C1;
static method fn() → core::int {
core::int x = 0;
assert(x.{core::num::==}(0), "fail");
return x;
}
+static method fn2() → core::int {
+ core::int x = 0;
+ assert((() → core::bool {
+ core::int y = x.{core::num::+}(1);
+ return y.{core::num::==}(1);
+ }).call());
+ return x;
+}
static method main() → void {
exp::Expect::equals(#C1, 0);
+ exp::Expect::equals(#C1, 0);
}
constants {
diff --git a/pkg/front_end/testcases/const_functions/const_functions_assert_statements.dart.textual_outline.expect b/pkg/front_end/testcases/const_functions/const_functions_assert_statements.dart.textual_outline.expect
index 574b595..62e4cad 100644
--- a/pkg/front_end/testcases/const_functions/const_functions_assert_statements.dart.textual_outline.expect
+++ b/pkg/front_end/testcases/const_functions/const_functions_assert_statements.dart.textual_outline.expect
@@ -2,4 +2,6 @@
const var1 = fn();
int fn() {}
+const var2 = fn2();
+int fn2() {}
void main() {}
diff --git a/pkg/front_end/testcases/const_functions/const_functions_assert_statements.dart.textual_outline_modelled.expect b/pkg/front_end/testcases/const_functions/const_functions_assert_statements.dart.textual_outline_modelled.expect
index 574b595..6f46f7e 100644
--- a/pkg/front_end/testcases/const_functions/const_functions_assert_statements.dart.textual_outline_modelled.expect
+++ b/pkg/front_end/testcases/const_functions/const_functions_assert_statements.dart.textual_outline_modelled.expect
@@ -1,5 +1,7 @@
import "package:expect/expect.dart";
const var1 = fn();
+const var2 = fn2();
int fn() {}
+int fn2() {}
void main() {}
diff --git a/pkg/front_end/testcases/const_functions/const_functions_assert_statements.dart.weak.expect b/pkg/front_end/testcases/const_functions/const_functions_assert_statements.dart.weak.expect
index 8fa0afb..6f5f7b1 100644
--- a/pkg/front_end/testcases/const_functions/const_functions_assert_statements.dart.weak.expect
+++ b/pkg/front_end/testcases/const_functions/const_functions_assert_statements.dart.weak.expect
@@ -6,13 +6,23 @@
import "package:expect/expect.dart";
static const field core::int var1 = #C1;
+static const field core::int var2 = #C1;
static method fn() → core::int {
core::int x = 0;
assert(x.{core::num::==}(0), "fail");
return x;
}
+static method fn2() → core::int {
+ core::int x = 0;
+ assert((() → core::bool {
+ core::int y = x.{core::num::+}(1);
+ return y.{core::num::==}(1);
+ }).call());
+ return x;
+}
static method main() → void {
exp::Expect::equals(#C1, 0);
+ exp::Expect::equals(#C1, 0);
}
constants {
diff --git a/pkg/front_end/testcases/const_functions/const_functions_assert_statements.dart.weak.outline.expect b/pkg/front_end/testcases/const_functions/const_functions_assert_statements.dart.weak.outline.expect
index 786a90f..92461e9 100644
--- a/pkg/front_end/testcases/const_functions/const_functions_assert_statements.dart.weak.outline.expect
+++ b/pkg/front_end/testcases/const_functions/const_functions_assert_statements.dart.weak.outline.expect
@@ -5,7 +5,10 @@
import "package:expect/expect.dart";
static const field core::int var1 = self::fn();
+static const field core::int var2 = self::fn2();
static method fn() → core::int
;
+static method fn2() → core::int
+ ;
static method main() → void
;
diff --git a/pkg/front_end/testcases/const_functions/const_functions_assert_statements.dart.weak.transformed.expect b/pkg/front_end/testcases/const_functions/const_functions_assert_statements.dart.weak.transformed.expect
index 8fa0afb..6f5f7b1 100644
--- a/pkg/front_end/testcases/const_functions/const_functions_assert_statements.dart.weak.transformed.expect
+++ b/pkg/front_end/testcases/const_functions/const_functions_assert_statements.dart.weak.transformed.expect
@@ -6,13 +6,23 @@
import "package:expect/expect.dart";
static const field core::int var1 = #C1;
+static const field core::int var2 = #C1;
static method fn() → core::int {
core::int x = 0;
assert(x.{core::num::==}(0), "fail");
return x;
}
+static method fn2() → core::int {
+ core::int x = 0;
+ assert((() → core::bool {
+ core::int y = x.{core::num::+}(1);
+ return y.{core::num::==}(1);
+ }).call());
+ return x;
+}
static method main() → void {
exp::Expect::equals(#C1, 0);
+ exp::Expect::equals(#C1, 0);
}
constants {
diff --git a/pkg/front_end/testcases/const_functions/const_functions_closures.dart b/pkg/front_end/testcases/const_functions/const_functions_closures.dart
new file mode 100644
index 0000000..70ffa7c
--- /dev/null
+++ b/pkg/front_end/testcases/const_functions/const_functions_closures.dart
@@ -0,0 +1,91 @@
+// 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 closures with const functions.
+
+import "package:expect/expect.dart";
+
+const var1 = foo();
+int foo() {
+ var f = () {
+ int count = 0;
+ int baz() {
+ ++count;
+ return count;
+ }
+
+ return baz;
+ };
+ var c1 = f();
+ var c2 = f();
+
+ var c1_val1 = c1();
+ assert(c1_val1 == 1);
+ var c1_val2 = c1();
+ assert(c1_val2 == 2);
+ var c1_val3 = c1();
+ assert(c1_val3 == 3);
+
+ var c2_val1 = c2();
+ assert(c1_val1 == 1);
+ var c2_val2 = c2();
+ assert(c1_val2 == 2);
+ var c2_val3 = c2();
+ assert(c1_val3 == 3);
+
+ return 0;
+}
+
+const var2 = fn();
+int fn() {
+ return (() => 0)();
+}
+
+const y = 1;
+const var3 = fn3();
+int fn3() {
+ int y = 2;
+ return y;
+}
+
+const var4 = fn4();
+int fn4() {
+ var x = 0;
+ int innerFn() {
+ return x;
+ }
+
+ return innerFn();
+}
+
+const var5 = fn5(3);
+int fn5(int a) {
+ int recurse(int b) {
+ if (b == 1) return 1;
+ int result = recurse(b - 1);
+ return b * result;
+ }
+
+ return recurse(a);
+}
+
+const var6 = fn6(4);
+int fn6(int a) {
+ int recurse() {
+ a--;
+ if (a == 1) return 1;
+ return a * recurse();
+ }
+
+ return recurse();
+}
+
+void main() {
+ Expect.equals(var1, 0);
+ Expect.equals(var2, 0);
+ Expect.equals(var3, 2);
+ Expect.equals(var4, 0);
+ Expect.equals(var5, 6);
+ Expect.equals(var6, 6);
+}
diff --git a/pkg/front_end/testcases/const_functions/const_functions_closures.dart.strong.expect b/pkg/front_end/testcases/const_functions/const_functions_closures.dart.strong.expect
new file mode 100644
index 0000000..64a08b6
--- /dev/null
+++ b/pkg/front_end/testcases/const_functions/const_functions_closures.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 = #C1;
+static const field core::int y = #C2;
+static const field core::int var3 = #C3;
+static const field core::int var4 = #C1;
+static const field core::int var5 = #C4;
+static const field core::int var6 = #C4;
+static method foo() → core::int {
+ () → () → core::int f = () → () → core::int {
+ core::int count = 0;
+ function baz() → core::int {
+ count = count.{core::num::+}(1);
+ return count;
+ }
+ return baz;
+ };
+ () → core::int c1 = f.call();
+ () → core::int c2 = f.call();
+ core::int c1_val1 = c1.call();
+ assert(c1_val1.{core::num::==}(1));
+ core::int c1_val2 = c1.call();
+ assert(c1_val2.{core::num::==}(2));
+ core::int c1_val3 = c1.call();
+ assert(c1_val3.{core::num::==}(3));
+ core::int c2_val1 = c2.call();
+ assert(c1_val1.{core::num::==}(1));
+ core::int c2_val2 = c2.call();
+ assert(c1_val2.{core::num::==}(2));
+ core::int c2_val3 = c2.call();
+ assert(c1_val3.{core::num::==}(3));
+ return 0;
+}
+static method fn() → core::int {
+ return (() → core::int => 0).call();
+}
+static method fn3() → core::int {
+ core::int y = 2;
+ return y;
+}
+static method fn4() → core::int {
+ core::int x = 0;
+ function innerFn() → core::int {
+ return x;
+ }
+ return innerFn.call();
+}
+static method fn5(core::int a) → core::int {
+ function recurse(core::int b) → core::int {
+ if(b.{core::num::==}(1))
+ return 1;
+ core::int result = recurse.call(b.{core::num::-}(1));
+ return b.{core::num::*}(result);
+ }
+ return recurse.call(a);
+}
+static method fn6(core::int a) → core::int {
+ function recurse() → core::int {
+ a = a.{core::num::-}(1);
+ if(a.{core::num::==}(1))
+ return 1;
+ return a.{core::num::*}(recurse.call());
+ }
+ return recurse.call();
+}
+static method main() → void {
+ exp::Expect::equals(#C1, 0);
+ exp::Expect::equals(#C1, 0);
+ exp::Expect::equals(#C3, 2);
+ exp::Expect::equals(#C1, 0);
+ exp::Expect::equals(#C4, 6);
+ exp::Expect::equals(#C4, 6);
+}
+
+constants {
+ #C1 = 0
+ #C2 = 1
+ #C3 = 2
+ #C4 = 6
+}
diff --git a/pkg/front_end/testcases/const_functions/const_functions_closures.dart.strong.transformed.expect b/pkg/front_end/testcases/const_functions/const_functions_closures.dart.strong.transformed.expect
new file mode 100644
index 0000000..64a08b6
--- /dev/null
+++ b/pkg/front_end/testcases/const_functions/const_functions_closures.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 = #C1;
+static const field core::int y = #C2;
+static const field core::int var3 = #C3;
+static const field core::int var4 = #C1;
+static const field core::int var5 = #C4;
+static const field core::int var6 = #C4;
+static method foo() → core::int {
+ () → () → core::int f = () → () → core::int {
+ core::int count = 0;
+ function baz() → core::int {
+ count = count.{core::num::+}(1);
+ return count;
+ }
+ return baz;
+ };
+ () → core::int c1 = f.call();
+ () → core::int c2 = f.call();
+ core::int c1_val1 = c1.call();
+ assert(c1_val1.{core::num::==}(1));
+ core::int c1_val2 = c1.call();
+ assert(c1_val2.{core::num::==}(2));
+ core::int c1_val3 = c1.call();
+ assert(c1_val3.{core::num::==}(3));
+ core::int c2_val1 = c2.call();
+ assert(c1_val1.{core::num::==}(1));
+ core::int c2_val2 = c2.call();
+ assert(c1_val2.{core::num::==}(2));
+ core::int c2_val3 = c2.call();
+ assert(c1_val3.{core::num::==}(3));
+ return 0;
+}
+static method fn() → core::int {
+ return (() → core::int => 0).call();
+}
+static method fn3() → core::int {
+ core::int y = 2;
+ return y;
+}
+static method fn4() → core::int {
+ core::int x = 0;
+ function innerFn() → core::int {
+ return x;
+ }
+ return innerFn.call();
+}
+static method fn5(core::int a) → core::int {
+ function recurse(core::int b) → core::int {
+ if(b.{core::num::==}(1))
+ return 1;
+ core::int result = recurse.call(b.{core::num::-}(1));
+ return b.{core::num::*}(result);
+ }
+ return recurse.call(a);
+}
+static method fn6(core::int a) → core::int {
+ function recurse() → core::int {
+ a = a.{core::num::-}(1);
+ if(a.{core::num::==}(1))
+ return 1;
+ return a.{core::num::*}(recurse.call());
+ }
+ return recurse.call();
+}
+static method main() → void {
+ exp::Expect::equals(#C1, 0);
+ exp::Expect::equals(#C1, 0);
+ exp::Expect::equals(#C3, 2);
+ exp::Expect::equals(#C1, 0);
+ exp::Expect::equals(#C4, 6);
+ exp::Expect::equals(#C4, 6);
+}
+
+constants {
+ #C1 = 0
+ #C2 = 1
+ #C3 = 2
+ #C4 = 6
+}
diff --git a/pkg/front_end/testcases/const_functions/const_functions_closures.dart.textual_outline.expect b/pkg/front_end/testcases/const_functions/const_functions_closures.dart.textual_outline.expect
new file mode 100644
index 0000000..03a1b47
--- /dev/null
+++ b/pkg/front_end/testcases/const_functions/const_functions_closures.dart.textual_outline.expect
@@ -0,0 +1,16 @@
+import "package:expect/expect.dart";
+
+const var1 = foo();
+int foo() {}
+const var2 = fn();
+int fn() {}
+const y = 1;
+const var3 = fn3();
+int fn3() {}
+const var4 = fn4();
+int fn4() {}
+const var5 = fn5(3);
+int fn5(int a) {}
+const var6 = fn6(4);
+int fn6(int a) {}
+void main() {}
diff --git a/pkg/front_end/testcases/const_functions/const_functions_closures.dart.textual_outline_modelled.expect b/pkg/front_end/testcases/const_functions/const_functions_closures.dart.textual_outline_modelled.expect
new file mode 100644
index 0000000..1833158
--- /dev/null
+++ b/pkg/front_end/testcases/const_functions/const_functions_closures.dart.textual_outline_modelled.expect
@@ -0,0 +1,16 @@
+import "package:expect/expect.dart";
+
+const var1 = foo();
+const var2 = fn();
+const var3 = fn3();
+const var4 = fn4();
+const var5 = fn5(3);
+const var6 = fn6(4);
+const y = 1;
+int fn() {}
+int fn3() {}
+int fn4() {}
+int fn5(int a) {}
+int fn6(int a) {}
+int foo() {}
+void main() {}
diff --git a/pkg/front_end/testcases/const_functions/const_functions_closures.dart.weak.expect b/pkg/front_end/testcases/const_functions/const_functions_closures.dart.weak.expect
new file mode 100644
index 0000000..64a08b6
--- /dev/null
+++ b/pkg/front_end/testcases/const_functions/const_functions_closures.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 = #C1;
+static const field core::int y = #C2;
+static const field core::int var3 = #C3;
+static const field core::int var4 = #C1;
+static const field core::int var5 = #C4;
+static const field core::int var6 = #C4;
+static method foo() → core::int {
+ () → () → core::int f = () → () → core::int {
+ core::int count = 0;
+ function baz() → core::int {
+ count = count.{core::num::+}(1);
+ return count;
+ }
+ return baz;
+ };
+ () → core::int c1 = f.call();
+ () → core::int c2 = f.call();
+ core::int c1_val1 = c1.call();
+ assert(c1_val1.{core::num::==}(1));
+ core::int c1_val2 = c1.call();
+ assert(c1_val2.{core::num::==}(2));
+ core::int c1_val3 = c1.call();
+ assert(c1_val3.{core::num::==}(3));
+ core::int c2_val1 = c2.call();
+ assert(c1_val1.{core::num::==}(1));
+ core::int c2_val2 = c2.call();
+ assert(c1_val2.{core::num::==}(2));
+ core::int c2_val3 = c2.call();
+ assert(c1_val3.{core::num::==}(3));
+ return 0;
+}
+static method fn() → core::int {
+ return (() → core::int => 0).call();
+}
+static method fn3() → core::int {
+ core::int y = 2;
+ return y;
+}
+static method fn4() → core::int {
+ core::int x = 0;
+ function innerFn() → core::int {
+ return x;
+ }
+ return innerFn.call();
+}
+static method fn5(core::int a) → core::int {
+ function recurse(core::int b) → core::int {
+ if(b.{core::num::==}(1))
+ return 1;
+ core::int result = recurse.call(b.{core::num::-}(1));
+ return b.{core::num::*}(result);
+ }
+ return recurse.call(a);
+}
+static method fn6(core::int a) → core::int {
+ function recurse() → core::int {
+ a = a.{core::num::-}(1);
+ if(a.{core::num::==}(1))
+ return 1;
+ return a.{core::num::*}(recurse.call());
+ }
+ return recurse.call();
+}
+static method main() → void {
+ exp::Expect::equals(#C1, 0);
+ exp::Expect::equals(#C1, 0);
+ exp::Expect::equals(#C3, 2);
+ exp::Expect::equals(#C1, 0);
+ exp::Expect::equals(#C4, 6);
+ exp::Expect::equals(#C4, 6);
+}
+
+constants {
+ #C1 = 0
+ #C2 = 1
+ #C3 = 2
+ #C4 = 6
+}
diff --git a/pkg/front_end/testcases/const_functions/const_functions_closures.dart.weak.outline.expect b/pkg/front_end/testcases/const_functions/const_functions_closures.dart.weak.outline.expect
new file mode 100644
index 0000000..6be7921
--- /dev/null
+++ b/pkg/front_end/testcases/const_functions/const_functions_closures.dart.weak.outline.expect
@@ -0,0 +1,27 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+
+import "package:expect/expect.dart";
+
+static const field core::int var1 = self::foo();
+static const field core::int var2 = self::fn();
+static const field core::int y = 1;
+static const field core::int var3 = self::fn3();
+static const field core::int var4 = self::fn4();
+static const field core::int var5 = self::fn5(3);
+static const field core::int var6 = self::fn6(4);
+static method foo() → core::int
+ ;
+static method fn() → core::int
+ ;
+static method fn3() → core::int
+ ;
+static method fn4() → core::int
+ ;
+static method fn5(core::int a) → core::int
+ ;
+static method fn6(core::int a) → core::int
+ ;
+static method main() → void
+ ;
diff --git a/pkg/front_end/testcases/const_functions/const_functions_closures.dart.weak.transformed.expect b/pkg/front_end/testcases/const_functions/const_functions_closures.dart.weak.transformed.expect
new file mode 100644
index 0000000..64a08b6
--- /dev/null
+++ b/pkg/front_end/testcases/const_functions/const_functions_closures.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 = #C1;
+static const field core::int y = #C2;
+static const field core::int var3 = #C3;
+static const field core::int var4 = #C1;
+static const field core::int var5 = #C4;
+static const field core::int var6 = #C4;
+static method foo() → core::int {
+ () → () → core::int f = () → () → core::int {
+ core::int count = 0;
+ function baz() → core::int {
+ count = count.{core::num::+}(1);
+ return count;
+ }
+ return baz;
+ };
+ () → core::int c1 = f.call();
+ () → core::int c2 = f.call();
+ core::int c1_val1 = c1.call();
+ assert(c1_val1.{core::num::==}(1));
+ core::int c1_val2 = c1.call();
+ assert(c1_val2.{core::num::==}(2));
+ core::int c1_val3 = c1.call();
+ assert(c1_val3.{core::num::==}(3));
+ core::int c2_val1 = c2.call();
+ assert(c1_val1.{core::num::==}(1));
+ core::int c2_val2 = c2.call();
+ assert(c1_val2.{core::num::==}(2));
+ core::int c2_val3 = c2.call();
+ assert(c1_val3.{core::num::==}(3));
+ return 0;
+}
+static method fn() → core::int {
+ return (() → core::int => 0).call();
+}
+static method fn3() → core::int {
+ core::int y = 2;
+ return y;
+}
+static method fn4() → core::int {
+ core::int x = 0;
+ function innerFn() → core::int {
+ return x;
+ }
+ return innerFn.call();
+}
+static method fn5(core::int a) → core::int {
+ function recurse(core::int b) → core::int {
+ if(b.{core::num::==}(1))
+ return 1;
+ core::int result = recurse.call(b.{core::num::-}(1));
+ return b.{core::num::*}(result);
+ }
+ return recurse.call(a);
+}
+static method fn6(core::int a) → core::int {
+ function recurse() → core::int {
+ a = a.{core::num::-}(1);
+ if(a.{core::num::==}(1))
+ return 1;
+ return a.{core::num::*}(recurse.call());
+ }
+ return recurse.call();
+}
+static method main() → void {
+ exp::Expect::equals(#C1, 0);
+ exp::Expect::equals(#C1, 0);
+ exp::Expect::equals(#C3, 2);
+ exp::Expect::equals(#C1, 0);
+ exp::Expect::equals(#C4, 6);
+ exp::Expect::equals(#C4, 6);
+}
+
+constants {
+ #C1 = 0
+ #C2 = 1
+ #C3 = 2
+ #C4 = 6
+}
diff --git a/tests/language/const_functions/const_functions_assert_statements_test.dart b/tests/language/const_functions/const_functions_assert_statements_test.dart
index 1fe02bb..86c0fe7 100644
--- a/tests/language/const_functions/const_functions_assert_statements_test.dart
+++ b/tests/language/const_functions/const_functions_assert_statements_test.dart
@@ -17,6 +17,19 @@
return x;
}
+const var2 = fn2();
+// ^^^^^
+// [analyzer] COMPILE_TIME_ERROR.CONST_INITIALIZED_WITH_NON_CONSTANT_VALUE
+int fn2() {
+ int x = 0;
+ assert(() {
+ var y = x + 1;
+ return y == 1;
+ }());
+ return x;
+}
+
void main() {
Expect.equals(var1, 0);
+ Expect.equals(var2, 0);
}
diff --git a/tests/language/const_functions/const_functions_closures_error_test.dart b/tests/language/const_functions/const_functions_closures_error_test.dart
new file mode 100644
index 0000000..1383983
--- /dev/null
+++ b/tests/language/const_functions/const_functions_closures_error_test.dart
@@ -0,0 +1,81 @@
+// 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 erroneous closure situations with const functions.
+
+// SharedOptions=--enable-experiment=const-functions
+
+import "package:expect/expect.dart";
+
+var varVariable = 1;
+const var1 = fn();
+// ^^^^
+// [analyzer] COMPILE_TIME_ERROR.CONST_INITIALIZED_WITH_NON_CONSTANT_VALUE
+// [cfe] Constant evaluation error:
+int fn() {
+ return varVariable;
+}
+
+final finalVariable = 1;
+const var2 = fn2();
+// ^^^^^
+// [analyzer] COMPILE_TIME_ERROR.CONST_INITIALIZED_WITH_NON_CONSTANT_VALUE
+// [cfe] Constant evaluation error:
+int fn2() {
+ return finalVariable;
+}
+
+const var3 = fn3();
+// ^^^^^
+// [analyzer] COMPILE_TIME_ERROR.CONST_INITIALIZED_WITH_NON_CONSTANT_VALUE
+int fn3() {
+ int innerFn() {
+ return x;
+ // ^
+ // [analyzer] COMPILE_TIME_ERROR.REFERENCED_BEFORE_DECLARATION
+ }
+
+ const x = 0;
+ // ^
+ // [cfe] Can't declare 'x' because it was already used in this scope.
+ return innerFn();
+}
+
+const var4 = fn4();
+// ^^^^^
+// [analyzer] COMPILE_TIME_ERROR.CONST_INITIALIZED_WITH_NON_CONSTANT_VALUE
+int fn4() {
+ var a = () {
+ return x;
+ // ^
+ // [analyzer] COMPILE_TIME_ERROR.REFERENCED_BEFORE_DECLARATION
+ };
+ var x = 1;
+ // ^
+ // [cfe] Can't declare 'x' because it was already used in this scope.
+ return a();
+}
+
+const var5 = fn5(1);
+// ^^^^^^
+// [analyzer] COMPILE_TIME_ERROR.CONST_INITIALIZED_WITH_NON_CONSTANT_VALUE
+fn5(a) {
+ var a = () => a;
+ // ^
+ // [cfe] Can't declare 'a' because it was already used in this scope.
+ // ^
+ // [analyzer] COMPILE_TIME_ERROR.REFERENCED_BEFORE_DECLARATION
+ return a();
+}
+
+const x = 0;
+void fn6() {
+ var x = 1;
+ int a() => x;
+ const z = a();
+ // ^^^
+ // [analyzer] COMPILE_TIME_ERROR.CONST_INITIALIZED_WITH_NON_CONSTANT_VALUE
+ // ^
+ // [cfe] Constant evaluation error:
+}
diff --git a/tests/language/const_functions/const_functions_closures_test.dart b/tests/language/const_functions/const_functions_closures_test.dart
new file mode 100644
index 0000000..9cb6dcc
--- /dev/null
+++ b/tests/language/const_functions/const_functions_closures_test.dart
@@ -0,0 +1,105 @@
+// 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 closures with const functions.
+
+// SharedOptions=--enable-experiment=const-functions
+
+import "package:expect/expect.dart";
+
+const var1 = foo();
+// ^^^^^
+// [analyzer] COMPILE_TIME_ERROR.CONST_INITIALIZED_WITH_NON_CONSTANT_VALUE
+int foo() {
+ var f = () {
+ int count = 0;
+ int baz() {
+ ++count;
+ return count;
+ }
+
+ return baz;
+ };
+ var c1 = f();
+ var c2 = f();
+
+ var c1_val1 = c1();
+ assert(c1_val1 == 1);
+ var c1_val2 = c1();
+ assert(c1_val2 == 2);
+ var c1_val3 = c1();
+ assert(c1_val3 == 3);
+
+ var c2_val1 = c2();
+ assert(c1_val1 == 1);
+ var c2_val2 = c2();
+ assert(c1_val2 == 2);
+ var c2_val3 = c2();
+ assert(c1_val3 == 3);
+
+ return 0;
+}
+
+const var2 = fn();
+// ^^^^
+// [analyzer] COMPILE_TIME_ERROR.CONST_INITIALIZED_WITH_NON_CONSTANT_VALUE
+int fn() {
+ return (() => 0)();
+}
+
+const y = 1;
+const var3 = fn3();
+// ^^^^^
+// [analyzer] COMPILE_TIME_ERROR.CONST_INITIALIZED_WITH_NON_CONSTANT_VALUE
+int fn3() {
+ int y = 2;
+ return y;
+}
+
+const var4 = fn4();
+// ^^^^^
+// [analyzer] COMPILE_TIME_ERROR.CONST_INITIALIZED_WITH_NON_CONSTANT_VALUE
+int fn4() {
+ var x = 0;
+ int innerFn() {
+ return x;
+ }
+
+ return innerFn();
+}
+
+const var5 = fn5(3);
+// ^^^^^^
+// [analyzer] COMPILE_TIME_ERROR.CONST_INITIALIZED_WITH_NON_CONSTANT_VALUE
+int fn5(int a) {
+ int recurse(int b) {
+ if (b == 1) return 1;
+ int result = recurse(b - 1);
+ return b * result;
+ }
+
+ return recurse(a);
+}
+
+const var6 = fn6(4);
+// ^^^^^^
+// [analyzer] COMPILE_TIME_ERROR.CONST_INITIALIZED_WITH_NON_CONSTANT_VALUE
+int fn6(int a) {
+ int recurse() {
+ a--;
+ if (a == 1) return 1;
+ return a * recurse();
+ }
+
+ return recurse();
+}
+
+void main() {
+ Expect.equals(var1, 0);
+ Expect.equals(var2, 0);
+ Expect.equals(var3, 2);
+ Expect.equals(var4, 0);
+ Expect.equals(var5, 6);
+ Expect.equals(var6, 6);
+}