diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,11 @@
 
 ## Unreleased
 
+## v0.3.4.0 (2024-07-09)
+
++ Fixed a performance bug causing exponential compilation times when using
+  lots of interpolations (thanks mpickering!)
+
 ## v0.3.3.0 (2024-01-24)
 
 + Added support for GHC 9.8
diff --git a/README.md b/README.md
--- a/README.md
+++ b/README.md
@@ -308,3 +308,17 @@
 Enable either the `text-builder` or `bytestring-builder` Cabal flag, depending
 on your need, and you should see speedups constructing large strings, at the
 cost of slowing down smaller outputs.
+
+## Release policy
+
+As of July 2024, `string-interpolate` is essentially "done;" it does its job
+reliably, and no further enhancements or feature additions are planned. Going
+forward, the expectation is that any further releases are mostly for performance
+improvements, bugfixes, or necessary security patches.
+
+If major API rework or breaking changes become planned in the future,
+`string-interpolate` will maintain strict backwards compatibility (i.e. no code
+changes should be necessary in any existing code consumers) for a period no less
+than 3 years. Note that this only applies to the exports of the
+`Data.String.Interpolate` module; all other modules should be considered
+internal.
diff --git a/src/lib/Data/String/Interpolate.hs b/src/lib/Data/String/Interpolate.hs
--- a/src/lib/Data/String/Interpolate.hs
+++ b/src/lib/Data/String/Interpolate.hs
@@ -61,7 +61,6 @@
 
 import Data.Foldable ( traverse_ )
 import Data.List     ( intercalate )
-import Data.Proxy
 
 import qualified Language.Haskell.Exts.Extension as Ext
 import           Language.Haskell.Exts.Parser
@@ -70,7 +69,7 @@
 import           Language.Haskell.TH
 import           Language.Haskell.TH.Quote       ( QuasiQuoter(..) )
 
-import Data.String.Interpolate.Conversion ( build, finalize, interpolate, ofString )
+import Data.String.Interpolate.Conversion ( proxyWrapper, build, finalize, interpolate, ofString )
 
 import Data.String.Interpolate.Lines      ( IndentWarning(..), Mindent(..), handleIndents )
 import Data.String.Interpolate.Parse
@@ -116,16 +115,21 @@
 -- Produce the final Template Haskell expression. Handles collapsing
 -- intermediate strings.
 outputToExp :: [OutputSegment] -> Q Exp
-outputToExp segs = [|finalize Proxy $(go (collapseStrings segs))|]
+outputToExp segs = finalExp
   where
-    go :: [OutputSegment] -> Q Exp
-    go = foldr
-      (\seg qexp -> [|build Proxy $(renderExp seg) $(qexp)|])
-      [|ofString Proxy ""|]
+    finalExp = [| proxyWrapper $ \proxy -> $(mkFinalize [| proxy |]) |]
 
-    renderExp :: OutputSegment -> Q Exp
-    renderExp (OfString str)     = [|ofString Proxy str|]
-    renderExp (Interpolate expr) = [|interpolate Proxy $(reifyExpression expr)|]
+    mkFinalize :: Q Exp -> Q Exp
+    mkFinalize proxy = [|finalize $proxy $(go proxy (collapseStrings segs))|]
+
+    go :: Q Exp -> [OutputSegment] -> Q Exp
+    go proxy = foldr
+      (\seg qexp -> [|build $proxy $(renderExp proxy seg) $(qexp)|])
+      [|ofString $proxy ""|]
+
+    renderExp :: Q Exp -> OutputSegment -> Q Exp
+    renderExp proxy (OfString str)     = [|ofString $proxy str|]
+    renderExp proxy (Interpolate expr) = [|interpolate $proxy $(reifyExpression expr)|]
 
 type Interpolator = ParseOutput -> Q Lines
 
diff --git a/src/lib/Data/String/Interpolate/Conversion.hs b/src/lib/Data/String/Interpolate/Conversion.hs
--- a/src/lib/Data/String/Interpolate/Conversion.hs
+++ b/src/lib/Data/String/Interpolate/Conversion.hs
@@ -19,7 +19,7 @@
 
 module Data.String.Interpolate.Conversion
   ( IsCustomSink, InterpSink(..), Interpolatable(..)
-  , bsToTextBuilder, lbsToTextBuilder, encodeCharUTF8
+  , bsToTextBuilder, lbsToTextBuilder, encodeCharUTF8, proxyWrapper
   )
 where
 
