diff --git a/Changelog.md b/Changelog.md
--- a/Changelog.md
+++ b/Changelog.md
@@ -1,5 +1,26 @@
 # Change log for unwitch project
 
+## Version 3.0.0
+
+### Breaking changes (GHC only)
++ Int32.toInt is now total on GHC (Int32 -> Int instead of Int32 -> Maybe Int).
+  GHC's Int# is always at least 32 bits wide (WORD_SIZE_IN_BITS), so every
+  Int32 value fits without loss. Code pattern matching on Just/Nothing will
+  need updating.
++ On non-GHC compilers, Int32.toInt retains the Maybe Int return type since
+  the Haskell Report only guarantees Int has 30 bits.
++ CInt.toInt now delegates to Int32.toInt instead of using fromIntegral.
+
+### Added
++ Add MicroHs CI support — all exposed modules are compiled with mhs
++ Guard GHC-specific code behind CPP: unboxed types, GHC.Exts imports,
+  MagicHash/UnboxedSums/UnboxedTuples extensions, and ghc-bignum dependency
++ Guard CInt module and all toCInt functions behind __GLASGOW_HASKELL__
+  (MicroHs defines CInt as a newtype over Int, not Int32)
++ Replace GHC.Float.double2Float/float2Double with portable realToFrac
++ Replace Data.Text.all with Prelude.all for MicroHs compatibility
++ CI module list is now extracted from the cabal file automatically
+
 ## Version 2.2.0
 + New module Unwitch.Convert.CInt — conversions from CInt (Foreign.C.Types)
   to all supported numeric types (Int, Int8–64, Word, Word8–64, Integer,
diff --git a/src/Unwitch/Convert/ByteString.hs b/src/Unwitch/Convert/ByteString.hs
--- a/src/Unwitch/Convert/ByteString.hs
+++ b/src/Unwitch/Convert/ByteString.hs
@@ -5,11 +5,13 @@
   , toWord8s
   , fromWord8s
   , toTextLatin1
+#ifdef __GLASGOW_HASKELL__
   , toTextUtf8
   , toTextUtf16LE
   , toTextUtf16BE
   , toTextUtf32LE
   , toTextUtf32BE
+#endif
   )
 where
 
@@ -20,9 +22,11 @@
 import Data.ByteString.Short (ShortByteString)
 import Data.Text (Text)
 import Data.Text.Encoding qualified as TE
-import Data.Text.Encoding.Error (UnicodeException)
 import Data.Word (Word8)
+#ifdef __GLASGOW_HASKELL__
+import Data.Text.Encoding.Error (UnicodeException)
 import Unwitch.TryEvaluate (tryEvaluate)
+#endif
 
 toLazyByteString :: ByteString -> LBS.ByteString
 toLazyByteString = LBS.fromStrict
@@ -39,6 +43,7 @@
 toTextLatin1 :: ByteString -> Text
 toTextLatin1 = TE.decodeLatin1
 
+#ifdef __GLASGOW_HASKELL__
 toTextUtf8 :: ByteString -> Either UnicodeException Text
 toTextUtf8 = TE.decodeUtf8'
 
@@ -53,4 +58,4 @@
 
 toTextUtf32BE :: ByteString -> Either UnicodeException Text
 toTextUtf32BE = tryEvaluate . TE.decodeUtf32BE
-
+#endif
diff --git a/src/Unwitch/Convert/CInt.hs b/src/Unwitch/Convert/CInt.hs
--- a/src/Unwitch/Convert/CInt.hs
+++ b/src/Unwitch/Convert/CInt.hs
@@ -1,5 +1,8 @@
 -- | Conversions from 'CInt'.
+-- CInt is GHC-specific (wraps Int32 in GHC, Int in MicroHs),
+-- so this module is only available under GHC.
 module Unwitch.Convert.CInt
+#ifdef __GLASGOW_HASKELL__
   ( -- * Conversions
     toInt8
   , toInt16
@@ -16,8 +19,10 @@
   , toFloat
   , toDouble
   )
+#endif
 where
 
+#ifdef __GLASGOW_HASKELL__
 import           Unwitch.Errors
 import qualified Unwitch.Convert.Int32 as Int32
 import           Data.Word
@@ -44,7 +49,7 @@
 
 -- | Total conversion, Int is at least 32 bits wide.
 toInt :: CInt -> Int
-toInt (CInt x) = fromIntegral x
+toInt (CInt x) = Int32.toInt x
 
 -- | Total conversion to Integer.
 toInteger :: CInt -> Integer
@@ -81,3 +86,4 @@
 -- | Total conversion, all Int32 values are exactly representable as Double.
 toDouble :: CInt -> Double
 toDouble (CInt x) = Int32.toDouble x
+#endif
diff --git a/src/Unwitch/Convert/Char.hs b/src/Unwitch/Convert/Char.hs
--- a/src/Unwitch/Convert/Char.hs
+++ b/src/Unwitch/Convert/Char.hs
@@ -5,22 +5,28 @@
   , toWord
   , fromInt
   , fromWord
+#ifdef __GLASGOW_HASKELL__
   -- * Unboxed conversions
   -- $unboxed
   , fromInt#
   , fromWord#
+#endif
   )
 where
 
 import Data.Char (ord, chr)
