diff --git a/.hlint.yaml b/.hlint.yaml
new file mode 100644
--- /dev/null
+++ b/.hlint.yaml
@@ -0,0 +1,2 @@
+- ignore: {name: Unused LANGUAGE pragma}
+- ignore: {name: Avoid lambda}
diff --git a/.travis.yml b/.travis.yml
deleted file mode 100644
--- a/.travis.yml
+++ /dev/null
@@ -1,161 +0,0 @@
-# This Travis job script has been generated by a script via
-#
-#   runghc make_travis_yml_2.hs '-o' '.travis.yml' '--ghc-head' '--jobs=2' '--irc-channel=irc.freenode.org#haskell-lens' '--no-no-tests-no-bench' '--no-install-dependencies' 'cabal.project'
-#
-# For more information, see https://github.com/hvr/multi-ghc-travis
-#
-language: c
-sudo: false
-
-git:
-  submodules: false  # whether to recursively clone submodules
-
-notifications:
-  irc:
-    channels:
-      - "irc.freenode.org#haskell-lens"
-    skip_join: true
-    template:
-      - "\x0313reflection\x03/\x0306%{branch}\x03 \x0314%{commit}\x03 %{build_url} %{message}"
-
-cache:
-  directories:
-    - $HOME/.cabal/packages
-    - $HOME/.cabal/store
-
-before_cache:
-  - rm -fv $HOME/.cabal/packages/hackage.haskell.org/build-reports.log
-  # remove files that are regenerated by 'cabal update'
-  - rm -fv $HOME/.cabal/packages/hackage.haskell.org/00-index.*
-  - rm -fv $HOME/.cabal/packages/hackage.haskell.org/*.json
-  - rm -fv $HOME/.cabal/packages/hackage.haskell.org/01-index.cache
-  - rm -fv $HOME/.cabal/packages/hackage.haskell.org/01-index.tar
-  - rm -fv $HOME/.cabal/packages/hackage.haskell.org/01-index.tar.idx
-
-  - rm -rfv $HOME/.cabal/packages/head.hackage
-
-matrix:
-  include:
-    - compiler: "ghc-8.6.1"
-      env: GHCHEAD=true
-      addons: {apt: {packages: [ghc-ppa-tools,cabal-install-head,ghc-8.6.1], sources: [hvr-ghc]}}
-    - compiler: "ghc-8.4.3"
-    # env: TEST=--disable-tests BENCH=--disable-benchmarks
-      addons: {apt: {packages: [ghc-ppa-tools,cabal-install-2.2,ghc-8.4.3], sources: [hvr-ghc]}}
-    - compiler: "ghc-8.2.2"
-    # env: TEST=--disable-tests BENCH=--disable-benchmarks
-      addons: {apt: {packages: [ghc-ppa-tools,cabal-install-2.2,ghc-8.2.2], sources: [hvr-ghc]}}
-    - compiler: "ghc-8.0.2"
-    # env: TEST=--disable-tests BENCH=--disable-benchmarks
-      addons: {apt: {packages: [ghc-ppa-tools,cabal-install-2.2,ghc-8.0.2], sources: [hvr-ghc]}}
-    - compiler: "ghc-7.10.3"
-    # env: TEST=--disable-tests BENCH=--disable-benchmarks
-      addons: {apt: {packages: [ghc-ppa-tools,cabal-install-2.2,ghc-7.10.3], sources: [hvr-ghc]}}
-    - compiler: "ghc-7.8.4"
-    # env: TEST=--disable-tests BENCH=--disable-benchmarks
-      addons: {apt: {packages: [ghc-ppa-tools,cabal-install-2.2,ghc-7.8.4], sources: [hvr-ghc]}}
-    - compiler: "ghc-7.6.3"
-    # env: TEST=--disable-tests BENCH=--disable-benchmarks
-      addons: {apt: {packages: [ghc-ppa-tools,cabal-install-2.2,ghc-7.6.3], sources: [hvr-ghc]}}
-    - compiler: "ghc-7.4.2"
-    # env: TEST=--disable-tests BENCH=--disable-benchmarks
-      addons: {apt: {packages: [ghc-ppa-tools,cabal-install-2.2,ghc-7.4.2], sources: [hvr-ghc]}}
-    - compiler: "ghc-7.2.2"
-    # env: TEST=--disable-tests BENCH=--disable-benchmarks
-      addons: {apt: {packages: [ghc-ppa-tools,cabal-install-2.2,ghc-7.2.2], sources: [hvr-ghc]}}
-    - compiler: "ghc-7.0.4"
-    # env: TEST=--disable-tests BENCH=--disable-benchmarks
-      addons: {apt: {packages: [ghc-ppa-tools,cabal-install-2.2,ghc-7.0.4], sources: [hvr-ghc]}}
-    - compiler: "ghc-head"
-      env: GHCHEAD=true
-      addons: {apt: {packages: [ghc-ppa-tools,cabal-install-head,ghc-head], sources: [hvr-ghc]}}
-
-  allow_failures:
-    - compiler: "ghc-head"
-    - compiler: "ghc-7.0.4"
-    - compiler: "ghc-7.2.2"
-    - compiler: "ghc-8.6.1"
-
-before_install:
-  - HC=${CC}
-  - HCPKG=${HC/ghc/ghc-pkg}
-  - unset CC
-  - ROOTDIR=$(pwd)
-  - mkdir -p $HOME/.local/bin
-  - "PATH=/opt/ghc/bin:/opt/ghc-ppa-tools/bin:$HOME/local/bin:$PATH"
-  - HCNUMVER=$(( $(${HC} --numeric-version|sed -E 's/([0-9]+)\.([0-9]+)\.([0-9]+).*/\1 * 10000 + \2 * 100 + \3/') ))
-  - echo $HCNUMVER
-
-install:
-  - cabal --version
-  - echo "$(${HC} --version) [$(${HC} --print-project-git-commit-id 2> /dev/null || echo '?')]"
-  - BENCH=${BENCH---enable-benchmarks}
-  - TEST=${TEST---enable-tests}
-  - HADDOCK=${HADDOCK-true}
-  - UNCONSTRAINED=${UNCONSTRAINED-true}
-  - NOINSTALLEDCONSTRAINTS=${NOINSTALLEDCONSTRAINTS-false}
-  - GHCHEAD=${GHCHEAD-false}
-  - travis_retry cabal update -v
-  - "sed -i.bak 's/^jobs:/-- jobs:/' ${HOME}/.cabal/config"
-  - rm -fv cabal.project cabal.project.local
-  - "sed -i.bak 's/^-- jobs:.*/jobs: 2/' ${HOME}/.cabal/config"
-  # Overlay Hackage Package Index for GHC HEAD: https://github.com/hvr/head.hackage
-  - |
-    if $GHCHEAD; then
-      sed -i 's/-- allow-newer: .*/allow-newer: *:base/' ${HOME}/.cabal/config
-      for pkg in $($HCPKG list --simple-output); do pkg=$(echo $pkg | sed 's/-[^-]*$//'); sed -i "s/allow-newer: /allow-newer: *:$pkg, /" ${HOME}/.cabal/config; done
-
-      echo 'repository head.hackage'                                                        >> ${HOME}/.cabal/config
-      echo '   url: http://head.hackage.haskell.org/'                                       >> ${HOME}/.cabal/config
-      echo '   secure: True'                                                                >> ${HOME}/.cabal/config
-      echo '   root-keys: 07c59cb65787dedfaef5bd5f987ceb5f7e5ebf88b904bbd4c5cbdeb2ff71b740' >> ${HOME}/.cabal/config
-      echo '              2e8555dde16ebd8df076f1a8ef13b8f14c66bad8eafefd7d9e37d0ed711821fb' >> ${HOME}/.cabal/config
-      echo '              8f79fd2389ab2967354407ec852cbe73f2e8635793ac446d09461ffb99527f6e' >> ${HOME}/.cabal/config
-      echo '   key-threshold: 3'                                                            >> ${HOME}/.cabal.config
-
-      grep -Ev -- '^\s*--' ${HOME}/.cabal/config | grep -Ev '^\s*$'
-
-      cabal new-update head.hackage -v
-    fi
-  - grep -Ev -- '^\s*--' ${HOME}/.cabal/config | grep -Ev '^\s*$'
-  - "printf 'packages: \".\"\\n' > cabal.project"
-  - touch cabal.project.local
-  - "if ! $NOINSTALLEDCONSTRAINTS; then for pkg in $($HCPKG list --simple-output); do echo $pkg | sed 's/^/constraints: /' | sed 's/-[^-]*$/ installed/' >> cabal.project.local; done; fi"
-  - cat cabal.project || true
-  - cat cabal.project.local || true
-  - if [ -f "./configure.ac" ]; then
-      (cd "." && autoreconf -i);
-    fi
-  - rm -f cabal.project.freeze
-  - rm -rf .ghc.environment.* "."/dist
-  - DISTDIR=$(mktemp -d /tmp/dist-test.XXXX)
-
-# Here starts the actual work to be performed for the package under test;
-# any command which exits with a non-zero exit code causes the build to fail.
-script:
-  # test that source-distributions can be generated
-  - (cd "." && cabal sdist)
-  - mv "."/dist/reflection-*.tar.gz ${DISTDIR}/
-  - cd ${DISTDIR} || false
-  - find . -maxdepth 1 -name '*.tar.gz' -exec tar -xvf '{}' \;
-  - "printf 'packages: reflection-*/*.cabal\\n' > cabal.project"
-  - touch cabal.project.local
-  - "if ! $NOINSTALLEDCONSTRAINTS; then for pkg in $($HCPKG list --simple-output); do echo $pkg | sed 's/^/constraints: /' | sed 's/-[^-]*$/ installed/' >> cabal.project.local; done; fi"
-  - cat cabal.project || true
-  - cat cabal.project.local || true
-
-  # build & run tests, build benchmarks
-  - cabal new-build -w ${HC} ${TEST} ${BENCH} all
-
-  # cabal check
-  - (cd reflection-* && cabal check)
-
-  # haddock
-  - rm -rf ./dist-newstyle
-  - if $HADDOCK; then cabal new-haddock -w ${HC} ${TEST} ${BENCH} all; else echo "Skipping haddock generation";fi
-
-  # Build without installed constraints for packages in global-db
-  - if $UNCONSTRAINED; then rm -f cabal.project.local; echo cabal new-build -w ${HC} --disable-tests --disable-benchmarks all; else echo "Not building without installed constraints"; fi
-
-# REGENDATA ["-o",".travis.yml","--ghc-head","--jobs=2","--irc-channel=irc.freenode.org#haskell-lens","--no-no-tests-no-bench","--no-install-dependencies","cabal.project"]
-# EOF
diff --git a/CHANGELOG.markdown b/CHANGELOG.markdown
--- a/CHANGELOG.markdown
+++ b/CHANGELOG.markdown
@@ -1,3 +1,23 @@
+# 2.1.9 [2024.12.04]
+* Drop support for pre-8.0 versions of GHC.
+
+# 2.1.8 [2024.05.04]
+* Fix a memory leak in `reifyTypeable`.
+
+# 2.1.7 [2023.02.28]
+* When building with `base-4.18` (GHC 9.6) or later, implement `reifyNat` and
+  `reifySymbol` using the API provided by `GHC.TypeLits` instead of resorting
+  to `unsafeCoerce`.
+
+# 2.1.6 [2020.05.16]
+* Fix a bug in which `give` (and possibly `reify`, `reifyNat`, and
+  `reifySymbol`) could be unsoundly inlined by GHC 8.10 or older to produce
+  incorrect runtime results.
+
+# 2.1.5 [2019.08.27]
+* Fix a bug in which `reifyNat` would yield incorrect results for very large
+  `Integer`s on GHC 8.2 or later.
+
 # 2.1.4 [2018.07.02]
 * Allow building with GHC 8.6.
 