diff --git a/src/lib/Data/String/Interpolate/Conversion/Classes.hs b/src/lib/Data/String/Interpolate/Conversion/Classes.hs
--- a/src/lib/Data/String/Interpolate/Conversion/Classes.hs
+++ b/src/lib/Data/String/Interpolate/Conversion/Classes.hs
@@ -2,10 +2,12 @@
 {-# LANGUAGE MultiParamTypeClasses #-}
 {-# LANGUAGE TypeFamilies          #-}
 {-# LANGUAGE TypeOperators         #-}
+{-# LANGUAGE ScopedTypeVariables   #-}
+{-# LANGUAGE TypeApplications      #-}
 
 module Data.String.Interpolate.Conversion.Classes
   ( B(..)
-  , IsCustomSink, InterpSink(..), Interpolatable(..)
+  , IsCustomSink, InterpSink(..), Interpolatable(..), proxyWrapper
   )
 where
 
@@ -35,6 +37,13 @@
   IsCustomSink LB.ByteString = 'True
   IsCustomSink LB.Builder = 'True
   IsCustomSink _ = 'False
+
+
+-- | Used to indicate to GHC that the value of all the proxy arguments is the same.
+proxyWrapper :: forall final flag . (IsCustomSink final ~ flag) => (Proxy flag -> final) -> final
+proxyWrapper k =
+  let proxy = Proxy @(IsCustomSink final)
+  in  k proxy
 
 -- | Something that can be interpolated into.
 class IsCustomSink dst ~ flag => InterpSink (flag :: Bool) dst where
diff --git a/string-interpolate.cabal b/string-interpolate.cabal
--- a/string-interpolate.cabal
+++ b/string-interpolate.cabal
@@ -1,7 +1,7 @@
 cabal-version: 1.18
 
 name:           string-interpolate
-version:        0.3.3.0
+version:        0.3.4.0
 synopsis:       Haskell string/text/bytestring interpolation that just works
 description:    Unicode-aware string interpolation that handles all textual types.
                 .
@@ -91,7 +91,7 @@
     build-depends:
         base ==4.*
       , string-interpolate
-      , QuickCheck <2.15
+      , QuickCheck <2.16
       , bytestring <0.13
       , text <2.2
       , template-haskell <2.22
@@ -112,7 +112,7 @@
     build-depends:
         base ==4.*
       , string-interpolate
-      , QuickCheck <2.15
+      , QuickCheck <2.16
       , bytestring <0.13
       , text <2.2
       , deepseq <1.6
diff --git a/test/spec.hs b/test/spec.hs
--- a/test/spec.hs
+++ b/test/spec.hs
@@ -18,7 +18,7 @@
 import           Data.Char               ( chr, isSpace )
 import           Data.Foldable           ( foldMap )
 import qualified Data.HashMap.Strict     as HM
-import           Data.List               ( sort )
+import           Data.List               ( sort, intersperse )
 import           Data.Semigroup
 import qualified Data.Text               as T
 import qualified Data.Text.Lazy          as LT
@@ -75,6 +75,71 @@
     it "should parse TypeApplications" $ do
       let expected :: String = "2"
       [i|#{show @Int 2}|] `shouldBe` expected
+
+    -- This test is primarily a regression test against a performance issue
+    -- caused by GHC needing to unify all the proxies passed in the generated
+    -- code.
+    -- See <https://gitlab.com/williamyaoh/string-interpolate/-/merge_requests/72>
+    -- and <https://gitlab.haskell.org/ghc/ghc/-/issues/24984>
+    -- It would be preferable if we had something that would actually *fail*
+    -- if compilation took too long, but compilation of the test suite being
+    -- slow should be enough of a smoke signal.
+    it "should work with many interpolations" $
+      let x = ()
+          (expected :: String) = concat $ intersperse " : " (replicate 780 (show x))
+      in [iii| #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} :
+          #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} :
+          #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} :
+          #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} :
+          #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} :
+          #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} :
+          #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} :
+          #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} :
+          #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} :
+          #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} :
+          #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} :
+          #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} :
+          #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} :
+          #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} :
+          #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} :
+          #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} :
+          #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} :
+          #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} :
+          #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} :
+          #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} :
+          #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} :
+          #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} :
+          #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} :
+          #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} :
+          #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} :
+          #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} :
+          #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} :
+          #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} :
+          #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} :
+          #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} :
+          #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} :
+          #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} :
+          #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} :
+          #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} :
+          #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} :
+          #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} :
+          #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} :
+          #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} :
+          #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} :
+          #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} :
+          #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} :
+          #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} :
+          #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} :
+          #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} :
+          #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} :
+          #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} :
+          #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} :
+          #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} :
+          #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} :
+          #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} :
+          #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} :
+          #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x} : #{x}
+      |] `shouldBe` expected
 
     context "when using String as a parameter" $ do
       prop "just interpolating should be id" $
