Skip to content

Commit 47d5502

Browse files
kallentuCommit Queue
authored andcommitted
[flow] Issue 1721 - Allow better promotions for final variables.
Promotions should happen for final variables because they are assigned and won't be re-assigned by the time they're evaluated. In a conservative join, promotions should not be cancelled for final variables. Language tests made in https://dart-review.googlesource.com/c/sdk/+/390340. Bug: dart-lang/language#1721 Change-Id: I7bb577a694ddb5572a28884de70bd8c5b68e3c25 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/390803 Reviewed-by: Chloe Stefantsova <[email protected]> Reviewed-by: Paul Berry <[email protected]> Commit-Queue: Kallen Tu <[email protected]>
1 parent dacf5bc commit 47d5502

18 files changed

+856
-0
lines changed

pkg/_fe_analyzer_shared/lib/src/flow_analysis/flow_analysis.dart

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2313,6 +2313,11 @@ class FlowModel<Type extends Object> {
23132313
PromotionModel<Type>? info =
23142314
result.promotionInfo?.get(helper, variableKey);
23152315
if (info == null) continue;
2316+
2317+
// We don't need to discard promotions for final variables. They are
2318+
// guaranteed to be already assigned and won't be assigned again.
2319+
if (helper.isFinal(variableKey)) continue;
2320+
23162321
PromotionModel<Type> newInfo =
23172322
info.discardPromotionsAndMarkNotUnassigned();
23182323
if (!identical(info, newInfo)) {
@@ -2823,6 +2828,10 @@ mixin FlowModelHelper<Type extends Object> {
28232828
/// subtyping.
28242829
@visibleForTesting
28252830
FlowAnalysisTypeOperations<Type> get typeOperations;
2831+
2832+
/// Whether the variable of [variableKey] was declared with the `final`
2833+
/// modifier and the `inference-update-4` feature flag is enabled.
2834+
bool isFinal(int variableKey);
28262835
}
28272836

28282837
/// Documentation links that might be presented to the user to accompany a "why
@@ -4993,6 +5002,14 @@ class _FlowAnalysisImpl<Node extends Object, Statement extends Node,
49935002
}
49945003
}
49955004

5005+
@override
5006+
bool isFinal(int variableKey) {
5007+
if (!inferenceUpdate4Enabled) return false;
5008+
Variable? variable = promotionKeyStore.variableForKey(variableKey);
5009+
if (variable != null && operations.isFinal(variable)) return true;
5010+
return false;
5011+
}
5012+
49965013
@override
49975014
bool isUnassigned(Variable variable) {
49985015
return _current.promotionInfo

pkg/_fe_analyzer_shared/lib/src/flow_analysis/flow_analysis_operations.dart

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@ library;
1010
/// representation of variables and types.
1111
abstract interface class FlowAnalysisOperations<Variable extends Object,
1212
Type extends Object> implements FlowAnalysisTypeOperations<Type> {
13+
/// Whether the given [variable] was declared with the `final` modifier.
14+
bool isFinal(Variable variable);
15+
1316
/// Determines whether the given property can be promoted.
1417
///
1518
/// [property] will correspond to a `propertyMember` value passed to

pkg/_fe_analyzer_shared/test/flow_analysis/flow_analysis_mini_ast.dart

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,13 @@ class FlowAnalysisTestHarness extends Harness
3939
@override
4040
FlowAnalysisOperations<Var, SharedTypeView<Type>> get typeOperations =>
4141
typeAnalyzer.operations;
42+
43+
@override
44+
bool isFinal(int variableKey) {
45+
Var? variable = promotionKeyStore.variableForKey(variableKey);
46+
if (variable != null && operations.isFinal(variable)) return true;
47+
return false;
48+
}
4249
}
4350

4451
/// Helper class allowing tests to examine the values of variables' SSA nodes.

pkg/_fe_analyzer_shared/test/flow_analysis/flow_analysis_test.dart

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1018,6 +1018,99 @@ main() {
10181018
]);
10191019
});
10201020

