ECR join(ECR e1, ECR e2) { if (e1.equals(e2)) return e1; checkStructCollapse(e1, e2); ValueType t1 = getType(e1); ValueType t2 = getType(e2); Collection<Pair<Size, ECR>> ccjoins1 = ImmutableList.copyOf(getCCjoins(e1)); Collection<Pair<Size, ECR>> ccjoins2 = ImmutableList.copyOf(getCCjoins(e2)); Collection<ECR> cjoins1 = ImmutableList.copyOf(getCjoins(e1)); Collection<ECR> cjoins2 = ImmutableList.copyOf(getCjoins(e2)); ECR root = union(e1, e2); switch (t1.getKind()) { case BOTTOM: { switch (t2.getKind()) { case BOTTOM: { Collection<ECR> cjoins = Sets.newHashSet(); cjoins.addAll(cjoins1); cjoins.addAll(cjoins2); root.addCjoins(cjoins); Collection<Pair<Size, ECR>> ccjoins = Sets.newHashSet(); ccjoins.addAll(ccjoins1); ccjoins.addAll(ccjoins2); root.addCCjoins(ccjoins); break; } default: { root.setType(t2); root.clearCCjoins(ccjoins1); for (Pair<Size, ECR> pair : ccjoins1) ccjoin(pair.fst(), root, pair.snd()); root.clearCjoins(cjoins1); for (ECR cjoin : cjoins1) cjoin(root, cjoin); break; } } break; } default: { switch (t2.getKind()) { case BOTTOM: { root.setType(t1); root.clearCCjoins(ccjoins2); for (Pair<Size, ECR> pair : ccjoins2) ccjoin(pair.fst(), root, pair.snd()); root.clearCjoins(cjoins2); for (ECR cjoin : cjoins2) cjoin(root, cjoin); break; } default: { root.setType(t1); ValueType unionType = unify(t1, t2); ValueType freshType = getType(root); if (!freshType.equals(t1)) { unionType = resolveType(root, unionType, freshType); } root.setType(unionType); root.clearCCjoins(ccjoins1); root.clearCCjoins(ccjoins2); for (Pair<Size, ECR> pair : ccjoins1) ccjoin(pair.fst(), root, pair.snd()); for (Pair<Size, ECR> pair : ccjoins2) ccjoin(pair.fst(), root, pair.snd()); break; } } break; } } return root; }