Skip to content

Commit 4d6adbf

Browse files
stereotype441commit-bot@chromium.org
authored andcommitted
Flow analysis: Move join into State and document it.
Change-Id: I8cec06c0d19107b4973e192534d63f05a5b3d902 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/112036 Reviewed-by: Johnni Winther <johnniwinther@google.com> Reviewed-by: Konstantin Shcheglov <scheglov@google.com>
1 parent 9b5abe5 commit 4d6adbf

File tree

2 files changed

+96
-79
lines changed

2 files changed

+96
-79
lines changed

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

Lines changed: 76 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -400,44 +400,6 @@ class FlowAnalysis<Statement, Expression, Variable, Type> {
400400
}
401401
}
402402

403-
@visibleForTesting
404-
Map<Variable, Type> joinPromoted(
405-
Map<Variable, Type> first,
406-
Map<Variable, Type> second,
407-
) {
408-
if (identical(first, second)) return first;
409-
if (first.isEmpty || second.isEmpty) return const {};
410-
411-
var result = <Variable, Type>{};
412-
var alwaysFirst = true;
413-
var alwaysSecond = true;
414-
for (var variable in first.keys) {
415-
var firstType = first[variable];
416-
var secondType = second[variable];
417-
if (secondType != null) {
418-
if (identical(firstType, secondType)) {
419-
result[variable] = firstType;
420-
} else if (typeOperations.isSubtypeOf(firstType, secondType)) {
421-
result[variable] = secondType;
422-
alwaysFirst = false;
423-
} else if (typeOperations.isSubtypeOf(secondType, firstType)) {
424-
result[variable] = firstType;
425-
alwaysSecond = false;
426-
} else {
427-
alwaysFirst = false;
428-
alwaysSecond = false;
429-
}
430-
} else {
431-
alwaysFirst = false;
432-
}
433-
}
434-
435-
if (alwaysFirst) return first;
436-
if (alwaysSecond && result.length == second.length) return second;
437-
if (result.isEmpty) return const {};
438-
return result;
439-
}
440-
441403
void logicalAnd_end(Expression andExpression, Expression rightOperand) {
442404
_conditionalEnd(rightOperand);
443405
// Tail of the stack: falseLeft, trueLeft, falseRight, trueRight
@@ -643,27 +605,8 @@ class FlowAnalysis<Statement, Expression, Variable, Type> {
643605
}
644606

645607
State<Variable, Type> _join(
646-
State<Variable, Type> first,
647-
State<Variable, Type> second,
648-
) {
649-
if (first == null) return second;
650-
if (second == null) return first;
651-
652-
if (first.reachable && !second.reachable) return first;
653-
if (!first.reachable && second.reachable) return second;
654-
655-
var newReachable = first.reachable || second.reachable;
656-
var newNotAssigned = first.notAssigned.union(second.notAssigned);
657-
var newPromoted = joinPromoted(first.promoted, second.promoted);
658-
659-
return State._identicalOrNew(
660-
first,
661-
second,
662-
newReachable,
663-
newNotAssigned,
664-
newPromoted,
665-
);
666-
}
608+
State<Variable, Type> first, State<Variable, Type> second) =>
609+
State.join(typeOperations, first, second);
667610

668611
/// If assertions are enabled, records that the given variable has been
669612
/// referenced. The [finish] method will verify that all referenced variables
@@ -1019,6 +962,80 @@ class State<Variable, Type> {
1019962
return result;
1020963
}
1021964