+#ifdef __GLASGOW_HASKELL__
 import GHC.Exts (Int(..), Word(..), Char(..), chr#,
                  word2Int#, (>=#), (<=#), leWord#, gtWord#)
+#endif
 
+#ifdef __GLASGOW_HASKELL__
 -- $unboxed
 -- These use GHC unboxed types and unboxed sums for zero-allocation
 -- failure handling. Requires the @MagicHash@, @UnboxedSums@ and
 -- @UnboxedTuples@ language extensions.
 -- See the <https://downloads.haskell.org/ghc/latest/docs/users_guide/exts/primitives.html GHC manual on unboxed types>.
+#endif
 
 -- | Converts a Char to its Unicode codepoint as Int. Infallible.
 toInt :: Char -> Int
@@ -37,6 +43,14 @@
   then Just $ chr i
   else Nothing
 
+-- | Converts a Word to a Char if it is a valid Unicode codepoint.
+-- Valid range: 0..0xD7FF and 0xE000..0x10FFFF (excludes surrogates).
+fromWord :: Word -> Maybe Char
+fromWord w = if isValidCodepoint (fromIntegral w)
+  then Just $ chr (fromIntegral w)
+  else Nothing
+
+#ifdef __GLASGOW_HASKELL__
 -- | Unboxed variant of 'fromInt'. Checks valid Unicode codepoint range.
 fromInt# :: Int -> (# Char | (# #) #)
 fromInt# (I# i#) = case i# >=# 0# of
@@ -49,13 +63,6 @@
       _  -> (# | (# #) #)
   _  -> (# | (# #) #)
 
--- | Converts a Word to a Char if it is a valid Unicode codepoint.
--- Valid range: 0..0xD7FF and 0xE000..0x10FFFF (excludes surrogates).
-fromWord :: Word -> Maybe Char
-fromWord w = if isValidCodepoint (fromIntegral w)
-  then Just $ chr (fromIntegral w)
-  else Nothing
-
 -- | Unboxed variant of 'fromWord'. Checks valid Unicode codepoint range.
 fromWord# :: Word -> (# Char | (# #) #)
 fromWord# (W# w#) = case leWord# w# 0xD7FF## of
@@ -65,6 +72,7 @@
       1# -> (# C# (chr# (word2Int# w#)) | #)
       _  -> (# | (# #) #)
     _  -> (# | (# #) #)
+#endif
 
 isValidCodepoint :: Integer -> Bool
 isValidCodepoint cp =
diff --git a/src/Unwitch/Convert/Double.hs b/src/Unwitch/Convert/Double.hs
--- a/src/Unwitch/Convert/Double.hs
+++ b/src/Unwitch/Convert/Double.hs
@@ -15,7 +15,9 @@
   , toWord64
   , toWord
   , toNatural
+#ifdef __GLASGOW_HASKELL__
   , toCInt
+#endif
   , ViaIntegerErrors(..)
   , IntegerErrors(..)
   , RationalErrors(..)
@@ -25,7 +27,6 @@
 import           Data.Bifunctor(first)
 import           Data.Fixed (Fixed, HasResolution)
 import           Unwitch.Constant
-import qualified GHC.Float as F
 import           Unwitch.Convert.Ratio(unwrapIfDenominatorOne)
 import qualified Prelude
 import           Unwitch.Errors
@@ -34,11 +35,13 @@
 import Data.Word
 import Data.Int
 import Numeric.Natural (Natural)
+#ifdef __GLASGOW_HASKELL__
 import Foreign.C.Types (CInt(CInt))
+#endif
 
 -- | Lossy narrowing conversion, may lose precision.
 toFloat :: Double -> Float
-toFloat = F.double2Float
+toFloat = realToFrac
 
 -- | Converts a Double to a Fixed value. Rejects NaN and infinities.
 toFixed :: (HasResolution a) => Double -> Either RationalErrors (Fixed a)
@@ -103,9 +106,11 @@
     Left err -> Left $ MkInteger $ IntegerFlow integer err
     Right n -> Right n
 
+#ifdef __GLASGOW_HASKELL__
 -- | Converts via 'Integer', fails if not a whole number or out of range.
 toCInt :: Double -> Either ViaIntegerErrors CInt
 toCInt x = CInt <$> toInt32 x
+#endif
 
 -- | Convert via 'Integer' then narrow, combining errors.
 toViaInteger :: (Integer -> Maybe a) -> Double -> Either ViaIntegerErrors a
diff --git a/src/Unwitch/Convert/Float.hs b/src/Unwitch/Convert/Float.hs
--- a/src/Unwitch/Convert/Float.hs
+++ b/src/Unwitch/Convert/Float.hs
@@ -14,7 +14,9 @@
   , toWord64
   , toWord
   , toNatural
+#ifdef __GLASGOW_HASKELL__
   , toCInt
+#endif
   , ViaIntegerErrors(..)
   , IntegerErrors(..)
   , RationalErrors(..)
@@ -23,7 +25,6 @@
 
 import           Data.Bifunctor(first)
 import           Unwitch.Constant
-import qualified GHC.Float as F
 import           Unwitch.Convert.Ratio(unwrapIfDenominatorOne)
 import qualified Prelude
 import           Unwitch.Errors
@@ -32,10 +33,12 @@
 import Data.Word
 import Data.Int
 import Numeric.Natural (Natural)
+#ifdef __GLASGOW_HASKELL__
 import Foreign.C.Types (CInt(CInt))
+#endif
 
 toDouble :: Float -> Double
-toDouble = F.float2Double
+toDouble = realToFrac
 
 data IntegerErrors = IntegerFlow Integer Overflows
                    | RationalConversion RationalErrors
@@ -100,9 +103,11 @@
     Left err -> Left $ MkInteger $ IntegerFlow integer err
     Right n -> Right n
 
+#ifdef __GLASGOW_HASKELL__
 -- | Converts via 'Integer', fails if not a whole number or out of range.
 toCInt :: Float -> Either ViaIntegerErrors CInt
 toCInt x = CInt <$> toInt32 x
+#endif
 
 -- | Convert via 'Integer' then narrow, combining errors.
 toViaInteger :: (Integer -> Maybe a) -> Float -> Either ViaIntegerErrors a
diff --git a/src/Unwitch/Convert/Int.hs b/src/Unwitch/Convert/Int.hs
--- a/src/Unwitch/Convert/Int.hs
+++ b/src/Unwitch/Convert/Int.hs
@@ -14,7 +14,10 @@
   , toNatural
   , toFloat
   , toDouble
+#ifdef __GLASGOW_HASKELL__
   , toCInt
+#endif
+#ifdef __GLASGOW_HASKELL__
   -- * Unboxed conversions
   -- $unboxed
   , toInt8#
@@ -28,6 +31,7 @@
   , toNatural#
   , toFloat#
   , toDouble#
+#endif
   )
 where
 
@@ -37,8 +41,9 @@
 import           Data.Word
 import           Data.Int
 import           Numeric.Natural (Natural)
-import           Foreign.C.Types (CInt(CInt))
 import           Prelude hiding (toInteger)
+#ifdef __GLASGOW_HASKELL__
+import           Foreign.C.Types (CInt(CInt))
 import           GHC.Exts (Int(..), Word(..), Float(..), Double(..),
                            intToInt8#, int8ToInt#, intToInt16#, int16ToInt#,
                            intToInt32#, int32ToInt#,
@@ -52,12 +57,15 @@
 import           GHC.Int (Int8(..), Int16(..), Int32(..))
 import           GHC.Word (Word8(..), Word16(..), Word32(..), Word64(..))
 import           GHC.Num.Natural (Natural(NS))
+#endif
 
+#ifdef __GLASGOW_HASKELL__
 -- $unboxed
 -- These use GHC unboxed types and unboxed sums for zero-allocation
 -- failure handling. Requires the @MagicHash@, @UnboxedSums@ and
 -- @UnboxedTuples@ language extensions.
 -- See the <https://downloads.haskell.org/ghc/latest/docs/users_guide/exts/primitives.html GHC manual on unboxed types>.
+#endif
 
 toInt8 :: Int -> Maybe Int8
 toInt8 = Bits.toIntegralSized
@@ -102,9 +110,11 @@
   | x > maxIntegralRepFloat  -> Left Overflow
   | otherwise                -> Right $ fromIntegral x
 
+#ifdef __GLASGOW_HASKELL__
 -- | Narrowing conversion via Int32, fails if outside Int32 range.
 toCInt :: Int -> Maybe CInt
 toCInt x = CInt <$> toInt32 x
+#endif
 
 -- | Checked conversion, fails if outside exact double integer range (+/-9007199254740991).
 toDouble :: Int -> Either Overflows Double
@@ -113,6 +123,7 @@
   | fromIntegral x > (maxIntegralRepDouble :: Integer)  -> Left Overflow
   | otherwise                                           -> Right $ fromIntegral x
 
+#ifdef __GLASGOW_HASKELL__
 -- | Signed narrowing, roundtrip at Int#
 toInt8# :: Int -> (# Int8 | (# #) #)
 toInt8# (I# x#) =
@@ -194,3 +205,4 @@
   _  -> case i# ># 9007199254740991# of
     1# -> (# Overflow | #)
     _  -> (# | D# (int2Double# i#) #)
+#endif
diff --git a/src/Unwitch/Convert/Int16.hs b/src/Unwitch/Convert/Int16.hs
--- a/src/Unwitch/Convert/Int16.hs
+++ b/src/Unwitch/Convert/Int16.hs
@@ -14,7 +14,10 @@
   , toNatural
   , toFloat
   , toDouble
+#ifdef __GLASGOW_HASKELL__
   , toCInt
+#endif
+#ifdef __GLASGOW_HASKELL__
   -- * Unboxed conversions
   -- $unboxed
   , toInt8#
@@ -24,6 +27,7 @@
   , toWord64#
   , toWord#
   , toNatural#
+#endif
   )
 where
 
@@ -32,8 +36,9 @@
 import           Data.Word
 import           Data.Int
 import           Numeric.Natural (Natural)
-import           Foreign.C.Types (CInt(CInt))
 import           Prelude hiding (toInteger)
+#ifdef __GLASGOW_HASKELL__
+import           Foreign.C.Types (CInt(CInt))
 import           GHC.Exts (Word(..), int16ToInt#, intToInt8#, int8ToInt#,
                            int2Word#, word2Int#,
                            wordToWord8#, word8ToWord#,
@@ -42,12 +47,15 @@
 import           GHC.Int (Int8(..), Int16(..))
 import           GHC.Word (Word8(..), Word16(..), Word32(..), Word64(..))
 import           GHC.Num.Natural (Natural(NS))
+#endif
 
+#ifdef __GLASGOW_HASKELL__
 -- $unboxed
 -- These use GHC unboxed types and unboxed sums for zero-allocation
 -- failure handling. Requires the @MagicHash@, @UnboxedSums@ and
 -- @UnboxedTuples@ language extensions.
 -- See the <https://downloads.haskell.org/ghc/latest/docs/users_guide/exts/primitives.html GHC manual on unboxed types>.
+#endif
 
 toInt8 :: Int16 -> Maybe Int8
 toInt8 = Bits.toIntegralSized
@@ -85,9 +93,11 @@
   | x < 0     -> Left Underflow
   | otherwise  -> Right $ fromIntegral x
 
+#ifdef __GLASGOW_HASKELL__
 -- | Widening conversion via Int32, always succeeds.
 toCInt :: Int16 -> CInt
 toCInt x = CInt $ toInt32 x
+#endif
 
 toFloat :: Int16 -> Float
 toFloat = fromIntegral
@@ -95,6 +105,7 @@
 toDouble :: Int16 -> Double
 toDouble = fromIntegral
 
+#ifdef __GLASGOW_HASKELL__
 -- | Signed narrowing, roundtrip at Int#
 toInt8# :: Int16 -> (# Int8 | (# #) #)
 toInt8# (I16# x16#) =
@@ -142,3 +153,4 @@
 toNatural# (I16# x16#) = case int16ToInt# x16# >=# 0# of
   1# -> (# | NS (int2Word# (int16ToInt# x16#)) #)
   _  -> (# Underflow | #)
+#endif
diff --git a/src/Unwitch/Convert/Int32.hs b/src/Unwitch/Convert/Int32.hs
--- a/src/Unwitch/Convert/Int32.hs
+++ b/src/Unwitch/Convert/Int32.hs
@@ -14,7 +14,10 @@
   , toNatural
   , toFloat
   , toDouble
+#ifdef __GLASGOW_HASKELL__
   , toCInt
+#endif
+#ifdef __GLASGOW_HASKELL__
   -- * Unboxed conversions
   -- $unboxed
   , toInt8#
@@ -27,6 +30,7 @@
   , toWord#
   , toNatural#
   , toFloat#
+#endif
   )
 where
 
@@ -36,8 +40,9 @@
 import           Data.Word
 import           Data.Int
 import           Numeric.Natural (Natural)
-import           Foreign.C.Types (CInt(CInt))
 import           Prelude hiding (toInteger)
+#ifdef __GLASGOW_HASKELL__
+import           Foreign.C.Types (CInt(CInt))
 import           GHC.Exts (Int(..), Word(..), Float(..),
                            int32ToInt#, intToInt8#, int8ToInt#,
                            intToInt16#, int16ToInt#,
@@ -50,12 +55,15 @@
 import           GHC.Int (Int8(..), Int16(..), Int32(..))
 import           GHC.Word (Word8(..), Word16(..), Word32(..), Word64(..))
 import           GHC.Num.Natural (Natural(NS))
+#endif
 
+#ifdef __GLASGOW_HASKELL__
 -- $unboxed
 -- These use GHC unboxed types and unboxed sums for zero-allocation
 -- failure handling. Requires the @MagicHash@, @UnboxedSums@ and
 -- @UnboxedTuples@ language extensions.
 -- See the <https://downloads.haskell.org/ghc/latest/docs/users_guide/exts/primitives.html GHC manual on unboxed types>.
+#endif
 
 toInt8 :: Int32 -> Maybe Int8
 toInt8 = Bits.toIntegralSized
@@ -66,8 +74,26 @@
 toInt64 :: Int32 -> Int64
 toInt64 = fromIntegral
 
+#ifdef __GLASGOW_HASKELL__
+-- | Total conversion — GHC implements 'Int' using the primitive type
+-- @Int#@, whose size equals the @MachDeps.h@ constant
+-- @WORD_SIZE_IN_BITS@ (32 on 32-bit platforms, 64 on 64-bit).
+-- Since @Int#@ is always at least 32 bits wide, every 'Int32' value
+-- fits without loss.
+--
+-- See: <https://hackage.haskell.org/package/ghc-prim-0.9.0/docs/src/GHC.Prim.html GHC.Prim>
+-- ("Int32 always fits in Int (Int is at least 32 bits)") and
+-- <https://hackage.haskell.org/package/base/docs/Data-Int.html Data.Int>
+-- ("A fixed-precision integer type with at least the range [-2^29 .. 2^29-1]").
+toInt :: Int32 -> Int
+toInt (I32# x#) = I# (int32ToInt# x#)
+#else
+-- | Fallible conversion — the Haskell Report only guarantees 'Int' has
+-- at least the range @[-2^29 .. 2^29-1]@ (30 bits), so 'Int32' values
+-- outside that range may not fit on non-GHC compilers.
 toInt :: Int32 -> Maybe Int
 toInt = Bits.toIntegralSized
+#endif
 
 toInteger :: Int32 -> Integer
 toInteger = fromIntegral
@@ -100,13 +126,16 @@
   | x > maxIntegralRepFloat  -> Left Overflow
   | otherwise                -> Right $ fromIntegral x
 
+#ifdef __GLASGOW_HASKELL__
 -- | Direct wrapping, CInt is a newtype over Int32.
 toCInt :: Int32 -> CInt
 toCInt = CInt
+#endif
 
 toDouble :: Int32 -> Double
 toDouble = fromIntegral
 
+#ifdef __GLASGOW_HASKELL__
 -- | Signed narrowing, roundtrip at Int#
 toInt8# :: Int32 -> (# Int8 | (# #) #)
 toInt8# (I32# x32#) =
@@ -180,3 +209,4 @@
     _  -> case i# ># 16777215# of
       1# -> (# Overflow | #)
       _  -> (# | F# (int2Float# i#) #)
+#endif
diff --git a/src/Unwitch/Convert/Int64.hs b/src/Unwitch/Convert/Int64.hs
--- a/src/Unwitch/Convert/Int64.hs
+++ b/src/Unwitch/Convert/Int64.hs
@@ -14,7 +14,10 @@
   , toNatural
   , toFloat
   , toDouble
+#ifdef __GLASGOW_HASKELL__
   , toCInt
+#endif
+#ifdef __GLASGOW_HASKELL__
   -- * Unboxed conversions
   -- $unboxed
   , toInt8#
@@ -29,6 +32,7 @@
   , toNatural#
   , toFloat#
   , toDouble#
+#endif
   )
 where
 
@@ -38,8 +42,9 @@
 import           Data.Word
 import           Data.Int
 import           Numeric.Natural (Natural)
-import           Foreign.C.Types (CInt(CInt))
 import           Prelude hiding (toInteger)
+#ifdef __GLASGOW_HASKELL__
+import           Foreign.C.Types (CInt(CInt))
 import           GHC.Exts (Int(..), Word(..), Float(..), Double(..),
                            int64ToInt#, intToInt64#,
                            intToInt8#, int8ToInt#,
@@ -56,12 +61,15 @@
 import           GHC.Int (Int8(..), Int16(..), Int32(..), Int64(..))
 import           GHC.Word (Word8(..), Word16(..), Word32(..), Word64(..))
 import           GHC.Num.Natural (Natural(NS))
+#endif
 
+#ifdef __GLASGOW_HASKELL__
 -- $unboxed
 -- These use GHC unboxed types and unboxed sums for zero-allocation
 -- failure handling. Requires the @MagicHash@, @UnboxedSums@ and
 -- @UnboxedTuples@ language extensions.
 -- See the <https://downloads.haskell.org/ghc/latest/docs/users_guide/exts/primitives.html GHC manual on unboxed types>.
+#endif
 
 toInt8 :: Int64 -> Maybe Int8
 toInt8 = Bits.toIntegralSized
@@ -75,9 +83,11 @@
 toInt :: Int64 -> Maybe Int
 toInt = Bits.toIntegralSized
 
+#ifdef __GLASGOW_HASKELL__
 -- | Narrowing conversion via Int32, fails if outside Int32 range.
 toCInt :: Int64 -> Maybe CInt
 toCInt x = CInt <$> toInt32 x
+#endif
 
 toInteger :: Int64 -> Integer
 toInteger = fromIntegral
@@ -117,6 +127,7 @@
   | x > maxIntegralRepDouble  -> Left Overflow
   | otherwise                 -> Right $ fromIntegral x
 
+#ifdef __GLASGOW_HASKELL__
 -- | Narrow through Int#, compare at Int64#
 toInt8# :: Int64 -> (# Int8 | (# #) #)
 toInt8# (I64# x64#) =
@@ -230,3 +241,4 @@
     _  -> case geInt64# x64# (intToInt64# 0#) of
       1# -> (# Overflow | #)
       _  -> (# Underflow | #)
+#endif
diff --git a/src/Unwitch/Convert/Int8.hs b/src/Unwitch/Convert/Int8.hs
--- a/src/Unwitch/Convert/Int8.hs
+++ b/src/Unwitch/Convert/Int8.hs
@@ -14,7 +14,10 @@
   , toNatural
   , toFloat
   , toDouble
+#ifdef __GLASGOW_HASKELL__
   , toCInt
+#endif
+#ifdef __GLASGOW_HASKELL__
   -- * Unboxed conversions
   -- $unboxed
   , toWord8#
@@ -23,6 +26,7 @@
   , toWord64#
   , toWord#
   , toNatural#
+#endif
   )
 where
 
@@ -31,20 +35,24 @@
 import           Data.Word
 import           Data.Int
 import           Numeric.Natural (Natural)
-import           Foreign.C.Types (CInt(CInt))
 import           Prelude hiding (toInteger)
+#ifdef __GLASGOW_HASKELL__
+import           Foreign.C.Types (CInt(CInt))
 import           GHC.Exts (Word(..), int8ToInt#, int2Word#,
                            wordToWord8#, wordToWord16#, wordToWord32#,
                            wordToWord64#, (>=#))
 import           GHC.Int (Int8(..))
 import           GHC.Word (Word8(..), Word16(..), Word32(..), Word64(..))
 import           GHC.Num.Natural (Natural(NS))
+#endif
 
+#ifdef __GLASGOW_HASKELL__
 -- $unboxed
 -- These use GHC unboxed types and unboxed sums for zero-allocation
 -- failure handling. Requires the @MagicHash@, @UnboxedSums@ and
 -- @UnboxedTuples@ language extensions.
 -- See the <https://downloads.haskell.org/ghc/latest/docs/users_guide/exts/primitives.html GHC manual on unboxed types>.
+#endif
 
 toInt16 :: Int8 -> Int16
 toInt16 = fromIntegral
@@ -82,9 +90,11 @@
   | x < 0     -> Left Underflow
   | otherwise  -> Right $ fromIntegral x
 
+#ifdef __GLASGOW_HASKELL__
 -- | Widening conversion via Int32, always succeeds.
 toCInt :: Int8 -> CInt
 toCInt x = CInt $ toInt32 x
+#endif
 
 toFloat :: Int8 -> Float
 toFloat = fromIntegral
@@ -92,6 +102,7 @@
 toDouble :: Int8 -> Double
 toDouble = fromIntegral
 
+#ifdef __GLASGOW_HASKELL__
 -- | Signed->unsigned, check non-negative
 toWord8# :: Int8 -> (# Word8 | (# #) #)
 toWord8# (I8# x8#) = case int8ToInt# x8# >=# 0# of
@@ -127,3 +138,4 @@
 toNatural# (I8# x8#) = case int8ToInt# x8# >=# 0# of
   1# -> (# | NS (int2Word# (int8ToInt# x8#)) #)
   _  -> (# Underflow | #)
+#endif
diff --git a/src/Unwitch/Convert/Integer.hs b/src/Unwitch/Convert/Integer.hs
--- a/src/Unwitch/Convert/Integer.hs
+++ b/src/Unwitch/Convert/Integer.hs
@@ -14,7 +14,10 @@
   , toWord32
   , toWord64
   , toWord
+#ifdef __GLASGOW_HASKELL__
   , toCInt
+#endif
+#ifdef __GLASGOW_HASKELL__
   -- * Unboxed conversions
   -- $unboxed
   , toDouble#
@@ -30,6 +33,7 @@
   , toWord32#
   , toWord64#
   , toWord#
+#endif
   )
 where
 
@@ -39,6 +43,7 @@
 import Data.Word
 import Data.Int
 import Numeric.Natural (Natural)
+#ifdef __GLASGOW_HASKELL__
 import Foreign.C.Types (CInt(CInt))
 import           GHC.Exts (Int(..), Word(..), Float(..), Double(..),
                            intToInt8#, int8ToInt#,
@@ -57,21 +62,24 @@
 import           GHC.Num.Integer (Integer(..), integerToWord#,
                                   integerFromWord#, integerEq#)
 import           GHC.Num.Natural (Natural(NS, NB))
+#endif
 
+#ifdef __GLASGOW_HASKELL__
 -- $unboxed
 -- These use GHC unboxed types and unboxed sums for zero-allocation
 -- failure handling. Requires the @MagicHash@, @UnboxedSums@ and
 -- @UnboxedTuples@ language extensions.
 -- See the <https://downloads.haskell.org/ghc/latest/docs/users_guide/exts/primitives.html GHC manual on unboxed types>.
+#endif
 
--- | Checked conversion, fails if outside exact double integer range (\u00b19007199254740991).
+-- | Checked conversion, fails if outside exact double integer range (±9007199254740991).
 toDouble :: Integer -> Either Overflows Double
 toDouble integer = if
     | integer < -maxIntegralRepDouble -> Left Underflow
     | integer > maxIntegralRepDouble -> Left Overflow
     | otherwise -> Right $ Prelude.fromIntegral integer
 
--- | Checked conversion, fails if outside exact float integer range (\u00b116777215).
+-- | Checked conversion, fails if outside exact float integer range (±16777215).
 toFloat :: Integer -> Either Overflows Float
 toFloat integer = if
     | integer < -maxIntegralRepFloat -> Left Underflow
@@ -114,10 +122,13 @@
 toWord :: Integer -> Maybe Word
 toWord = Bits.toIntegralSized
 
+#ifdef __GLASGOW_HASKELL__
 -- | Narrowing conversion via Int32, fails if outside Int32 range.
 toCInt :: Integer -> Maybe CInt
 toCInt x = CInt <$> toInt32 x
+#endif
 
+#ifdef __GLASGOW_HASKELL__
 -- | Bounds-checked double conversion via IS/IP/IN
 toDouble# :: Integer -> (# Overflows | Double #)
 toDouble# x = case x of
@@ -254,3 +265,4 @@
       1# -> (# W# w# | #)
       _  -> (# | (# #) #)
   IN _ -> (# | (# #) #)
+#endif
diff --git a/src/Unwitch/Convert/LazyByteString.hs b/src/Unwitch/Convert/LazyByteString.hs
--- a/src/Unwitch/Convert/LazyByteString.hs
+++ b/src/Unwitch/Convert/LazyByteString.hs
@@ -3,22 +3,26 @@
   ( toByteString
   , toWord8s
   , fromWord8s
+#ifdef __GLASGOW_HASKELL__
   , toLazyTextLatin1
   , toLazyTextUtf8
   , toLazyTextUtf16LE
   , toLazyTextUtf16BE
   , toLazyTextUtf32LE
   , toLazyTextUtf32BE
+#endif
   )
 where
 
 import Data.ByteString (ByteString)
 import Data.ByteString.Lazy qualified as LBS
+import Data.Word (Word8)
+#ifdef __GLASGOW_HASKELL__
 import Data.Text.Lazy qualified as LT
 import Data.Text.Lazy.Encoding qualified as LTE
 import Data.Text.Encoding.Error (UnicodeException)
-import Data.Word (Word8)
 import Unwitch.TryEvaluate (tryEvaluate)
+#endif
 
 toByteString :: LBS.ByteString -> ByteString
 toByteString = LBS.toStrict
@@ -29,6 +33,7 @@
 fromWord8s :: [Word8] -> LBS.ByteString
 fromWord8s = LBS.pack
 
+#ifdef __GLASGOW_HASKELL__
 toLazyTextLatin1 :: LBS.ByteString -> LT.Text
 toLazyTextLatin1 = LTE.decodeLatin1
 
@@ -46,4 +51,4 @@
 
 toLazyTextUtf32BE :: LBS.ByteString -> Either UnicodeException LT.Text
 toLazyTextUtf32BE = tryEvaluate . LTE.decodeUtf32BE
-
+#endif
diff --git a/src/Unwitch/Convert/LazyText.hs b/src/Unwitch/Convert/LazyText.hs
--- a/src/Unwitch/Convert/LazyText.hs
+++ b/src/Unwitch/Convert/LazyText.hs
@@ -3,20 +3,24 @@
   ( toText
   , toString
   , fromString
+#ifdef __GLASGOW_HASKELL__
   , toLazyByteStringUtf8
   , toLazyByteStringUtf16LE
   , toLazyByteStringUtf16BE
   , toLazyByteStringUtf32LE
   , toLazyByteStringUtf32BE
   , toLazyByteStringLatin1
+#endif
   )
 where
 
-import Data.ByteString.Lazy qualified as LBS
-import Data.ByteString.Lazy.Char8 qualified as LBSC8
 import Data.Text (Text)
 import Data.Text.Lazy qualified as LT
+#ifdef __GLASGOW_HASKELL__
+import Data.ByteString.Lazy qualified as LBS
+import Data.ByteString.Lazy.Char8 qualified as LBSC8
 import Data.Text.Lazy.Encoding qualified as LTE
+#endif
 
 toText :: LT.Text -> Text
 toText = LT.toStrict
@@ -27,6 +31,7 @@
 fromString :: String -> LT.Text
 fromString = LT.pack
 
+#ifdef __GLASGOW_HASKELL__
 toLazyByteStringUtf8 :: LT.Text -> LBS.ByteString
 toLazyByteStringUtf8 = LTE.encodeUtf8
 
@@ -50,3 +55,4 @@
 
 isLatin1 :: Char -> Bool
 isLatin1 c = c <= '\xFF'
+#endif
diff --git a/src/Unwitch/Convert/Natural.hs b/src/Unwitch/Convert/Natural.hs
--- a/src/Unwitch/Convert/Natural.hs
+++ b/src/Unwitch/Convert/Natural.hs
@@ -14,7 +14,10 @@
   , toInteger
   , toFloat
   , toDouble
+#ifdef __GLASGOW_HASKELL__
   , toCInt
+#endif
+#ifdef __GLASGOW_HASKELL__
   -- * Unboxed conversions
   -- $unboxed
   , toWord8#
@@ -29,6 +32,7 @@
   , toInt#
   , toFloat#
   , toDouble#
+#endif
   )
 where
 
@@ -38,8 +42,9 @@
 import           Data.Word
 import           Data.Int
 import           Numeric.Natural (Natural)
-import           Foreign.C.Types (CInt(CInt))
 import           Prelude hiding (toInteger)
+#ifdef __GLASGOW_HASKELL__
+import           Foreign.C.Types (CInt(CInt))
 import           GHC.Exts (Int(..), Word(..), Float(..), Double(..),
                            word2Int#,
                            wordToWord8#, word8ToWord#,
@@ -55,12 +60,15 @@
 import           GHC.Int (Int8(..), Int16(..), Int32(..), Int64(..))
 import           GHC.Word (Word8(..), Word16(..), Word32(..), Word64(..))
 import           GHC.Num.Natural (naturalToWordMaybe#)
+#endif
 
+#ifdef __GLASGOW_HASKELL__
 -- $unboxed
 -- These use GHC unboxed types and unboxed sums for zero-allocation
 -- failure handling. Requires the @MagicHash@, @UnboxedSums@ and
 -- @UnboxedTuples@ language extensions.
 -- See the <https://downloads.haskell.org/ghc/latest/docs/users_guide/exts/primitives.html GHC manual on unboxed types>.
+#endif
 
 toWord8 :: Natural -> Maybe Word8
 toWord8 = Bits.toIntegralSized
@@ -95,22 +103,25 @@
 toInteger :: Natural -> Integer
 toInteger = fromIntegral
 
+#ifdef __GLASGOW_HASKELL__
 -- | Narrowing conversion via Int32, fails if outside Int32 range.
 toCInt :: Natural -> Maybe CInt
 toCInt x = CInt <$> toInt32 x
+#endif
 
--- | Checked conversion, fails if outside exact float integer range (\u00b116777215).
+-- | Checked conversion, fails if outside exact float integer range (±16777215).
 toFloat :: Natural -> Either Overflows Float
 toFloat x = if
   | x > maxIntegralRepFloat -> Left Overflow
   | otherwise               -> Right $ fromIntegral x
 
--- | Checked conversion, fails if outside exact double integer range (\u00b19007199254740991).
+-- | Checked conversion, fails if outside exact double integer range (±9007199254740991).
 toDouble :: Natural -> Either Overflows Double
 toDouble x = if
   | x > maxIntegralRepDouble -> Left Overflow
   | otherwise                -> Right $ fromIntegral x
 
+#ifdef __GLASGOW_HASKELL__
 -- | Via naturalToWordMaybe#, then narrow and roundtrip at Word#
 toWord8# :: Natural -> (# Word8 | (# #) #)
 toWord8# nat = case naturalToWordMaybe# nat of
@@ -212,3 +223,4 @@
   (# | w# #) -> case leWord# w# 9007199254740991## of
     1# -> (# | D# (int2Double# (word2Int# w#)) #)
     _  -> (# Overflow | #)
+#endif
diff --git a/src/Unwitch/Convert/Text.hs b/src/Unwitch/Convert/Text.hs
--- a/src/Unwitch/Convert/Text.hs
+++ b/src/Unwitch/Convert/Text.hs
@@ -20,7 +20,11 @@
 import Data.Text.Lazy qualified as LT
 
 toLazyText :: Text -> LT.Text
+#ifdef __GLASGOW_HASKELL__
 toLazyText = LT.fromStrict
+#else
+toLazyText = LT.toLazy
+#endif
 
 toString :: Text -> String
 toString = T.unpack
@@ -45,10 +49,10 @@
 
 -- | Returns 'Nothing' if any character exceeds @\xFF@.
 toByteStringLatin1 :: Text -> Maybe ByteString
-toByteStringLatin1 t = if T.all isLatin1 t
-  then Just $ BSC8.pack (T.unpack t)
+toByteStringLatin1 t = if all isLatin1 str
+  then Just $ BSC8.pack str
   else Nothing
+  where str = T.unpack t
 
 isLatin1 :: Char -> Bool
 isLatin1 c = c <= '\xFF'
-
diff --git a/src/Unwitch/Convert/Word.hs b/src/Unwitch/Convert/Word.hs
--- a/src/Unwitch/Convert/Word.hs
+++ b/src/Unwitch/Convert/Word.hs
@@ -14,7 +14,10 @@
   , toInteger
   , toFloat
   , toDouble
+#ifdef __GLASGOW_HASKELL__
   , toCInt
+#endif
+#ifdef __GLASGOW_HASKELL__
   -- * Unboxed conversions
   -- $unboxed
   , toWord8#
@@ -27,6 +30,7 @@
   , toInt#
   , toFloat#
   , toDouble#
+#endif
   )
 where
 
@@ -36,8 +40,9 @@
 import           Data.Word
 import           Data.Int
 import           Numeric.Natural (Natural)
-import           Foreign.C.Types (CInt(CInt))
 import           Prelude hiding (toInteger)
+#ifdef __GLASGOW_HASKELL__
+import           Foreign.C.Types (CInt(CInt))
 import           GHC.Exts (Int(..), Word(..), Float(..), Double(..),
                            wordToWord8#, word8ToWord#,
                            wordToWord16#, word16ToWord#,
@@ -49,12 +54,15 @@
                            eqWord#, leWord#, (>=#))
 import           GHC.Int (Int8(..), Int16(..), Int32(..), Int64(..))
 import           GHC.Word (Word8(..), Word16(..), Word32(..))
+#endif
 
+#ifdef __GLASGOW_HASKELL__
 -- $unboxed
 -- These use GHC unboxed types and unboxed sums for zero-allocation
 -- failure handling. Requires the @MagicHash@, @UnboxedSums@ and
 -- @UnboxedTuples@ language extensions.
 -- See the <https://downloads.haskell.org/ghc/latest/docs/users_guide/exts/primitives.html GHC manual on unboxed types>.
+#endif
 
 toWord8 :: Word -> Maybe Word8
 toWord8 = Bits.toIntegralSized
@@ -89,9 +97,11 @@
 toInteger :: Word -> Integer
 toInteger = fromIntegral
 
+#ifdef __GLASGOW_HASKELL__
 -- | Narrowing conversion via Int32, fails if outside Int32 range.
 toCInt :: Word -> Maybe CInt
 toCInt x = CInt <$> toInt32 x
+#endif
 
 -- | Checked conversion, fails if outside exact float integer range (+-16777215).
 toFloat :: Word -> Either Overflows Float
@@ -105,6 +115,7 @@
   | fromIntegral x > (maxIntegralRepDouble :: Integer) -> Left Overflow
   | otherwise                                          -> Right $ fromIntegral x
 
+#ifdef __GLASGOW_HASKELL__
 -- | Unsigned narrowing, roundtrip at Word#
 toWord8# :: Word -> (# Word8 | (# #) #)
 toWord8# (W# w#) =
@@ -174,3 +185,4 @@
 toDouble# (W# w#) = case leWord# w# 9007199254740991## of
   1# -> (# | D# (int2Double# (word2Int# w#)) #)
   _  -> (# Overflow | #)
+#endif
diff --git a/src/Unwitch/Convert/Word16.hs b/src/Unwitch/Convert/Word16.hs
--- a/src/Unwitch/Convert/Word16.hs
+++ b/src/Unwitch/Convert/Word16.hs
@@ -14,12 +14,16 @@
   , toInteger
   , toFloat
   , toDouble
+#ifdef __GLASGOW_HASKELL__
   , toCInt
+#endif
+#ifdef __GLASGOW_HASKELL__
   -- * Unboxed conversions
   -- $unboxed
   , toWord8#
   , toInt8#
   , toInt16#
+#endif
   )
 where
 
@@ -27,8 +31,9 @@
 import           Data.Word
 import           Data.Int
 import           Numeric.Natural (Natural)
-import           Foreign.C.Types (CInt(CInt))
 import           Prelude hiding (toInteger)
+#ifdef __GLASGOW_HASKELL__
+import           Foreign.C.Types (CInt(CInt))
 import           GHC.Exts (word16ToWord#, word2Int#,
                            wordToWord8#, word8ToWord#,
                            intToInt8#, int8ToInt#,
@@ -36,12 +41,15 @@
                            eqWord#, (==#))
 import           GHC.Int (Int8(..), Int16(..))
 import           GHC.Word (Word8(..), Word16(..))
+#endif
 
+#ifdef __GLASGOW_HASKELL__
 -- $unboxed
 -- These use GHC unboxed types and unboxed sums for zero-allocation
 -- failure handling. Requires the @MagicHash@, @UnboxedSums@ and
 -- @UnboxedTuples@ language extensions.
 -- See the <https://downloads.haskell.org/ghc/latest/docs/users_guide/exts/primitives.html GHC manual on unboxed types>.
+#endif
 
 toWord8 :: Word16 -> Maybe Word8
 toWord8 = Bits.toIntegralSized
@@ -82,10 +90,13 @@
 toDouble :: Word16 -> Double
 toDouble = fromIntegral
 
+#ifdef __GLASGOW_HASKELL__
 -- | Widening conversion via Int32, always succeeds.
 toCInt :: Word16 -> CInt
 toCInt x = CInt $ toInt32 x
+#endif
 
+#ifdef __GLASGOW_HASKELL__
 -- | Unsigned narrowing, roundtrip at Word#
 toWord8# :: Word16 -> (# Word8 | (# #) #)
 toWord8# (W16# w16#) =
@@ -112,3 +123,4 @@
   in case int16ToInt# n# ==# i# of
     1# -> (# I16# n# | #)
     _  -> (# | (# #) #)
+#endif
diff --git a/src/Unwitch/Convert/Word32.hs b/src/Unwitch/Convert/Word32.hs
--- a/src/Unwitch/Convert/Word32.hs
+++ b/src/Unwitch/Convert/Word32.hs
@@ -14,7 +14,10 @@
   , toInteger
   , toFloat
   , toDouble
+#ifdef __GLASGOW_HASKELL__
   , toCInt
+#endif
+#ifdef __GLASGOW_HASKELL__
   -- * Unboxed conversions
   -- $unboxed
   , toWord8#
@@ -25,6 +28,7 @@
   , toInt32#
   , toInt#
   , toFloat#
+#endif
   )
 where
 
@@ -34,8 +38,9 @@
 import           Data.Word
 import           Data.Int
 import           Numeric.Natural (Natural)
-import           Foreign.C.Types (CInt(CInt))
 import           Prelude hiding (toInteger)
+#ifdef __GLASGOW_HASKELL__
+import           Foreign.C.Types (CInt(CInt))
 import           GHC.Exts (Int(..), Word(..), Float(..),
                            word32ToWord#, word2Int#,
                            wordToWord8#, word8ToWord#,
@@ -47,12 +52,15 @@
                            eqWord#, leWord#, (==#), (>=#))
 import           GHC.Int (Int8(..), Int16(..), Int32(..))
 import           GHC.Word (Word8(..), Word16(..), Word32(..))
+#endif
 
+#ifdef __GLASGOW_HASKELL__
 -- $unboxed
 -- These use GHC unboxed types and unboxed sums for zero-allocation
 -- failure handling. Requires the @MagicHash@, @UnboxedSums@ and
 -- @UnboxedTuples@ language extensions.
 -- See the <https://downloads.haskell.org/ghc/latest/docs/users_guide/exts/primitives.html GHC manual on unboxed types>.
+#endif
 
 toWord8 :: Word32 -> Maybe Word8
 toWord8 = Bits.toIntegralSized
@@ -96,10 +104,13 @@
 toDouble :: Word32 -> Double
 toDouble = fromIntegral
 
+#ifdef __GLASGOW_HASKELL__
 -- | Narrowing conversion via Int32, fails if outside Int32 range.
 toCInt :: Word32 -> Maybe CInt
 toCInt x = CInt <$> toInt32 x
+#endif
 
+#ifdef __GLASGOW_HASKELL__
 -- | Unsigned narrowing, roundtrip at Word#
 toWord8# :: Word32 -> (# Word8 | (# #) #)
 toWord8# (W32# w32#) =
@@ -162,3 +173,4 @@
 toFloat# (W32# w32#) = case leWord# (word32ToWord# w32#) 16777215## of
   1# -> (# | F# (int2Float# (word2Int# (word32ToWord# w32#))) #)
   _  -> (# Overflow | #)
+#endif
diff --git a/src/Unwitch/Convert/Word64.hs b/src/Unwitch/Convert/Word64.hs
--- a/src/Unwitch/Convert/Word64.hs
+++ b/src/Unwitch/Convert/Word64.hs
@@ -14,7 +14,10 @@
   , toInteger
   , toFloat
   , toDouble
+#ifdef __GLASGOW_HASKELL__
   , toCInt
+#endif
+#ifdef __GLASGOW_HASKELL__
   -- * Unboxed conversions
   -- $unboxed
   , toWord8#
@@ -28,6 +31,7 @@
   , toInt#
   , toFloat#
   , toDouble#
+#endif
   )
 where
 
@@ -37,8 +41,9 @@
 import           Data.Word
 import           Data.Int
 import           Numeric.Natural (Natural)
-import           Foreign.C.Types (CInt(CInt))
 import           Prelude hiding (toInteger)
+#ifdef __GLASGOW_HASKELL__
+import           Foreign.C.Types (CInt(CInt))
 import           GHC.Exts (Int(..), Word(..), Float(..), Double(..),
                            word64ToWord#, wordToWord64#,
                            word2Int#,
@@ -52,12 +57,15 @@
                            (>=#))
 import           GHC.Int (Int8(..), Int16(..), Int32(..), Int64(..))
 import           GHC.Word (Word8(..), Word16(..), Word32(..), Word64(..))
+#endif
 
+#ifdef __GLASGOW_HASKELL__
 -- $unboxed
 -- These use GHC unboxed types and unboxed sums for zero-allocation
 -- failure handling. Requires the @MagicHash@, @UnboxedSums@ and
 -- @UnboxedTuples@ language extensions.
 -- See the <https://downloads.haskell.org/ghc/latest/docs/users_guide/exts/primitives.html GHC manual on unboxed types>.
+#endif
 
 toWord8 :: Word64 -> Maybe Word8
 toWord8 = Bits.toIntegralSized
@@ -92,9 +100,11 @@
 toInteger :: Word64 -> Integer
 toInteger = fromIntegral
 
+#ifdef __GLASGOW_HASKELL__
 -- | Narrowing conversion via Int32, fails if outside Int32 range.
 toCInt :: Word64 -> Maybe CInt
 toCInt x = CInt <$> toInt32 x
+#endif
 
 -- | Checked conversion, fails with 'Overflow' if outside exact float integer range.
 toFloat :: Word64 -> Either Overflows Float
@@ -108,6 +118,7 @@
   | x > maxIntegralRepDouble -> Left Overflow
   | otherwise                -> Right $ fromIntegral x
 
+#ifdef __GLASGOW_HASKELL__
 -- | Unsigned narrowing via Word64# comparison
 toWord8# :: Word64 -> (# Word8 | (# #) #)
 toWord8# (W64# w64#) =
@@ -194,3 +205,4 @@
 toDouble# (W64# w64#) = case leWord64# w64# (wordToWord64# 9007199254740991##) of
   1# -> (# | D# (int2Double# (word2Int# (word64ToWord# w64#))) #)
   _  -> (# Overflow | #)
+#endif
diff --git a/src/Unwitch/Convert/Word8.hs b/src/Unwitch/Convert/Word8.hs
--- a/src/Unwitch/Convert/Word8.hs
+++ b/src/Unwitch/Convert/Word8.hs
@@ -14,10 +14,14 @@
   , toInteger
   , toFloat
   , toDouble
+#ifdef __GLASGOW_HASKELL__
   , toCInt
+#endif
+#ifdef __GLASGOW_HASKELL__
   -- * Unboxed conversions
   -- $unboxed
   , toInt8#
+#endif
   )
 where
 
@@ -25,18 +29,22 @@
 import           Data.Word
 import           Data.Int
 import           Numeric.Natural (Natural)
-import           Foreign.C.Types (CInt(CInt))
 import           Prelude hiding (toInteger)
+#ifdef __GLASGOW_HASKELL__
+import           Foreign.C.Types (CInt(CInt))
 import           GHC.Exts (word8ToWord#, word2Int#, intToInt8#, int8ToInt#,
                            (==#))
 import           GHC.Int (Int8(..))
 import           GHC.Word (Word8(..))
+#endif
 
+#ifdef __GLASGOW_HASKELL__
 -- $unboxed
 -- These use GHC unboxed types and unboxed sums for zero-allocation
 -- failure handling. Requires the @MagicHash@, @UnboxedSums@ and
 -- @UnboxedTuples@ language extensions.
 -- See the <https://downloads.haskell.org/ghc/latest/docs/users_guide/exts/primitives.html GHC manual on unboxed types>.
+#endif
 
 toWord16 :: Word8 -> Word16
 toWord16 = fromIntegral
@@ -77,10 +85,13 @@
 toDouble :: Word8 -> Double
 toDouble = fromIntegral
 
+#ifdef __GLASGOW_HASKELL__
 -- | Widening conversion via Int32, always succeeds.
 toCInt :: Word8 -> CInt
 toCInt x = CInt $ toInt32 x
+#endif
 
+#ifdef __GLASGOW_HASKELL__
 -- | Unsigned->signed, source fits in Int#, roundtrip at Int#
 toInt8# :: Word8 -> (# Int8 | (# #) #)
 toInt8# (W8# w8#) =
@@ -89,3 +100,4 @@
   in case int8ToInt# n# ==# i# of
     1# -> (# I8# n# | #)
     _  -> (# | (# #) #)
+#endif
diff --git a/test/Test/Convert/Int32Spec.hs b/test/Test/Convert/Int32Spec.hs
--- a/test/Test/Convert/Int32Spec.hs
+++ b/test/Test/Convert/Int32Spec.hs
@@ -20,9 +20,13 @@
     it "rejects out-of-range" $
       Int32.toInt8 (200 :: Int32) `shouldBe` Nothing
 
-  describe "toInt (fallible via toIntegralSized)" $
+  describe "toInt (total)" $ do
     it "converts 0" $
-      Int32.toInt 0 `shouldBe` Just 0
+      Int32.toInt 0 `shouldBe` (0 :: Int)
+    it "converts maxBound" $
+      Int32.toInt maxBound `shouldBe` (2147483647 :: Int)
+    it "converts minBound" $
+      Int32.toInt minBound `shouldBe` (-2147483648 :: Int)
 
   describe "toWord8 (fallible)" $ do
     it "rejects negative" $
diff --git a/test/Test/Convert/UnboxedSpec.hs b/test/Test/Convert/UnboxedSpec.hs
--- a/test/Test/Convert/UnboxedSpec.hs
+++ b/test/Test/Convert/UnboxedSpec.hs
@@ -5,6 +5,8 @@
 where
 
 import Test.Hspec
+
+#ifdef __GLASGOW_HASKELL__
 import Test.Hspec.QuickCheck (prop)
 
 import qualified Unwitch.Convert.Integer as Integer
@@ -23,8 +25,10 @@
 import Data.Int
 import Data.Word
 import Numeric.Natural (Natural)
+#endif
 
 spec :: Spec
+#ifdef __GLASGOW_HASKELL__
 spec = describe "Unboxed sum variants" $ do
 
   describe "Integer" $ do
@@ -152,3 +156,6 @@
         (# y | #)      -> Char.fromInt x `shouldBe` Just y
         (# | (# #) #)  -> Char.fromInt x `shouldBe` Nothing
 
+#else
+spec = pure ()
+#endif
diff --git a/unwitch.cabal b/unwitch.cabal
--- a/unwitch.cabal
+++ b/unwitch.cabal
@@ -1,7 +1,7 @@
 cabal-version:      3.0
 
 name:           unwitch
-version:        2.2.0
+version:        3.0.0
 homepage:       https://github.com/jappeace/unwitch#readme
 synopsis:  converts between primitives
 description: 
@@ -37,6 +37,7 @@
 
 common common-options
   default-extensions:
+      CPP
       EmptyCase
       FlexibleContexts
       FlexibleInstances
@@ -58,21 +59,26 @@
       TypeApplications
       NumericUnderscores
       ImportQualifiedPost
-      UnboxedSums
-      UnboxedTuples
-      MagicHash
 
-  ghc-options:
-    -Wall -Wincomplete-uni-patterns
-    -Wincomplete-record-updates -Widentities -Wredundant-constraints
-    -Wcpp-undef -fwarn-tabs -Wpartial-fields
-    -fdefer-diagnostics -Wunused-packages
-    -fenable-th-splice-warnings
-    -fno-omit-yields
+  if impl(ghc)
+    default-extensions:
+        UnboxedSums
+        UnboxedTuples
+        MagicHash
 
+    ghc-options:
+      -Wall -Wincomplete-uni-patterns
+      -Wincomplete-record-updates -Widentities -Wredundant-constraints
+      -Wcpp-undef -fwarn-tabs -Wpartial-fields
+      -fdefer-diagnostics -Wunused-packages
+      -fenable-th-splice-warnings
+      -fno-omit-yields
+
+    build-depends:
+        ghc-bignum < 2
+
   build-depends:
       base >=4.18.0.0 && <4.23,
-      ghc-bignum < 2,
       bytestring >= 0.10 && < 0.13,
       text >= 1.2 && < 2.2
 
