[cfe] Switch statements for const functions.

Change-Id: I1194f9091b74ba3e288d5e7bd386c204fa25c837
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/193360
Commit-Queue: Kallen Tu <kallentu@google.com>
Reviewed-by: Dmitry Stefantsov <dmitryas@google.com>
Reviewed-by: Jake Macdonald <jakemac@google.com>
This commit is contained in:
Kallen Tu 2021-03-30 03:56:58 +00:00 committed by commit-bot@chromium.org
parent 2ac0590f29
commit 5b3a5acf01
11 changed files with 637 additions and 0 deletions

View file

@ -3372,6 +3372,10 @@ class StatementConstantEvaluator extends StatementVisitor<ExecutionStatus> {
ExecutionStatus visitBreakStatement(BreakStatement node) =>
new BreakStatus(node.target);
@override
ExecutionStatus visitContinueSwitchStatement(ContinueSwitchStatement node) =>
node.target.body.accept(this);
@override
ExecutionStatus visitDoStatement(DoStatement node) {
Constant condition;
@ -3453,6 +3457,21 @@ class StatementConstantEvaluator extends StatementVisitor<ExecutionStatus> {
ExecutionStatus visitReturnStatement(ReturnStatement node) =>
new ReturnStatus(evaluate(node.expression));
@override
ExecutionStatus visitSwitchStatement(SwitchStatement node) {
final Constant value = evaluate(node.expression);
if (value is AbortConstant) return new AbortStatus(value);
for (SwitchCase switchCase in node.cases) {
if (switchCase.isDefault) return switchCase.body.accept(this);
for (Expression expr in switchCase.expressions) {
final Constant caseValue = evaluate(expr);
if (value == caseValue) return switchCase.body.accept(this);
}
}
return const ProceedStatus();
}
@override
ExecutionStatus visitVariableDeclaration(VariableDeclaration node) {
Constant value = evaluate(node.initializer);

View file

@ -0,0 +1,65 @@
// 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 switch statements for const functions.
import "package:expect/expect.dart";
const var1 = basicSwitch(1);
const var2 = basicSwitch(2);
int basicSwitch(int x) {
switch (x) {
case 1:
return 100;
default:
x++;
break;
}
return x;
}
const var3 = multipleCaseSwitch(1);
const var4 = multipleCaseSwitch(2);
const var5 = multipleCaseSwitch(3);
int multipleCaseSwitch(int x) {
switch (x) {
case 1:
case 2:
return 100;
default:
break;
}
return 0;
}
const var6 = continueLabelSwitch(1);
const var7 = continueLabelSwitch(2);
const var8 = continueLabelSwitch(3);
const var9 = continueLabelSwitch(4);
int continueLabelSwitch(int x) {
switch (x) {
label1:
case 1:
x = x + 100;
continue label3;
case 2:
continue label1;
label3:
case 3:
return x + 3;
}
return 0;
}
void main() {
Expect.equals(var1, 100);
Expect.equals(var2, 3);
Expect.equals(var3, 100);
Expect.equals(var4, 100);
Expect.equals(var5, 0);
Expect.equals(var6, 104);
Expect.equals(var7, 105);
Expect.equals(var8, 6);
Expect.equals(var9, 0);
}

View file

@ -0,0 +1,93 @@
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 = #C1;
static const field core::int var4 = #C1;
static const field core::int var5 = #C3;
static const field core::int var6 = #C4;
static const field core::int var7 = #C5;
static const field core::int var8 = #C6;
static const field core::int var9 = #C3;
static method basicSwitch(core::int x) → core::int {
#L1:
switch(x) {
#L2:
case #C7:
{
return 100;
}
#L3:
default:
{
x = x.{core::num::+}(1);
break #L1;
}
}
return x;
}
static method multipleCaseSwitch(core::int x) → core::int {
#L4:
switch(x) {
#L5:
case #C7:
case #C8:
{
return 100;
}
#L6:
default:
{
break #L4;
}
}
return 0;
}
static method continueLabelSwitch(core::int x) → core::int {
switch(x) {
#L7:
case #C7:
{
x = x.{core::num::+}(100);
continue #L8;
}
#L9:
case #C8:
{
continue #L7;
}
#L8:
case #C2:
{
return x.{core::num::+}(3);
}
}
return 0;
}
static method main() → void {
exp::Expect::equals(#C1, 100);
exp::Expect::equals(#C2, 3);
exp::Expect::equals(#C1, 100);
exp::Expect::equals(#C1, 100);
exp::Expect::equals(#C3, 0);
exp::Expect::equals(#C4, 104);
exp::Expect::equals(#C5, 105);
exp::Expect::equals(#C6, 6);
exp::Expect::equals(#C3, 0);
}
constants {
#C1 = 100
#C2 = 3
#C3 = 0
#C4 = 104
#C5 = 105
#C6 = 6
#C7 = 1
#C8 = 2
}

View file

@ -0,0 +1,93 @@
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 = #C1;
static const field core::int var4 = #C1;
static const field core::int var5 = #C3;
static const field core::int var6 = #C4;
static const field core::int var7 = #C5;
static const field core::int var8 = #C6;
static const field core::int var9 = #C3;
static method basicSwitch(core::int x) → core::int {
#L1:
switch(x) {
#L2:
case #C7:
{
return 100;
}
#L3:
default:
{
x = x.{core::num::+}(1);
break #L1;
}
}
return x;
}
static method multipleCaseSwitch(core::int x) → core::int {
#L4:
switch(x) {
#L5:
case #C7:
case #C8:
{
return 100;
}
#L6:
default:
{
break #L4;
}
}
return 0;
}
static method continueLabelSwitch(core::int x) → core::int {
switch(x) {
#L7:
case #C7:
{
x = x.{core::num::+}(100);
continue #L8;
}
#L9:
case #C8:
{
continue #L7;
}
#L8:
case #C2:
{
return x.{core::num::+}(3);
}
}
return 0;
}
static method main() → void {
exp::Expect::equals(#C1, 100);
exp::Expect::equals(#C2, 3);
exp::Expect::equals(#C1, 100);
exp::Expect::equals(#C1, 100);
exp::Expect::equals(#C3, 0);
exp::Expect::equals(#C4, 104);
exp::Expect::equals(#C5, 105);
exp::Expect::equals(#C6, 6);
exp::Expect::equals(#C3, 0);
}
constants {
#C1 = 100
#C2 = 3
#C3 = 0
#C4 = 104
#C5 = 105
#C6 = 6
#C7 = 1
#C8 = 2
}

View file

@ -0,0 +1,15 @@
import "package:expect/expect.dart";
const var1 = basicSwitch(1);
const var2 = basicSwitch(2);
int basicSwitch(int x) {}
const var3 = multipleCaseSwitch(1);
const var4 = multipleCaseSwitch(2);
const var5 = multipleCaseSwitch(3);
int multipleCaseSwitch(int x) {}
const var6 = continueLabelSwitch(1);
const var7 = continueLabelSwitch(2);
const var8 = continueLabelSwitch(3);
const var9 = continueLabelSwitch(4);
int continueLabelSwitch(int x) {}
void main() {}

View file

@ -0,0 +1,15 @@
import "package:expect/expect.dart";
const var1 = basicSwitch(1);
const var2 = basicSwitch(2);
const var3 = multipleCaseSwitch(1);
const var4 = multipleCaseSwitch(2);
const var5 = multipleCaseSwitch(3);
const var6 = continueLabelSwitch(1);
const var7 = continueLabelSwitch(2);
const var8 = continueLabelSwitch(3);
const var9 = continueLabelSwitch(4);
int basicSwitch(int x) {}
int continueLabelSwitch(int x) {}
int multipleCaseSwitch(int x) {}
void main() {}

View file

@ -0,0 +1,93 @@
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 = #C1;
static const field core::int var4 = #C1;
static const field core::int var5 = #C3;
static const field core::int var6 = #C4;
static const field core::int var7 = #C5;
static const field core::int var8 = #C6;
static const field core::int var9 = #C3;
static method basicSwitch(core::int x) → core::int {
#L1:
switch(x) {
#L2:
case #C7:
{
return 100;
}
#L3:
default:
{
x = x.{core::num::+}(1);
break #L1;
}
}
return x;
}
static method multipleCaseSwitch(core::int x) → core::int {
#L4:
switch(x) {
#L5:
case #C7:
case #C8:
{
return 100;
}
#L6:
default:
{
break #L4;
}
}
return 0;
}
static method continueLabelSwitch(core::int x) → core::int {
switch(x) {
#L7:
case #C7:
{
x = x.{core::num::+}(100);
continue #L8;
}
#L9:
case #C8:
{
continue #L7;
}
#L8:
case #C2:
{
return x.{core::num::+}(3);
}
}
return 0;
}
static method main() → void {
exp::Expect::equals(#C1, 100);
exp::Expect::equals(#C2, 3);
exp::Expect::equals(#C1, 100);
exp::Expect::equals(#C1, 100);
exp::Expect::equals(#C3, 0);
exp::Expect::equals(#C4, 104);
exp::Expect::equals(#C5, 105);
exp::Expect::equals(#C6, 6);
exp::Expect::equals(#C3, 0);
}
constants {
#C1 = 100
#C2 = 3
#C3 = 0
#C4 = 104
#C5 = 105
#C6 = 6
#C7 = 1
#C8 = 2
}

View file

@ -0,0 +1,23 @@
library /*isNonNullableByDefault*/;
import self as self;
import "dart:core" as core;
import "package:expect/expect.dart";
static const field core::int var1 = self::basicSwitch(1);
static const field core::int var2 = self::basicSwitch(2);
static const field core::int var3 = self::multipleCaseSwitch(1);
static const field core::int var4 = self::multipleCaseSwitch(2);
static const field core::int var5 = self::multipleCaseSwitch(3);
static const field core::int var6 = self::continueLabelSwitch(1);
static const field core::int var7 = self::continueLabelSwitch(2);
static const field core::int var8 = self::continueLabelSwitch(3);
static const field core::int var9 = self::continueLabelSwitch(4);
static method basicSwitch(core::int x) → core::int
;
static method multipleCaseSwitch(core::int x) → core::int
;
static method continueLabelSwitch(core::int x) → core::int
;
static method main() → void
;

View file

@ -0,0 +1,93 @@
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 = #C1;
static const field core::int var4 = #C1;
static const field core::int var5 = #C3;
static const field core::int var6 = #C4;
static const field core::int var7 = #C5;
static const field core::int var8 = #C6;
static const field core::int var9 = #C3;
static method basicSwitch(core::int x) → core::int {
#L1:
switch(x) {
#L2:
case #C7:
{
return 100;
}
#L3:
default:
{
x = x.{core::num::+}(1);
break #L1;
}
}
return x;
}
static method multipleCaseSwitch(core::int x) → core::int {
#L4:
switch(x) {
#L5:
case #C7:
case #C8:
{
return 100;
}
#L6:
default:
{
break #L4;
}
}
return 0;
}
static method continueLabelSwitch(core::int x) → core::int {
switch(x) {
#L7:
case #C7:
{
x = x.{core::num::+}(100);
continue #L8;
}
#L9:
case #C8:
{
continue #L7;
}
#L8:
case #C2:
{
return x.{core::num::+}(3);
}
}
return 0;
}
static method main() → void {
exp::Expect::equals(#C1, 100);
exp::Expect::equals(#C2, 3);
exp::Expect::equals(#C1, 100);
exp::Expect::equals(#C1, 100);
exp::Expect::equals(#C3, 0);
exp::Expect::equals(#C4, 104);
exp::Expect::equals(#C5, 105);
exp::Expect::equals(#C6, 6);
exp::Expect::equals(#C3, 0);
}
constants {
#C1 = 100
#C2 = 3
#C3 = 0
#C4 = 104
#C5 = 105
#C6 = 6
#C7 = 1
#C8 = 2
}

View file

@ -0,0 +1,43 @@
// 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 switch statements for const functions.
// SharedOptions=--enable-experiment=const-functions
import "package:expect/expect.dart";
const var1 = labelDoesNotExistSwitch(1);
// ^^^^^^^^^^^^^^^^^^^^^^^^^^
// [analyzer] COMPILE_TIME_ERROR.CONST_INITIALIZED_WITH_NON_CONSTANT_VALUE
int labelDoesNotExistSwitch(int x) {
switch (x) {
labelOtherSwitch:
case 1:
break;
}
switch (x) {
case 1:
continue labelOtherSwitch;
// ^
// [cfe] Can't find label 'labelOtherSwitch'.
// ^^^^^^^^^^^^^^^^
// [analyzer] COMPILE_TIME_ERROR.LABEL_UNDEFINED
}
return 0;
}
const var2 = wrongTypeSwitch(1);
// ^^^^^^^^^^^^^^^^^^
// [analyzer] COMPILE_TIME_ERROR.CONST_INITIALIZED_WITH_NON_CONSTANT_VALUE
int wrongTypeSwitch(int x) {
switch (x) {
case "string":
// ^^^^^^^^
// [analyzer] COMPILE_TIME_ERROR.CASE_EXPRESSION_TYPE_IS_NOT_SWITCH_EXPRESSION_SUBTYPE
// [cfe] Type 'String' of the case expression is not a subtype of type 'int' of this switch expression.
return 100;
}
return 0;
}

View file

@ -0,0 +1,85 @@
// 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 switch statements for const functions.
// SharedOptions=--enable-experiment=const-functions
import "package:expect/expect.dart";
const var1 = basicSwitch(1);
// ^^^^^^^^^^^^^^
// [analyzer] COMPILE_TIME_ERROR.CONST_INITIALIZED_WITH_NON_CONSTANT_VALUE
const var2 = basicSwitch(2);
// ^^^^^^^^^^^^^^
// [analyzer] COMPILE_TIME_ERROR.CONST_INITIALIZED_WITH_NON_CONSTANT_VALUE
int basicSwitch(int x) {
switch (x) {
case 1:
return 100;
default:
x++;
break;
}
return x;
}
const var3 = multipleCaseSwitch(1);
// ^^^^^^^^^^^^^^^^^^^^^
// [analyzer] COMPILE_TIME_ERROR.CONST_INITIALIZED_WITH_NON_CONSTANT_VALUE
const var4 = multipleCaseSwitch(2);
// ^^^^^^^^^^^^^^^^^^^^^
// [analyzer] COMPILE_TIME_ERROR.CONST_INITIALIZED_WITH_NON_CONSTANT_VALUE
const var5 = multipleCaseSwitch(3);
// ^^^^^^^^^^^^^^^^^^^^^
// [analyzer] COMPILE_TIME_ERROR.CONST_INITIALIZED_WITH_NON_CONSTANT_VALUE
int multipleCaseSwitch(int x) {
switch (x) {
case 1:
case 2:
return 100;
default:
break;
}
return 0;
}
const var6 = continueLabelSwitch(1);
// ^^^^^^^^^^^^^^^^^^^^^^
// [analyzer] COMPILE_TIME_ERROR.CONST_INITIALIZED_WITH_NON_CONSTANT_VALUE
const var7 = continueLabelSwitch(2);
// ^^^^^^^^^^^^^^^^^^^^^^
// [analyzer] COMPILE_TIME_ERROR.CONST_INITIALIZED_WITH_NON_CONSTANT_VALUE
const var8 = continueLabelSwitch(3);
// ^^^^^^^^^^^^^^^^^^^^^^
// [analyzer] COMPILE_TIME_ERROR.CONST_INITIALIZED_WITH_NON_CONSTANT_VALUE
const var9 = continueLabelSwitch(4);
// ^^^^^^^^^^^^^^^^^^^^^^
// [analyzer] COMPILE_TIME_ERROR.CONST_INITIALIZED_WITH_NON_CONSTANT_VALUE
int continueLabelSwitch(int x) {
switch (x) {
label1:
case 1:
x = x + 100;
continue label3;
case 2:
continue label1;
label3:
case 3:
return x + 3;
}
return 0;
}
void main() {
Expect.equals(var1, 100);
Expect.equals(var2, 3);
Expect.equals(var3, 100);
Expect.equals(var4, 100);
Expect.equals(var5, 0);
Expect.equals(var6, 104);
Expect.equals(var7, 105);
Expect.equals(var8, 6);
Expect.equals(var9, 0);
}