Make `UnionType::hashCode()` independent of the order of the union elements.
This fixes the equals-hashCode contract for `UnionType`, eliminating a source of non-determinism in the compiler.
This change also stops eagerly computing `hashCode()` since it's of unclear value and has some risk about freshness.
@@ -67,7 +67,7 @@ import java.util.TreeSet;
* unions.<p>
*/
public class UnionType extends JSType {
- private static final long serialVersionUID = 1L;
+ private static final long serialVersionUID = 2L;
// NOTE: to avoid allocating iterators, all the loops below iterate over alternates by index
// instead of using the for-each loop idiom.
@@ -76,7 +76,6 @@ public class UnionType extends JSType {
ImmutableList<JSType> alternatesWithoutStucturalTyping;
// alternates under structural typing
ImmutableList<JSType> alternates;
- private int hashcode;
/**
* Creates a union type.
@@ -93,7 +92,6 @@ public class UnionType extends JSType {
builder.addAlternate(alternate, true);
}
this.alternates = builder.getAlternates();
- this.hashcode = this.alternatesWithoutStucturalTyping.hashCode();
}
/**
@@ -163,7 +161,6 @@ public class UnionType extends JSType {
builder.addAlternate(alternate, true);
}
alternates = builder.getAlternates();
- hashcode = alternatesWithoutStucturalTyping.hashCode();
}
/**
@@ -508,8 +505,14 @@ public class UnionType extends JSType {
}
@Override
- public int hashCode() {
- return this.hashcode;
+ public final int hashCode() {
+ int hashCode = alternatesWithoutStucturalTyping.size();
+ for (int i = 0; i < alternatesWithoutStucturalTyping.size(); i++) {
+ // To be determinisitic this aggregation must be order-independent. Using a commutative
+ // operatator (multiplication) allows us to achieve that without sorting.
+ hashCode *= alternatesWithoutStucturalTyping.get(i).hashCode();
+ }
+ return hashCode;
}
@Override
