[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>
This commit is contained in:
Kallen Tu
2021-04-08 15:09:39 +00:00
committed by commit-bot@chromium.org
parent 40441c9685
commit 6f90c5531e
23 changed files with 826 additions and 17 deletions
@@ -1352,6 +1352,28 @@ Message _withArgumentsConstEvalFailedAssertionWithMessage(
arguments: {'stringOKEmpty': stringOKEmpty});
}
// 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 =
@@ -59,6 +59,7 @@ import '../fasta_codes.dart'
templateConstEvalElementImplementsEqual,
templateConstEvalFailedAssertionWithMessage,
templateConstEvalFreeTypeParameter,
templateConstEvalGetterNotFound,
templateConstEvalInvalidType,
templateConstEvalInvalidBinaryOperandType,
templateConstEvalInvalidEqualsOperandType,
@@ -559,7 +560,7 @@ class ConstantsTransformer extends RemovingTransformer {
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 @@ class ConstantEvaluator implements ExpressionVisitor<Constant> {
@override
Constant visitFunctionExpression(FunctionExpression node) {
if (enableConstFunctions) {
return new FunctionValue(node.function, env);
}
return createInvalidExpressionConstant(node, "Function literal");
}
@@ -2165,6 +2169,9 @@ class ConstantEvaluator implements ExpressionVisitor<Constant> {
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 @@ class ConstantEvaluator implements ExpressionVisitor<Constant> {
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 @@ class ConstantEvaluator implements ExpressionVisitor<Constant> {
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 @@ class ConstantEvaluator implements ExpressionVisitor<Constant> {
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 @@ class ConstantEvaluator implements ExpressionVisitor<Constant> {
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 @@ class ConstantEvaluator implements ExpressionVisitor<Constant> {
env.addVariableValue(parameter, value);
}
return execute(function.body);
});
}
if (functionEnvironment != null) {
return withEnvironment(functionEnvironment, executeFunction);
}
return withNewEnvironment(executeFunction);
}
@override
@@ -3264,6 +3274,14 @@ class ConstantEvaluator implements ExpressionVisitor<Constant> {
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) {
@@ -3423,6 +3441,17 @@ class StatementConstantEvaluator extends StatementVisitor<ExecutionStatus> {
return const ProceedStatus();
}
@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);
@@ -3740,11 +3769,13 @@ class BreakStatus extends ExecutionStatus {
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) {
+2
View File
@@ -106,6 +106,8 @@ ConstEvalFailedAssertion/example: Fail
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
+3
View File
@@ -150,6 +150,9 @@ ConstEvalTruncateError:
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
@@ -13,6 +13,17 @@ int fn() {
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);
}
@@ -6,13 +6,23 @@ 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 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 {
@@ -6,13 +6,23 @@ 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 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 {
@@ -2,4 +2,6 @@ import "package:expect/expect.dart";
const var1 = fn();
int fn() {}
const var2 = fn2();
int fn2() {}
void main() {}
@@ -1,5 +1,7 @@
import "package:expect/expect.dart";
const var1 = fn();
const var2 = fn2();
int fn() {}
int fn2() {}
void main() {}
@@ -6,13 +6,23 @@ 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 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 {
@@ -5,7 +5,10 @@ import "dart:core" as core;
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
;
@@ -6,13 +6,23 @@ 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 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 {
@@ -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);
}
@@ -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
}
@@ -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
}
@@ -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() {}
@@ -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() {}
@@ -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
}
@@ -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
;
@@ -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
}
@@ -17,6 +17,19 @@ int fn() {
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);
}
@@ -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:
}
@@ -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);
}