From f9536375e146f85e4d823ca8dd5b98edec170dfd Mon Sep 17 00:00:00 2001 From: Kallen Tu Date: Tue, 13 Apr 2021 14:49:06 +0000 Subject: [PATCH] [cfe] Invalid empty return statements in const function invocations. Change-Id: Id1408bff917200f3825846bd6d483e52ca326d5b Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/194840 Reviewed-by: Jake Macdonald Reviewed-by: Dmitry Stefantsov Commit-Queue: Kallen Tu --- .../src/fasta/kernel/constant_evaluator.dart | 13 ++++-- .../const_functions_return.dart | 30 ++++++++++++++ .../const_functions_return.dart.strong.expect | 30 ++++++++++++++ ...ions_return.dart.strong.transformed.expect | 35 ++++++++++++++++ ...nctions_return.dart.textual_outline.expect | 11 +++++ ...eturn.dart.textual_outline_modelled.expect | 11 +++++ .../const_functions_return.dart.weak.expect | 30 ++++++++++++++ ..._functions_return.dart.weak.outline.expect | 20 ++++++++++ ...ctions_return.dart.weak.transformed.expect | 35 ++++++++++++++++ .../const_functions_return_test.dart | 40 +++++++++++++++++++ 10 files changed, 252 insertions(+), 3 deletions(-) create mode 100644 pkg/front_end/testcases/const_functions/const_functions_return.dart create mode 100644 pkg/front_end/testcases/const_functions/const_functions_return.dart.strong.expect create mode 100644 pkg/front_end/testcases/const_functions/const_functions_return.dart.strong.transformed.expect create mode 100644 pkg/front_end/testcases/const_functions/const_functions_return.dart.textual_outline.expect create mode 100644 pkg/front_end/testcases/const_functions/const_functions_return.dart.textual_outline_modelled.expect create mode 100644 pkg/front_end/testcases/const_functions/const_functions_return.dart.weak.expect create mode 100644 pkg/front_end/testcases/const_functions/const_functions_return.dart.weak.outline.expect create mode 100644 pkg/front_end/testcases/const_functions/const_functions_return.dart.weak.transformed.expect create mode 100644 tests/language/const_functions/const_functions_return_test.dart 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 bd6b554c3ed..495ae0cce53 100644 --- a/pkg/front_end/lib/src/fasta/kernel/constant_evaluator.dart +++ b/pkg/front_end/lib/src/fasta/kernel/constant_evaluator.dart @@ -1036,15 +1036,22 @@ class ConstantEvaluator implements ExpressionVisitor { return result; } - /// Execute the statement using the [StatementConstantEvaluator]. - Constant execute(Statement statement) { + /// Execute a function body using the [StatementConstantEvaluator]. + Constant executeBody(Statement statement) { StatementConstantEvaluator statementEvaluator = new StatementConstantEvaluator(this); ExecutionStatus status = statement.accept(statementEvaluator); if (status is ReturnStatus) { + if (status.value == null) { + // Void return type from executing the function body. + return new NullConstant(); + } return status.value; } else if (status is AbortStatus) { return status.error; + } else if (status is ProceedStatus) { + // No return statement in function body with void return type. + return new NullConstant(); } return createInvalidExpressionConstant(statement, 'No valid constant returned from the execution of $statement.'); @@ -2870,7 +2877,7 @@ class ConstantEvaluator implements ExpressionVisitor { if (value is AbortConstant) return value; env.addVariableValue(parameter, value); } - return execute(function.body); + return executeBody(function.body); } if (functionEnvironment != null) { diff --git a/pkg/front_end/testcases/const_functions/const_functions_return.dart b/pkg/front_end/testcases/const_functions/const_functions_return.dart new file mode 100644 index 00000000000..6a2eb8d5bf9 --- /dev/null +++ b/pkg/front_end/testcases/const_functions/const_functions_return.dart @@ -0,0 +1,30 @@ +// 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 function invocation return types. + +import "package:expect/expect.dart"; + +const var1 = fn(); +void fn() {} + +const var2 = fn2(); +void fn2() { + return; +} + +const var3 = fn3(); +int? fn3() => null; + +const var4 = fn4(); +int? fn4() { + return null; +} + +void main() { + Expect.equals((var1 as dynamic), null); + Expect.equals((var2 as dynamic), null); + Expect.equals(var3, null); + Expect.equals(var4, null); +} diff --git a/pkg/front_end/testcases/const_functions/const_functions_return.dart.strong.expect b/pkg/front_end/testcases/const_functions/const_functions_return.dart.strong.expect new file mode 100644 index 00000000000..32536564a33 --- /dev/null +++ b/pkg/front_end/testcases/const_functions/const_functions_return.dart.strong.expect @@ -0,0 +1,30 @@ +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 void var1 = #C1; +static const field void var2 = #C1; +static const field core::int? var3 = #C1; +static const field core::int? var4 = #C1; +static method fn() → void {} +static method fn2() → void { + return; +} +static method fn3() → core::int? + return null; +static method fn4() → core::int? { + return null; +} +static method main() → void { + exp::Expect::equals((#C1) as{ForNonNullableByDefault} dynamic, null); + exp::Expect::equals((#C1) as{ForNonNullableByDefault} dynamic, null); + exp::Expect::equals(#C1, null); + exp::Expect::equals(#C1, null); +} + +constants { + #C1 = null +} diff --git a/pkg/front_end/testcases/const_functions/const_functions_return.dart.strong.transformed.expect b/pkg/front_end/testcases/const_functions/const_functions_return.dart.strong.transformed.expect new file mode 100644 index 00000000000..59de3d1ce1e --- /dev/null +++ b/pkg/front_end/testcases/const_functions/const_functions_return.dart.strong.transformed.expect @@ -0,0 +1,35 @@ +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 void var1 = #C1; +static const field void var2 = #C1; +static const field core::int? var3 = #C1; +static const field core::int? var4 = #C1; +static method fn() → void {} +static method fn2() → void { + return; +} +static method fn3() → core::int? + return null; +static method fn4() → core::int? { + return null; +} +static method main() → void { + exp::Expect::equals((#C1) as{ForNonNullableByDefault} dynamic, null); + exp::Expect::equals((#C1) as{ForNonNullableByDefault} dynamic, null); + exp::Expect::equals(#C1, null); + exp::Expect::equals(#C1, null); +} + +constants { + #C1 = null +} + +Extra constant evaluation status: +Evaluated: AsExpression @ org-dartlang-testcase:///const_functions_return.dart:26:23 -> NullConstant(null) +Evaluated: AsExpression @ org-dartlang-testcase:///const_functions_return.dart:27:23 -> NullConstant(null) +Extra constant evaluation: evaluated: 6, effectively constant: 2 diff --git a/pkg/front_end/testcases/const_functions/const_functions_return.dart.textual_outline.expect b/pkg/front_end/testcases/const_functions/const_functions_return.dart.textual_outline.expect new file mode 100644 index 00000000000..cf82ba37e11 --- /dev/null +++ b/pkg/front_end/testcases/const_functions/const_functions_return.dart.textual_outline.expect @@ -0,0 +1,11 @@ +import "package:expect/expect.dart"; + +const var1 = fn(); +void fn() {} +const var2 = fn2(); +void fn2() {} +const var3 = fn3(); +int? fn3() => null; +const var4 = fn4(); +int? fn4() {} +void main() {} diff --git a/pkg/front_end/testcases/const_functions/const_functions_return.dart.textual_outline_modelled.expect b/pkg/front_end/testcases/const_functions/const_functions_return.dart.textual_outline_modelled.expect new file mode 100644 index 00000000000..06ba06e1036 --- /dev/null +++ b/pkg/front_end/testcases/const_functions/const_functions_return.dart.textual_outline_modelled.expect @@ -0,0 +1,11 @@ +import "package:expect/expect.dart"; + +const var1 = fn(); +const var2 = fn2(); +const var3 = fn3(); +const var4 = fn4(); +int? fn3() => null; +int? fn4() {} +void fn() {} +void fn2() {} +void main() {} diff --git a/pkg/front_end/testcases/const_functions/const_functions_return.dart.weak.expect b/pkg/front_end/testcases/const_functions/const_functions_return.dart.weak.expect new file mode 100644 index 00000000000..32536564a33 --- /dev/null +++ b/pkg/front_end/testcases/const_functions/const_functions_return.dart.weak.expect @@ -0,0 +1,30 @@ +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 void var1 = #C1; +static const field void var2 = #C1; +static const field core::int? var3 = #C1; +static const field core::int? var4 = #C1; +static method fn() → void {} +static method fn2() → void { + return; +} +static method fn3() → core::int? + return null; +static method fn4() → core::int? { + return null; +} +static method main() → void { + exp::Expect::equals((#C1) as{ForNonNullableByDefault} dynamic, null); + exp::Expect::equals((#C1) as{ForNonNullableByDefault} dynamic, null); + exp::Expect::equals(#C1, null); + exp::Expect::equals(#C1, null); +} + +constants { + #C1 = null +} diff --git a/pkg/front_end/testcases/const_functions/const_functions_return.dart.weak.outline.expect b/pkg/front_end/testcases/const_functions/const_functions_return.dart.weak.outline.expect new file mode 100644 index 00000000000..1587dfde139 --- /dev/null +++ b/pkg/front_end/testcases/const_functions/const_functions_return.dart.weak.outline.expect @@ -0,0 +1,20 @@ +library /*isNonNullableByDefault*/; +import self as self; +import "dart:core" as core; + +import "package:expect/expect.dart"; + +static const field void var1 = self::fn(); +static const field void var2 = self::fn2(); +static const field core::int? var3 = self::fn3(); +static const field core::int? var4 = self::fn4(); +static method fn() → void + ; +static method fn2() → void + ; +static method fn3() → core::int? + ; +static method fn4() → core::int? + ; +static method main() → void + ; diff --git a/pkg/front_end/testcases/const_functions/const_functions_return.dart.weak.transformed.expect b/pkg/front_end/testcases/const_functions/const_functions_return.dart.weak.transformed.expect new file mode 100644 index 00000000000..59de3d1ce1e --- /dev/null +++ b/pkg/front_end/testcases/const_functions/const_functions_return.dart.weak.transformed.expect @@ -0,0 +1,35 @@ +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 void var1 = #C1; +static const field void var2 = #C1; +static const field core::int? var3 = #C1; +static const field core::int? var4 = #C1; +static method fn() → void {} +static method fn2() → void { + return; +} +static method fn3() → core::int? + return null; +static method fn4() → core::int? { + return null; +} +static method main() → void { + exp::Expect::equals((#C1) as{ForNonNullableByDefault} dynamic, null); + exp::Expect::equals((#C1) as{ForNonNullableByDefault} dynamic, null); + exp::Expect::equals(#C1, null); + exp::Expect::equals(#C1, null); +} + +constants { + #C1 = null +} + +Extra constant evaluation status: +Evaluated: AsExpression @ org-dartlang-testcase:///const_functions_return.dart:26:23 -> NullConstant(null) +Evaluated: AsExpression @ org-dartlang-testcase:///const_functions_return.dart:27:23 -> NullConstant(null) +Extra constant evaluation: evaluated: 6, effectively constant: 2 diff --git a/tests/language/const_functions/const_functions_return_test.dart b/tests/language/const_functions/const_functions_return_test.dart new file mode 100644 index 00000000000..793c629d75d --- /dev/null +++ b/tests/language/const_functions/const_functions_return_test.dart @@ -0,0 +1,40 @@ +// 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 function invocation return types. + +// SharedOptions=--enable-experiment=const-functions + +import "package:expect/expect.dart"; + +const var1 = fn(); +// ^^^^ +// [analyzer] COMPILE_TIME_ERROR.CONST_INITIALIZED_WITH_NON_CONSTANT_VALUE +void fn() {} + +const var2 = fn2(); +// ^^^^^ +// [analyzer] COMPILE_TIME_ERROR.CONST_INITIALIZED_WITH_NON_CONSTANT_VALUE +void fn2() { + return; +} + +const var3 = fn3(); +// ^^^^^ +// [analyzer] COMPILE_TIME_ERROR.CONST_INITIALIZED_WITH_NON_CONSTANT_VALUE +int? fn3() => null; + +const var4 = fn4(); +// ^^^^^ +// [analyzer] COMPILE_TIME_ERROR.CONST_INITIALIZED_WITH_NON_CONSTANT_VALUE +int? fn4() { + return null; +} + +void main() { + Expect.equals((var1 as dynamic), null); + Expect.equals((var2 as dynamic), null); + Expect.equals(var3, null); + Expect.equals(var4, null); +}