[cfe] Variable assignments (VariableSet) for const functions.
Change-Id: Iab23493fc09b8fdad78968d905ac8f28e1236aa4
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/190620
Commit-Queue: Kallen Tu <kallentu@google.com>
Reviewed-by: Dmitry Stefantsov <dmitryas@google.com>
Reviewed-by: Jake Macdonald <jakemac@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 2fba597..1089515 100644
--- a/pkg/front_end/lib/src/fasta/kernel/constant_evaluator.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/constant_evaluator.dart
@@ -543,7 +543,7 @@
if (node.function != null) {
node.function = transform(node.function)..parent = node;
}
- constantEvaluator.env.updateVariableValue(
+ constantEvaluator.env.addVariableValue(
node.variable, new IntermediateValue(node.function));
} else {
return super.visitFunctionDeclaration(node, removalSentinel);
@@ -559,7 +559,7 @@
if (node.initializer != null) {
if (node.isConst) {
final Constant constant = evaluateWithContext(node, node.initializer);
- constantEvaluator.env.updateVariableValue(node, constant);
+ constantEvaluator.env.addVariableValue(node, constant);
node.initializer = makeConstantExpression(constant, node.initializer)
..parent = node;
@@ -1669,14 +1669,14 @@
// TODO(johnniwinther): This should call [_evaluateSubexpression].
: _evaluateNullableSubexpression(parameter.initializer);
if (value is AbortConstant) return value;
- env.updateVariableValue(parameter, value);
+ env.addVariableValue(parameter, value);
}
for (final VariableDeclaration parameter in function.namedParameters) {
final Constant value = namedArguments[parameter.name] ??
// TODO(johnniwinther): This should call [_evaluateSubexpression].
_evaluateNullableSubexpression(parameter.initializer);
if (value is AbortConstant) return value;
- env.updateVariableValue(parameter, value);
+ env.addVariableValue(parameter, value);
}
// Step 2) Run all initializers (including super calls) with environment
@@ -1697,7 +1697,7 @@
final VariableDeclaration variable = init.variable;
Constant constant = _evaluateSubexpression(variable.initializer);
if (constant is AbortConstant) return constant;
- env.updateVariableValue(variable, constant);
+ env.addVariableValue(variable, constant);
} else if (init is SuperInitializer) {
AbortConstant error = checkConstructorConst(init, constructor);
if (error != null) return error;
@@ -2412,7 +2412,7 @@
Constant visitLet(Let node) {
Constant value = _evaluateSubexpression(node.variable.initializer);
if (value is AbortConstant) return value;
- env.updateVariableValue(node.variable, value);
+ env.addVariableValue(node.variable, value);
return _evaluateSubexpression(node.body);
}
@@ -2445,6 +2445,19 @@
node, 'Variable get of a non-const variable.');
}
+ @override
+ Constant visitVariableSet(VariableSet node) {
+ if (enableConstFunctions) {
+ final VariableDeclaration variable = node.variable;
+ Constant value = _evaluateSubexpression(node.value);
+ if (value is AbortConstant) return value;
+ return env.updateVariableValue(variable, value) ??
+ createInvalidExpressionConstant(
+ node, 'Variable set of an unknown value.');
+ }
+ return defaultExpression(node);
+ }
+
/// Computes the constant for [expression] defined in the context of [member].
///
/// This compute the constant as seen in the current evaluation mode even when
@@ -2772,14 +2785,14 @@
// TODO(johnniwinther): This should call [_evaluateSubexpression].
: _evaluateNullableSubexpression(parameter.initializer);
if (value is AbortConstant) return value;
- env.updateVariableValue(parameter, value);
+ env.addVariableValue(parameter, value);
}
for (final VariableDeclaration parameter in function.namedParameters) {
final Constant value = namedArguments[parameter.name] ??
// TODO(johnniwinther): This should call [_evaluateSubexpression].
_evaluateNullableSubexpression(parameter.initializer);
if (value is AbortConstant) return value;
- env.updateVariableValue(parameter, value);
+ env.addVariableValue(parameter, value);
}
return execute(function.body);
});
@@ -3295,9 +3308,6 @@
@override
Constant visitThrow(Throw node) => defaultExpression(node);
-
- @override
- Constant visitVariableSet(VariableSet node) => defaultExpression(node);
}
class StatementConstantEvaluator extends StatementVisitor<ExecutionStatus> {
@@ -3330,6 +3340,13 @@
}
@override
+ ExecutionStatus visitExpressionStatement(ExpressionStatement node) {
+ Constant value = evaluate(node.expression);
+ if (value is AbortConstant) return new ReturnStatus(value);
+ return const ProceedStatus();
+ }
+
+ @override
ExecutionStatus visitReturnStatement(ReturnStatement node) =>
new ReturnStatus(evaluate(node.expression));
@@ -3337,7 +3354,7 @@
ExecutionStatus visitVariableDeclaration(VariableDeclaration node) {
Constant value = evaluate(node.initializer);
if (value is AbortConstant) return new ReturnStatus(value);
- exprEvaluator.env.updateVariableValue(node, value);
+ exprEvaluator.env.addVariableValue(node, value);
return const ProceedStatus();
}
}
@@ -3426,17 +3443,21 @@
_typeVariables[parameter] = value;
}
- void updateVariableValue(VariableDeclaration variable, Constant value) {
- if (!_variables.containsKey(variable)) {
- _variables[variable] = new EvaluationReference(value);
- } else {
- _variables[variable].value = value;
- }
+ void addVariableValue(VariableDeclaration variable, Constant value) {
+ _variables[variable] = new EvaluationReference(value);
if (value is UnevaluatedConstant) {
_unreadUnevaluatedVariables.add(variable);
}
}
+ Constant updateVariableValue(VariableDeclaration variable, Constant value) {
+ if (_variables.containsKey(variable)) {
+ _variables[variable].value = value;
+ return value;
+ }
+ return _parent?.updateVariableValue(variable, value);
+ }
+
Constant lookupVariable(VariableDeclaration variable) {
Constant value = _variables[variable]?.value;
if (value is UnevaluatedConstant) {
diff --git a/pkg/front_end/testcases/const_functions/const_functions_variable_assignments.dart b/pkg/front_end/testcases/const_functions/const_functions_variable_assignments.dart
new file mode 100644
index 0000000..3a3fc6f
--- /dev/null
+++ b/pkg/front_end/testcases/const_functions/const_functions_variable_assignments.dart
@@ -0,0 +1,42 @@
+// 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 variable assignments for const functions.
+
+// SharedOptions=--enable-experiment=const-functions
+
+import "package:expect/expect.dart";
+
+const var1 = varAssignmentTest(1);
+int varAssignmentTest(int a) {
+ int x = 4;
+ {
+ x = 3;
+ }
+ return x;
+}
+
+int function() {
+ int varAssignmentTest2() {
+ int x = 2;
+ x += 1;
+ return x;
+ }
+
+ const var2 = varAssignmentTest2();
+ return var2;
+}
+
+const var3 = varAssignmentTest3(1);
+int varAssignmentTest3(int a) {
+ int x = 4;
+ x = a + 1;
+ return x;
+}
+
+void main() {
+ Expect.equals(var1, 3);
+ Expect.equals(function(), 3);
+ Expect.equals(var3, 2);
+}
diff --git a/pkg/front_end/testcases/const_functions/const_functions_variable_assignments.dart.strong.expect b/pkg/front_end/testcases/const_functions/const_functions_variable_assignments.dart.strong.expect
new file mode 100644
index 0000000..337c656
--- /dev/null
+++ b/pkg/front_end/testcases/const_functions/const_functions_variable_assignments.dart.strong.expect
@@ -0,0 +1,39 @@
+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 var3 = #C2;
+static method varAssignmentTest(core::int a) → core::int {
+ core::int x = 4;
+ {
+ x = 3;
+ }
+ return x;
+}
+static method function() → core::int {
+ function varAssignmentTest2() → core::int {
+ core::int x = 2;
+ x = x.{core::num::+}(1);
+ return x;
+ }
+ return #C1;
+}
+static method varAssignmentTest3(core::int a) → core::int {
+ core::int x = 4;
+ x = a.{core::num::+}(1);
+ return x;
+}
+static method main() → void {
+ exp::Expect::equals(#C1, 3);
+ exp::Expect::equals(self::function(), 3);
+ exp::Expect::equals(#C2, 2);
+}
+
+constants {
+ #C1 = 3
+ #C2 = 2
+}
diff --git a/pkg/front_end/testcases/const_functions/const_functions_variable_assignments.dart.strong.transformed.expect b/pkg/front_end/testcases/const_functions/const_functions_variable_assignments.dart.strong.transformed.expect
new file mode 100644
index 0000000..337c656
--- /dev/null
+++ b/pkg/front_end/testcases/const_functions/const_functions_variable_assignments.dart.strong.transformed.expect
@@ -0,0 +1,39 @@
+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 var3 = #C2;
+static method varAssignmentTest(core::int a) → core::int {
+ core::int x = 4;
+ {
+ x = 3;
+ }
+ return x;
+}
+static method function() → core::int {
+ function varAssignmentTest2() → core::int {
+ core::int x = 2;
+ x = x.{core::num::+}(1);
+ return x;
+ }
+ return #C1;
+}
+static method varAssignmentTest3(core::int a) → core::int {
+ core::int x = 4;
+ x = a.{core::num::+}(1);
+ return x;
+}
+static method main() → void {
+ exp::Expect::equals(#C1, 3);
+ exp::Expect::equals(self::function(), 3);
+ exp::Expect::equals(#C2, 2);
+}
+
+constants {
+ #C1 = 3
+ #C2 = 2
+}
diff --git a/pkg/front_end/testcases/const_functions/const_functions_variable_assignments.dart.textual_outline.expect b/pkg/front_end/testcases/const_functions/const_functions_variable_assignments.dart.textual_outline.expect
new file mode 100644
index 0000000..801ab46
--- /dev/null
+++ b/pkg/front_end/testcases/const_functions/const_functions_variable_assignments.dart.textual_outline.expect
@@ -0,0 +1,8 @@
+import "package:expect/expect.dart";
+
+const var1 = varAssignmentTest(1);
+int varAssignmentTest(int a) {}
+int function() {}
+const var3 = varAssignmentTest3(1);
+int varAssignmentTest3(int a) {}
+void main() {}
diff --git a/pkg/front_end/testcases/const_functions/const_functions_variable_assignments.dart.textual_outline_modelled.expect b/pkg/front_end/testcases/const_functions/const_functions_variable_assignments.dart.textual_outline_modelled.expect
new file mode 100644
index 0000000..5f57ea4
--- /dev/null
+++ b/pkg/front_end/testcases/const_functions/const_functions_variable_assignments.dart.textual_outline_modelled.expect
@@ -0,0 +1,8 @@
+import "package:expect/expect.dart";
+
+const var1 = varAssignmentTest(1);
+const var3 = varAssignmentTest3(1);
+int function() {}
+int varAssignmentTest(int a) {}
+int varAssignmentTest3(int a) {}
+void main() {}
diff --git a/pkg/front_end/testcases/const_functions/const_functions_variable_assignments.dart.weak.expect b/pkg/front_end/testcases/const_functions/const_functions_variable_assignments.dart.weak.expect
new file mode 100644
index 0000000..337c656
--- /dev/null
+++ b/pkg/front_end/testcases/const_functions/const_functions_variable_assignments.dart.weak.expect
@@ -0,0 +1,39 @@
+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 var3 = #C2;
+static method varAssignmentTest(core::int a) → core::int {
+ core::int x = 4;
+ {
+ x = 3;
+ }
+ return x;
+}
+static method function() → core::int {
+ function varAssignmentTest2() → core::int {
+ core::int x = 2;
+ x = x.{core::num::+}(1);
+ return x;
+ }
+ return #C1;
+}
+static method varAssignmentTest3(core::int a) → core::int {
+ core::int x = 4;
+ x = a.{core::num::+}(1);
+ return x;
+}
+static method main() → void {
+ exp::Expect::equals(#C1, 3);
+ exp::Expect::equals(self::function(), 3);
+ exp::Expect::equals(#C2, 2);
+}
+
+constants {
+ #C1 = 3
+ #C2 = 2
+}
diff --git a/pkg/front_end/testcases/const_functions/const_functions_variable_assignments.dart.weak.outline.expect b/pkg/front_end/testcases/const_functions/const_functions_variable_assignments.dart.weak.outline.expect
new file mode 100644
index 0000000..71847ee
--- /dev/null
+++ b/pkg/front_end/testcases/const_functions/const_functions_variable_assignments.dart.weak.outline.expect
@@ -0,0 +1,16 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+
+import "package:expect/expect.dart";
+
+static const field core::int var1 = self::varAssignmentTest(1);
+static const field core::int var3 = self::varAssignmentTest3(1);
+static method varAssignmentTest(core::int a) → core::int
+ ;
+static method function() → core::int
+ ;
+static method varAssignmentTest3(core::int a) → core::int
+ ;
+static method main() → void
+ ;
diff --git a/pkg/front_end/testcases/const_functions/const_functions_variable_assignments.dart.weak.transformed.expect b/pkg/front_end/testcases/const_functions/const_functions_variable_assignments.dart.weak.transformed.expect
new file mode 100644
index 0000000..337c656
--- /dev/null
+++ b/pkg/front_end/testcases/const_functions/const_functions_variable_assignments.dart.weak.transformed.expect
@@ -0,0 +1,39 @@
+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 var3 = #C2;
+static method varAssignmentTest(core::int a) → core::int {
+ core::int x = 4;
+ {
+ x = 3;
+ }
+ return x;
+}
+static method function() → core::int {
+ function varAssignmentTest2() → core::int {
+ core::int x = 2;
+ x = x.{core::num::+}(1);
+ return x;
+ }
+ return #C1;
+}
+static method varAssignmentTest3(core::int a) → core::int {
+ core::int x = 4;
+ x = a.{core::num::+}(1);
+ return x;
+}
+static method main() → void {
+ exp::Expect::equals(#C1, 3);
+ exp::Expect::equals(self::function(), 3);
+ exp::Expect::equals(#C2, 2);
+}
+
+constants {
+ #C1 = 3
+ #C2 = 2
+}
diff --git a/tests/language/const_functions/const_functions_variable_assignments_test.dart b/tests/language/const_functions/const_functions_variable_assignments_test.dart
new file mode 100644
index 0000000..f2c47cb
--- /dev/null
+++ b/tests/language/const_functions/const_functions_variable_assignments_test.dart
@@ -0,0 +1,48 @@
+// 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 variable assignments for const functions.
+
+// SharedOptions=--enable-experiment=const-functions
+
+import "package:expect/expect.dart";
+
+const var1 = varAssignmentTest(1);
+// ^^^^^^^^^^^^^^^^^^^^
+// [analyzer] COMPILE_TIME_ERROR.CONST_INITIALIZED_WITH_NON_CONSTANT_VALUE
+int varAssignmentTest(int a) {
+ int x = 4;
+ {
+ x = 3;
+ }
+ return x;
+}
+
+int function() {
+ int varAssignmentTest2() {
+ int x = 2;
+ x += 1;
+ return x;
+ }
+
+ const var2 = varAssignmentTest2();
+ // ^^^^^^^^^^^^^^^^^^^^
+ // [analyzer] COMPILE_TIME_ERROR.CONST_INITIALIZED_WITH_NON_CONSTANT_VALUE
+ return var2;
+}
+
+const var3 = varAssignmentTest3(1);
+// ^^^^^^^^^^^^^^^^^^^^^
+// [analyzer] COMPILE_TIME_ERROR.CONST_INITIALIZED_WITH_NON_CONSTANT_VALUE
+int varAssignmentTest3(int a) {
+ int x = 4;
+ x = a + 1;
+ return x;
+}
+
+void main() {
+ Expect.equals(var1, 3);
+ Expect.equals(function(), 3);
+ Expect.equals(var3, 2);
+}