Skip to content

Commit 924a33c

Browse files
natebiggsCommit Queue
authored andcommitted
[dart2js] Fix valid/invalid refinement logic.
The issue here was that isInMask does not hold inversely. The key is that we want to verify which refinements to skip, therefore we want to calculate *in*valid refinements. We want to skip refines where the new type is a Union AND the old tpye is a supertype of the new type. after.isInMask(before) gives us that but !before.isInMask(after) does not. I've renamed isValidRefinement to isInvalidRefinement to more accurately capture this distinction. Change-Id: I0d99479357a140095a5d0dfb7e2f987556097891 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/312160 Commit-Queue: Nate Biggs <[email protected]> Reviewed-by: Mayank Patke <[email protected]>
1 parent ec893a4 commit 924a33c

File tree

7 files changed

+54
-38
lines changed

7 files changed

+54
-38
lines changed

pkg/compiler/lib/src/inferrer/abstract_value_domain.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -660,7 +660,7 @@ abstract class AbstractValueDomain {
660660
/// Serializes this [value] for this domain to [sink].
661661
void writeAbstractValueToDataSink(DataSinkWriter sink, AbstractValue? value);
662662

663-
bool isValidRefinement(AbstractValue before, AbstractValue after);
663+
bool isInvalidRefinement(AbstractValue before, AbstractValue after);
664664

665665
void finalizeMetrics() {}
666666

pkg/compiler/lib/src/inferrer/computable.dart

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -629,9 +629,9 @@ class ComputableAbstractValueDomain with AbstractValueDomain {
629629
}
630630

631631
@override
632-
bool isValidRefinement(covariant ComputableAbstractValue before,
632+
bool isInvalidRefinement(covariant ComputableAbstractValue before,
633633
covariant ComputableAbstractValue after) {
634-
return _wrappedDomain.isValidRefinement(_unwrap(before), _unwrap(after));
634+
return _wrappedDomain.isInvalidRefinement(_unwrap(before), _unwrap(after));
635635
}
636636

637637
@override

pkg/compiler/lib/src/inferrer/engine.dart

Lines changed: 37 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// for details. All rights reserved. Use of this source code is governed by a
33
// BSD-style license that can be found in the LICENSE file.
44

5+
import 'package:collection/collection.dart';
56
import 'package:kernel/ast.dart' as ir;
67

78
import '../../compiler_api.dart' as api;
@@ -480,60 +481,66 @@ class InferrerEngine {
480481
_processCalledTargets(shouldMarkCalled: true);
481482

482483
if (debug.PRINT_SUMMARY) {
483-
types.allocatedLists.values.forEach((_info) {
484+
void printSorted(Iterable<String> toSort) {
485+
print(toSort.sorted((a, b) => a.compareTo(b)).join('\n'));
486+
}
487+
488+
printSorted(types.allocatedLists.values.map((_info) {
484489
ListTypeInformation info = _info;
485-
print('${info.type} '
490+
return '${info.type} '
486491
'for ${abstractValueDomain.getAllocationNode(info.originalType)} '
487492
'at ${abstractValueDomain.getAllocationElement(info.originalType)}'
488-
'after ${info.refineCount}');
489-
});
490-
types.allocatedSets.values.forEach((_info) {
493+
'after ${info.refineCount}';
494+
}));
495+
printSorted(types.allocatedSets.values.map((_info) {
491496
SetTypeInformation info = _info;
492-
print('${info.type} '
497+
return ('${info.type} '
493498
'for ${abstractValueDomain.getAllocationNode(info.originalType)} '
494499
'at ${abstractValueDomain.getAllocationElement(info.originalType)} '
495500
'after ${info.refineCount}');
496-
});
497-
types.allocatedMaps.values.forEach((_info) {
501+
}));
502+
printSorted(types.allocatedMaps.values.map((_info) {
498503
MapTypeInformation info = _info;
499-
print('${info.type} '
504+
return '${info.type} '
500505
'for ${abstractValueDomain.getAllocationNode(info.originalType)} '
501506
'at ${abstractValueDomain.getAllocationElement(info.originalType)}'
502-
'after ${info.refineCount}');
503-
});
504-
types.allocatedClosures.forEach((TypeInformation info) {
507+
'after ${info.refineCount}';
508+
}));
509+
printSorted(types.allocatedClosures.map((TypeInformation info) {
505510
if (info is ElementTypeInformation) {
506-
print('${info.getInferredSignature(types)} for '
507-
'${info.debugName}');
511+
return '${info.getInferredSignature(types)} for '
512+
'${info.debugName}';
508513
} else if (info is ClosureTypeInformation) {
509-
print('${info.getInferredSignature(types)} for '
510-
'${info.debugName}');
514+
return '${info.getInferredSignature(types)} for '
515+
'${info.debugName}';
511516
} else if (info is DynamicCallSiteTypeInformation) {
517+
var str = '';
512518
if (info.hasClosureCallTargets) {
513-
print('<Closure.call>');
519+
str += '<Closure.call>';
514520
}
515521
info.forEachConcreteTarget(memberHierarchyBuilder, (member) {
516522
if (member is FunctionEntity) {
517-
print('${types.getInferredSignatureOfMethod(member)} '
518-
'for ${member}');
523+
str += '${types.getInferredSignatureOfMethod(member)} '
524+
'for ${member}';
519525
} else {
520-
print('${types.getInferredTypeOfMember(member).type} '
521-
'for ${member}');
526+
str += '${types.getInferredTypeOfMember(member).type} '
527+
'for ${member}';
522528
}
523529
return true;
524530
});
531+
return str;
525532
} else if (info is StaticCallSiteTypeInformation) {
526533
final cls = info.calledElement.enclosingClass!;
527534
final callMethod = _lookupCallMethod(cls)!;
528-
print('${types.getInferredSignatureOfMethod(callMethod)} for ${cls}');
535+
return '${types.getInferredSignatureOfMethod(callMethod)} for ${cls}';
529536
} else {
530-
print('${info.type} for some unknown kind of closure');
537+
return '${info.type} for some unknown kind of closure';
531538
}
532-
});
533-
_analyzedElements.forEach((MemberEntity elem) {
539+
}));
540+
printSorted(_analyzedElements.map((MemberEntity elem) {
534541
TypeInformation type = types.getInferredTypeOfMember(elem);
535-
print('${elem} :: ${type} from ${type.inputs} ');
536-
});
542+
return '${elem} :: ${type} from ${type.inputs} ';
543+
}));
537544
}
538545
dump?.afterAnalysis();
539546

@@ -779,7 +786,9 @@ class InferrerEngine {
779786
// Check that refinement has not accidentally changed the type.
780787
assert(oldType == info.type);
781788
if (info.abandonInferencing) info.doNotEnqueue = true;
782-
if ((info.type = newType) != oldType) {
789+
final invalidRefine =
790+
abstractValueDomain.isInvalidRefinement(oldType, newType);
791+
if (!invalidRefine && (info.type = newType) != oldType) {
783792
_overallRefineCount++;
784793
info.incrementRefineCount();
785794
if (info.refineCount > _MAX_CHANGE_COUNT) {

pkg/compiler/lib/src/inferrer/powersets/powersets.dart

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -848,9 +848,9 @@ class PowersetDomain with AbstractValueDomain {
848848
receiver.abstractValue, selector, memberHierarchyBuilder);
849849

850850
@override
851-
bool isValidRefinement(
851+
bool isInvalidRefinement(
852852
covariant PowersetValue before, covariant PowersetValue after) {
853-
return _abstractValueDomain.isValidRefinement(
853+
return _abstractValueDomain.isInvalidRefinement(
854854
before._abstractValue, after._abstractValue);
855855
}
856856
}

pkg/compiler/lib/src/inferrer/trivial.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -393,7 +393,7 @@ class TrivialAbstractValueDomain with AbstractValueDomain {
393393
const [];
394394

395395
@override
396-
bool isValidRefinement(AbstractValue before, AbstractValue after) => true;
396+
bool isInvalidRefinement(AbstractValue before, AbstractValue after) => true;
397397

398398
@override
399399
AbstractValue get asyncStarStreamType => const TrivialAbstractValue();

pkg/compiler/lib/src/inferrer/typemasks/masks.dart

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -993,7 +993,8 @@ class CommonMasks with AbstractValueDomain {
993993
}
994994

995995
@override
996-
bool isValidRefinement(covariant TypeMask before, covariant TypeMask after) {
996+
bool isInvalidRefinement(
997+
covariant TypeMask before, covariant TypeMask after) {
997998
// Consider a typegraph node which simply outputs the union of its inputs,
998999
// and suppose such a node has K inputs with types: A, B, C, and D. The
9991000
// union would flatten to a common supertype, e.g. `Object`. However, a
@@ -1014,7 +1015,13 @@ class CommonMasks with AbstractValueDomain {
10141015
// (i.e. omit UnionTypeMask type check). But since it is only the behavior
10151016
// of UnionTypeMask that can lead to a narrowing, we save work by only doing
10161017
// the subtype check when a UnionTypeMask is involved.
1017-
return after is! UnionTypeMask || before.isInMask(after, _closedWorld);
1018+
//
1019+
// Note: [UnionTypeMask.isInMask] can return false negatives. We have to
1020+
// ensure we take the conservative action when we get a false negative and
1021+
// treat those as valid refinements. Invoking `after.isInMask` instead of
1022+
// `!before.isInMask` ensures that the false negatives are handled
1023+
// correctly.
1024+
return after is UnionTypeMask && after.isInMask(before, _closedWorld);
10181025
}
10191026

10201027
@override

pkg/compiler/lib/src/inferrer/wrapped.dart

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -641,9 +641,9 @@ class WrappedAbstractValueDomain with AbstractValueDomain {
641641
receiver._abstractValue, selector, memberHierarchyBuilder);
642642

643643
@override
644-
bool isValidRefinement(covariant WrappedAbstractValue before,
644+
bool isInvalidRefinement(covariant WrappedAbstractValue before,
645645
covariant WrappedAbstractValue after) {
646-
return _abstractValueDomain.isValidRefinement(
646+
return _abstractValueDomain.isInvalidRefinement(
647647
before._abstractValue, after._abstractValue);
648648
}
649649
}

0 commit comments

Comments
 (0)