Add wrapper to prevent iteration of unordered maps
diff --git a/gen/build/src/cfg.rs b/gen/build/src/cfg.rs
index c15a173..6818bda 100644
--- a/gen/build/src/cfg.rs
+++ b/gen/build/src/cfg.rs
@@ -266,10 +266,10 @@
 #[cfg(not(doc))]
 mod r#impl {
     use crate::intern::{intern, InternedString};
+    use crate::syntax::map::UnorderedMap as Map;
     use crate::vec::{self, InternedVec as _};
     use lazy_static::lazy_static;
     use std::cell::RefCell;
-    use std::collections::HashMap;
     use std::fmt::{self, Debug};
     use std::marker::PhantomData;
     use std::ops::{Deref, DerefMut};
@@ -316,7 +316,7 @@
         //         cfg: AtomicPtr<super::Cfg>,
         //     }
         //
-        static CONST_DEREFS: RefCell<HashMap<Handle, Box<super::Cfg<'static>>>> = RefCell::default();
+        static CONST_DEREFS: RefCell<Map<Handle, Box<super::Cfg<'static>>>> = RefCell::default();
     }
 
     #[derive(Eq, PartialEq, Hash)]
diff --git a/gen/build/src/intern.rs b/gen/build/src/intern.rs
index 51bc07c..25b8706 100644
--- a/gen/build/src/intern.rs
+++ b/gen/build/src/intern.rs
@@ -1,5 +1,5 @@
+use crate::syntax::set::UnorderedSet as Set;
 use lazy_static::lazy_static;
-use std::collections::HashSet;
 use std::sync::{Mutex, PoisonError};
 
 #[derive(Copy, Clone, Default)]
@@ -13,7 +13,7 @@
 
 pub fn intern(s: &str) -> InternedString {
     lazy_static! {
-        static ref INTERN: Mutex<HashSet<&'static str>> = Mutex::new(HashSet::new());
+        static ref INTERN: Mutex<Set<&'static str>> = Mutex::new(Set::new());
     }
 
     let mut set = INTERN.lock().unwrap_or_else(PoisonError::into_inner);
diff --git a/gen/build/src/lib.rs b/gen/build/src/lib.rs
index f7d4182..204c89f 100644
--- a/gen/build/src/lib.rs
+++ b/gen/build/src/lib.rs
@@ -90,10 +90,10 @@
 use crate::gen::error::report;
 use crate::gen::Opt;
 use crate::paths::PathExt;
+use crate::syntax::map::{Entry, UnorderedMap};
 use crate::target::TargetDir;
 use cc::Build;
-use std::collections::btree_map::Entry;
-use std::collections::{BTreeMap, BTreeSet};
+use std::collections::BTreeSet;
 use std::env;
 use std::ffi::{OsStr, OsString};
 use std::io::{self, Write};
@@ -288,7 +288,7 @@
         });
     }
 