1021+
test(
1022+
'functionExpression_begin() cancels promotions of final vars'
1023+
' with inference-update-4 disabled', () {
1024+
// See test for "functionExpression_begin() preserves promotions of final
1025+
// variables" for enabled behavior.
1026+
var x = Var('x', isFinal: true);
1027+
h.disableInferenceUpdate4();
1028+
h.run([
1029+
declare(x, type: 'num'),
1030+
if_(expr('bool'), [
1031+
x.write(expr('int')),
1032+
], [
1033+
x.write(expr('double')),
1034+
]),
1035+
if_(x.is_('int'), [
1036+
localFunction([
1037+
checkNotPromoted(x),
1038+
]),
1039+
], [
1040+
localFunction([
1041+
checkNotPromoted(x),
1042+
]),
1043+
]),
1044+
]);
1045+
});
1046+
1047+
test('functionExpression_begin() cancels promotions of non-final vars', () {
1048+
// num x;
1049+
// if (<bool>) {
1050+
// x = <int>;
1051+
// } else {
1052+
// x = <double>;
1053+
// }
1054+
// if (x is int) {
1055+
// () => x is not promoted
1056+
// } else {
1057+
// () => x is not promoted
1058+
// }
1059+
1060+
var x = Var('x');
1061+
h.run([
1062+
declare(x, type: 'num'),
1063+
if_(expr('bool'), [
1064+
x.write(expr('int')),
1065+
], [
1066+
x.write(expr('double')),
1067+
]),
1068+
if_(x.is_('int'), [
1069+
localFunction([
1070+
checkNotPromoted(x),
1071+
]),
1072+
], [
1073+
localFunction([
1074+
checkNotPromoted(x),
1075+
]),
1076+
]),
1077+
]);
1078+
});
1079+
1080+
test('functionExpression_begin() preserves promotions of final variables',
1081+
() {
1082+
// final num x;
1083+
// if (<bool>) {
1084+
// x = <int>;
1085+
// } else {
1086+
// x = <double>;
1087+
// }
1088+
// if (x is int) {
1089+
// () => x is promoted to int
1090+
// } else {
1091+
// () => x is not promoted
1092+
// }
1093+
1094+
var x = Var('x', isFinal: true);
1095+
h.run([
1096+
declare(x, type: 'num'),
1097+
if_(expr('bool'), [
1098+
x.write(expr('int')),
1099+
], [
1100+
x.write(expr('double')),
1101+
]),
1102+
if_(x.is_('int'), [
1103+
localFunction([
1104+
checkPromoted(x, 'int'),
1105+
]),
1106+
], [
1107+
localFunction([
1108+
checkNotPromoted(x),
1109+
]),
1110+
]),
1111+
]);
1112+
});
1113+
10211114
test('functionExpression_begin() preserves promotions of initialized vars',
10221115
() {
10231116
var x = Var('x');

pkg/_fe_analyzer_shared/test/mini_ast.dart

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2963,6 +2963,11 @@ class MiniAstOperations
29632963
return false;
29642964
}
29652965

2966+
@override
2967+
bool isFinal(Var variable) {
2968+
return variable.isFinal;
2969+
}
2970+
29662971
@override
29672972
bool isInterfaceType(SharedTypeView<Type> type) {
29682973
Type unwrappedType = type.unwrapTypeView();

pkg/analyzer/lib/src/dart/resolver/flow_analysis_visitor.dart

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -543,6 +543,11 @@ class TypeSystemOperations
543543
unwrappedType.element is ExtensionTypeElement;
544544
}
545545

546+
@override
547+
bool isFinal(PromotableElement variable) {
548+
return variable.isFinal;
549+
}
550+
546551
@override
547552
bool isInterfaceType(SharedTypeView<DartType> type) {
548553
DartType unwrappedType = type.unwrapTypeView();

0 commit comments

Comments
 (0)