diff --git a/CHANGES.md b/CHANGES.md
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -1,6 +1,17 @@
 `th-desugar` release notes
 ==========================
 
+Version 1.19 [2026.01.10]
+-------------------------
+* Support GHC 9.14.
+* Support specialising expressions in `SPECIALISE` pragmas. As part of these
+  changes, a `DSpecialiseEP` data constructor has been added to `DPragma`, and
+  the existing `DSpecialiseP` data constructor has been converted to a pattern
+  synonym defined in terms of `DSpecialiseEP`.
+* Add `mkTupleDType :: [DType] -> DType`, which offers functionality similar to
+  the existing `mkTupleD{Exp,Pat}` functions, but for types instead of
+  expressions or patterns.
+
 Version 1.18 [2024.12.11]
 -------------------------
 * Support GHC 9.12.
diff --git a/Language/Haskell/TH/Desugar.hs b/Language/Haskell/TH/Desugar.hs
--- a/Language/Haskell/TH/Desugar.hs
+++ b/Language/Haskell/TH/Desugar.hs
@@ -43,7 +43,7 @@
   DCon(..), DConFields(..), DDeclaredInfix, DBangType, DVarBangType,
   Bang(..), SourceUnpackedness(..), SourceStrictness(..),
   DForeign(..),
-  DPragma(..), DRuleBndr(..), DTySynEqn(..), DInfo(..), DInstanceDec,
+  DPragma(DSpecialiseP, ..), DRuleBndr(..), DTySynEqn(..), DInfo(..), DInstanceDec,
   Role(..), AnnTarget(..),
 
   -- * The 'Desugar' class
@@ -101,7 +101,8 @@
   getDataD, dataConNameToDataName, dataConNameToCon,
   nameOccursIn, allNamesIn, flattenDValD, getRecordSelectors,
   mkTypeName, mkDataName, newUniqueName,
-  mkTupleDExp, mkTupleDPat, maybeDLetE, maybeDCaseE, maybeDCasesE,
+  mkTupleDExp, mkTupleDPat, mkTupleDType,
+  maybeDLetE, maybeDCaseE, maybeDCasesE,
   dCaseE, dCasesE, dLamE, dLamCaseE, mkDLamEFromDPats,
   tupleNameDegree_maybe,
   unboxedSumNameDegree_maybe, unboxedTupleNameDegree_maybe,
diff --git a/Language/Haskell/TH/Desugar/AST.hs b/Language/Haskell/TH/Desugar/AST.hs
--- a/Language/Haskell/TH/Desugar/AST.hs
+++ b/Language/Haskell/TH/Desugar/AST.hs
@@ -442,7 +442,7 @@
 
 -- | Corresponds to TH's @Pragma@ type.
 data DPragma = DInlineP Name Inline RuleMatch Phases
-             | DSpecialiseP Name DType (Maybe Inline) Phases
+             | DSpecialiseEP (Maybe [DTyVarBndr ()]) [DRuleBndr] DExp (Maybe Inline) Phases
              | DSpecialiseInstP DType
              | DRuleP String (Maybe [DTyVarBndrUnit]) [DRuleBndr] DExp DExp Phases
              | DAnnP AnnTarget DExp
@@ -451,6 +451,12 @@
              | DOpaqueP Name
              | DSCCP Name (Maybe String)
              deriving (Eq, Show, Data, Generic, Lift)
