diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,12 @@
 # Revision history for futhark-manifest
 
+## 1.3.0.0
+
+* Added `SumOps` and `SumVariant`.
+
+* The `TypeOpaque` constructor now has an additional field of type
+  `SumOps`.
+
 ## 1.2.0.1 -- 2023-03-10
 
 * Fix test suite.
diff --git a/futhark-manifest.cabal b/futhark-manifest.cabal
--- a/futhark-manifest.cabal
+++ b/futhark-manifest.cabal
@@ -1,6 +1,6 @@
 cabal-version:      2.4
 name:               futhark-manifest
-version:            1.2.0.1
+version:            1.3.0.0
 
 synopsis:           Definition and serialisation instances for Futhark manifests.
 
diff --git a/src/Futhark/Manifest.hs b/src/Futhark/Manifest.hs
--- a/src/Futhark/Manifest.hs
+++ b/src/Futhark/Manifest.hs
@@ -29,6 +29,8 @@
     ArrayOps (..),
     RecordField (..),
     RecordOps (..),
+    SumVariant (..),
+    SumOps (..),
     OpaqueOps (..),
     manifestToJSON,
     manifestFromJSON,
@@ -121,6 +123,34 @@
   }
   deriving (Eq, Ord, Show)
 
+-- | Information about a variant of a sum type.
+data SumVariant = SumVariant
+  { -- | The name of the constructor. This may be a name that is not a
+    -- valid C identifier.
+    sumVariantName :: T.Text,
+    -- | The payload of this variant; also corresponding to the
+    -- arguments of the constructor and destructor functions.
+    sumVariantPayload :: [TypeName],
+    sumVariantConstruct :: CFuncName,
+    -- | Note that despite the name, "destruction" does not entail
+    -- freeing the sum type value.
+    sumVariantDestruct :: CFuncName
+  }
+  deriving (Eq, Ord, Show)
+
+-- | Some opaque types are sum types, from which we can (try to)
+-- extract the payload of a constructor, as well as construct them
+-- from payloads. As with records, we can ignore these facilities and
+-- simply treat them as completely opaque.
+data SumOps = SumOps
+  { sumVariants :: [SumVariant],
+    -- | This function returns an integer that identifies which
+    -- variant a value is an instance of. This integer is a valid
+    -- index in 'sumVariants'.
+    sumVariant :: CFuncName
+  }
+  deriving (Eq, Ord, Show)
+
 -- | The names of the C functions implementing the operations on some
 -- opaque type.
 data OpaqueOps = OpaqueOps
@@ -130,12 +160,14 @@
   }
   deriving (Eq, Ord, Show)
 
--- | Manifest info for a non-scalar type.  Scalar types are not part
--- of the manifest for a program.
+-- | Manifest info for a non-scalar type. Scalar types are not part of
+-- the manifest for a program. Although this representation allows a
+-- type to be both a a record and a sum type, this will never actually
+-- happen.
 data Type
   = -- | ctype, Futhark elemtype, rank.
     TypeArray CTypeName TypeName Int ArrayOps
-  | TypeOpaque CTypeName OpaqueOps (Maybe RecordOps)
+  | TypeOpaque CTypeName OpaqueOps (Maybe RecordOps) (Maybe SumOps)
   deriving (Eq, Ord, Show)
 
 -- | A manifest for a compiled program.
@@ -180,6 +212,22 @@
         ("new", toJSON new)
       ]
 
+instance JSON.ToJSON SumVariant where
+  toJSON (SumVariant name payload construct destruct) =
+    object
+      [ ("name", toJSON name),
+        ("payload", toJSON payload),
+        ("construct", toJSON construct),
+        ("destruct", toJSON destruct)
+      ]
+
+instance JSON.ToJSON SumOps where
+  toJSON (SumOps variants variant) =
+    object
+      [ ("variants", toJSON variants),
+        ("variant", toJSON variant)
+      ]
+
 instance JSON.ToJSON OpaqueOps where
   toJSON (OpaqueOps free store restore) =
     object $
@@ -230,13 +278,14 @@
             ("elemtype", toJSON et),
             ("ops", toJSON ops)
           ]
-      onType (TypeOpaque t ops record) =
+      onType (TypeOpaque t ops record sumops) =
         object $
           [ ("kind", "opaque"),
             ("ctype", toJSON t),
             ("ops", toJSON ops)
           ]
             ++ maybeToList (("record",) . toJSON <$> record)
+            ++ maybeToList (("sum",) . toJSON <$> sumops)
 
 instance JSON.FromJSON ArrayOps where
   parseJSON = JSON.withObject "ArrayOps" $ \v ->
@@ -250,6 +299,18 @@
   parseJSON = JSON.withObject "RecordOps" $ \v ->
     RecordOps <$> v .: "fields" <*> v .: "new"
 
+instance JSON.FromJSON SumVariant where
+  parseJSON = JSON.withObject "SumVariant" $ \v ->
+    SumVariant
+      <$> v .: "name"
+      <*> v .: "payload"
+      <*> v .: "construct"
+      <*> v .: "destruct"
+
+instance JSON.FromJSON SumOps where
+  parseJSON = JSON.withObject "SumOps" $ \v ->
+    SumOps <$> v .: "variants" <*> v .: "variant"
+
 instance JSON.FromJSON OpaqueOps where
   parseJSON = JSON.withObject "OpaqueOps" $ \v ->
     OpaqueOps <$> v .: "free" <*> v .: "store" <*> v .: "restore"
@@ -282,7 +343,7 @@
           <*> ty .: "ops"
       pOpaque ty = do
         guard . (== ("opaque" :: T.Text)) =<< (ty .: "kind")
-        TypeOpaque <$> ty .: "ctype" <*> ty .: "ops" <*> ty .:? "record"
+        TypeOpaque <$> ty .: "ctype" <*> ty .: "ops" <*> ty .:? "record" <*> ty .:? "sum"
 
 instance JSON.FromJSON Manifest where
   parseJSON = JSON.withObject "Manifest" $ \v ->
diff --git a/tests/Tests.hs b/tests/Tests.hs
--- a/tests/Tests.hs
+++ b/tests/Tests.hs
@@ -21,6 +21,12 @@
 instance Arbitrary RecordOps where
   arbitrary = RecordOps <$> arbitrary <*> arbitrary
 
+instance Arbitrary SumVariant where
+  arbitrary = SumVariant <$> arbitrary <*> arbitrary <*> arbitrary <*> arbitrary
+
+instance Arbitrary SumOps where
+  arbitrary = SumOps <$> arbitrary <*> arbitrary
+
 instance Arbitrary OpaqueOps where
   arbitrary = OpaqueOps <$> arbitrary <*> arbitrary <*> arbitrary
 
@@ -28,7 +34,7 @@
   arbitrary =
     oneof
       [ TypeArray <$> arbitrary <*> arbitrary <*> arbitrary <*> arbitrary,
-        TypeOpaque <$> arbitrary <*> arbitrary <*> arbitrary
+        TypeOpaque <$> arbitrary <*> arbitrary <*> arbitrary <*> arbitrary
       ]
 
 instance Arbitrary Output where
