diff --git a/CHANGES.md b/CHANGES.md
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -1,3 +1,24 @@
+## [0.2.18.0]
+
+* [Fix strictness properties of `Strict.mapMaybe[WithKey]`](https://github.com/haskell-unordered-containers/unordered-containers/pull/385)
+
+* [Fix strictness properties of `Strict.alterFEager`](https://github.com/haskell-unordered-containers/unordered-containers/pull/384)
+
+* [Fix space leaks in `union[With[Key]]`](https://github.com/haskell-unordered-containers/unordered-containers/pull/380)
+
+* [Fix space leak in `Lazy.fromListWith`](https://github.com/haskell-unordered-containers/unordered-containers/pull/386)
+
+* [Speed up `difference*` and `intersection*` with `unsafeInsert`](https://github.com/haskell-unordered-containers/unordered-containers/pull/372)
+
+* [`unionArrayBy`: Find next 1-bits with `countTrailingZeros`](https://github.com/haskell-unordered-containers/unordered-containers/pull/395)
+  - This speeds up `union*` for sparsely filled nodes, while penalizing `union` operations on densely filled nodes.
+
+* [Reduce reboxing in internal array operations](https://github.com/haskell-unordered-containers/unordered-containers/pull/377)
+
+* [Reduce code size of array operations in `union*`](https://github.com/haskell-unordered-containers/unordered-containers/pull/376)
+
+[0.2.18.0]: https://github.com/haskell-unordered-containers/unordered-containers/compare/v0.2.17.0...v0.2.18.0
+
 ## [0.2.17.0]
 
 * [Define `dataCast1` for `HashMap`](https://github.com/haskell-unordered-containers/unordered-containers/pull/345)
diff --git a/Data/HashMap/Internal.hs b/Data/HashMap/Internal.hs
--- a/Data/HashMap/Internal.hs
+++ b/Data/HashMap/Internal.hs
@@ -122,7 +122,6 @@
     , update32
     , update32M
     , update32With'
-    , updateOrConcatWith
     , updateOrConcatWithKey
     , filterMapAux
     , equalKeys
@@ -145,7 +144,7 @@
 import Control.Monad.ST           (ST, runST)
 import Data.Bifoldable            (Bifoldable (..))
 import Data.Bits                  (complement, popCount, unsafeShiftL,
-                                   unsafeShiftR, (.&.), (.|.))
+                                   unsafeShiftR, (.&.), (.|.), countTrailingZeros)
 import Data.Coerce                (coerce)
 import Data.Data                  (Constr, Data (..), DataType)
 import Data.Functor.Classes       (Eq1 (..), Eq2 (..), Ord1 (..), Ord2 (..),
@@ -194,7 +193,7 @@
 
 -- | @since 0.2.14.0
 instance NFData k => NFData1 (Leaf k) where
-    liftRnf rnf2 = liftRnf2 rnf rnf2
+    liftRnf = liftRnf2 rnf
 
 -- | @since 0.2.14.0
 instance NFData2 Leaf where
@@ -226,7 +225,7 @@
 
 -- | @since 0.2.14.0
 instance NFData k => NFData1 (HashMap k) where
-    liftRnf rnf2 = liftRnf2 rnf rnf2
+    liftRnf = liftRnf2 rnf
 
 -- | @since 0.2.14.0
 instance NFData2 HashMap where
@@ -334,8 +333,7 @@
 instance (Eq k, Hashable k, Read k, Read e) => Read (HashMap k e) where
     readPrec = parens $ prec 10 $ do
       Ident "fromList" <- lexP
-      xs <- readPrec
-      return (fromList xs)
+      fromList <$> readPrec
 
     readListPrec = readListPrecDefault
 
@@ -395,7 +393,7 @@
 
 equal2 :: (k -> k' -> Bool) -> (v -> v' -> Bool)
       -> HashMap k v -> HashMap k' v' -> Bool
-equal2 eqk eqv t1 t2 = go (toList' t1 []) (toList' t2 [])
+equal2 eqk eqv t1 t2 = go (leavesAndCollisions t1 []) (leavesAndCollisions t2 [])
   where
     -- If the two trees are the same, then their lists of 'Leaf's and
     -- 'Collision's read from left to right should be the same (modulo the
@@ -429,7 +427,7 @@
 
 cmp :: (k -> k' -> Ordering) -> (v -> v' -> Ordering)
     -> HashMap k v -> HashMap k' v' -> Ordering
-cmp cmpk cmpv t1 t2 = go (toList' t1 []) (toList' t2 [])
+cmp cmpk cmpv t1 t2 = go (leavesAndCollisions t1 []) (leavesAndCollisions t2 [])
   where
     go (Leaf k1 l1 : tl1) (Leaf k2 l2 : tl2)
       = compare k1 k2 `mappend`
@@ -445,13 +443,13 @@
     go [] [] = EQ
     go [] _  = LT
     go _  [] = GT
-    go _ _ = error "cmp: Should never happen, toList' includes non Leaf / Collision"
+    go _ _ = error "cmp: Should never happen, leavesAndCollisions includes non Leaf / Collision"
 
     leafCompare (L k v) (L k' v') = cmpk k k' `mappend` cmpv v v'
 
 -- Same as 'equal2' but doesn't compare the values.
 equalKeys1 :: (k -> k' -> Bool) -> HashMap k v -> HashMap k' v' -> Bool
-equalKeys1 eq t1 t2 = go (toList' t1 []) (toList' t2 [])
+equalKeys1 eq t1 t2 = go (leavesAndCollisions t1 []) (leavesAndCollisions t2 [])
   where
     go (Leaf k1 l1 : tl1) (Leaf k2 l2 : tl2)
       | k1 == k2 && leafEq l1 l2
@@ -482,7 +480,7 @@
     leafEq (L k1 _) (L k2 _) = k1 == k2
 
 instance Hashable2 HashMap where
-    liftHashWithSalt2 hk hv salt hm = go salt (toList' hm [])
+    liftHashWithSalt2 hk hv salt hm = go salt (leavesAndCollisions hm [])
       where
         -- go :: Int -> [HashMap k v] -> Int
         go s [] = s
@@ -531,15 +529,15 @@
         arrayHashesSorted :: Int -> A.Array (Leaf k v) -> [Int]
         arrayHashesSorted s = List.sort . List.map (hashLeafWithSalt s) . A.toList
 
-  -- Helper to get 'Leaf's and 'Collision's as a list.
-toList' :: HashMap k v -> [HashMap k v] -> [HashMap k v]
-toList' (BitmapIndexed _ ary) a = A.foldr toList' a ary
-toList' (Full ary)            a = A.foldr toList' a ary
-toList' l@(Leaf _ _)          a = l : a
-toList' c@(Collision _ _)     a = c : a
-toList' Empty                 a = a
+-- | Helper to get 'Leaf's and 'Collision's as a list.
+leavesAndCollisions :: HashMap k v -> [HashMap k v] -> [HashMap k v]
+leavesAndCollisions (BitmapIndexed _ ary) a = A.foldr leavesAndCollisions a ary
+leavesAndCollisions (Full ary)            a = A.foldr leavesAndCollisions a ary
+leavesAndCollisions l@(Leaf _ _)          a = l : a
+leavesAndCollisions c@(Collision _ _)     a = c : a
+leavesAndCollisions Empty                 a = a
 
--- Helper function to detect 'Leaf's and 'Collision's.
+-- | Helper function to detect 'Leaf's and 'Collision's.
 isLeafOrCollision :: HashMap k v -> Bool
 isLeafOrCollision (Leaf _ _)      = True
 isLeafOrCollision (Collision _ _) = True
@@ -717,7 +715,7 @@
 lookupDefault :: (Eq k, Hashable k)
               => v          -- ^ Default value to return.
               -> k -> HashMap k v -> v
-lookupDefault def k t = findWithDefault def k t
+lookupDefault = findWithDefault
 {-# INLINE lookupDefault #-}
 
 -- | /O(log n)/ Return the value to which the specified key is mapped.
@@ -741,7 +739,10 @@
 
 -- | Create a 'BitmapIndexed' or 'Full' node.
 bitmapIndexedOrFull :: Bitmap -> A.Array (HashMap k v) -> HashMap k v
-bitmapIndexedOrFull b ary
+-- The strictness in @ary@ helps achieve a nice code size reduction in
+-- @unionWith[Key]@ with GHC 9.2.2. See the Core diffs in
+-- https://github.com/haskell-unordered-containers/unordered-containers/pull/376.
+bitmapIndexedOrFull b !ary
     | b == fullNodeMask = Full ary
     | otherwise         = BitmapIndexed b ary
 {-# INLINE bitmapIndexedOrFull #-}
@@ -968,7 +969,7 @@
         | hy == h = if ky == k
                     then case f y of
                       (# v' #) | ptrEq y v' -> t
-                               | otherwise -> Leaf h (L k (v'))
+                               | otherwise -> Leaf h (L k v')
                     else collision h l (L k x)
         | otherwise = runST (two s h k x hy t)
     go h k s t@(BitmapIndexed b ary)
@@ -1025,11 +1026,11 @@
 unsafeInsertWith :: forall k v. (Eq k, Hashable k)
                  => (v -> v -> v) -> k -> v -> HashMap k v
                  -> HashMap k v
-unsafeInsertWith f k0 v0 m0 = unsafeInsertWithKey (const f) k0 v0 m0
+unsafeInsertWith f k0 v0 m0 = unsafeInsertWithKey (\_ a b -> (# f a b #)) k0 v0 m0
 {-# INLINABLE unsafeInsertWith #-}
 
 unsafeInsertWithKey :: forall k v. (Eq k, Hashable k)
-                 => (k -> v -> v -> v) -> k -> v -> HashMap k v
+                 => (k -> v -> v -> (# v #)) -> k -> v -> HashMap k v
                  -> HashMap k v
 unsafeInsertWithKey f k0 v0 m0 = runST (go h0 k0 v0 0 m0)
   where
@@ -1038,7 +1039,8 @@
     go !h !k x !_ Empty = return $! Leaf h (L k x)
     go h k x s t@(Leaf hy l@(L ky y))
         | hy == h = if ky == k
-                    then return $! Leaf h (L k (f k x y))
+                    then case f k x y of
+                        (# v #) -> return $! Leaf h (L k v)
                     else return $! collision h l (L k x)
         | otherwise = two s h k x hy t
     go h k x s t@(BitmapIndexed b ary)
@@ -1059,7 +1061,7 @@
         return t
       where i = index h s
     go h k x s t@(Collision hy v)
-        | h == hy   = return $! Collision h (updateOrSnocWithKey (\key a b -> (# f key a b #) ) k x v)
+        | h == hy   = return $! Collision h (updateOrSnocWithKey f k x v)
         | otherwise = go h k x s $ BitmapIndexed (mask hy s) (A.singleton t)
 {-# INLINABLE unsafeInsertWithKey #-}
 
@@ -1264,10 +1266,9 @@
   let
     !h = hash k
     mv = lookup' h k m
-  in (<$> f mv) $ \fres ->
-    case fres of
-      Nothing -> maybe m (const (delete' h k m)) mv
-      Just v' -> insert' h k v' m
+  in (<$> f mv) $ \case
+    Nothing -> maybe m (const (delete' h k m)) mv
+    Just v' -> insert' h k v' m
 
 -- We unconditionally rewrite alterF in RULES, but we expose an
 -- unfolding just in case it's used in some way that prevents the
@@ -1356,8 +1357,7 @@
 -- eagerly, whether or not the given function requires that information.
 alterFEager :: (Functor f, Eq k, Hashable k)
        => (Maybe v -> f (Maybe v)) -> k -> HashMap k v -> f (HashMap k v)
-alterFEager f !k m = (<$> f mv) $ \fres ->
-  case fres of
+alterFEager f !k m = (<$> f mv) $ \case
 
     ------------------------------
     -- Delete the key from the map.
@@ -1407,7 +1407,7 @@
 --
 -- @since 0.2.12
 isSubmapOf :: (Eq k, Hashable k, Eq v) => HashMap k v -> HashMap k v -> Bool
-isSubmapOf = (Exts.inline isSubmapOfBy) (==)
+isSubmapOf = Exts.inline isSubmapOfBy (==)
 {-# INLINABLE isSubmapOf #-}
 
 -- | /O(n*log m)/ Inclusion of maps with value comparison. A map is included in
@@ -1551,7 +1551,7 @@
         | h1 == h2  = Collision h1 (updateOrSnocWithKey (\k a b -> (# f k b a #)) k2 v2 ls1)
         | otherwise = goDifferentHash s h1 h2 t1 t2
     go s t1@(Collision h1 ls1) t2@(Collision h2 ls2)
-        | h1 == h2  = Collision h1 (updateOrConcatWithKey f ls1 ls2)
+        | h1 == h2  = Collision h1 (updateOrConcatWithKey (\k a b -> (# f k a b #)) ls1 ls2)
         | otherwise = goDifferentHash s h1 h2 t1 t2
     -- branch vs. branch
     go s (BitmapIndexed b1 ary1) (BitmapIndexed b2 ary2) =
@@ -1618,27 +1618,31 @@
 -- | Strict in the result of @f@.
 unionArrayBy :: (a -> a -> a) -> Bitmap -> Bitmap -> A.Array a -> A.Array a
              -> A.Array a
-unionArrayBy f b1 b2 ary1 ary2 = A.run $ do
-    let b' = b1 .|. b2
-    mary <- A.new_ (popCount b')
+-- The manual forcing of @b1@, @b2@, @ary1@ and @ary2@ results in handsome
+-- Core size reductions with GHC 9.2.2. See the Core diffs in
+-- https://github.com/haskell-unordered-containers/unordered-containers/pull/376.
+unionArrayBy f !b1 !b2 !ary1 !ary2 = A.run $ do
+    let bCombined = b1 .|. b2
+    mary <- A.new_ (popCount bCombined)
     -- iterate over nonzero bits of b1 .|. b2
-    -- it would be nice if we could shift m by more than 1 each time
-    let ba = b1 .&. b2
-        go !i !i1 !i2 !m
-            | m > b'        = return ()
-            | b' .&. m == 0 = go i i1 i2 (m `unsafeShiftL` 1)
-            | ba .&. m /= 0 = do
+    let go !i !i1 !i2 !b
+            | b == 0 = return ()
+            | testBit (b1 .&. b2) = do
                 x1 <- A.indexM ary1 i1
                 x2 <- A.indexM ary2 i2
                 A.write mary i $! f x1 x2
-                go (i+1) (i1+1) (i2+1) (m `unsafeShiftL` 1)
-            | b1 .&. m /= 0 = do
+                go (i+1) (i1+1) (i2+1) b'
+            | testBit b1 = do
                 A.write mary i =<< A.indexM ary1 i1
-                go (i+1) (i1+1) (i2  ) (m `unsafeShiftL` 1)
-            | otherwise     = do
+                go (i+1) (i1+1) i2 b'
+            | otherwise = do
                 A.write mary i =<< A.indexM ary2 i2
-                go (i+1) (i1  ) (i2+1) (m `unsafeShiftL` 1)
-    go 0 0 0 (b' .&. negate b') -- XXX: b' must be non-zero
+                go (i+1) i1 (i2+1) b'
+          where
+            m = 1 `unsafeShiftL` (countTrailingZeros b)
+            testBit x = x .&. m /= 0
+            b' = b .&. complement m
+    go 0 0 0 bCombined
     return mary
     -- TODO: For the case where b1 .&. b2 == b1, i.e. when one is a
     -- subset of the other, we could use a slightly simpler algorithm,
@@ -1748,7 +1752,7 @@
 difference a b = foldlWithKey' go empty a
   where
     go m k v = case lookup k b of
-                 Nothing -> insert k v m
+                 Nothing -> unsafeInsert k v m
                  _       -> m
 {-# INLINABLE difference #-}
 
@@ -1760,8 +1764,8 @@
 differenceWith f a b = foldlWithKey' go empty a
   where
     go m k v = case lookup k b of
-                 Nothing -> insert k v m
-                 Just w  -> maybe m (\y -> insert k y m) (f v w)
+                 Nothing -> unsafeInsert k v m
+                 Just w  -> maybe m (\y -> unsafeInsert k y m) (f v w)
 {-# INLINABLE differenceWith #-}
 
 -- | /O(n*log m)/ Intersection of two maps. Return elements of the first
@@ -1770,7 +1774,7 @@
 intersection a b = foldlWithKey' go empty a
   where
     go m k v = case lookup k b of
-                 Just _ -> insert k v m
+                 Just _ -> unsafeInsert k v m
                  _      -> m
 {-# INLINABLE intersection #-}
 
@@ -1782,7 +1786,7 @@
 intersectionWith f a b = foldlWithKey' go empty a
   where
     go m k v = case lookup k b of
-                 Just w -> insert k (f v w) m
+                 Just w -> unsafeInsert k (f v w) m
                  _      -> m
 {-# INLINABLE intersectionWith #-}
 
@@ -1794,7 +1798,7 @@
 intersectionWithKey f a b = foldlWithKey' go empty a
   where
     go m k v = case lookup k b of
-                 Just w -> insert k (f k v w) m
+                 Just w -> unsafeInsert k (f k v w) m
                  _      -> m
 {-# INLINABLE intersectionWithKey #-}
 
@@ -2102,7 +2106,7 @@
 --
 -- @since 0.2.11
 fromListWithKey :: (Eq k, Hashable k) => (k -> v -> v -> v) -> [(k, v)] -> HashMap k v
-fromListWithKey f = List.foldl' (\ m (k, v) -> unsafeInsertWithKey f k v m) empty
+fromListWithKey f = List.foldl' (\ m (k, v) -> unsafeInsertWithKey (\k' a b -> (# f k' a b #)) k v m) empty
 {-# INLINE fromListWithKey #-}
 
 ------------------------------------------------------------------------
@@ -2174,11 +2178,7 @@
             = go k v ary (i+1) n
 {-# INLINABLE updateOrSnocWithKey #-}
 
-updateOrConcatWith :: Eq k => (v -> v -> v) -> A.Array (Leaf k v) -> A.Array (Leaf k v) -> A.Array (Leaf k v)
-updateOrConcatWith f = updateOrConcatWithKey (const f)
-{-# INLINABLE updateOrConcatWith #-}
-
-updateOrConcatWithKey :: Eq k => (k -> v -> v -> v) -> A.Array (Leaf k v) -> A.Array (Leaf k v) -> A.Array (Leaf k v)
+updateOrConcatWithKey :: Eq k => (k -> v -> v -> (# v #)) -> A.Array (Leaf k v) -> A.Array (Leaf k v) -> A.Array (Leaf k v)
 updateOrConcatWithKey f ary1 ary2 = A.run $ do
     -- TODO: instead of mapping and then folding, should we traverse?
     -- We'll have to be careful to avoid allocating pairs or similar.
@@ -2200,7 +2200,7 @@
                Just i1 -> do -- key occurs in both arrays, store combination in position i1
                              L k v1 <- A.indexM ary1 i1
                              L _ v2 <- A.indexM ary2 i2
-                             A.write mary i1 (L k (f k v1 v2))
+                             case f k v1 v2 of (# v3 #) -> A.write mary i1 (L k v3)
                              go iEnd (i2+1)
                Nothing -> do -- key is only in ary2, append to end
                              A.write mary iEnd =<< A.indexM ary2 i2
@@ -2268,7 +2268,7 @@
 -- | Mask out the 'bitsPerSubkey' bits used for indexing at this level
 -- of the tree.
 index :: Hash -> Shift -> Int
-index w s = fromIntegral $ (unsafeShiftR w s) .&. subkeyMask
+index w s = fromIntegral $ unsafeShiftR w s .&. subkeyMask
 {-# INLINE index #-}
 
 -- | A bitmask with the 'bitsPerSubkey' least significant bits set.
diff --git a/Data/HashMap/Internal/Array.hs b/Data/HashMap/Internal/Array.hs
--- a/Data/HashMap/Internal/Array.hs
+++ b/Data/HashMap/Internal/Array.hs
@@ -442,9 +442,10 @@
     in run $ do
         mary <- new_ n
         go ary mary 0 n
+        return mary
   where
     go ary mary i n
-        | i >= n    = return mary
+        | i >= n    = return ()
         | otherwise = do
              x <- indexM ary i
              write mary i $ f x
@@ -458,9 +459,10 @@
     in run $ do
         mary <- new_ n
         go ary mary 0 n
+        return mary
   where
     go ary mary i n
-        | i >= n    = return mary
+        | i >= n    = return ()
         | otherwise = do
              x <- indexM ary i
              write mary i $! f x
@@ -473,10 +475,11 @@
         run $ do
             mary <- new_ n
             go xs0 mary 0
+            return mary
   where
-    go [] !mary !_   = return mary
-    go (x:xs) mary i = do write mary i x
-                          go xs mary (i+1)
+    go []     !_   !_ = return ()
+    go (x:xs) mary i  = do write mary i x
+                           go xs mary (i+1)
 
 fromList' :: Int -> [a] -> Array a
 fromList' n xs0 =
@@ -484,10 +487,11 @@
         run $ do
             mary <- new_ n
             go xs0 mary 0
+            return mary
   where
-    go [] !mary !_   = return mary
-    go (!x:xs) mary i = do write mary i x
-                           go xs mary (i+1)
+    go []      !_   !_ = return ()
+    go (!x:xs) mary i  = do write mary i x
+                            go xs mary (i+1)
 
 -- | @since 0.2.17.0
 instance TH.Lift a => TH.Lift (Array a) where
diff --git a/Data/HashMap/Internal/Strict.hs b/Data/HashMap/Internal/Strict.hs
--- a/Data/HashMap/Internal/Strict.hs
+++ b/Data/HashMap/Internal/Strict.hs
@@ -1,5 +1,6 @@
 {-# LANGUAGE BangPatterns  #-}
 {-# LANGUAGE CPP           #-}
+{-# LANGUAGE LambdaCase    #-}
 {-# LANGUAGE MagicHash     #-}
 {-# LANGUAGE PatternGuards #-}
 {-# LANGUAGE Trustworthy   #-}
@@ -48,74 +49,74 @@
       HashMap
 
       -- * Construction
-    , empty
+    , HM.empty
     , singleton
 
       -- * Basic interface
     , HM.null
-    , size
+    , HM.size
     , HM.member
     , HM.lookup
     , (HM.!?)
     , HM.findWithDefault
-    , lookupDefault
-    , (!)
+    , HM.lookupDefault
+    , (HM.!)
     , insert
     , insertWith
-    , delete
+    , HM.delete
     , adjust
     , update
     , alter
     , alterF
-    , isSubmapOf
-    , isSubmapOfBy
+    , HM.isSubmapOf
+    , HM.isSubmapOfBy
 
       -- * Combine
       -- ** Union
-    , union
+    , HM.union
     , unionWith
     , unionWithKey
-    , unions
+    , HM.unions
 
     -- ** Compose
-    , compose
+    , HM.compose
 
       -- * Transformations
     , map
     , mapWithKey
     , traverseWithKey
-    , mapKeys
+    , HM.mapKeys
 
       -- * Difference and intersection
-    , difference
+    , HM.difference
     , differenceWith
-    , intersection
+    , HM.intersection
     , intersectionWith
     , intersectionWithKey
 
       -- * Folds
-    , foldMapWithKey
-    , foldr'
-    , foldl'
-    , foldrWithKey'
-    , foldlWithKey'
+    , HM.foldMapWithKey
+    , HM.foldr'
+    , HM.foldl'
+    , HM.foldrWithKey'
+    , HM.foldlWithKey'
     , HM.foldr
     , HM.foldl
-    , foldrWithKey
-    , foldlWithKey
+    , HM.foldrWithKey
+    , HM.foldlWithKey
 
       -- * Filter
     , HM.filter
-    , filterWithKey
+    , HM.filterWithKey
     , mapMaybe
     , mapMaybeWithKey
 
       -- * Conversions
-    , keys
-    , elems
+    , HM.keys
+    , HM.elems
 
       -- ** Lists
-    , toList
+    , HM.toList
     , fromList
     , fromListWith
     , fromListWithKey
@@ -126,19 +127,31 @@
 import Data.Bits             ((.&.), (.|.))
 import Data.Coerce           (coerce)
 import Data.Functor.Identity (Identity (..))
-import Data.HashMap.Internal hiding (adjust, alter, alterF, differenceWith,
-                              fromList, fromListWith, fromListWithKey, insert,
-                              insertWith, intersectionWith, intersectionWithKey,
-                              map, mapMaybe, mapMaybeWithKey, mapWithKey,
-                              singleton, traverseWithKey, unionWith,
-                              unionWithKey, update)
+-- See Note [Imports from Data.HashMap.Internal]
+import Data.HashMap.Internal (Hash, HashMap (..), Leaf (..), LookupRes (..),
+                              bitsPerSubkey, fullNodeMask, hash, index, mask,
+                              ptrEq, sparseIndex)
 import Data.Hashable         (Hashable)
 import Prelude               hiding (lookup, map)
 
+-- See Note [Imports from Data.HashMap.Internal]
 import qualified Data.HashMap.Internal       as HM
 import qualified Data.HashMap.Internal.Array as A
 import qualified Data.List                   as List
 
+{-
+Note [Imports from Data.HashMap.Internal]
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+It is very important for code in this module not to make mistakes about
+the strictness properties of any utilities. Mistakes can easily lead to space
+leaks, see e.g. #383.
+
+Therefore nearly all functions imported from Data.HashMap.Internal should be
+imported qualified. Only functions that do not manipulate HashMaps or their
+values are exempted.
+-}
+
 -- $strictness
 --
 -- This module satisfies the following strictness properties:
@@ -181,12 +194,12 @@
     go h k x s t@(Leaf hy l@(L ky y))
         | hy == h = if ky == k
                     then leaf h k (f x y)
-                    else x `seq` (collision h l (L k x))
-        | otherwise = x `seq` runST (two s h k x hy t)
+                    else x `seq` HM.collision h l (L k x)
+        | otherwise = x `seq` runST (HM.two s h k x hy t)
     go h k x s (BitmapIndexed b ary)
         | b .&. m == 0 =
             let ary' = A.insert ary i $! leaf h k x
-            in bitmapIndexedOrFull (b .|. m) ary'
+            in HM.bitmapIndexedOrFull (b .|. m) ary'
         | otherwise =
             let st   = A.index ary i
                 st'  = go h k x (s+bitsPerSubkey) st
@@ -197,7 +210,7 @@
     go h k x s (Full ary) =
         let st   = A.index ary i
             st'  = go h k x (s+bitsPerSubkey) st
-            ary' = update32 ary i $! st'
+            ary' = HM.update32 ary i $! st'
         in Full ary'
       where i = index h s
     go h k x s t@(Collision hy v)
@@ -221,13 +234,13 @@
         | hy == h = if ky == k
                     then return $! leaf h k (f k x y)
                     else do
-                        let l' = x `seq` (L k x)
-                        return $! collision h l l'
-        | otherwise = x `seq` two s h k x hy t
+                        let l' = x `seq` L k x
+                        return $! HM.collision h l l'
+        | otherwise = x `seq` HM.two s h k x hy t
     go h k x s t@(BitmapIndexed b ary)
         | b .&. m == 0 = do
             ary' <- A.insertM ary i $! leaf h k x
-            return $! bitmapIndexedOrFull (b .|. m) ary'
+            return $! HM.bitmapIndexedOrFull (b .|. m) ary'
         | otherwise = do
             st <- A.indexM ary i
             st' <- go h k x (s+bitsPerSubkey) st
@@ -268,7 +281,7 @@
         let i    = index h s
             st   = A.index ary i
             st'  = go h k (s+bitsPerSubkey) st
-            ary' = update32 ary i $! st'
+            ary' = HM.update32 ary i $! st'
         in Full ary'
     go h k _ t@(Collision hy v)
         | h == hy   = Collision h (updateWith f k v)
@@ -293,7 +306,7 @@
 alter :: (Eq k, Hashable k) => (Maybe v -> Maybe v) -> k -> HashMap k v -> HashMap k v
 alter f k m =
   case f (HM.lookup k m) of
-    Nothing -> delete k m
+    Nothing -> HM.delete k m
     Just v  -> insert k v m
 {-# INLINABLE alter #-}
 
@@ -315,11 +328,10 @@
 -- @f@ and a functor that is similar to Const but not actually Const.
 alterF f = \ !k !m ->
   let !h = hash k
-      mv = lookup' h k m
-  in (<$> f mv) $ \fres ->
-    case fres of
-      Nothing -> maybe m (const (delete' h k m)) mv
-      Just !v' -> insert' h k v' m
+      mv = HM.lookup' h k m
+  in (<$> f mv) $ \case
+    Nothing -> maybe m (const (HM.delete' h k m)) mv
+    Just !v' -> HM.insert' h k v' m
 
 -- We rewrite this function unconditionally in RULES, but we expose
 -- an unfolding just in case it's used in a context where the rules
@@ -345,13 +357,13 @@
 
 "alterFconstant" forall (f :: Maybe a -> Identity (Maybe a)) x.
   alterFWeird x x f = \ !k !m ->
-    Identity (case runIdentity x of {Nothing -> delete k m; Just a -> insert k a m})
+    Identity (case runIdentity x of {Nothing -> HM.delete k m; Just a -> insert k a m})
 
 "alterFinsertWith" [1] forall (f :: Maybe a -> Identity (Maybe a)) x y.
   alterFWeird (coerce (Just x)) (coerce (Just y)) f =
-    coerce (insertModifying x (\mold -> case runIdentity (f (Just mold)) of
-                                            Nothing -> bogus# (# #)
-                                            Just !new -> (# new #)))
+    coerce (HM.insertModifying x (\mold -> case runIdentity (f (Just mold)) of
+                                               Nothing -> bogus# (# #)
+                                               Just !new -> (# new #)))
 
 -- This rule is written a bit differently than the one for lazy
 -- maps because the adjust here is strict. We could write it the
@@ -363,7 +375,7 @@
                                Nothing -> impossibleAdjust))
 
 "alterFlookup" forall _ign1 _ign2 (f :: Maybe a -> Const r (Maybe a)) .
-  alterFWeird _ign1 _ign2 f = \ !k !m -> Const (getConst (f (lookup k m)))
+  alterFWeird _ign1 _ign2 f = \ !k !m -> Const (getConst (f (HM.lookup k m)))
  #-}
 
 -- This is a very unsafe version of alterF used for RULES. When calling
@@ -397,25 +409,25 @@
       Absent -> m
 
       -- Key did exist, no collision
-      Present _ collPos -> deleteKeyExists collPos h k m
+      Present _ collPos -> HM.deleteKeyExists collPos h k m
 
     ------------------------------
     -- Update value
-    Just v' -> case lookupRes of
+    Just !v' -> case lookupRes of
 
       -- Key did not exist before, insert v' under a new key
-      Absent -> insertNewKey h k v' m
+      Absent -> HM.insertNewKey h k v' m
 
       -- Key existed before, no hash collision
-      Present v collPos -> v' `seq`
+      Present v collPos ->
         if v `ptrEq` v'
         -- If the value is identical, no-op
         then m
         -- If the value changed, update the value.
-        else insertKeyExists collPos h k v' m
+        else HM.insertKeyExists collPos h k v' m
 
   where !h = hash k
-        !lookupRes = lookupRecordCollision h k m
+        !lookupRes = HM.lookupRecordCollision h k m
         !mv = case lookupRes of
           Absent -> Nothing
           Present v _ -> Just v
@@ -444,7 +456,7 @@
     go s t1@(Leaf h1 l1@(L k1 v1)) t2@(Leaf h2 l2@(L k2 v2))
         | h1 == h2  = if k1 == k2
                       then leaf h1 k1 (f k1 v1 v2)
-                      else collision h1 l1 l2
+                      else HM.collision h1 l1 l2
         | otherwise = goDifferentHash s h1 h2 t1 t2
     go s t1@(Leaf h1 (L k1 v1)) t2@(Collision h2 ls2)
         | h1 == h2  = Collision h1 (updateOrSnocWithKey f k1 v1 ls2)
@@ -453,28 +465,28 @@
         | h1 == h2  = Collision h1 (updateOrSnocWithKey (flip . f) k2 v2 ls1)
         | otherwise = goDifferentHash s h1 h2 t1 t2
     go s t1@(Collision h1 ls1) t2@(Collision h2 ls2)
-        | h1 == h2  = Collision h1 (updateOrConcatWithKey f ls1 ls2)
+        | h1 == h2  = Collision h1 (HM.updateOrConcatWithKey (\k a b -> let !v = f k a b in (# v #)) ls1 ls2)
         | otherwise = goDifferentHash s h1 h2 t1 t2
     -- branch vs. branch
     go s (BitmapIndexed b1 ary1) (BitmapIndexed b2 ary2) =
         let b'   = b1 .|. b2
-            ary' = unionArrayBy (go (s+bitsPerSubkey)) b1 b2 ary1 ary2
-        in bitmapIndexedOrFull b' ary'
+            ary' = HM.unionArrayBy (go (s+bitsPerSubkey)) b1 b2 ary1 ary2
+        in HM.bitmapIndexedOrFull b' ary'
     go s (BitmapIndexed b1 ary1) (Full ary2) =
-        let ary' = unionArrayBy (go (s+bitsPerSubkey)) b1 fullNodeMask ary1 ary2
+        let ary' = HM.unionArrayBy (go (s+bitsPerSubkey)) b1 fullNodeMask ary1 ary2
         in Full ary'
     go s (Full ary1) (BitmapIndexed b2 ary2) =
-        let ary' = unionArrayBy (go (s+bitsPerSubkey)) fullNodeMask b2 ary1 ary2
+        let ary' = HM.unionArrayBy (go (s+bitsPerSubkey)) fullNodeMask b2 ary1 ary2
         in Full ary'
     go s (Full ary1) (Full ary2) =
-        let ary' = unionArrayBy (go (s+bitsPerSubkey)) fullNodeMask fullNodeMask
+        let ary' = HM.unionArrayBy (go (s+bitsPerSubkey)) fullNodeMask fullNodeMask
                    ary1 ary2
         in Full ary'
     -- leaf vs. branch
     go s (BitmapIndexed b1 ary1) t2
         | b1 .&. m2 == 0 = let ary' = A.insert ary1 i t2
                                b'   = b1 .|. m2
-                           in bitmapIndexedOrFull b' ary'
+                           in HM.bitmapIndexedOrFull b' ary'
         | otherwise      = let ary' = A.updateWith' ary1 i $ \st1 ->
                                    go (s+bitsPerSubkey) st1 t2
                            in BitmapIndexed b1 ary'
@@ -485,7 +497,7 @@
     go s t1 (BitmapIndexed b2 ary2)
         | b2 .&. m1 == 0 = let ary' = A.insert ary2 i $! t1
                                b'   = b2 .|. m1
-                           in bitmapIndexedOrFull b' ary'
+                           in HM.bitmapIndexedOrFull b' ary'
         | otherwise      = let ary' = A.updateWith' ary2 i $ \st2 ->
                                    go (s+bitsPerSubkey) t1 st2
                            in BitmapIndexed b2 ary'
@@ -496,12 +508,12 @@
     go s (Full ary1) t2 =
         let h2   = leafHashCode t2
             i    = index h2 s
-            ary' = update32With' ary1 i $ \st1 -> go (s+bitsPerSubkey) st1 t2
+            ary' = HM.update32With' ary1 i $ \st1 -> go (s+bitsPerSubkey) st1 t2
         in Full ary'
     go s t1 (Full ary2) =
         let h1   = leafHashCode t1
             i    = index h1 s
-            ary' = update32With' ary2 i $ \st2 -> go (s+bitsPerSubkey) t1 st2
+            ary' = HM.update32With' ary2 i $ \st2 -> go (s+bitsPerSubkey) t1 st2
         in Full ary'
 
     leafHashCode (Leaf h _) = h
@@ -544,11 +556,11 @@
 -- | /O(n)/ Transform this map by applying a function to every value
 --   and retaining only some of them.
 mapMaybeWithKey :: (k -> v1 -> Maybe v2) -> HashMap k v1 -> HashMap k v2
-mapMaybeWithKey f = filterMapAux onLeaf onColl
+mapMaybeWithKey f = HM.filterMapAux onLeaf onColl
   where onLeaf (Leaf h (L k v)) | Just v' <- f k v = Just (leaf h k v')
         onLeaf _ = Nothing
 
-        onColl (L k v) | Just v' <- f k v = Just (L k v')
+        onColl (L k v) | Just !v' <- f k v = Just (L k v')
                        | otherwise = Nothing
 {-# INLINE mapMaybeWithKey #-}
 
@@ -592,11 +604,11 @@
 -- If it returns 'Nothing', the element is discarded (proper set difference). If
 -- it returns (@'Just' y@), the element is updated with a new value @y@.
 differenceWith :: (Eq k, Hashable k) => (v -> w -> Maybe v) -> HashMap k v -> HashMap k w -> HashMap k v
-differenceWith f a b = foldlWithKey' go empty a
+differenceWith f a b = HM.foldlWithKey' go HM.empty a
   where
     go m k v = case HM.lookup k b of
-                 Nothing -> insert k v m
-                 Just w  -> maybe m (\y -> insert k y m) (f v w)
+                 Nothing -> v `seq` HM.unsafeInsert k v m
+                 Just w  -> maybe m (\ !y -> HM.unsafeInsert k y m) (f v w)
 {-# INLINABLE differenceWith #-}
 
 -- | /O(n+m)/ Intersection of two maps. If a key occurs in both maps
@@ -604,10 +616,10 @@
 -- maps.
 intersectionWith :: (Eq k, Hashable k) => (v1 -> v2 -> v3) -> HashMap k v1
                  -> HashMap k v2 -> HashMap k v3
-intersectionWith f a b = foldlWithKey' go empty a
+intersectionWith f a b = HM.foldlWithKey' go HM.empty a
   where
     go m k v = case HM.lookup k b of
-                 Just w -> insert k (f v w) m
+                 Just w -> let !x = f v w in HM.unsafeInsert k x m
                  _      -> m
 {-# INLINABLE intersectionWith #-}
 
@@ -616,10 +628,10 @@
 -- maps.
 intersectionWithKey :: (Eq k, Hashable k) => (k -> v1 -> v2 -> v3)
                     -> HashMap k v1 -> HashMap k v2 -> HashMap k v3
-intersectionWithKey f a b = foldlWithKey' go empty a
+intersectionWithKey f a b = HM.foldlWithKey' go HM.empty a
   where
     go m k v = case HM.lookup k b of
-                 Just w -> insert k (f k v w) m
+                 Just w -> let !x = f k v w in HM.unsafeInsert k x m
                  _      -> m
 {-# INLINABLE intersectionWithKey #-}
 
@@ -630,7 +642,7 @@
 -- list contains duplicate mappings, the later mappings take
 -- precedence.
 fromList :: (Eq k, Hashable k) => [(k, v)] -> HashMap k v
-fromList = List.foldl' (\ m (k, !v) -> HM.unsafeInsert k v m) empty
+fromList = List.foldl' (\ m (k, !v) -> HM.unsafeInsert k v m) HM.empty
 {-# INLINABLE fromList #-}
 
 -- | /O(n*log n)/ Construct a map from a list of elements.  Uses
@@ -664,7 +676,7 @@
 -- > fromListWith f [(k, a), (k, b), (k, c), (k, d)]
 -- > = fromList [(k, f d (f c (f b a)))]
 fromListWith :: (Eq k, Hashable k) => (v -> v -> v) -> [(k, v)] -> HashMap k v
-fromListWith f = List.foldl' (\ m (k, v) -> unsafeInsertWith f k v m) empty
+fromListWith f = List.foldl' (\ m (k, v) -> unsafeInsertWith f k v m) HM.empty
 {-# INLINE fromListWith #-}
 
 -- | /O(n*log n)/ Construct a map from a list of elements.  Uses
@@ -694,7 +706,7 @@
 --
 -- @since 0.2.11
 fromListWithKey :: (Eq k, Hashable k) => (k -> v -> v -> v) -> [(k, v)] -> HashMap k v
-fromListWithKey f = List.foldl' (\ m (k, v) -> unsafeInsertWithKey f k v m) empty
+fromListWithKey f = List.foldl' (\ m (k, v) -> unsafeInsertWithKey f k v m) HM.empty
 {-# INLINE fromListWithKey #-}
 
 ------------------------------------------------------------------------
@@ -734,7 +746,7 @@
             -- Not found, append to the end.
             mary <- A.new_ (n + 1)
             A.copy ary 0 mary 0 n
-            let !l = v `seq` (L k v)
+            let !l = v `seq` L k v
             A.write mary n l
             return mary
         | otherwise = case A.index ary i of
diff --git a/Data/HashSet/Internal.hs b/Data/HashSet/Internal.hs
--- a/Data/HashSet/Internal.hs
+++ b/Data/HashSet/Internal.hs
@@ -214,8 +214,7 @@
 instance (Eq a, Hashable a, Read a) => Read (HashSet a) where
     readPrec = parens $ prec 10 $ do
       Ident "fromList" <- lexP
-      xs <- readPrec
-      return (fromList xs)
+      fromList <$> readPrec
 
     readListPrec = readListPrecDefault
 
@@ -442,7 +441,7 @@
 -- | /O(n)/ Return a list of this set's elements.  The list is
 -- produced lazily.
 toList :: HashSet a -> [a]
-toList t = Exts.build (\ c z -> foldrWithKey ((const .) c) z (asMap t))
+toList t = Exts.build (\ c z -> foldrWithKey (const . c) z (asMap t))
 {-# INLINE toList #-}
 
 -- | /O(n*min(W, n))/ Construct a set from a list of elements.
diff --git a/benchmarks/Benchmarks.hs b/benchmarks/Benchmarks.hs
--- a/benchmarks/Benchmarks.hs
+++ b/benchmarks/Benchmarks.hs
@@ -314,7 +314,10 @@
             ]
 
             -- Combine
-          , bench "union" $ whnf (HM.union hmi) hmi2
+          , bgroup "union" 
+            [ bench "Int" $ whnf (HM.union hmi) hmi2
+            , bench "ByteString" $ whnf (HM.union hmbs) hmbsSubset
+            ]
 
             -- Transformations
           , bench "map" $ whnf (HM.map (\ v -> v + 1)) hmi
diff --git a/tests/Regressions.hs b/tests/Regressions.hs
--- a/tests/Regressions.hs
+++ b/tests/Regressions.hs
@@ -1,5 +1,7 @@
+{-# LANGUAGE CPP                 #-}
 {-# LANGUAGE MagicHash           #-}
 {-# LANGUAGE ScopedTypeVariables #-}
+{-# LANGUAGE TypeApplications    #-}
 {-# LANGUAGE UnboxedTuples       #-}
 module Regressions (tests) where
 
@@ -10,6 +12,7 @@
 import Data.Maybe            (isJust, isNothing)
 import GHC.Exts              (touch#)
 import GHC.IO                (IO (..))
+import Numeric.Natural       (Natural)
 import System.Mem            (performGC)
 import System.Mem.Weak       (deRefWeak, mkWeakPtr)
 import System.Random         (randomIO)
@@ -22,6 +25,13 @@
 import qualified Data.HashMap.Lazy   as HML
 import qualified Data.HashMap.Strict as HMS
 
+#if MIN_VERSION_base(4,12,0)
+-- nothunks requires base >= 4.12
+#define HAVE_NOTHUNKS
+import qualified Data.Foldable  as Foldable
+import           NoThunks.Class (noThunksInValues)
+#endif
+
 issue32 :: Assertion
 issue32 = assert $ isJust $ HMS.lookup 7 m'
   where
@@ -125,6 +135,120 @@
   assert $ isNothing res
 
 ------------------------------------------------------------------------
+-- Issue #379
+
+#ifdef HAVE_NOTHUNKS
+
+issue379Union :: Assertion
+issue379Union = do
+  let m0 = HMS.fromList [(KC 1, ()), (KC 2, ())]
+  let m1 = HMS.fromList [(KC 2, ()), (KC 3, ())]
+  let u = m0 `HMS.union` m1
+  mThunkInfo <- noThunksInValues mempty (Foldable.toList u)
+  assert $ isNothing mThunkInfo
+
+issue379StrictUnionWith :: Assertion
+issue379StrictUnionWith = do
+  let m0 = HMS.fromList [(KC 1, 10), (KC 2, 20 :: Int)]
+  let m1 = HMS.fromList [(KC 2, 20), (KC 3, 30)]
+  let u = HMS.unionWith (+) m0 m1
+  mThunkInfo <- noThunksInValues mempty (Foldable.toList u)
+  assert $ isNothing mThunkInfo
+
+issue379StrictUnionWithKey :: Assertion
+issue379StrictUnionWithKey = do
+  let m0 = HMS.fromList [(KC 1, 10), (KC 2, 20 :: Int)]
+  let m1 = HMS.fromList [(KC 2, 20), (KC 3, 30)]
+  let u = HMS.unionWithKey (\(KC i) v0 v1 -> i + v0 + v1) m0 m1
+  mThunkInfo <- noThunksInValues mempty (Foldable.toList u)
+  assert $ isNothing mThunkInfo
+
+#endif
+
+-- Another key type that always collides.
+--
+-- Note (sjakobi): The KC newtype of Int somehow can't be used to demonstrate
+-- the space leak in issue379LazyUnionWith. This type does the trick.
+newtype SC = SC String
+  deriving (Eq, Ord, Show)
+instance Hashable SC where
+  hashWithSalt salt _ = salt
+
+issue379LazyUnionWith :: Assertion
+issue379LazyUnionWith = do
+  i :: Int <- randomIO
+  let k = SC (show i)
+  weakK <- mkWeakPtr k Nothing -- add the ability to test whether k is alive
+  let f :: Int -> Int
+      f x = error ("Should not be evaluated " ++ show x)
+  let m = HML.fromList [(SC "1", f 1), (SC "2", f 2), (k, f 3)]
+  let u = HML.unionWith (+) m m
+  Just v <- evaluate $ HML.lookup k u
+  performGC
+  res <- deRefWeak weakK -- gives Just if k is still alive
+  touch v -- makes sure that we didn't GC away the combined value
+  assert $ isNothing res
+
+------------------------------------------------------------------------
+-- Issue #381
+
+#ifdef HAVE_NOTHUNKS
+
+issue381mapMaybe :: Assertion
+issue381mapMaybe = do
+  let m0 = HMS.fromList [(KC 1, 10), (KC 2, 20 :: Int)]
+  let m1 = HMS.mapMaybe (Just . (+ 1)) m0
+  mThunkInfo <- noThunksInValues mempty (Foldable.toList m1)
+  assert $ isNothing mThunkInfo
+
+issue381mapMaybeWithKey :: Assertion
+issue381mapMaybeWithKey = do
+  let m0 = HMS.fromList [(KC 1, 10), (KC 2, 20 :: Int)]
+  let m1 = HMS.mapMaybeWithKey (\(KC k) v -> Just (k + v)) m0
+  mThunkInfo <- noThunksInValues mempty (Foldable.toList m1)
+  assert $ isNothing mThunkInfo
+
+#endif
+
+------------------------------------------------------------------------
+-- Issue #382
+
+issue382 :: Assertion
+issue382 = do
+  i :: Int <- randomIO
+  let k = SC (show i)
+  weakK <- mkWeakPtr k Nothing -- add the ability to test whether k is alive
+  let f :: Int -> Int -> Int
+      f x = error ("Should not be evaluated " ++ show x)
+  let m = HML.fromListWith f [(k, 1), (k, 2)]
+  Just v <- evaluate $ HML.lookup k m
+  performGC
+  res <- deRefWeak weakK -- gives Just if k is still alive
+  touch v -- makes sure that we didn't GC away the combined value
+  assert $ isNothing res
+
+------------------------------------------------------------------------
+-- Issue #383
+
+#ifdef HAVE_NOTHUNKS
+
+-- Custom Functor to prevent interference from alterF rules
+newtype MyIdentity a = MyIdentity a
+instance Functor MyIdentity where
+  fmap f (MyIdentity x) = MyIdentity (f x)
+
+issue383 :: Assertion
+issue383 = do
+  i :: Int <- randomIO
+  let f Nothing = MyIdentity (Just (fromIntegral @Int @Natural (abs i)))
+      f Just{}  = MyIdentity (error "Impossible")
+  let (MyIdentity m) = HMS.alterF f () mempty
+  mThunkInfo <- noThunksInValues mempty (Foldable.toList m)
+  assert $ isNothing mThunkInfo
+
+#endif
+
+------------------------------------------------------------------------
 -- * Test list
 
 tests :: TestTree
@@ -135,4 +259,22 @@
     , testProperty "issue39b" propEqAfterDelete
     , testCase "issue254 lazy" issue254Lazy
     , testCase "issue254 strict" issue254Strict
+    , testGroup "issue379"
+          [ testCase "Lazy.unionWith" issue379LazyUnionWith
+#ifdef HAVE_NOTHUNKS
+          , testCase "union" issue379Union
+          , testCase "Strict.unionWith" issue379StrictUnionWith
+          , testCase "Strict.unionWithKey" issue379StrictUnionWithKey
+#endif
+          ]
+#ifdef HAVE_NOTHUNKS
+    , testGroup "issue381"
+          [ testCase "mapMaybe" issue381mapMaybe
+          , testCase "mapMaybeWithKey" issue381mapMaybeWithKey
+          ]
+#endif
+    , testCase "issue382" issue382
+#ifdef HAVE_NOTHUNKS
+    , testCase "issue383" issue383
+#endif
     ]
diff --git a/tests/Strictness.hs b/tests/Strictness.hs
--- a/tests/Strictness.hs
+++ b/tests/Strictness.hs
@@ -48,7 +48,7 @@
 pSingletonKeyStrict v = isBottom $ HM.singleton (bottom :: Key) v
 
 pSingletonValueStrict :: Key -> Bool
-pSingletonValueStrict k = isBottom $ (HM.singleton k (bottom :: Int))
+pSingletonValueStrict k = isBottom $ HM.singleton k (bottom :: Int)
 
 pLookupDefaultKeyStrict :: Int -> HashMap Key Int -> Bool
 pLookupDefaultKeyStrict def m = isBottom $ HM.lookupDefault def bottom m
diff --git a/unordered-containers.cabal b/unordered-containers.cabal
--- a/unordered-containers.cabal
+++ b/unordered-containers.cabal
@@ -1,5 +1,5 @@
 name:           unordered-containers
-version:        0.2.17.0
+version:        0.2.18.0
 synopsis:       Efficient hashing-based container types
 description:
   Efficient hashing-based container types.  The containers have been
@@ -69,6 +69,11 @@
 
   ghc-options: -Wall -O2 -fwarn-tabs -ferror-spans
 
+  -- For dumping the generated code:
+  -- ghc-options: -ddump-simpl -ddump-stg-final -ddump-cmm -ddump-asm -ddump-to-file
+  -- ghc-options: -dsuppress-coercions -dsuppress-unfoldings -dsuppress-module-prefixes
+  -- ghc-options: -dsuppress-uniques -dsuppress-timestamps
+
   if flag(debug)
     cpp-options: -DASSERTS
 
@@ -97,6 +102,10 @@
     tasty-hunit >= 0.10.0.3,
     tasty-quickcheck >= 0.10.1.2,
     unordered-containers
+
+  if impl(ghc >= 8.6)
+    build-depends:
+      nothunks >= 0.1.3
 
   default-language: Haskell2010
   ghc-options: -Wall