diff --git a/README.markdown b/README.markdown
--- a/README.markdown
+++ b/README.markdown
@@ -1,7 +1,7 @@
 reflection
 ==========
 
-[![Hackage](https://img.shields.io/hackage/v/reflection.svg)](https://hackage.haskell.org/package/reflection) [![Build Status](https://secure.travis-ci.org/ekmett/reflection.png?branch=master)](http://travis-ci.org/ekmett/reflection)
+[![Hackage](https://img.shields.io/hackage/v/reflection.svg)](https://hackage.haskell.org/package/reflection) [![Build Status](https://github.com/ekmett/reflection/workflows/Haskell-CI/badge.svg)](https://github.com/ekmett/reflection/actions?query=workflow%3AHaskell-CI)
 
 This package provides an implementation of the ideas presented in [Functional Pearl: Implicit Configurations](http://okmij.org/ftp/Haskell/tr-15-04.pdf) by Oleg Kiselyov and Chung-Chieh Shan. However, the API has been implemented in a much more efficient manner.
 
diff --git a/examples/Constraints.hs b/examples/Constraints.hs
--- a/examples/Constraints.hs
+++ b/examples/Constraints.hs
@@ -1,21 +1,24 @@
-{-# LANGUAGE Rank2Types, TypeFamilies, TypeOperators, ConstraintKinds, PolyKinds, FlexibleInstances, MultiParamTypeClasses, ScopedTypeVariables, FlexibleContexts, UndecidableInstances #-}
+{-# LANGUAGE Rank2Types, TypeFamilies, TypeOperators, ConstraintKinds, PolyKinds, FlexibleInstances, MultiParamTypeClasses, ScopedTypeVariables, FlexibleContexts, UndecidableInstances, CPP #-}
 module Constraints where
 
-import Control.Newtype        -- from newtype
-import Data.Constraint        -- from constraints
-import Data.Constraint.Unsafe -- from constraints
-import Data.Monoid            -- from base
-import Data.Proxy             -- from tagged
-import Data.Reflection        -- from reflection
+import Control.Newtype.Generics -- from newtype-generics
+import Data.Constraint          -- from constraints
+import Data.Constraint.Unsafe   -- from constraints
+import Data.Proxy               -- from tagged
+import Data.Reflection          -- from reflection
+#if !(MIN_VERSION_base(4,11,0))
+import Data.Semigroup           -- from semigroups
+#endif
 
--- | Values in our dynamically constructed monoid over 'a'
+-- | Values in our dynamically constructed monoid over @a@
 newtype Lift (p :: * -> Constraint) (a :: *) (s :: *) = Lift { lower :: a }
 
 class ReifiableConstraint p where
   data Def (p :: * -> Constraint) (a :: *)
   reifiedIns :: Reifies s (Def p a) :- p (Lift p a s)
 
-instance Newtype (Lift p a s) a where
+instance Newtype (Lift p a s) where
+  type O (Lift p a s) = a
   pack = Lift
   unpack = lower
 
@@ -42,8 +45,11 @@
   data Def Monoid a = Monoid { mappend_ :: a -> a -> a, mempty_ :: a }
   reifiedIns = Sub Dict
 
+instance Reifies s (Def Monoid a) => Semigroup (Lift Monoid a s) where
+  a <> b             = Lift $ mappend_ (reflect a) (lower a) (lower b)
+
 instance Reifies s (Def Monoid a) => Monoid (Lift Monoid a s) where
-  mappend a b        = Lift $ mappend_ (reflect a) (lower a) (lower b)
+  mappend            = (<>)
   mempty = a where a = Lift $ mempty_ (reflect a)
 
 data ClassProxy (p :: * -> Constraint) = ClassProxy
diff --git a/examples/FromJSON.hs b/examples/FromJSON.hs
--- a/examples/FromJSON.hs
+++ b/examples/FromJSON.hs
@@ -1,27 +1,30 @@
 -- Example of a dynamically generated FromJSON instance.
 --
--- Can be useful when one needs to use a function with a 
--- FromJSON constraint, but some detail about the 
+-- Can be useful when one needs to use a function with a
+-- FromJSON constraint, but some detail about the
 -- conversion from JSON is not known until runtime.
+{-# LANGUAGE CPP #-}
 {-# LANGUAGE Rank2Types #-}
 {-# LANGUAGE FlexibleContexts #-}
 {-# LANGUAGE UndecidableInstances #-}
 {-# LANGUAGE ScopedTypeVariables #-}
 {-# LANGUAGE OverloadedStrings #-}
 
-import Data.Reflection -- from reflection
-import Data.Monoid     -- from base
-import Data.Proxy      -- from tagged
-import Data.Text       -- from text
-import Data.Monoid  
-import Data.Aeson      -- from aeson     
+import Data.Aeson             -- from aeson
+import qualified Data.Aeson.Key as Key
 import Data.Aeson.Types (Parser)
+import Data.Proxy             -- from tagged
+import Data.Reflection        -- from reflection
+#if !(MIN_VERSION_base(4,11,0))
+import Data.Semigroup         -- from base
+#endif
+import Data.Text (Text, pack) -- from text
 
 import Control.Applicative
 
 -- These imports are only for constructing the example value
-import Control.Lens (preview)           -- from lens
-import Data.Aeson.Lens (_Value,_String) -- form lens-aeson
+import Lens.Micro.Aeson (_Value)   -- form microlens-aeson
+import Lens.Micro.Extras (preview) -- from microlens
 
 data Foo = Foo
     {
@@ -31,19 +34,20 @@
 
 fooParser :: Text -> Object -> Parser Foo
 fooParser prefix o = do
-    Foo <$> o .: (prefix <> "field1") <*> o .: (prefix <> "field2")
+    Foo <$> o .: (Key.fromText prefix <> "field1") <*> o .: (Key.fromText prefix <> "field2")
 
 -- A wrapper over Foo carrying a phantom type s
 newtype J a s = J { runJ :: a }
- 
--- If the phantom type s reifies the parsing function, we can 
--- use reflect to recover the function and implement 
+
+-- If the phantom type s reifies the parsing function, we can
+-- use reflect to recover the function and implement
 -- our FromJSON instance for J.
 instance Reifies s (Object -> Parser a) => FromJSON (J a s) where
     parseJSON (Object v) = J <$> reflect (Proxy :: Proxy s) v
- 
+    parseJSON _          = empty
+
 -- Convince the compiler that the phantom type in the proxy
--- supplied by reify is the same as the phantom type in J. 
+-- supplied by reify is the same as the phantom type in J.
 --
 -- Otherwise the FromJSON instance for J won't kick in.
 asProxyJ :: Proxy s -> J a s -> J a s
@@ -53,18 +57,18 @@
 exampleJSON = maybe Null id (preview _Value str)
   where
     str = "{ \"zzfield1\" : 5, \"zzfield2\" : 7 }"::Text
- 
+
 main :: IO ()
 main = do
     putStrLn "Enter prefix for the fields: "
     -- "zz" must be entered for the parse to succeed
     prefix <- fmap pack getLine
- 
+
     -- fromJSON uses the dynamically generated FromJSON instance
     let result = reify (fooParser prefix) $ \proxy ->
             -- We must eliminate the J newtype before returning
             -- because, thanks to parametricity,
             -- the phantom type cannot escape the callback.
-            runJ . asProxyJ proxy <$> fromJSON exampleJSON 
- 
+            runJ . asProxyJ proxy <$> fromJSON exampleJSON
+
     putStrLn (show (result :: Result Foo))
diff --git a/examples/Monoid.hs b/examples/Monoid.hs
--- a/examples/Monoid.hs
+++ b/examples/Monoid.hs
@@ -1,11 +1,13 @@
 {-# LANGUAGE CPP, Rank2Types, FlexibleContexts, UndecidableInstances #-}
 module Monoid where
 
+import Data.Proxy      -- from tagged
 import Data.Reflection -- from reflection
+#if !(MIN_VERSION_base(4,11,0))
 import Data.Semigroup  -- from base
-import Data.Proxy      -- from tagged
+#endif
 
--- | Values in our dynamically-constructed 'Monoid' over 'a'
+-- | Values in our dynamically-constructed 'Monoid' over @a@
 newtype M a s = M { runM :: a } deriving (Eq,Ord)
 
 -- | A dictionary describing a 'Monoid'
diff --git a/examples/ReaderLike.hs b/examples/ReaderLike.hs
--- a/examples/ReaderLike.hs
+++ b/examples/ReaderLike.hs
@@ -9,7 +9,6 @@
 
 import Data.Proxy
 import Data.Reflection
-import Control.Applicative
 import System.IO
 
 data MyConfig = MyConfig
@@ -37,7 +36,7 @@
   conf = reflect (Proxy :: Proxy p)
 
 run :: forall p. (Reifies p MyConfig) => Proxy p -> IO ()
-run p = do
+run _ = do
   d1 <- ask "Datum 1:" :: IO (Datum p)
   d2 <- ask "Datum 2:" :: IO (Datum p)
   -- look ma, no plumbing
@@ -66,4 +65,6 @@
     where conf = reflect (Proxy :: Proxy p)
   abs = undefined
   signum = undefined
+  (-) = undefined
+  negate = undefined
   fromInteger = Datum
diff --git a/examples/reflection-examples.cabal b/examples/reflection-examples.cabal
--- a/examples/reflection-examples.cabal
+++ b/examples/reflection-examples.cabal
@@ -15,16 +15,19 @@
                 2004 Oleg Kiselyov and Chung-chieh Shan
 build-type:     Simple
 cabal-version:  >= 1.10
-tested-with:   GHC == 7.0.4
-             , GHC == 7.2.2
-             , GHC == 7.4.2
-             , GHC == 7.6.3
-             , GHC == 7.8.4
-             , GHC == 7.10.3
-             , GHC == 8.0.2
+tested-with:   GHC == 8.0.2
              , GHC == 8.2.2
-             , GHC == 8.4.3
-             , GHC == 8.6.1
+             , GHC == 8.4.4
+             , GHC == 8.6.5
+             , GHC == 8.8.4
+             , GHC == 8.10.7
+             , GHC == 9.0.2
+             , GHC == 9.2.8
+             , GHC == 9.4.8
+             , GHC == 9.6.6
+             , GHC == 9.8.4
+             , GHC == 9.10.1
+             , GHC == 9.12.1
 
 flag examples
   default: True
@@ -34,24 +37,28 @@
     buildable:         False
   exposed-modules:     Constraints
                        Monoid
-  build-depends:       base >= 4.8 && < 5,
+  build-depends:       base >= 4.9 && < 5,
                        constraints,
-                       newtype,
+                       newtype-generics >= 0.5.3,
                        reflection,
+                       semigroups,
                        tagged
   hs-source-dirs:      .
   ghc-options:         -Wall
+  if impl(ghc >= 8.6)
+    ghc-options:       -Wno-star-is-type
   default-language:    Haskell2010
 
 executable reflection-from-json
   if !flag(examples)
     buildable:         False
   main-is:             FromJSON.hs
-  build-depends:       aeson,
-                       base >= 4.8 && < 5,
-                       lens,
-                       lens-aeson,
+  build-depends:       aeson >= 2 && < 2.3,
+                       base >= 4.9 && < 5,
+                       microlens,
+                       microlens-aeson >= 2.5.1,
                        reflection,
+                       semigroups,
                        tagged,
                        text
   hs-source-dirs:      .
@@ -62,10 +69,12 @@
   if !flag(examples)
     buildable:         False
   main-is:             ReaderLike.hs
-  build-depends:       base >= 4.8 && < 5,
+  build-depends:       base >= 4.9 && < 5,
                        reflection,
                        tagged
   hs-source-dirs:      .
   ghc-options:         -Wall -threaded -rtsopts
+  if impl(ghc >= 8.6)
+    ghc-options:       -Wno-star-is-type
   default-language:    Haskell2010
 
diff --git a/fast/Data/Reflection.hs b/fast/Data/Reflection.hs
--- a/fast/Data/Reflection.hs
+++ b/fast/Data/Reflection.hs
@@ -1,4 +1,5 @@
 {-# LANGUAGE CPP #-}
+{-# LANGUAGE DataKinds #-}
 {-# LANGUAGE EmptyDataDecls #-}
 {-# LANGUAGE FlexibleContexts #-}
 {-# LANGUAGE FlexibleInstances #-}
@@ -6,36 +7,23 @@
 {-# LANGUAGE KindSignatures #-}
 {-# LANGUAGE MultiParamTypeClasses #-}
 {-# LANGUAGE PatternGuards #-}
+{-# LANGUAGE PolyKinds #-}
 {-# LANGUAGE Rank2Types #-}
 {-# LANGUAGE ScopedTypeVariables #-}
-#if __GLASGOW_HASKELL__ >= 706
-{-# LANGUAGE DataKinds #-}
-{-# LANGUAGE PolyKinds #-}
 {-# LANGUAGE TypeOperators #-}
-#define USE_TYPE_LITS 1
-#endif
 #ifdef MIN_VERSION_template_haskell
-# if __GLASGOW_HASKELL__ >= 800
 -- TH-subset that works with stage1 & unregisterised GHCs
 {-# LANGUAGE TemplateHaskellQuotes #-}
-# else
-{-# LANGUAGE TemplateHaskell #-}
-# endif
 #endif
 
 {-# LANGUAGE TypeFamilies #-}
-{-# LANGUAGE DeriveDataTypeable #-}
 {-# LANGUAGE UndecidableInstances #-}
 
 {-# OPTIONS_GHC -fno-cse #-}
 {-# OPTIONS_GHC -fno-full-laziness #-}
 {-# OPTIONS_GHC -fno-float-in #-}
-{-# OPTIONS_GHC -fno-warn-orphans #-}
-{-# OPTIONS_GHC -fno-warn-unused-binds #-}
-
-#ifndef MIN_VERSION_base
-#define MIN_VERSION_base(x,y,z) 1
-#endif
+{-# OPTIONS_GHC -Wno-orphans #-}
+{-# OPTIONS_GHC -Wno-unused-binds #-}
 
 ----------------------------------------------------------------------------
 -- |
@@ -77,10 +65,8 @@
     -- * Reflection
       Reifies(..)
     , reify
-#if __GLASGOW_HASKELL__ >= 708
     , reifyNat
     , reifySymbol
-#endif
     , reifyTypeable
     -- * Given
     , Given(..)
@@ -108,31 +94,26 @@
     ) where
 
 import Control.Applicative
+import Control.Exception
 
 #ifdef MIN_VERSION_template_haskell
 import Control.Monad
 #endif
 
 import Data.Bits
-
-#if __GLASGOW_HASKELL__ < 710
-import Data.Foldable
-#endif
-
-import Data.Semigroup as Sem
+import Data.Coerce (Coercible, coerce)
 import Data.Proxy
-
-#if __GLASGOW_HASKELL__ < 710
-import Data.Traversable
-#endif
-
+import Data.Semigroup as Sem
 import Data.Typeable
 import Data.Word
 import Foreign.Ptr
 import Foreign.StablePtr
 
-#if (__GLASGOW_HASKELL__ >= 707) || (defined(MIN_VERSION_template_haskell) && USE_TYPE_LITS)
 import GHC.TypeLits
+#if MIN_VERSION_base(4,10,0)
+import qualified Numeric.Natural as Numeric (Natural)
+#else
+import Control.Exception (ArithException(..), throw)
 #endif
 
 #ifdef __HUGS__
@@ -149,10 +130,21 @@
 import Unsafe.Coerce
 #endif
 
-#ifdef HLINT
-{-# ANN module "HLint: ignore Avoid lambda" #-}
+#if MIN_VERSION_base(4,18,0)
+import qualified GHC.TypeNats as TN
 #endif
 
+-- Due to https://gitlab.haskell.org/ghc/ghc/issues/16893, inlining
+-- unsafeCoerce too aggressively can cause optimization to become unsound on
+-- old versions of GHC. As a workaround, we mark unsafeCoerce-using definitions
+-- as NOINLINE where necessary.
+-- See https://github.com/ekmett/reflection/issues/47.
+#if __GLASGOW_HASKELL__ >= 811
+# define INLINE_UNSAFE_COERCE INLINE
+#else
+# define INLINE_UNSAFE_COERCE NOINLINE
+#endif
+
 ------------------------------------------------------------------------------
 -- Reifies
 ------------------------------------------------------------------------------
@@ -167,28 +159,27 @@
 -- | Reify a value at the type level, to be recovered with 'reflect'.
 reify :: forall a r. a -> (forall (s :: *). Reifies s a => Proxy s -> r) -> r
 reify a k = unsafeCoerce (Magic k :: Magic a r) (const a) Proxy
-{-# INLINE reify #-}
+{-# INLINE_UNSAFE_COERCE reify #-}
 
-#if __GLASGOW_HASKELL__ >= 707
 instance KnownNat n => Reifies n Integer where
   reflect = natVal
 
 instance KnownSymbol n => Reifies n String where
   reflect = symbolVal
-#endif
 
-#if __GLASGOW_HASKELL__ >= 708
-
 --------------------------------------------------------------------------------
 -- KnownNat
 --------------------------------------------------------------------------------
 
-newtype MagicNat r = MagicNat (forall (n :: Nat). KnownNat n => Proxy n -> r)
-
 -- | This upgraded version of 'reify' can be used to generate a 'KnownNat' suitable for use with other APIs.
 --
+-- Attemping to pass a negative 'Integer' as an argument will result in an
+-- 'Underflow' exception.
+--
 -- /Available only on GHC 7.8+/
 --
+-- >>> import GHC.TypeLits
+--
 -- >>> reifyNat 4 natVal
 -- 4
 --
@@ -196,28 +187,59 @@
 -- 4
 
 reifyNat :: forall r. Integer -> (forall (n :: Nat). KnownNat n => Proxy n -> r) -> r
-reifyNat n k = unsafeCoerce (MagicNat k :: MagicNat r) n Proxy
+#if MIN_VERSION_base(4,18,0)
+-- With base-4.18 or later, we can use the API in GHC.TypeNats to define this
+-- function directly.
+reifyNat n k = TN.withSomeSNat (fromInteger n :: Numeric.Natural) $
+               \(sn :: (SNat n)) -> TN.withKnownNat sn $ k (Proxy :: Proxy n)
+{-# INLINE reifyNat #-}
+#else
+-- On older versions of base, we resort to unsafeCoerce.
+reifyNat n k = unsafeCoerce (MagicNat k :: MagicNat r)
+# if MIN_VERSION_base(4,10,0)
+                             -- Starting with base-4.10, the internal
+                             -- representation of KnownNat changed from Integer
+                             -- to Natural, so make sure to perform the same
+                             -- conversion before unsafeCoercing.
+                             (fromInteger n :: Numeric.Natural)
+# else
+                             (if n < 0 then throw Underflow else n)
+# endif
+                             Proxy
+{-# INLINE_UNSAFE_COERCE reifyNat #-}
 
+newtype MagicNat r = MagicNat (forall (n :: Nat). KnownNat n => Proxy n -> r)
+#endif
+
 --------------------------------------------------------------------------------
 -- KnownSymbol
 --------------------------------------------------------------------------------
 
-newtype MagicSymbol r = MagicSymbol (forall (n :: Symbol). KnownSymbol n => Proxy n -> r)
-
 -- | This upgraded version of 'reify' can be used to generate a 'KnownSymbol' suitable for use with other APIs.
 --
 -- /Available only on GHC 7.8+/
 --
+-- >>> import GHC.TypeLits
+--
 -- >>> reifySymbol "hello" symbolVal
 -- "hello"
 --
 -- >>> reifySymbol "hello" reflect
 -- "hello"
 reifySymbol :: forall r. String -> (forall (n :: Symbol). KnownSymbol n => Proxy n -> r) -> r
+#if MIN_VERSION_base(4,18,0)
+-- With base-4.18 or later, we can use the API in GHC.TypeNats to define this
+-- function directly.
+reifySymbol s k = withSomeSSymbol s $ \(ss :: SSymbol s) -> withKnownSymbol ss (k (Proxy :: Proxy s))
+{-# INLINE reifySymbol #-}
+#else
+-- On older versions of base, we resort to unsafeCoerce.
 reifySymbol n k = unsafeCoerce (MagicSymbol k :: MagicSymbol r) n Proxy
-
+{-# INLINE_UNSAFE_COERCE reifySymbol #-}
 #endif
 
+newtype MagicSymbol r = MagicSymbol (forall (n :: Symbol). KnownSymbol n => Proxy n -> r)
+
 ------------------------------------------------------------------------------
 -- Given
 ------------------------------------------------------------------------------
@@ -238,7 +260,7 @@
 -- are in scope, then the behavior is implementation defined.
 give :: forall a r. a -> (Given a => r) -> r
 give a k = unsafeCoerce (Gift k :: Gift a r) a
-{-# INLINE give #-}
+{-# INLINE_UNSAFE_COERCE give #-}
 
 --------------------------------------------------------------------------------
 -- Explicit Numeric Reflection
@@ -288,9 +310,7 @@
 -- in the \"Functional Pearl: Implicit Configurations\" paper by Oleg Kiselyov and Chung-Chieh Shan.
 --
 -- @instance Num (Q Exp)@ provided in this package allows writing @$(3)@
--- instead of @$(int 3)@. Sometimes the two will produce the same
--- representation (if compiled without the @-DUSE_TYPE_LITS@ preprocessor
--- directive).
+-- instead of @$(int 3)@.
 int :: Int -> TypeQ
 int n = case quotRem n 2 of
   (0, 0) -> conT ''Z
@@ -307,12 +327,6 @@
   | n >= 0 = int n
   | otherwise = error "nat: negative"
 
-#if defined(__GLASGOW_HASKELL__) && __GLASGOW_HASKELL__ < 704
-instance Show (Q a) where
-  show _ = "Q"
-instance Eq (Q a) where
-  _ == _ = False
-#endif
 instance Num a => Num (Q a) where
   (+) = liftM2 (+)
   (*) = liftM2 (*)
@@ -329,29 +343,13 @@
 
 -- | This permits the use of $(5) as a type splice.
 instance Num Type where
-#ifdef USE_TYPE_LITS
   LitT (NumTyLit a) + LitT (NumTyLit b) = LitT (NumTyLit (a+b))
   a + b = AppT (AppT (VarT ''(+)) a) b
 
   LitT (NumTyLit a) * LitT (NumTyLit b) = LitT (NumTyLit (a*b))
   (*) a b = AppT (AppT (VarT ''(GHC.TypeLits.*)) a) b
-#if MIN_VERSION_base(4,8,0)
   a - b = AppT (AppT (VarT ''(-)) a) b
-#else
-  (-) = error "Type.(-): undefined"
-#endif
   fromInteger = LitT . NumTyLit
-#else
-  (+) = error "Type.(+): undefined"
-  (*) = error "Type.(*): undefined"
-  (-) = error "Type.(-): undefined"
-  fromInteger n = case quotRem n 2 of
-      (0, 0) -> ConT ''Z
-      (q,-1) -> ConT ''PD `AppT` fromInteger q
-      (q, 0) -> ConT ''D  `AppT` fromInteger q
-      (q, 1) -> ConT ''SD `AppT` fromInteger q
-      _ -> error "ghc is bad at math"
-#endif
   abs = error "Type.abs"
   signum = error "Type.signum"
 
@@ -386,27 +384,12 @@
   signum = onProxyType1 signum
   fromInteger n = ConE 'Proxy `SigE` (ConT ''Proxy `AppT` fromInteger n)
 
-#ifdef USE_TYPE_LITS
 addProxy :: Proxy a -> Proxy b -> Proxy (a + b)
 addProxy _ _ = Proxy
 mulProxy :: Proxy a -> Proxy b -> Proxy (a * b)
 mulProxy _ _ = Proxy
-#if MIN_VERSION_base(4,8,0)
 subProxy :: Proxy a -> Proxy b -> Proxy (a - b)
 subProxy _ _ = Proxy
-#else
-subProxy :: Proxy a -> Proxy b -> Proxy c
-subProxy _ _ = error "Exp.(-): undefined"
-#endif
---  fromInteger = LitT . NumTyLit
-#else
-addProxy :: Proxy a -> Proxy b -> Proxy c
-addProxy _ _ = error "Exp.(+): undefined"
-mulProxy :: Proxy a -> Proxy b -> Proxy c
-mulProxy _ _ = error "Exp.(*): undefined"
-subProxy :: Proxy a -> Proxy b -> Proxy c
-subProxy _ _ = error "Exp.(-): undefined"
-#endif
 
 #endif
 
@@ -448,7 +431,7 @@
   GO(T253,253) GO(T254,254) GO(T255,255)
 
 #define GO(Tn,n) \
-  newtype Tn = Tn Tn deriving Typeable; \
+  newtype Tn = Tn Tn; \
   instance B Tn where { \
     reflectByte _ = n \
   };
@@ -466,9 +449,16 @@
 _ -> impossible
 }
 
-newtype W (b0 :: *) (b1 :: *) (b2 :: *) (b3 :: *) = W (W b0 b1 b2 b3) deriving Typeable
-newtype Stable (w0 :: *) (w1 :: *) (a :: *) = Stable (Stable w0 w1 a) deriving Typeable
+newtype W (b0 :: *) (b1 :: *) (b2 :: *) (b3 :: *) = W (W b0 b1 b2 b3)
+newtype StableBox (w0 :: *) (w1 :: *) (a :: *) = StableBox (StableBox w0 w1 a)
+newtype Stable (w0 :: *) (w1 :: *) (a :: *) = Stable (Stable w0 w1 a)
 
+data Box a = Box a
+
+stableBox :: p (Stable w1 w2 a) -> Proxy (StableBox w1 w2 a)
+stableBox _ = Proxy
+{-# INLINE stableBox #-}
+
 stable :: p b0 -> p b1 -> p b2 -> p b3 -> p b4 -> p b5 -> p b6 -> p b7
        -> Proxy (Stable (W b0 b1 b2 b3) (W b4 b5 b6 b7) a)
 stable _ _ _ _ _ _ _ _ = Proxy
@@ -482,35 +472,35 @@
 intPtrToStablePtr = castPtrToStablePtr . intPtrToPtr
 {-# INLINE intPtrToStablePtr #-}
 
-byte0 :: p (Stable (W b0 b1 b2 b3) w1 a) -> Proxy b0
+byte0 :: p (StableBox (W b0 b1 b2 b3) w1 a) -> Proxy b0
 byte0 _ = Proxy
 
-byte1 :: p (Stable (W b0 b1 b2 b3) w1 a) -> Proxy b1
+byte1 :: p (StableBox (W b0 b1 b2 b3) w1 a) -> Proxy b1
 byte1 _ = Proxy
 
-byte2 :: p (Stable (W b0 b1 b2 b3) w1 a) -> Proxy b2
+byte2 :: p (StableBox (W b0 b1 b2 b3) w1 a) -> Proxy b2
 byte2 _ = Proxy
 
-byte3 :: p (Stable (W b0 b1 b2 b3) w1 a) -> Proxy b3
+byte3 :: p (StableBox (W b0 b1 b2 b3) w1 a) -> Proxy b3
 byte3 _ = Proxy
 
-byte4 :: p (Stable w0 (W b4 b5 b6 b7) a) -> Proxy b4
+byte4 :: p (StableBox w0 (W b4 b5 b6 b7) a) -> Proxy b4
 byte4 _ = Proxy
 
-byte5 :: p (Stable w0 (W b4 b5 b6 b7) a) -> Proxy b5
+byte5 :: p (StableBox w0 (W b4 b5 b6 b7) a) -> Proxy b5
 byte5 _ = Proxy
 
-byte6 :: p (Stable w0 (W b4 b5 b6 b7) a) -> Proxy b6
+byte6 :: p (StableBox w0 (W b4 b5 b6 b7) a) -> Proxy b6
 byte6 _ = Proxy
 
-byte7 :: p (Stable w0 (W b4 b5 b6 b7) a) -> Proxy b7
+byte7 :: p (StableBox w0 (W b4 b5 b6 b7) a) -> Proxy b7
 byte7 _ = Proxy
 
 argument :: (p s -> r) -> Proxy s
 argument _ = Proxy
 
 instance (B b0, B b1, B b2, B b3, B b4, B b5, B b6, B b7, w0 ~ W b0 b1 b2 b3, w1 ~ W b4 b5 b6 b7)
-    => Reifies (Stable w0 w1 a) a where
+    => Reifies (StableBox w0 w1 a) (Box a) where
   reflect = r where
       r = unsafePerformIO $ const <$> deRefStablePtr p <* freeStablePtr p
       s = argument r
@@ -525,22 +515,30 @@
         (reflectByte (byte7 s) `shiftL` 56)
   {-# NOINLINE reflect #-}
 
--- This had to be moved to the top level, due to an apparent bug in
--- the ghc inliner introduced in ghc 7.0.x
-reflectBefore :: forall (proxy :: * -> *) s b. (Proxy s -> b) -> proxy s -> b
-reflectBefore f = const $! f Proxy
-{-# NOINLINE reflectBefore #-}
+instance Reifies (StableBox w0 w1 a) (Box b) => Reifies (Stable w0 w1 a) b where
+  reflect p = case reflect (stableBox p) of
+    Box a -> a
 
+-- Ensure that exactly one dictionary of Reifies (StableBox ...) is created and evaluated per reifyTypeable call.
+--
+-- Evaluating the dictionary's thunk frees the allocated StablePtr, and the contents of the StablePtr replace the thunk.
+-- Creating two dictionaries would mean a double free upon their evaluation, and leaving a dictionary unevaluated would
+-- leak the StablePtr (see https://github.com/ekmett/reflection/issues/54).
+--
+-- To separate evaluation of the dictionary and evaluation of the actual argument passed to reifyTypeable, we insert a
+-- Box in between.
+withStableBox :: Reifies (StableBox w0 w1 a) (Box a) => (Reifies (Stable w0 w1 a) a => Proxy (Stable w0 w1 a) -> r) -> Proxy (Stable w0 w1 a) -> IO r
+withStableBox k p = do
+  _ <- evaluate $ reflect (stableBox p)
+  evaluate $ k p
+{-# NOINLINE withStableBox #-}
+
 -- | Reify a value at the type level in a 'Typeable'-compatible fashion, to be recovered with 'reflect'.
 --
 -- This can be necessary to work around the changes to @Data.Typeable@ in GHC HEAD.
 reifyTypeable :: Typeable a => a -> (forall (s :: *). (Typeable s, Reifies s a) => Proxy s -> r) -> r
-#if MIN_VERSION_base(4,4,0)
 reifyTypeable a k = unsafeDupablePerformIO $ do
-#else
-reifyTypeable a k = unsafePerformIO $ do
-#endif
-  p <- newStablePtr a
+  p <- newStablePtr (Box a)
   let n = stablePtrToIntPtr p
   reifyByte (fromIntegral n) (\s0 ->
     reifyByte (fromIntegral (n `shiftR` 8)) (\s1 ->
@@ -550,9 +548,7 @@
             reifyByte (fromIntegral (n `shiftR` 40)) (\s5 ->
               reifyByte (fromIntegral (n `shiftR` 48)) (\s6 ->
                 reifyByte (fromIntegral (n `shiftR` 56)) (\s7 ->
-                  reflectBefore (fmap return k) $
-                    stable s0 s1 s2 s3 s4 s5 s6 s7))))))))
-
+                  withStableBox k $ stable s0 s1 s2 s3 s4 s5 s6 s7))))))))
 
 data ReifiedMonoid a = ReifiedMonoid { reifiedMappend :: a -> a -> a, reifiedMempty :: a }
 
@@ -577,7 +573,7 @@
 reifyMonoid f z m xs = reify (ReifiedMonoid f z) (unreflectedMonoid (m xs))
 
 -- | Fold a value using its 'Foldable' instance using
--- explicitly provided 'Monoid' operations. This is like 'fold'
+-- explicitly provided 'Monoid' operations. This is like 'Data.Foldable.fold'
 -- where the 'Monoid' instance can be manually specified.
 --
 -- @
@@ -634,5 +630,5 @@
 sequenceBy :: Traversable t => (forall x. x -> f x) -> (forall x y. f (x -> y) -> f x -> f y) -> t (f a) -> f (t a)
 sequenceBy pur app = reifyApplicative pur app (traverse ReflectedApplicative)
 
-(#.) :: (b -> c) -> (a -> b) -> a -> c
-(#.) _ = unsafeCoerce
+(#.) :: Coercible c b => (b -> c) -> (a -> b) -> (a -> c)
+(#.) _ = coerce (\x -> x :: b) :: forall a b. Coercible b a => a -> b
diff --git a/reflection.cabal b/reflection.cabal
--- a/reflection.cabal
+++ b/reflection.cabal
@@ -1,5 +1,5 @@
 name:           reflection
-version:        2.1.4
+version:        2.1.9
 license:        BSD3
 license-file:   LICENSE
 author:         Edward A. Kmett, Elliott Hird, Oleg Kiselyov and Chung-chieh Shan
@@ -25,20 +25,28 @@
   and Chung-chieh Shan (<http://okmij.org/ftp/Haskell/tr-15-04.pdf original paper>).
   However, the API has been streamlined to improve performance.
   .
-  Austin Seipp's tutorial <https://www.schoolofhaskell.com/user/thoughtpolice/using-reflection Reflecting values to types and back> provides a summary of the
+  There are a handful of tutorials on how to use this library:
+  .
+  * Austin Seipp's tutorial <https://www.schoolofhaskell.com/user/thoughtpolice/using-reflection Reflecting values to types and back> provides a summary of the
   approach taken by this library, along with more motivating examples.
-tested-with:   GHC == 7.0.4
-             , GHC == 7.2.2
-             , GHC == 7.4.2
-             , GHC == 7.6.3
-             , GHC == 7.8.4
-             , GHC == 7.10.3
-             , GHC == 8.0.2
+  .
+  * Arnaud Spiwack's tutorial <https://www.tweag.io/posts/2017-12-21-reflection-tutorial.html All about reflection> explains how to use this library.
+tested-with:   GHC == 8.0.2
              , GHC == 8.2.2
-             , GHC == 8.4.3
-             , GHC == 8.6.1
+             , GHC == 8.4.4
+             , GHC == 8.6.5
+             , GHC == 8.8.4
+             , GHC == 8.10.7
+             , GHC == 9.0.2
+             , GHC == 9.2.8
+             , GHC == 9.4.8
+             , GHC == 9.6.6
+             , GHC == 9.8.4
+             , GHC == 9.10.1
+             , GHC == 9.12.1
 
 extra-source-files:
+  .hlint.yaml
   examples/reflection-examples.cabal
   examples/LICENSE
   examples/*.hs
@@ -46,7 +54,6 @@
   README.markdown
   slow/Data/Reflection.hs
   fast/Data/Reflection.hs
-  .travis.yml
 
 flag slow
   description:
@@ -68,28 +75,18 @@
 
 library
   ghc-options: -Wall
+  if impl(ghc >= 8.6)
+    ghc-options: -Wno-star-is-type
 
-  if impl(ghc >= 7.2)
-    default-extensions: Trustworthy
+  default-extensions: Trustworthy
 
   build-depends:
-    base >= 2 && < 5
-
-  if impl(ghc < 7.8)
-    build-depends:
-      tagged >= 0.4.4 && < 1
-
-  if !impl(ghc >= 8.0)
-    build-depends:
-      semigroups >= 0.11 && < 0.19
+    base >= 4.9 && < 5
 
   default-language: Haskell98
 
   if flag(template-haskell) && impl(ghc)
-    if !impl(ghc >= 8.0)
-      other-extensions: TemplateHaskell
-    -- else
-    --   other-extensions: TemplateHaskellQuotes -- Hackage doesn't know this extension yet
+    -- other-extensions: TemplateHaskellQuotes -- Hackage doesn't know this extension yet
     build-depends: template-haskell
 
   if !flag(slow) && (impl(ghc) || impl(hugs))
@@ -105,3 +102,19 @@
     CPP
 
   exposed-modules: Data.Reflection
+
+test-suite spec
+  type: exitcode-stdio-1.0
+  hs-source-dirs: tests
+  main-is: Spec.hs
+  other-modules: ReifyNatSpec
+                 T47Spec
+  ghc-options: -Wall
+  default-language: Haskell98
+  build-tool-depends: hspec-discover:hspec-discover >= 1.8
+  build-depends:
+    base       >= 4.9 && < 5,
+    containers >= 0.1 && < 0.8,
+    hspec      >= 2   && < 3,
+    QuickCheck >= 2   && < 3,
+    reflection
diff --git a/slow/Data/Reflection.hs b/slow/Data/Reflection.hs
--- a/slow/Data/Reflection.hs
+++ b/slow/Data/Reflection.hs
@@ -1,12 +1,15 @@
 {-# LANGUAGE CPP #-}
+{-# LANGUAGE MonoLocalBinds #-}
 {-# LANGUAGE MultiParamTypeClasses #-}
 {-# LANGUAGE FunctionalDependencies #-}
+{-# LANGUAGE FlexibleContexts #-}
 {-# LANGUAGE FlexibleInstances #-}
 {-# LANGUAGE Rank2Types #-}
+{-# LANGUAGE UndecidableInstances #-}
 {-# OPTIONS_GHC -fno-cse #-}
 {-# OPTIONS_GHC -fno-full-laziness #-}
 {-# OPTIONS_GHC -fno-float-in #-}
-{-# OPTIONS_GHC -fno-warn-unused-binds #-}
+{-# OPTIONS_GHC -Wno-unused-binds #-}
 ----------------------------------------------------------------------------
 -- |
 -- Module     : Data.Reflection
@@ -49,6 +52,7 @@
 import Foreign.StablePtr
 import System.IO.Unsafe
 import Control.Applicative
+import Control.Exception
 import Data.Proxy
 import Data.Bits
 import Data.Word
@@ -115,9 +119,18 @@
   -- reified type.
   reflect :: proxy s -> a
 
+newtype StableBox b0 b1 b2 b3 b4 b5 b6 b7 a =
+  StableBox (StableBox b0 b1 b2 b3 b4 b5 b6 b7 a)
 newtype Stable b0 b1 b2 b3 b4 b5 b6 b7 a =
   Stable (Stable b0 b1 b2 b3 b4 b5 b6 b7 a)
 
+data Box a = Box a
+
+stableBox :: p (Stable b0 b1 b2 b3 b4 b5 b6 b7 a)
+          -> Proxy (StableBox b0 b1 b2 b3 b4 b5 b6 b7 a)
+stableBox _ = Proxy
+{-# INLINE stableBox #-}
+
 stable :: p b0 -> p b1 -> p b2 -> p b3 -> p b4 -> p b5 -> p b6 -> p b7
        -> Proxy (Stable b0 b1 b2 b3 b4 b5 b6 b7 a)
 stable _ _ _ _ _ _ _ _ = Proxy
@@ -131,35 +144,35 @@
 intPtrToStablePtr = castPtrToStablePtr . intPtrToPtr
 {-# INLINE intPtrToStablePtr #-}
 
-byte0 :: p (Stable b0 b1 b2 b3 b4 b5 b6 b7 a) -> Proxy b0
+byte0 :: p (StableBox b0 b1 b2 b3 b4 b5 b6 b7 a) -> Proxy b0
 byte0 _ = Proxy
 
-byte1 :: p (Stable b0 b1 b2 b3 b4 b5 b6 b7 a) -> Proxy b1
+byte1 :: p (StableBox b0 b1 b2 b3 b4 b5 b6 b7 a) -> Proxy b1
 byte1 _ = Proxy
 
-byte2 :: p (Stable b0 b1 b2 b3 b4 b5 b6 b7 a) -> Proxy b2
+byte2 :: p (StableBox b0 b1 b2 b3 b4 b5 b6 b7 a) -> Proxy b2
 byte2 _ = Proxy
 
-byte3 :: p (Stable b0 b1 b2 b3 b4 b5 b6 b7 a) -> Proxy b3
+byte3 :: p (StableBox b0 b1 b2 b3 b4 b5 b6 b7 a) -> Proxy b3
 byte3 _ = Proxy
 
-byte4 :: p (Stable b0 b1 b2 b3 b4 b5 b6 b7 a) -> Proxy b4
+byte4 :: p (StableBox b0 b1 b2 b3 b4 b5 b6 b7 a) -> Proxy b4
 byte4 _ = Proxy
 
-byte5 :: p (Stable b0 b1 b2 b3 b4 b5 b6 b7 a) -> Proxy b5
+byte5 :: p (StableBox b0 b1 b2 b3 b4 b5 b6 b7 a) -> Proxy b5
 byte5 _ = Proxy
 
-byte6 :: p (Stable b0 b1 b2 b3 b4 b5 b6 b7 a) -> Proxy b6
+byte6 :: p (StableBox b0 b1 b2 b3 b4 b5 b6 b7 a) -> Proxy b6
 byte6 _ = Proxy
 
-byte7 :: p (Stable b0 b1 b2 b3 b4 b5 b6 b7 a) -> Proxy b7
+byte7 :: p (StableBox b0 b1 b2 b3 b4 b5 b6 b7 a) -> Proxy b7
 byte7 _ = Proxy
 
 argument :: (p s -> r) -> Proxy s
 argument _ = Proxy
 
 instance (B b0, B b1, B b2, B b3, B b4, B b5, B b6, B b7)
-    => Reifies (Stable b0 b1 b2 b3 b4 b5 b6 b7 a) a where
+    => Reifies (StableBox b0 b1 b2 b3 b4 b5 b6 b7 a) (Box a) where
   reflect = r where
       r = unsafePerformIO $ const <$> deRefStablePtr p <* freeStablePtr p
       s = argument r
@@ -174,12 +187,34 @@
         (reflectByte (byte7 s) `shiftL` 56)
   {-# NOINLINE reflect #-}
 
--- This had to be moved to the top level, due to an apparent bug in
--- the ghc inliner introduced in ghc 7.0.x
-reflectBefore :: Reifies s a => (Proxy s -> b) -> proxy s -> b
-reflectBefore f = const $! f Proxy
-{-# NOINLINE reflectBefore #-}
+instance Reifies (StableBox b0 b1 b2 b3 b4 b5 b6 b7 a) (Box b)
+    => Reifies (Stable b0 b1 b2 b3 b4 b5 b6 b7 a) b where
+  reflect p = case reflect (stableBox p) of
+    Box a -> a
 
+-- Ensure that exactly one dictionary of Reifies (StableBox ...) is created and
+-- evaluated per reifyTypeable call.
+--
+-- Evaluating the dictionary's thunk frees the allocated StablePtr, and the
+-- contents of the StablePtr replace the thunk. Creating two dictionaries would
+-- mean a double free upon their evaluation, and leaving a dictionary
+-- unevaluated would leak the StablePtr
+-- (see https://github.com/ekmett/reflection/issues/54).
+--
+-- To separate evaluation of the dictionary and evaluation of the actual
+-- argument passed to reifyTypeable, we insert a Box in between.
+withStableBox
+  :: Reifies (StableBox b0 b1 b2 b3 b4 b5 b6 b7 a) (Box a)
+  => (Reifies (Stable b0 b1 b2 b3 b4 b5 b6 b7 a) a
+    => Proxy (Stable b0 b1 b2 b3 b4 b5 b6 b7 a)
+    -> r)
+  -> Proxy (Stable b0 b1 b2 b3 b4 b5 b6 b7 a)
+  -> IO r
+withStableBox k p = do
+  _ <- evaluate $ reflect (stableBox p)
+  evaluate $ k p
+{-# NOINLINE withStableBox #-}
+
 -- | Reify a value at the type level, to be recovered with 'reflect'.
 reify :: a -> (forall s. (Reifies s a) => Proxy s -> r) -> r
 reify a k = unsafeDupablePerformIO $ do
@@ -193,5 +228,4 @@
             reifyByte (fromIntegral (n `shiftR` 40)) (\s5 ->
               reifyByte (fromIntegral (n `shiftR` 48)) (\s6 ->
                 reifyByte (fromIntegral (n `shiftR` 56)) (\s7 ->
-                  reflectBefore (fmap return k) $
-                    stable s0 s1 s2 s3 s4 s5 s6 s7))))))))
+                  withStableBox k $ stable s0 s1 s2 s3 s4 s5 s6 s7))))))))
diff --git a/tests/ReifyNatSpec.hs b/tests/ReifyNatSpec.hs
new file mode 100644
--- /dev/null
+++ b/tests/ReifyNatSpec.hs
@@ -0,0 +1,50 @@
+{-# LANGUAGE CPP #-}
+{-# LANGUAGE ScopedTypeVariables #-}
+module ReifyNatSpec where
+
+import Data.Reflection
+import Test.Hspec.QuickCheck
+import Test.QuickCheck (NonNegative(..))
+
+#if MIN_VERSION_base(4,10,0)
+import GHC.TypeNats (natVal)
+import Numeric.Natural (Natural)
+#endif
+
+#if __GLASGOW_HASKELL__ != 900
+import Control.Exception (ArithException(..), evaluate)
+import Test.QuickCheck (Negative(..))
+#endif
+
+import Test.Hspec
+
+main :: IO ()
+main = hspec spec
+
+spec :: Spec
+spec =
+  describe "reifyNat" $ do
+    prop "reify positive Integers and reflect them back" $
+      \(NonNegative (i :: Integer)) -> reifyNat i $ \p -> reflect p `shouldBe` i
+#if __GLASGOW_HASKELL__ != 900
+    -- Inexplicably, this test fails on GHC 9.0 with hspec-2.8.4 or later.
+    -- Moreover, I suspect that undefined behavior is involved in some way,
+    -- as the output of hspec will occasionally be swallowed entirely. I have
+    -- not been able to observe this phenomenon on any other version of GHC,
+    -- so I will simply disable this test on 9.0.
+    prop "should throw an Underflow exception on negative inputs" $
+      \(Negative (i :: Integer)) ->
+        reifyNat i (evaluate . reflect) `shouldThrow` (== Underflow)
+#endif
+#if MIN_VERSION_base(4,10,0)
+    it "should reflect very large Naturals correctly" $ do -- #41
+      let d42, d2_63, d2_64 :: Natural
+          d42   = 42
+          d2_63 = 2^(63 :: Natural)
+          d2_64 = 2^(64 :: Natural)
+      reifyNat (toInteger d42)       $ \p -> natVal p `shouldBe` d42
+      reifyNat (toInteger (d2_63-1)) $ \p -> natVal p `shouldBe` d2_63-1
+      reifyNat (toInteger d2_63)     $ \p -> natVal p `shouldBe` d2_63
+      reifyNat (toInteger (d2_64-1)) $ \p -> natVal p `shouldBe` d2_64-1
+      reifyNat (toInteger d2_64)     $ \p -> natVal p `shouldBe` d2_64
+#endif
diff --git a/tests/Spec.hs b/tests/Spec.hs
new file mode 100644
--- /dev/null
+++ b/tests/Spec.hs
@@ -0,0 +1,1 @@
+{-# OPTIONS_GHC -F -pgmF hspec-discover #-}
diff --git a/tests/T47Spec.hs b/tests/T47Spec.hs
new file mode 100644
--- /dev/null
+++ b/tests/T47Spec.hs
@@ -0,0 +1,64 @@
+{-# LANGUAGE FlexibleContexts #-}
+{-# LANGUAGE UndecidableInstances #-}
+-- | A regression test for issue #47.
+module T47Spec where
+
+import qualified Data.Map as M
+import Data.Map (Map)
+import Data.Reflection
+import Test.Hspec
+
+main :: IO ()
+main = hspec spec
+
+spec :: Spec
+spec =
+  describe "Given" $ do
+    it "should give Normal properly" $
+      give Normal (toJSON (Foo Bar)) `shouldBe`
+      Object (M.fromList [("Foo",String "Bar")])
+    it "should give ViaShow properly" $
+      give ViaShow (toJSON (Foo Bar)) `shouldBe`
+      Object (M.fromList [("Foo",String "SHOWBAR")])
+
+----------------------------------------------------------------------------
+-- Types
+----------------------------------------------------------------------------
+
+data Foo = Foo Bar
+
+instance Show Foo where
+  show _ = "SHOWFOO"
+
+data Bar = Bar | BarBar
+
+instance Show Bar where
+  show _ = "SHOWBAR"
+
+----------------------------------------------------------------------------
+-- ToJSON instances
+----------------------------------------------------------------------------
+
+instance Given Style => ToJSON Foo where
+  toJSON (Foo x) = Object $ M.singleton "Foo" (toJSON x)
+
+instance Given Style => ToJSON Bar where
+  toJSON x = case given of
+    Normal -> String $ case x of
+                Bar    -> "Bar"
+                BarBar -> "BarBar"
+    ViaShow -> String $ show x
+
+data Style = Normal | ViaShow
+
+----------------------------------------------------------------------------
+-- Minimized aeson
+----------------------------------------------------------------------------
+
+class ToJSON a where
+  toJSON :: a -> Value
+
+data Value
+  = Object !(Map String Value)
+  | String !String
+  deriving (Eq, Show)