-    let mut header_dirs_index = BTreeMap::new();
+    let mut header_dirs_index = UnorderedMap::new();
     let mut used_header_links = BTreeSet::new();
     let mut used_header_prefixes = BTreeSet::new();
     for krate in deps::direct_dependencies() {
diff --git a/gen/src/nested.rs b/gen/src/nested.rs
index d94d36d..c030717 100644
--- a/gen/src/nested.rs
+++ b/gen/src/nested.rs
@@ -1,6 +1,6 @@
+use crate::syntax::map::UnorderedMap as Map;
 use crate::syntax::Api;
 use proc_macro2::Ident;
-use std::collections::HashMap as Map;
 
 pub struct NamespaceEntries<'a> {
     direct: Vec<&'a Api>,
diff --git a/gen/src/write.rs b/gen/src/write.rs
index 8242b2e..d2dbe02 100644
--- a/gen/src/write.rs
+++ b/gen/src/write.rs
@@ -3,6 +3,8 @@
 use crate::gen::out::OutFile;
 use crate::gen::{builtin, include, Opt};
 use crate::syntax::atom::Atom::{self, *};
+use crate::syntax::map::UnorderedMap as Map;
+use crate::syntax::set::UnorderedSet;
 use crate::syntax::symbol::Symbol;
 use crate::syntax::trivial::{self, TrivialReason};
 use crate::syntax::{
@@ -10,7 +12,6 @@
     Type, TypeAlias, Types, Var,
 };
 use proc_macro2::Ident;
-use std::collections::{HashMap, HashSet};
 
 pub(super) fn gen(apis: &[Api], types: &Types, opt: &Opt, header: bool) -> Vec<u8> {
     let mut out_file = OutFile::new(header, opt, types);
@@ -65,7 +66,7 @@
 }
 
 fn write_data_structures<'a>(out: &mut OutFile<'a>, apis: &'a [Api]) {
-    let mut methods_for_type = HashMap::new();
+    let mut methods_for_type = Map::new();
     for api in apis {
         if let Api::CxxFunction(efn) | Api::RustFunction(efn) = api {
             if let Some(receiver) = &efn.sig.receiver {
@@ -77,7 +78,7 @@
         }
     }
 
-    let mut structs_written = HashSet::new();
+    let mut structs_written = UnorderedSet::new();
     let mut toposorted_structs = out.types.toposorted_structs.iter();
     for api in apis {
         match api {
diff --git a/syntax/instantiate.rs b/syntax/instantiate.rs
index 2891485..e9c6f29 100644
--- a/syntax/instantiate.rs
+++ b/syntax/instantiate.rs
@@ -1,7 +1,7 @@
 use crate::syntax::Type;
 use proc_macro2::Ident;
 
-#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
+#[derive(Copy, Clone, PartialEq, Eq, Hash)]
 pub enum ImplKey<'a> {
     RustBox(&'a Ident),
     RustVec(&'a Ident),
diff --git a/syntax/map.rs b/syntax/map.rs
new file mode 100644
index 0000000..a3b33ed
--- /dev/null
+++ b/syntax/map.rs
@@ -0,0 +1,89 @@
+use std::borrow::Borrow;
+use std::hash::Hash;
+use std::ops::Index;
+
+pub use self::unordered::UnorderedMap;
+pub use std::collections::hash_map::Entry;
+
+mod unordered {
+    use crate::syntax::set::UnorderedSet;
+    use std::borrow::Borrow;
+    use std::collections::hash_map::{Entry, HashMap};
+    use std::hash::Hash;
+
+    // Wrapper prohibits accidentally introducing iteration over the map, which
+    // could lead to nondeterministic generated code.
+    pub struct UnorderedMap<K, V>(HashMap<K, V>);
+
+    impl<K, V> UnorderedMap<K, V> {
+        pub fn new() -> Self {
+            UnorderedMap(HashMap::new())
+        }
+    }
+
+    impl<K, V> UnorderedMap<K, V>
+    where
+        K: Hash + Eq,
+    {
+        pub fn insert(&mut self, key: K, value: V) -> Option<V> {
+            self.0.insert(key, value)
+        }
+
+        pub fn contains_key<Q>(&self, key: &Q) -> bool
+        where
+            K: Borrow<Q>,
+            Q: ?Sized + Hash + Eq,
+        {
+            self.0.contains_key(key)
+        }
+
+        pub fn get<Q>(&self, key: &Q) -> Option<&V>
+        where
+            K: Borrow<Q>,
+            Q: ?Sized + Hash + Eq,
+        {
+            self.0.get(key)
+        }
+
+        pub fn entry(&mut self, key: K) -> Entry<K, V> {
+            self.0.entry(key)
+        }
+
+        pub fn remove<Q>(&mut self, key: &Q) -> Option<V>
+        where
+            K: Borrow<Q>,
+            Q: ?Sized + Hash + Eq,
+        {
+            self.0.remove(key)
+        }
+
+        pub fn keys(&self) -> UnorderedSet<K>
+        where
+            K: Copy,
+        {
+            let mut set = UnorderedSet::new();
+            for key in self.0.keys() {
+                set.insert(*key);
+            }
+            set
+        }
+    }
+}
+
+impl<K, V> Default for UnorderedMap<K, V> {
+    fn default() -> Self {
+        UnorderedMap::new()
+    }
+}
+
+impl<Q, K, V> Index<&Q> for UnorderedMap<K, V>
+where
+    K: Borrow<Q> + Hash + Eq,
+    Q: ?Sized + Hash + Eq,
+{
+    type Output = V;
+
+    fn index(&self, key: &Q) -> &V {
+        self.get(key).unwrap()
+    }
+}
diff --git a/syntax/mod.rs b/syntax/mod.rs
index 1dcb55c..96169e8 100644
--- a/syntax/mod.rs
+++ b/syntax/mod.rs
@@ -13,6 +13,7 @@
 mod improper;
 pub mod instantiate;
 pub mod mangle;
+pub mod map;
 mod names;
 pub mod namespace;
 mod parse;
diff --git a/syntax/set.rs b/syntax/set.rs
index 0df5f87..ca0c43e 100644
--- a/syntax/set.rs
+++ b/syntax/set.rs
@@ -105,6 +105,10 @@
         {
             self.0.get(value)
         }
+
+        pub fn retain(&mut self, f: impl FnMut(&T) -> bool) {
+            self.0.retain(f);
+        }
     }
 }
 
diff --git a/syntax/toposort.rs b/syntax/toposort.rs
index 876f9ad..8fe55b8 100644
--- a/syntax/toposort.rs
+++ b/syntax/toposort.rs
@@ -1,6 +1,6 @@
+use crate::syntax::map::{Entry, UnorderedMap as Map};
 use crate::syntax::report::Errors;
 use crate::syntax::{Api, Struct, Type, Types};
-use std::collections::btree_map::{BTreeMap as Map, Entry};
 
 enum Mark {
     Visiting,
diff --git a/syntax/trivial.rs b/syntax/trivial.rs
index 070a366..b7ba4f9 100644
--- a/syntax/trivial.rs
+++ b/syntax/trivial.rs
@@ -1,7 +1,7 @@
+use crate::syntax::map::UnorderedMap;
 use crate::syntax::set::{OrderedSet as Set, UnorderedSet};
 use crate::syntax::{Api, Enum, ExternFn, Pair, RustName, Struct, Type};
 use proc_macro2::Ident;
-use std::collections::BTreeMap as Map;
 use std::fmt::{self, Display};
 
 #[derive(Copy, Clone)]
@@ -17,11 +17,11 @@
 pub fn required_trivial_reasons<'a>(
     apis: &'a [Api],
     all: &Set<&'a Type>,
-    structs: &Map<&'a Ident, &'a Struct>,
-    enums: &Map<&'a Ident, &'a Enum>,
+    structs: &UnorderedMap<&'a Ident, &'a Struct>,
+    enums: &UnorderedMap<&'a Ident, &'a Enum>,
     cxx: &UnorderedSet<&'a Ident>,
-) -> Map<&'a Ident, Vec<TrivialReason<'a>>> {
-    let mut required_trivial = Map::new();
+) -> UnorderedMap<&'a Ident, Vec<TrivialReason<'a>>> {
+    let mut required_trivial = UnorderedMap::new();
 
     let mut insist_extern_types_are_trivial = |ident: &'a RustName, reason| {
         if cxx.contains(&ident.rust)
diff --git a/syntax/types.rs b/syntax/types.rs
index 87a2992..7a45939 100644
--- a/syntax/types.rs
+++ b/syntax/types.rs
@@ -1,5 +1,6 @@
 use crate::syntax::improper::ImproperCtype;
 use crate::syntax::instantiate::ImplKey;
+use crate::syntax::map::UnorderedMap;
 use crate::syntax::report::Errors;
 use crate::syntax::set::{OrderedSet as Set, UnorderedSet};
 use crate::syntax::trivial::{self, TrivialReason};
@@ -8,19 +9,18 @@
 };
 use proc_macro2::Ident;
 use quote::ToTokens;
-use std::collections::BTreeMap as Map;
 
 pub struct Types<'a> {
     pub all: Set<&'a Type>,
-    pub structs: Map<&'a Ident, &'a Struct>,
-    pub enums: Map<&'a Ident, &'a Enum>,
+    pub structs: UnorderedMap<&'a Ident, &'a Struct>,
+    pub enums: UnorderedMap<&'a Ident, &'a Enum>,
     pub cxx: UnorderedSet<&'a Ident>,
     pub rust: UnorderedSet<&'a Ident>,
-    pub aliases: Map<&'a Ident, &'a TypeAlias>,
-    pub untrusted: Map<&'a Ident, &'a ExternType>,
-    pub required_trivial: Map<&'a Ident, Vec<TrivialReason<'a>>>,
-    pub explicit_impls: Map<ImplKey<'a>, &'a Impl>,
-    pub resolutions: Map<&'a Ident, &'a Pair>,
+    pub aliases: UnorderedMap<&'a Ident, &'a TypeAlias>,
+    pub untrusted: UnorderedMap<&'a Ident, &'a ExternType>,
+    pub required_trivial: UnorderedMap<&'a Ident, Vec<TrivialReason<'a>>>,
+    pub explicit_impls: UnorderedMap<ImplKey<'a>, &'a Impl>,
+    pub resolutions: UnorderedMap<&'a Ident, &'a Pair>,
     pub struct_improper_ctypes: UnorderedSet<&'a Ident>,
     pub toposorted_structs: Vec<&'a Struct>,
 }
@@ -28,14 +28,14 @@
 impl<'a> Types<'a> {
     pub fn collect(cx: &mut Errors, apis: &'a [Api]) -> Self {
         let mut all = Set::new();
-        let mut structs = Map::new();
-        let mut enums = Map::new();
+        let mut structs = UnorderedMap::new();
+        let mut enums = UnorderedMap::new();
         let mut cxx = UnorderedSet::new();
         let mut rust = UnorderedSet::new();
-        let mut aliases = Map::new();
-        let mut untrusted = Map::new();
-        let mut explicit_impls = Map::new();
-        let mut resolutions = Map::new();
+        let mut aliases = UnorderedMap::new();
+        let mut untrusted = UnorderedMap::new();
+        let mut explicit_impls = UnorderedMap::new();
+        let mut resolutions = UnorderedMap::new();
         let struct_improper_ctypes = UnorderedSet::new();
         let toposorted_structs = Vec::new();
 
@@ -192,7 +192,7 @@
 
         types.toposorted_structs = toposort::sort(cx, apis, &types);
 
-        let mut unresolved_structs: Vec<&Ident> = types.structs.keys().copied().collect();
+        let mut unresolved_structs = types.structs.keys();
         let mut new_information = true;
         while new_information {
             new_information = false;