| Index: sdk/lib/_internal/compiler/implementation/types/type_mask.dart
|
| ===================================================================
|
| --- sdk/lib/_internal/compiler/implementation/types/type_mask.dart (revision 22031)
|
| +++ sdk/lib/_internal/compiler/implementation/types/type_mask.dart (working copy)
|
| @@ -18,6 +18,7 @@
|
|
|
| final DartType base;
|
| final int flags;
|
| + final Set<ClassElement> refined;
|
|
|
| TypeMask(DartType base, int kind, bool isNullable)
|
| : this.internal(base, (kind << 1) | (isNullable ? 1 : 0));
|
| @@ -40,9 +41,38 @@
|
| TypeMask.nonNullSubtype(DartType base)
|
| : this.internal(base, SUBTYPE << 1);
|
|
|
| - TypeMask.internal(DartType base, this.flags)
|
| + TypeMask.internal(DartType base, this.flags, [this.refined = null])
|
| : this.base = transformBase(base);
|
|
|
| + factory TypeMask.refineWith(TypeMask other,
|
| + Selector selector,
|
| + Compiler compiler) {
|
| + if (other.isExact) return other;
|
| + if (other.isEmpty) return other;
|
| +
|
| + List<ClassElement> classes =
|
| + compiler.world.allFunctions.findTargets(selector);
|
| +
|
| + // If it's not worth refining [other], just return it.
|
| + if (classes.isEmpty || classes[0] == compiler.objectClass) return other;
|
| +
|
| + Set<ClassElement> refined = new Set<ClassElement>.from(classes);
|
| + if (other.refined != null) {
|
| + refined = refined.intersection(other.refined);
|
| + }
|
| +
|
| + if (refined.length == 1) {
|
| + // If there is only one known target, use it as the new type. We
|
| + // know it has to be a non-null subclass.
|
| + ClassElement classFound = refined.elementAt(0);
|
| + if (isSubtypeOf(other.base, classFound.rawType, compiler)) return other;
|
| + if (classFound == other.base.element && other.isSubclass) return other;
|
| + return new TypeMask.nonNullSubclass(classFound.rawType);
|
| + }
|
| +
|
| + return new TypeMask.internal(other.base, other.flags & ~1, refined);
|
| + }
|
| +
|
| // TODO(kasperl): We temporarily transform the base to be the raw
|
| // variant of the type. Long term, we're going to keep the class
|
| // element corresponding to the type in the mask instead.
|
| @@ -157,9 +187,9 @@
|
| int combined = (flags > other.flags)
|
| ? flags | (other.flags & 1)
|
| : other.flags | (flags & 1);
|
| - if (flags == combined) {
|
| + if (flags == combined && refined == null) {
|
| return this;
|
| - } else if (other.flags == combined) {
|
| + } else if (other.flags == combined && other.refined == null) {
|
| return other;
|
| } else {
|
| return new TypeMask.internal(base, combined);
|
| @@ -182,7 +212,7 @@
|
| ? flags | (other.flags & 1)
|
| : other.flags | (flags & 1);
|
| }
|
| - return (flags != combined)
|
| + return (flags != combined || refined != null)
|
| ? new TypeMask.internal(base, combined)
|
| : this;
|
| }
|
| @@ -193,7 +223,7 @@
|
| // resulting union to be a subtype too. If either one of the masks
|
| // are nullable the result should be nullable too.
|
| int combined = (SUBTYPE << 1) | ((flags | other.flags) & 1);
|
| - return (flags != combined)
|
| + return (flags != combined || refined != null)
|
| ? new TypeMask.internal(base, combined)
|
| : this;
|
| }
|
| @@ -281,12 +311,13 @@
|
| int combined = (flags < other.flags)
|
| ? flags & ((other.flags & 1) | ~1)
|
| : other.flags & ((flags & 1) | ~1);
|
| - if (flags == combined) {
|
| + if (flags == combined && setEquals(refined, other.refined)) {
|
| return this;
|
| - } else if (other.flags == combined) {
|
| + } else if (other.flags == combined && setEquals(refined, other.refined)) {
|
| return other;
|
| } else {
|
| - return new TypeMask.internal(base, combined);
|
| + return new TypeMask.internal(
|
| + base, combined, setIntersection(refined, other.refined));
|
| }
|
| }
|
|
|
| @@ -299,10 +330,11 @@
|
| // so base the combined flags on the other mask. Only if both
|
| // masks are nullable, will the result be nullable too.
|
| int combined = other.flags & ((flags & 1) | ~1);
|
| - if (other.flags == combined) {
|
| + if (other.flags == combined && setEquals(refined, other.refined)) {
|
| return other;
|
| } else {
|
| - return new TypeMask.internal(other.base, combined);
|
| + return new TypeMask.internal(
|
| + other.base, combined, setIntersection(refined, other.refined));
|
| }
|
| }
|
|
|
| @@ -315,10 +347,11 @@
|
| // so base the combined flags on the other mask. Only if both
|
| // masks are nullable, will the result be nullable too.
|
| int combined = other.flags & ((flags & 1) | ~1);
|
| - if (other.flags == combined) {
|
| + if (other.flags == combined && setEquals(refined, other.refined)) {
|
| return other;
|
| } else {
|
| - return new TypeMask.internal(other.base, combined);
|
| + return new TypeMask.internal(
|
| + other.base, combined, setIntersection(refined, other.refined));
|
| }
|
| }
|
|
|
| @@ -502,10 +535,24 @@
|
| });
|
| }
|
|
|
| + bool setEquals(Set<ClassElement> first, Set<ClassElement> second) {
|
| + if (first == null) return second == null;
|
| + if (second == null) return false;
|
| + return first.containsAll(second) && second.containsAll(first);
|
| + }
|
| +
|
| + Set<ClassElement> setIntersection(Set<ClassElement> first,
|
| + Set<ClassElement> second) {
|
| + if (first == null || second == null) return null;
|
| + return first.intersection(second);
|
| + }
|
| +
|
| bool operator ==(var other) {
|
| if (other is !TypeMask) return false;
|
| TypeMask otherMask = other;
|
| - return (flags == otherMask.flags) && (base == otherMask.base);
|
| + return (flags == otherMask.flags)
|
| + && (base == otherMask.base)
|
| + && setEquals(refined, other.refined);
|
| }
|
|
|
| int get hashCode {
|
| @@ -520,6 +567,7 @@
|
| if (isSubclass) buffer.write('subclass=');
|
| if (isSubtype) buffer.write('subtype=');
|
| buffer.write(base.element.name.slowToString());
|
| + buffer.write(', $refined');
|
| return "[$buffer]";
|
| }
|
|
|
|
|