+
+-- | Old-form specialise pragma @{ {\-\# SPECIALISE [INLINE] [phases] (var :: ty) #-} }@.
+--
+-- Subsumed by the more general 'DSpecialiseEP' constructor.
+pattern DSpecialiseP :: Name -> DType -> Maybe Inline -> Phases -> DPragma
+pattern DSpecialiseP nm ty inl phases = DSpecialiseEP Nothing [] (DSigE (DVarE nm) ty) inl phases
 
 -- | Corresponds to TH's @RuleBndr@ type.
 data DRuleBndr = DRuleVar Name
diff --git a/Language/Haskell/TH/Desugar/Core.hs b/Language/Haskell/TH/Desugar/Core.hs
--- a/Language/Haskell/TH/Desugar/Core.hs
+++ b/Language/Haskell/TH/Desugar/Core.hs
@@ -1131,6 +1131,15 @@
 #if __GLASGOW_HASKELL__ >= 909
 dsPragma (SCCP nm mstr)                  = return $ DSCCP nm mstr
 #endif
+#if __GLASGOW_HASKELL__ >= 913
+dsPragma (SpecialiseEP mTyBndrs tmBndrs specE mInline phases) =
+  DSpecialiseEP
+    <$> mapM (mapM dsTvbUnit) mTyBndrs
+    <*> mapM dsRuleBndr tmBndrs
+    <*> dsExp specE
+    <*> pure mInline
+    <*> pure phases
+#endif
 
 -- | Desugar a @RuleBndr@.
 dsRuleBndr :: DsMonad q => RuleBndr -> q DRuleBndr
@@ -1665,6 +1674,11 @@
 mkTupleDPat :: [DPat] -> DPat
 mkTupleDPat [pat] = pat
 mkTupleDPat pats = DConP (tupleDataName (length pats)) [] pats
+
+-- | Make a tuple 'DType' from a list of 'DType's. Avoids using a 1-tuple.
+mkTupleDType :: [DType] -> DType
+mkTupleDType [ty] = ty
+mkTupleDType tys  = foldl DAppT (DConT $ tupleTypeName (length tys)) tys
 
 -- | Is this pattern guaranteed to match?
 isUniversalPattern :: DsMonad q => DPat -> q Bool
diff --git a/Language/Haskell/TH/Desugar/Sweeten.hs b/Language/Haskell/TH/Desugar/Sweeten.hs
--- a/Language/Haskell/TH/Desugar/Sweeten.hs
+++ b/Language/Haskell/TH/Desugar/Sweeten.hs
@@ -369,6 +369,17 @@
 #else
 pragmaToTH (DSCCP {}) = error "SCCP pragmas only supported in GHC 9.10+"
 #endif
+#if __GLASGOW_HASKELL__ >= 913
+pragmaToTH (DSpecialiseEP mTyBndrs tmBndrs specE mInline phases) =
+  SpecialiseEP
+    (fmap (fmap tvbToTH) mTyBndrs)
+    (map ruleBndrToTH tmBndrs)
+    (expToTH specE)
+    mInline
+    phases
+#else
+pragmaToTH (DSpecialiseEP {}) = error "DSpecialiseEP pragmas only supported in GHC 9.14+"
+#endif
 
 ruleBndrToTH :: DRuleBndr -> RuleBndr
 ruleBndrToTH (DRuleVar n) = RuleVar n
diff --git a/Test/Run.hs b/Test/Run.hs
--- a/Test/Run.hs
+++ b/Test/Run.hs
@@ -212,6 +212,9 @@
              , "embedded_forall_vis" ~: $(test71_embedded_forall_vis) @=? $(dsSplice test71_embedded_forall_vis)
              , "embedded_constraint" ~: $(test72_embedded_constraint) @=? $(dsSplice test72_embedded_constraint)
 #endif
+#if __GLASGOW_HASKELL__ >= 913
+             , "specialise_exp_pragma" ~: $(test73_specialise_exp_pragma) @=? $(dsSplice test73_specialise_exp_pragma)
+#endif
              ]
 
 test35a = $test35_expand
diff --git a/Test/Splices.hs b/Test/Splices.hs
--- a/Test/Splices.hs
+++ b/Test/Splices.hs
@@ -481,6 +481,14 @@
      in idv (forall a. (a ~ Bool) => a -> a) (\x -> not x) False |]
 #endif
 
+#if __GLASGOW_HASKELL__ >= 913
+test73_specialise_exp_pragma =
+  [| let {-# SPECIALISE f @() #-}
+         f :: forall a. Show a => a -> String
+         f = show
+     in f () |]
+#endif
+
 type family TFExpand x
 type instance TFExpand Int = Bool
 type instance TFExpand (Maybe a) = [a]
@@ -963,5 +971,8 @@
              , test70_embedded_forall_invis
              , test71_embedded_forall_vis
              , test72_embedded_constraint
+#endif
+#if __GLASGOW_HASKELL__ >= 913
+             , test73_specialise_exp_pragma
 #endif
              ]
diff --git a/th-desugar.cabal b/th-desugar.cabal
--- a/th-desugar.cabal
+++ b/th-desugar.cabal
@@ -1,5 +1,5 @@
 name:           th-desugar
-version:        1.18
+version:        1.19
 cabal-version:  >= 1.10
 synopsis:       Functions to desugar Template Haskell
 homepage:       https://github.com/goldfirere/th-desugar
@@ -21,10 +21,11 @@
               , GHC == 9.0.2
               , GHC == 9.2.8
               , GHC == 9.4.8
-              , GHC == 9.6.6
+              , GHC == 9.6.7
               , GHC == 9.8.4
-              , GHC == 9.10.1
-              , GHC == 9.12.1
+              , GHC == 9.10.3
+              , GHC == 9.12.2
+              , GHC == 9.14.1
 description:
     This package provides the Language.Haskell.TH.Desugar module, which desugars
     Template Haskell's rich encoding of Haskell syntax into a simpler encoding.
@@ -50,7 +51,7 @@
   build-depends:
       base >= 4.9 && < 5,
       ghc-prim,
-      template-haskell >= 2.11 && < 2.24,
+      template-haskell >= 2.11 && < 2.25,
       containers >= 0.5,
       mtl >= 2.1 && < 2.4,
       ordered-containers >= 0.2.2,
@@ -102,10 +103,8 @@
       ghc-prim,
       template-haskell,
       containers >= 0.5,
-      mtl >= 2.1,
       syb >= 0.4,
       HUnit >= 1.2,
       hspec >= 1.3,
       th-abstraction,
-      th-desugar,
-      th-orphans
+      th-desugar
