Skip to content

Commit d1124c3

Browse files
askeksacommit-bot@chromium.org
authored andcommitted
[CFE] Fail at runtime when a const constructor redirects to a non-const
If a const constructor redirects to a non-const constructor, report a compile-time error (no change here). At runtime, a const instantiation will result in an invalid expression, whereas a non-const (explicit or implicit new) instantiation will work normally. Closes #23622 Change-Id: Ief0e9c62fb8de34203df49fd862f20aba41a3cde Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/120042 Commit-Queue: Aske Simon Christensen <askesc@google.com> Reviewed-by: Johnni Winther <johnniwinther@google.com>
1 parent 05f56ef commit d1124c3

10 files changed

+154
-32
lines changed

pkg/front_end/lib/src/fasta/fasta_codes_generated.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1054,7 +1054,7 @@ const MessageCode messageConstConstructorWithNonConstSuper = const MessageCode(
10541054
"ConstConstructorWithNonConstSuper",
10551055
analyzerCodes: <String>["CONST_CONSTRUCTOR_WITH_NON_CONST_SUPER"],
10561056
message:
1057-
r"""Constant constructor can't call non-constant super constructors.""");
1057+
r"""A constant constructor can't call a non-constant super constructor.""");
10581058

10591059
// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
10601060
const Code<Null> codeConstEvalCircularity = messageConstEvalCircularity;

pkg/front_end/lib/src/fasta/kernel/body_builder.dart

Lines changed: 2 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -5029,15 +5029,6 @@ class BodyBuilder extends ScopeListener<JumpTarget>
50295029
..fileOffset = charOffset;
50305030
}
50315031