965+
/// Forms a new state to reflect a control flow path that might have come from
966+
/// either `this` or the [other] state.
967+
///
968+
/// The control flow path is considered reachable if either of the input
969+
/// states is reachable. Variables are considered definitely assigned if they
970+
/// were definitely assigned in both of the input states. Variable promotions
971+
/// are kept only if they are common to both input states; if a variable is
972+
/// promoted to one type in one state and a subtype in the other state, the
973+
/// less specific type promotion is kept.
974+
static State<Variable, Type> join<Variable, Type>(
975+
TypeOperations<Variable, Type> typeOperations,
976+
State<Variable, Type> first,
977+
State<Variable, Type> second,
978+
) {
979+
if (first == null) return second;
980+
if (second == null) return first;
981+
982+
if (first.reachable && !second.reachable) return first;
983+
if (!first.reachable && second.reachable) return second;
984+
985+
var newReachable = first.reachable || second.reachable;
986+
var newNotAssigned = first.notAssigned.union(second.notAssigned);
987+
var newPromoted =
988+
State.joinPromoted(typeOperations, first.promoted, second.promoted);
989+
990+
return State._identicalOrNew(
991+
first,
992+
second,
993+
newReachable,
994+
newNotAssigned,
995+
newPromoted,
996+
);
997+
}
998+
999+
/// Joins two "promoted" maps. See [join] for details.
1000+
@visibleForTesting
1001+
static Map<Variable, Type> joinPromoted<Variable, Type>(
1002+
TypeOperations<Variable, Type> typeOperations,
1003+
Map<Variable, Type> first,
1004+
Map<Variable, Type> second,
1005+
) {
1006+
if (identical(first, second)) return first;
1007+
if (first.isEmpty || second.isEmpty) return const {};
1008+
1009+
var result = <Variable, Type>{};
1010+
var alwaysFirst = true;
1011+
var alwaysSecond = true;
1012+
for (var variable in first.keys) {
1013+
var firstType = first[variable];
1014+
var secondType = second[variable];
1015+
if (secondType != null) {
1016+
if (identical(firstType, secondType)) {
1017+
result[variable] = firstType;
1018+
} else if (typeOperations.isSubtypeOf(firstType, secondType)) {
1019+
result[variable] = secondType;
1020+
alwaysFirst = false;
1021+
} else if (typeOperations.isSubtypeOf(secondType, firstType)) {
1022+
result[variable] = firstType;
1023+
alwaysSecond = false;
1024+
} else {
1025+
alwaysFirst = false;
1026+
alwaysSecond = false;
1027+
}
1028+
} else {
1029+
alwaysFirst = false;
1030+
}
1031+
}
1032+
1033+
if (alwaysFirst) return first;
1034+
if (alwaysSecond && result.length == second.length) return second;
1035+
if (result.isEmpty) return const {};
1036+
return result;
1037+
}
1038+
10221039
/// Creates a new [State] object, unless it is equivalent to either [first] or
10231040
/// [second], in which case one of those objects is re-used.
10241041
static State<Variable, Type> _identicalOrNew<Variable, Type>(

pkg/front_end/test/fasta/flow_analysis/flow_analysis_test.dart

Lines changed: 20 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -424,58 +424,58 @@ main() {
424424
const emptyMap = <Null, Null>{};
425425

426426
test('identical inputs', () {
427-
var flow = _Harness().flow;
427+
var h = _Harness();
428428
var p = {x: intType, y: stringType};
429-
expect(flow.joinPromoted(p, p), same(p));
429+
expect(State.joinPromoted(h, p, p), same(p));
430430
});
431431

432432
test('one input empty', () {
433-
var flow = _Harness().flow;
433+
var h = _Harness();
434434
var p1 = {x: intType, y: stringType};
435435
var p2 = <_Var, _Type>{};
436-
expect(flow.joinPromoted(p1, p2), same(emptyMap));
437-
expect(flow.joinPromoted(p2, p1), same(emptyMap));
436+
expect(State.joinPromoted(h, p1, p2), same(emptyMap));
437+
expect(State.joinPromoted(h, p2, p1), same(emptyMap));
438438
});
439439

440440
test('related types', () {
441-
var flow = _Harness().flow;
441+
var h = _Harness();
442442
var p1 = {x: intType};
443443
var p2 = {x: intQType};
444-
expect(flow.joinPromoted(p1, p2), same(p2));
445-
expect(flow.joinPromoted(p2, p1), same(p2));
444+
expect(State.joinPromoted(h, p1, p2), same(p2));
445+
expect(State.joinPromoted(h, p2, p1), same(p2));
446446
});
447447

448448
test('unrelated types', () {
449-
var flow = _Harness().flow;
449+
var h = _Harness();
450450
var p1 = {x: intType};
451451
var p2 = {x: stringType};
452-
expect(flow.joinPromoted(p1, p2), same(emptyMap));
453-
expect(flow.joinPromoted(p2, p1), same(emptyMap));
452+
expect(State.joinPromoted(h, p1, p2), same(emptyMap));
453+
expect(State.joinPromoted(h, p2, p1), same(emptyMap));
454454
});
455455

456456
test('sub-map', () {
457-
var flow = _Harness().flow;
457+
var h = _Harness();
458458
var p1 = {x: intType, y: stringType};
459459
var p2 = {x: intType};
460-
expect(flow.joinPromoted(p1, p2), same(p2));
461-
expect(flow.joinPromoted(p2, p1), same(p2));
460+
expect(State.joinPromoted(h, p1, p2), same(p2));
461+
expect(State.joinPromoted(h, p2, p1), same(p2));
462462
});
463463

464464
test('sub-map with matched subtype', () {
465-
var flow = _Harness().flow;
465+
var h = _Harness();
466466
var p1 = {x: intType, y: stringType};
467467
var p2 = {x: intQType};
468-
expect(flow.joinPromoted(p1, p2), same(p2));
469-
expect(flow.joinPromoted(p2, p1), same(p2));
468+
expect(State.joinPromoted(h, p1, p2), same(p2));
469+
expect(State.joinPromoted(h, p2, p1), same(p2));
470470
});
471471

472472
test('sub-map with mismatched subtype', () {
473-
var flow = _Harness().flow;
473+
var h = _Harness();
474474
var p1 = {x: intQType, y: stringType};
475475
var p2 = {x: intType};
476-
var join12 = flow.joinPromoted(p1, p2);
476+
var join12 = State.joinPromoted(h, p1, p2);
477477
_Type.allowComparisons(() => expect(join12, {x: intQType}));
478-
var join21 = flow.joinPromoted(p2, p1);
478+
var join21 = State.joinPromoted(h, p2, p1);
479479
_Type.allowComparisons(() => expect(join21, {x: intQType}));
480480
});
481481
});

0 commit comments

Comments
 (0)