[CFE] Handle invalid constant string interpolation in unevaluated constant expressions
Non-primitive constants were incorrectly stringified in an unevaluated
context. These are now, as with unevaluated constants, checked when the
string concatenation is fully evaluated.
Closes #36609
Change-Id: Ia3266ebcb9d497b277690244569812f7cd3e30c8
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/99461
Reviewed-by: Sigmund Cherem <sigmund@google.com>
Commit-Queue: Sigmund Cherem <sigmund@google.com>
Auto-Submit: Johnni Winther <johnniwinther@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 372484b..b35550c 100644
--- a/pkg/front_end/lib/src/fasta/kernel/constant_evaluator.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/constant_evaluator.dart
@@ -1951,6 +1951,9 @@
concatenated.add(new StringBuffer(value));
}
} else if (shouldBeUnevaluated) {
+ // The constant is either unevaluated or a non-primitive in an
+ // unevaluated context. In both cases we defer the evaluation and/or
+ // error reporting till later.
concatenated.add(constant);
} else {
return report(
@@ -1963,11 +1966,13 @@
final expressions = new List<Expression>(concatenated.length);
for (int i = 0; i < concatenated.length; i++) {
Object value = concatenated[i];
- if (value is UnevaluatedConstant) {
- expressions[i] = extract(value);
- } else {
+ if (value is StringBuffer) {
expressions[i] = new ConstantExpression(
canonicalize(new StringConstant(value.toString())));
+ } else {
+ // The value is either unevaluated constant or a non-primitive
+ // constant in an unevaluated expression.
+ expressions[i] = extract(value);
}
}
return unevaluated(node, new StringConcatenation(expressions));
diff --git a/tests/compiler/dart2js/model/cfe_constant_evaluation_test.dart b/tests/compiler/dart2js/model/cfe_constant_evaluation_test.dart
index c056024..52f93c0 100644
--- a/tests/compiler/dart2js/model/cfe_constant_evaluation_test.dart
+++ b/tests/compiler/dart2js/model/cfe_constant_evaluation_test.dart
@@ -99,11 +99,11 @@
const ConstantData(
'const {0: 1, 2: 3}',
'MapConstant(<int, int>{IntConstant(0): IntConstant(1), '
- 'IntConstant(2): IntConstant(3)})'),
+ 'IntConstant(2): IntConstant(3)})'),
const ConstantData(
'const <int, int>{0: 1, 2: 3}',
'MapConstant(<int, int>{IntConstant(0): IntConstant(1), '
- 'IntConstant(2): IntConstant(3)})'),
+ 'IntConstant(2): IntConstant(3)})'),
const ConstantData('const <int, int>{0: 1, 0: 2}', 'NonConstant',
expectedErrors: 'ConstEvalDuplicateKey'),
const ConstantData(
@@ -155,36 +155,36 @@
const ConstantData(
'const C()',
'ConstructedConstant(C(field1=IntConstant(42),'
- 'field2=BoolConstant(false)))'),
+ 'field2=BoolConstant(false)))'),
const ConstantData(
'const C(field1: 87)',
'ConstructedConstant(C(field1=IntConstant(87),'
- 'field2=BoolConstant(false)))'),
+ 'field2=BoolConstant(false)))'),
const ConstantData(
'const C(field2: true)',
'ConstructedConstant(C(field1=IntConstant(42),'
- 'field2=BoolConstant(true)))'),
+ 'field2=BoolConstant(true)))'),
const ConstantData(
'const C.named()',
'ConstructedConstant(C(field1=BoolConstant(false),'
- 'field2=BoolConstant(false)))'),
+ 'field2=BoolConstant(false)))'),
const ConstantData(
'const C.named(87)',
'ConstructedConstant(C(field1=IntConstant(87),'
- 'field2=IntConstant(87)))'),
+ 'field2=IntConstant(87)))'),
const ConstantData(
'const C(field1: a, field2: b)', const <Map<String, String>, String>{
const {}: 'ConstructedConstant(C(field1=BoolConstant(true),'
'field2=IntConstant(42)))',
const {'foo': 'false', 'bar': '87'}:
'ConstructedConstant(C(field1=BoolConstant(false),'
- 'field2=IntConstant(87)))',
+ 'field2=IntConstant(87)))',
}),
const ConstantData(
'const D(42, 87)',
'ConstructedConstant(D(field1=IntConstant(87),'
- 'field2=IntConstant(42),'
- 'field3=IntConstant(99)))'),
+ 'field2=IntConstant(42),'
+ 'field3=IntConstant(99)))'),
]),
const TestData('redirect', '''
class A<T> implements B<Null> {
@@ -252,19 +252,19 @@
const ConstantData(
'const A.named(99, 100)',
'ConstructedConstant(A('
- 't=IntConstant(100),'
- 'u=IntConstant(42),'
- 'x=IntConstant(3),'
- 'y=IntConstant(499),'
- 'z=IntConstant(99)))'),
+ 't=IntConstant(100),'
+ 'u=IntConstant(42),'
+ 'x=IntConstant(3),'
+ 'y=IntConstant(499),'
+ 'z=IntConstant(99)))'),
const ConstantData(
'const A(99, 100)',
'ConstructedConstant(A('
- 't=IntConstant(100),'
- 'u=IntConstant(42),'
- 'x=IntConstant(3),'
- 'y=IntConstant(499),'
- 'z=IntConstant(99)))'),
+ 't=IntConstant(100),'
+ 'u=IntConstant(42),'
+ 'x=IntConstant(3),'
+ 'y=IntConstant(499),'
+ 'z=IntConstant(99)))'),
]),
const TestData('errors', r'''
const dynamic null_ = const bool.fromEnvironment('x') ? null : null;
@@ -317,9 +317,8 @@
r'"$integer $string $boolean"', 'StringConstant("5 baz false")'),
const ConstantData('integer ? true : false', 'NonConstant',
expectedErrors: 'ConstEvalInvalidType'),
- // TODO(sigmund): CFE incorrectly stringifies proxy (issue 36609).
- //const ConstantData(r'"${proxy}"', 'NonConstant',
- // expectedErrors: 'ConstEvalInvalidStringInterpolationOperand'),
+ const ConstantData(r'"${proxy}"', 'NonConstant',
+ expectedErrors: 'ConstEvalInvalidStringInterpolationOperand'),
const ConstantData('0 + string', 'NonConstant',
expectedErrors: 'ConstEvalInvalidType'),
const ConstantData('string + 0', 'NonConstant',
@@ -442,13 +441,13 @@
const ConstantData(
'const C<int>(0, identity)',
'ConstructedConstant(C<int>(defaultValue=IntConstant(0),'
- 'identityFunction=InstantiationConstant([int],'
- 'FunctionConstant(identity))))'),
+ 'identityFunction=InstantiationConstant([int],'
+ 'FunctionConstant(identity))))'),
const ConstantData(
'const C<double>(0.5, identity)',
'ConstructedConstant(C<double>(defaultValue=DoubleConstant(0.5),'
- 'identityFunction=InstantiationConstant([double],'
- 'FunctionConstant(identity))))'),
+ 'identityFunction=InstantiationConstant([double],'
+ 'FunctionConstant(identity))))'),
]),
const TestData('generic class', '''
class C<T> {