5032-
Initializer buildInvalidSuperInitializer(
5033-
Constructor target, ArgumentsImpl arguments, Expression expression,
5034-
[int charOffset = -1]) {
5035-
needsImplicitSuperInitializer = false;
5036-
return new InvalidSuperInitializerJudgment(
5037-
target, arguments, new VariableDeclaration.forValue(expression))
5038-
..fileOffset = charOffset;
5039-
}
5040-
50415032
Initializer buildDuplicatedInitializer(Field field, Expression value,
50425033
String name, int offset, int previousInitializerOffset) {
50435034
return new ShadowInvalidFieldInitializer(
@@ -5139,12 +5130,8 @@ class BodyBuilder extends ScopeListener<JumpTarget>
51395130
bool isSynthetic, Constructor constructor, Arguments arguments,
51405131
[int charOffset = -1]) {
51415132
if (member.isConst && !constructor.isConst) {
5142-
return buildInvalidSuperInitializer(
5143-
constructor,
5144-
arguments,
5145-
buildProblem(fasta.messageConstConstructorWithNonConstSuper,
5146-
charOffset, constructor.name.name.length),
5147-
charOffset);
5133+
addProblem(fasta.messageConstConstructorWithNonConstSuper, charOffset,
5134+
constructor.name.name.length);
51485135
}
51495136
needsImplicitSuperInitializer = false;
51505137
return new SuperInitializer(constructor, arguments)

pkg/front_end/lib/src/fasta/kernel/constant_evaluator.dart

Lines changed: 18 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -893,18 +893,9 @@ class ConstantEvaluator extends RecursiveVisitor<Constant> {
893893
@override
894894
Constant visitConstructorInvocation(ConstructorInvocation node) {
895895
final Constructor constructor = node.target;
896+
checkConstructorConst(node, constructor);
897+
896898
final Class klass = constructor.enclosingClass;
897-
bool isSymbol = klass == coreTypes.internalSymbolClass;
898-
if (!constructor.isConst) {
899-
return reportInvalid(node, 'Non-const constructor invocation.');
900-
}
901-
if (constructor.function.body != null &&
902-
constructor.function.body is! EmptyStatement) {
903-
return reportInvalid(
904-
node,
905-
'Constructor "$node" has non-trivial body '
906-
'"${constructor.function.body.runtimeType}".');
907-
}
908899
if (klass.isAbstract) {
909900
return reportInvalid(
910901
node, 'Constructor "$node" belongs to abstract class "${klass}".');
@@ -914,6 +905,7 @@ class ConstantEvaluator extends RecursiveVisitor<Constant> {
914905
evaluatePositionalArguments(node.arguments);
915906
final Map<String, Constant> named = evaluateNamedArguments(node.arguments);
916907

908+
bool isSymbol = klass == coreTypes.internalSymbolClass;
917909
if (isSymbol && shouldBeUnevaluated) {
918910
return unevaluated(
919911
node,
@@ -964,6 +956,19 @@ class ConstantEvaluator extends RecursiveVisitor<Constant> {
964956
});
965957
}
966958

959+
void checkConstructorConst(TreeNode node, Constructor constructor) {
960+
if (!constructor.isConst) {
961+
reportInvalid(node, 'Non-const constructor invocation.');
962+
}
963+
if (constructor.function.body != null &&
964+
constructor.function.body is! EmptyStatement) {
965+
reportInvalid(
966+
node,
967+
'Constructor "$node" has non-trivial body '
968+
'"${constructor.function.body.runtimeType}".');
969+
}
970+
}
971+
967972
@override
968973
Constant visitInstanceCreation(InstanceCreation node) {
969974
return withNewInstanceBuilder(node.classNode, node.typeArguments, () {
@@ -1159,6 +1164,7 @@ class ConstantEvaluator extends RecursiveVisitor<Constant> {
11591164
env.addVariableValue(
11601165
variable, _evaluateSubexpression(variable.initializer));
11611166
} else if (init is SuperInitializer) {
1167+
checkConstructorConst(init, constructor);
11621168
handleConstructorInvocation(
11631169
init.target,
11641170
evaluateSuperTypeArguments(
@@ -1168,6 +1174,7 @@ class ConstantEvaluator extends RecursiveVisitor<Constant> {
11681174
} else if (init is RedirectingInitializer) {
11691175
// Since a redirecting constructor targets a constructor of the same
11701176
// class, we pass the same [typeArguments].
1177+
checkConstructorConst(init, constructor);
11711178
handleConstructorInvocation(
11721179
init.target,
11731180
typeArguments,

pkg/front_end/messages.status

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,6 @@ ConstAndFinal/part_wrapped_declaration4: Fail
6969
ConstConstructorInSubclassOfMixinApplication/example: Fail
7070
ConstConstructorNonFinalField/example: Fail
7171
ConstConstructorRedirectionToNonConst/analyzerCode: Fail # The analyzer doesn't report this error.
72-
ConstConstructorWithNonConstSuper/example: Fail
7372
ConstEvalCircularity/example: Fail
7473
ConstEvalContext/analyzerCode: Fail # This is just used for displaying the context.
7574
ConstEvalContext/example: Fail # This is just used for displaying the context.

pkg/front_end/messages.yaml

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -738,10 +738,6 @@ SuperInitializerNotLast:
738738
script:
739739
- "class C { int x; C.bad() : super(), x = 5; }"
740740

741-
ConstConstructorWithNonConstSuper:
742-
template: "Constant constructor can't call non-constant super constructors."
743-
analyzerCode: CONST_CONSTRUCTOR_WITH_NON_CONST_SUPER
744-
745741
ExtraneousModifier:
746742
index: 77
747743
template: "Can't have modifier '#lexeme' here."
@@ -2703,6 +2699,18 @@ ConstConstructorRedirectionToNonConst:
27032699
A.bar() {}
27042700
}
27052701
2702+
ConstConstructorWithNonConstSuper:
2703+
template: "A constant constructor can't call a non-constant super constructor."
2704+
analyzerCode: CONST_CONSTRUCTOR_WITH_NON_CONST_SUPER
2705+
script:
2706+
- >-
2707+
class A {
2708+
A.bar() {}
2709+
}
2710+
class B extends A {
2711+
const B.foo() : super.bar();
2712+
}
2713+
27062714
AccessError:
27072715
template: "Access error: '#name'."
27082716

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
// Copyright (c) 2019, the Dart project authors. Please see the AUTHORS file
2+
// for details. All rights reserved. Use of this source code is governed by a
3+
// BSD-style license that can be found in the LICENSE file.
4+
5+
class A {
6+
const A() : this.bad();
7+
8+
A.bad() {}
9+
}
10+
11+
class B extends A {
12+
const B() : super.bad();
13+
}
14+
15+
test() {
16+
print(const A());
17+
print(const B());
18+
}
19+
20+
main() {
21+
print(new A());
22+
print(new B());
23+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
library;
2+
//
3+
// Problems in library:
4+
//
5+
// pkg/front_end/testcases/general/const_redirect_to_nonconst.dart:12:21: Error: A constant constructor can't call a non-constant super constructor.
6+
// const B() : super.bad();
7+
// ^^^
8+
//
9+
import self as self;
10+
import "dart:core" as core;
11+
12+
class A extends core::Object {
13+
const constructor •() → self::A*
14+
: this self::A::bad()
15+
;
16+
constructor bad() → self::A*
17+
;
18+
}
19+
class B extends self::A {
20+
const constructor •() → self::B*
21+
: super self::A::bad()
22+
;
23+
}
24+
static method test() → dynamic
25+
;
26+
static method main() → dynamic
27+
;
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
library;
2+
//
3+
// Problems in library:
4+
//
5+
// pkg/front_end/testcases/general/const_redirect_to_nonconst.dart:12:21: Error: A constant constructor can't call a non-constant super constructor.
6+
// const B() : super.bad();
7+
// ^^^
8+
//
9+
// pkg/front_end/testcases/general/const_redirect_to_nonconst.dart:6:20: Error: A constant constructor can't call a non-constant constructor.
10+
// const A() : this.bad();
11+
// ^^^
12+
//
13+
import self as self;
14+
import "dart:core" as core;
15+
16+
class A extends core::Object {
17+
const constructor •() → self::A*
18+
: this self::A::bad()
19+
;
20+
constructor bad() → self::A*
21+
: super core::Object::•() {}
22+
}
23+
class B extends self::A {
24+
const constructor •() → self::B*
25+
: super self::A::bad()
26+
;
27+
}
28+
static method test() → dynamic {
29+
core::print(invalid-expression "Non-const constructor invocation.");
30+
core::print(invalid-expression "Non-const constructor invocation.");
31+
}
32+
static method main() → dynamic {
33+
core::print(new self::A::•());
34+
core::print(new self::B::•());
35+
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
library;
2+
//
3+
// Problems in library:
4+
//
5+
// pkg/front_end/testcases/general/const_redirect_to_nonconst.dart:12:21: Error: A constant constructor can't call a non-constant super constructor.
6+
// const B() : super.bad();
7+
// ^^^
8+
//
9+
// pkg/front_end/testcases/general/const_redirect_to_nonconst.dart:6:20: Error: A constant constructor can't call a non-constant constructor.
10+
// const A() : this.bad();
11+
// ^^^
12+
//
13+
import self as self;
14+
import "dart:core" as core;
15+
16+
class A extends core::Object {
17+
const constructor •() → self::A*
18+
: this self::A::bad()
19+
;
20+
constructor bad() → self::A*
21+
: super core::Object::•() {}
22+
}
23+
class B extends self::A {
24+
const constructor •() → self::B*
25+
: super self::A::bad()
26+
;
27+
}
28+
static method test() → dynamic {
29+
core::print(invalid-expression "Non-const constructor invocation.");
30+
core::print(invalid-expression "Non-const constructor invocation.");
31+
}
32+
static method main() → dynamic {
33+
core::print(new self::A::•());
34+
core::print(new self::B::•());
35+
}

pkg/front_end/testcases/text_serialization.status

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,7 @@ general/closure: TextSerializationFailure # Was: Pass
120120
general/co19_language_metadata_syntax_t04: TextSerializationFailure # Was: Pass
121121
general/complex_class_hierarchy: TextSerializationFailure
122122
general/compound_binary_implicit_as: TextSerializationFailure
123+
general/const_redirect_to_nonconst: TextSerializationFailure
123124
general/constructor_const_inference: TextSerializationFailure # Was: Pass
124125
general/constructor_cycle: TextSerializationFailure # Was: Pass
125126
general/constructor_function_types: TextSerializationFailure # Was: Pass

0 commit comments

Comments
 (0)