Skip to content

Commit f77dbfb

Browse files
committed
fix: merge maps and unions in sequence
1 parent e2d42bf commit f77dbfb

File tree

1 file changed

+49
-35
lines changed

1 file changed

+49
-35
lines changed

src/inferrer/optimizer.rs

Lines changed: 49 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ use bidirectional_map::Bimap;
22
use disjoint_sets::UnionFind;
33

44
use std::{
5-
collections::HashSet,
5+
collections::{HashMap, HashSet},
66
mem,
77
ops::{Deref, DerefMut, Drop},
88
};
@@ -25,43 +25,34 @@ impl Optimizer {
2525
}
2626

2727
pub fn optimize(&self, schema: &mut Schema) {
28+
// <del>
2829
// Note: Merging maps and unions at the same time may have produced results different from
2930
// seperate merging (find map sets - merge - flatten - find union sets - merge - flatten).
3031
// For simplicity, just take the first way.
31-
let sets = schema.arena.find_disjoint_sets(|a, b| {
32-
if let (Some(a), Some(b)) = (a.as_map(), b.as_map()) {
33-
self.to_merge_similar_datatypes && a.is_similar_to(b)
34-
} else if let (Some(a), Some(b)) = (a.as_union(), b.as_union()) {
35-
self.to_merge_same_unions && (a.types == b.types)
36-
} else {
37-
// TODO: merge same array?
38-
false
39-
}
40-
});
41-
let mut ufarena = TypeArenaWithDSU::from_type_arena(&mut schema.arena);
42-
for (leader, mut set) in sets.into_iter() {
43-
if ufarena.get(leader).map(|r#type| {
44-
(self.to_merge_similar_datatypes && r#type.is_map())
45-
|| (self.to_merge_same_unions && r#type.is_union())
46-
}) == Some(true)
47-
{
48-
set.insert(leader); // leader in disjoint set is now a follower
49-
50-
let compact_set = set
51-
.iter()
52-
.cloned()
53-
.filter(|&r#type| ufarena.contains(r#type))
54-
.collect::<Vec<ArenaIndex>>();
55-
// unioned is now the new leader
56-
let _leader = union(&mut ufarena, compact_set);
57-
// References to non-representative AreneIndex will be replaced automatically
58-
// when TypeArenaWithDSU is dropped
59-
}
60-
}
61-
// Although unioner always keeps the first map slot intact, there is no guarantee that
62-
// root would always be the first map in types to be unioned. So update it if necessary.
63-
schema.root = ufarena.find_representative(schema.root).unwrap();
64-
// arena.flatten();
32+
// </del>
33+
// Merging maps and unions in one pass leads to the issue #8. The reason might be some
34+
// reentrancy issues in union (mem::replace?).
35+
// TODO: merge same array?
36+
schema.root = do_merge(
37+
schema,
38+
schema.arena.find_disjoint_sets(|a, b| {
39+
if let (Some(a), Some(b)) = (a.as_map(), b.as_map()) {
40+
self.to_merge_similar_datatypes && a.is_similar_to(b)
41+
} else {
42+
false
43+
}
44+
}),
45+
);
46+
schema.root = do_merge(
47+
schema,
48+
schema.arena.find_disjoint_sets(|a, b| {
49+
if let (Some(a), Some(b)) = (a.as_union(), b.as_union()) {
50+
self.to_merge_same_unions && (a.types == b.types)
51+
} else {
52+
false
53+
}
54+
}),
55+
);
6556
}
6657

6758
/* pub fn merge_same_union(&self, schema: &mut Schema) {
@@ -104,6 +95,29 @@ impl Optimizer {
10495
*/
10596
}
10697

98+
fn do_merge(schema: &mut Schema, sets: HashMap<ArenaIndex, HashSet<ArenaIndex>>) -> ArenaIndex {
99+
let mut ufarena = TypeArenaWithDSU::from_type_arena(&mut schema.arena);
100+
for (leader, mut set) in sets.into_iter() {
101+
set.insert(leader); // leader in disjoint set is now a follower
102+
if set.len() <= 1 {
103+
continue;
104+
}
105+
let compact_set = set
106+
.iter()
107+
.cloned()
108+
.filter(|&r#type| ufarena.contains(r#type))
109+
.collect::<Vec<ArenaIndex>>();
110+
// unioned is now the new leader
111+
let _leader = union(&mut ufarena, compact_set);
112+
// References to non-representative AreneIndex will be replaced automatically
113+
// when TypeArenaWithDSU is dropped
114+
}
115+
// Although unioner always keeps the first map slot intact, there is no guarantee that
116+
// root would always be the first map in types to be unioned. So update it if necessary.
117+
ufarena.find_representative(schema.root).unwrap()
118+
// arena.flatten();
119+
}
120+
107121
/// A wrapper around `&mut TypeArena` with a Disjoint Set Union. `get` and `get_mut` are wrapped
108122
/// with DSU find to be DSU-aware. `remove_in_favor_of` is wrapped with DSU union.
109123
///

0 commit comments

Comments
 (0)