summaryrefslogtreecommitdiff
path: root/source/Felix/Checking/Exact/Datatype.hs
blob: be943cdac40a8b8e85fae5f5bf61721033f879aa (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
{-# LANGUAGE DerivingStrategies #-}
{-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE NoImplicitPrelude #-}

-- | Exact preparation of deterministic datatype declarations.
module Felix.Checking.Exact.Datatype
    ( PreparedExactDatatype
    , preparedExactDatatypeObjects
    , preparedExactDatatypeBindings
    , PreparedExactDatatypeFact
    , preparedExactDatatypeFacts
    , preparedExactDatatypeFactMarker
    , preparedExactDatatypeFactTarget
    , preparedExactDatatypeFactReference
    , preparedExactDatatypeDescriptor
    , prepareExactDatatype
    , CheckedExactDatatypeAuthorization
    , lowerPreparedExactDatatype
    , authorizeCheckedExactDatatype
    , ExactDatatypeError(..)
    , exactDatatypeErrorLocation
    , renderExactDatatypeError
    ) where

import Base hiding (Empty)
import Felix.Checking.Authority
import Felix.Checking.Core
import Felix.Checking.Datatype qualified as Datatype
import Felix.Checking.Declaration qualified as Declaration
import Felix.Checking.Exact.Global qualified as ExactGlobal
import Felix.Checking.Exact.Vocabulary
import Felix.Checking.Identity
import Felix.Checking.Semantic
import Felix.Checking.Typed.Inductive qualified as Typed
import Felix.Cache.Codec
import Felix.Module
import Felix.Meaning qualified as Meaning
import Felix.Report.Location
import Felix.Syntax.Abstract qualified as Raw
import Felix.Syntax.Interface
import Felix.Syntax.Internal qualified as Internal

import Control.Monad (unless, when)
import Control.Monad.Except (ExceptT)
import Control.Monad.Except qualified as Except
import Data.Bifunctor (first)
import Data.ByteString (ByteString)
import Data.List.NonEmpty qualified as NonEmpty
import Data.Map.Strict qualified as Map
import Data.Set qualified as Set
import Data.Text qualified as Text
import Numeric.Natural (Natural)


data PreparedDatatypeObject = PreparedDatatypeObject
    !Internal.Symbol
    !SemanticGlobalKey
    !CoreType
    !ObjectId
    !AssertedObject

data PreparedExactDatatypeFact = PreparedExactDatatypeFact
    !Internal.Marker
    !(FrozenCheckedCore ObjectId)
    !TheoremRef

preparedExactDatatypeFactMarker
    :: PreparedExactDatatypeFact
    -> Internal.Marker
preparedExactDatatypeFactMarker
        (PreparedExactDatatypeFact marker _target _reference) =
    marker

preparedExactDatatypeFactTarget
    :: PreparedExactDatatypeFact
    -> FrozenCheckedCore ObjectId
preparedExactDatatypeFactTarget
        (PreparedExactDatatypeFact _marker target _reference) =
    target

preparedExactDatatypeFactReference
    :: PreparedExactDatatypeFact
    -> TheoremRef
preparedExactDatatypeFactReference
        (PreparedExactDatatypeFact _marker _target reference) =
    reference

data PreparedExactDatatype = PreparedExactDatatype
    !Location
    !DeclarationSyntaxId
    !(NonEmpty PreparedDatatypeObject)
    !(NonEmpty PreparedExactDatatypeFact)
    !DatatypeCompilationDescriptor

data CheckedExactDatatypeAuthorization =
    CheckedExactDatatypeAuthorization
        !DatatypeCompilationDescriptor
        !ObjectId
        !(NonEmpty ObjectId)

preparedExactDatatypeObjects
    :: PreparedExactDatatype
    -> NonEmpty (ObjectId, CoreType)
preparedExactDatatypeObjects
        (PreparedExactDatatype _location _syntax objects _facts _descriptor) =
    fmap
        (\(PreparedDatatypeObject _symbol _key coreType identity _asserted) ->
            (identity, coreType))
        objects

preparedExactDatatypeBindings
    :: PreparedExactDatatype
    -> NonEmpty (SemanticGlobalKey, SemanticGlobalTarget)
preparedExactDatatypeBindings
        (PreparedExactDatatype _location _syntax objects _facts _descriptor) =
    fmap
        (\(PreparedDatatypeObject _symbol key _coreType identity _asserted) ->
            (key, GlobalReference identity))
        objects

preparedExactDatatypeFacts
    :: PreparedExactDatatype
    -> NonEmpty PreparedExactDatatypeFact
preparedExactDatatypeFacts
        (PreparedExactDatatype _location _syntax _objects facts _descriptor) =
    facts

preparedExactDatatypeDescriptor
    :: PreparedExactDatatype
    -> DatatypeCompilationDescriptor
preparedExactDatatypeDescriptor
        (PreparedExactDatatype _location _syntax _objects _facts descriptor) =
    descriptor

lowerPreparedExactDatatype
    :: PreparedExactDatatype
    -> Declaration.LoweringDriver
        (Either
            Declaration.DeclarationError
            (Declaration.CheckedDeclaration
                CheckedExactDatatypeAuthorization))
lowerPreparedExactDatatype
        (PreparedExactDatatype _location syntax objects facts descriptor) =
    do
        prepared <-
            traverse
                (\(PreparedExactDatatypeFact marker target _reference) ->
                    Declaration.prepareFrozenCandidateSpecLowering
                        assertedObjects
                        target
                        SearchEligible
                        [markerAlias marker])
                facts
        pure (buildChecked <$> sequence prepared)
  where
    assertedObjects =
        toList
            (fmap
                (\(PreparedDatatypeObject
                        _symbol _key _coreType _identity asserted) -> asserted)
                objects)
    bindings =
        toList
            (fmap
                (\(PreparedDatatypeObject
                        _symbol key _coreType identity _asserted) ->
                    semanticGlobalBinding key (GlobalReference identity))
                objects)
    carrier :| constructors = fmap objectIdentity objects
    constructorIds =
        case constructors of
            firstConstructor : remainingConstructors ->
                firstConstructor :| remainingConstructors
            [] -> impossible "a prepared datatype has no constructor"
    buildChecked specs =
        Declaration.checkedCompiledDeclaration
            syntax
            assertedObjects
            []
            bindings
            []
            [ fmap
                (\spec ->
                    Declaration.checkedCandidate
                        spec
                        (Declaration.checkedDatatypePlanning descriptor))
                specs
            ]
            (CheckedExactDatatypeAuthorization
                descriptor carrier constructorIds)

    markerAlias (Internal.Marker name) = semanticName name

    objectIdentity
            (PreparedDatatypeObject
                _symbol _key _coreType identity _asserted) =
        identity

authorizeCheckedExactDatatype
    :: CheckedExactDatatypeAuthorization
    -> [NonEmpty Declaration.ReservedCandidate]
    -> Declaration.Declaration ()
authorizeCheckedExactDatatype
        (CheckedExactDatatypeAuthorization descriptor carrier constructors) =
    \case
        [candidates] ->
            Declaration.authorizeDatatypeCompilationCandidates
                descriptor carrier constructors candidates
        stages ->
            Declaration.failDeclaration
                (Declaration.CheckedAuthorizationCandidateShapeMismatch
                    1 (length stages))

data ExactDatatypeError
    = ExactDatatypeUnsupportedBlock !Location
    | ExactDatatypeOccurrenceCountMismatch !Location !Int !Int
    | ExactDatatypeOccurrenceMismatch !Location
    | ExactDatatypeGlossFailed !Location !Meaning.GlossError
    | ExactDatatypeInvalid !Location !Text
    | ExactDatatypeDuplicateGlobal !Location !SemanticGlobalKey
    | ExactDatatypeFixedSemanticCollision !Location !SemanticGlobalKey
    | ExactDatatypeGlobalAlreadyVisible !Location !SemanticGlobalKey
    | ExactDatatypeObjectAlreadyAvailable !Location !ObjectId
    | ExactDatatypeGlobalResolutionFailed
        !Location
        !ExactGlobal.ExactGlobalResolutionError
    | ExactDatatypeLoweringFailed !Location !Typed.TypedInductiveError
    | ExactDatatypeExpectedSet !Location !CoreType
    | ExactDatatypeExpectedProposition !Location !CoreType
    deriving stock (Show, Eq)

exactDatatypeErrorLocation :: ExactDatatypeError -> Location
exactDatatypeErrorLocation = \case
    ExactDatatypeUnsupportedBlock location -> location
    ExactDatatypeOccurrenceCountMismatch location _expected _actual -> location
    ExactDatatypeOccurrenceMismatch location -> location
    ExactDatatypeGlossFailed location _failure -> location
    ExactDatatypeInvalid location _message -> location
    ExactDatatypeDuplicateGlobal location _key -> location
    ExactDatatypeFixedSemanticCollision location _key -> location
    ExactDatatypeGlobalAlreadyVisible location _key -> location
    ExactDatatypeObjectAlreadyAvailable location _identity -> location
    ExactDatatypeGlobalResolutionFailed location _failure -> location
    ExactDatatypeLoweringFailed location _failure -> location
    ExactDatatypeExpectedSet location _actual -> location
    ExactDatatypeExpectedProposition location _actual -> location

renderExactDatatypeError :: ExactDatatypeError -> Text
renderExactDatatypeError failure =
    locationToText (exactDatatypeErrorLocation failure)
        <> ": "
        <> case failure of
            ExactDatatypeUnsupportedBlock{} ->
                "this datatype source form is not supported by the typed checker"
            ExactDatatypeOccurrenceCountMismatch _location expected actual ->
                "the datatype has " <> shown actual
                    <> " syntax occurrences, but " <> shown expected
                    <> " are required"
            ExactDatatypeOccurrenceMismatch{} ->
                "a datatype syntax occurrence does not match its declaration"
            ExactDatatypeGlossFailed _location glossFailure ->
                "datatype elaboration failed: " <> shown glossFailure
            ExactDatatypeInvalid _location message ->
                "invalid datatype declaration: " <> message
            ExactDatatypeDuplicateGlobal _location key ->
                "the datatype declares the semantic key more than once: "
                    <> shown key
            ExactDatatypeFixedSemanticCollision _location key ->
                "the datatype collides with fixed semantics for " <> shown key
            ExactDatatypeGlobalAlreadyVisible _location key ->
                "the datatype global is already visible: " <> shown key
            ExactDatatypeObjectAlreadyAvailable _location identity ->
                "the datatype opaque object is already available: "
                    <> shown identity
            ExactDatatypeGlobalResolutionFailed _location resolution ->
                "datatype global resolution failed: " <> shown resolution
            ExactDatatypeLoweringFailed _location typedFailure ->
                "typed datatype lowering failed: " <> shown typedFailure
            ExactDatatypeExpectedSet _location actual ->
                "a datatype premise domain has type " <> shown actual
                    <> " instead of Set"
            ExactDatatypeExpectedProposition _location actual ->
                "a generated datatype fact has type " <> shown actual
                    <> " instead of Prop"
  where
    shown :: Show value => value -> Text
    shown = Text.pack . show

type Prepare =
    ExceptT ExactDatatypeError (Declaration.LoweringDriver)

type SourceOccurrence = (Location, Raw.Marker, CanonicalLexicalEntry)

exactDatatypeInvalid
    :: Location
    -> Datatype.DatatypeValidationError
    -> ExactDatatypeError
exactDatatypeInvalid declarationLocation failure =
    ExactDatatypeInvalid
        (fromMaybe
            declarationLocation
            (Datatype.datatypeValidationErrorLocation failure))
        (Datatype.renderDatatypeValidationError failure)

prepareExactDatatype
    :: Raw.Block
    -> [SourceOccurrence]
    -> Declaration.LoweringDriver
        (Either ExactDatatypeError PreparedExactDatatype)
prepareExactDatatype block occurrences =
    Except.runExceptT do
        (location, marker, rawDatatype) <-
            case block of
                Raw.BlockData blockLocation _title blockMarker datatype ->
                    pure (blockLocation, blockMarker, datatype)
                _ ->
                    Except.throwError
                        (ExactDatatypeUnsupportedBlock (locate block))
        keys <- validateOccurrences location marker rawDatatype occurrences
        internal <-
            case Meaning.meaning [block] of
                Right
                    [Internal.BlockData
                        _internalLocation _internalMarker datatype] ->
                            pure datatype
                Left failure ->
                    Except.throwError
                        (ExactDatatypeGlossFailed location failure)
                Right _ ->
                    Except.throwError
                        (ExactDatatypeUnsupportedBlock location)
        checked <-
            -- The new opaque carrier has no semantic binding while its
            -- declaration is prepared, so premise recursion cannot depend on
            -- abbreviation expansion. Revisit this if forward aliases become
            -- available.
            Except.lift
                (Datatype.prepareCheckedDatatype pure internal)
                >>= Except.liftEither
                    . first (exactDatatypeInvalid location)
        let symbols =
                Datatype.checkedDatatypeHeadSymbol checked
                    :| toList
                        (Datatype.checkedDatatypeConstructorSymbols checked)
            views = Datatype.checkedDatatypeClauseViews checked
            arities =
                0 :| (length
                    . Datatype.checkedDatatypeClauseViewArguments
                    <$> toList views)
        unless (NonEmpty.length symbols == NonEmpty.length keys)
            (Except.throwError
                (ExactDatatypeOccurrenceCountMismatch
                    location
                    (NonEmpty.length symbols)
                    (NonEmpty.length keys)))
        validateKeys occurrences keys
        slot <- Except.lift Declaration.nextDeclarationSlotLowering
        theory <- Except.lift Declaration.currentTheoryLowering
        objects <-
            sequence
                (NonEmpty.zipWith
                    (\index (occurrenceLocation, symbol, key, arity) ->
                        prepareObject
                            occurrenceLocation
                            slot theory index symbol key arity)
                    (0 :| [1 ..])
                    (NonEmpty.zipWith
                        (\occurrenceLocation (symbol, key, arity) ->
                            (occurrenceLocation, symbol, key, arity))
                        (validatedOccurrenceLocations occurrences)
                        (NonEmpty.zipWith
                            (\(symbol, key) arity ->
                                (symbol, key, arity))
                            (NonEmpty.zip symbols keys)
                            arities)))
        let ownedSymbols = Set.fromList (toList symbols)
            generated = Datatype.checkedDatatypeGeneratedFacts checked
            externalSymbols =
                (foldMap
                    (Internal.mentionedSymbols . snd)
                    generated
                    <> foldMap premiseSymbols views)
                    `Set.difference` ownedSymbols
        external <-
            Except.lift
                (ExactGlobal.resolveExactSourceGlobals externalSymbols)
                >>= Except.liftEither
                    . first (ExactDatatypeGlobalResolutionFailed location)
        let (externalGlobals, externalTypes) = external
            ownedGlobals =
                Map.fromList
                    [ (symbol, Typed.SourceGlobal identity Nothing)
                    | PreparedDatatypeObject
                        symbol _key _coreType identity _asserted <-
                            toList objects
                    ]
            ownedTypes =
                Map.fromList
                    [ (identity, coreType)
                    | PreparedDatatypeObject
                        _symbol _key coreType identity _asserted <-
                            toList objects
                    ]
            sourceGlobals = Map.union ownedGlobals externalGlobals
            globalTypes = Map.union ownedTypes externalTypes
            resolveGlobal = (`Map.lookup` sourceGlobals)
            globalType identity =
                fromMaybe
                    (impossible
                        "prepared datatype global has no checked type")
                    (Map.lookup identity globalTypes)
        preparedClauses <-
            Except.liftEither
                (traverse
                    (prepareClause location globalType resolveGlobal)
                    views)
        facts <-
            Except.liftEither
                (traverse
                    (prepareFact location theory globalType resolveGlobal)
                    generated)
        let carrier = objectIdentity (NonEmpty.head objects)
            constructors = objectIdentity <$> NonEmpty.tail objects
            descriptor =
                case NonEmpty.nonEmpty constructors of
                    Nothing ->
                        impossible
                            "a checked datatype has no constructors"
                    Just nonemptyConstructors ->
                        datatypeCompilationDescriptor
                            carrier
                            nonemptyConstructors
                            (preparedExactDatatypeFactReference
                                <$> toList facts)
            syntax =
                declarationSyntaxId
                    (encodePreparedDatatype
                        objects
                        preparedClauses
                        facts
                        descriptor)
        pure
            (PreparedExactDatatype
                location
                syntax
                objects
                facts
                descriptor)
  where
    objectIdentity
        (PreparedDatatypeObject _symbol _key _coreType identity _asserted) =
        identity

validateOccurrences
    :: Location
    -> Raw.Marker
    -> Raw.Datatype
    -> [SourceOccurrence]
    -> Prepare (NonEmpty SemanticGlobalKey)
validateOccurrences location marker datatype occurrences = do
    expected <-
        Except.liftEither
            (expectedOccurrences location marker datatype)
    unless (length occurrences == NonEmpty.length expected)
        (Except.throwError
            (ExactDatatypeOccurrenceCountMismatch
                location
                (NonEmpty.length expected)
                (length occurrences)))
    keys <-
        sequence
            (NonEmpty.zipWith validateOne expected
                (case NonEmpty.nonEmpty occurrences of
                    Just nonempty -> nonempty
                    Nothing ->
                        impossible
                            "equal nonzero occurrence counts became empty"))
    pure keys
  where
    validateOne
            (expectedLocation, expectedMarker, expectedPattern)
            (actualLocation, actualMarker, entry) =
        case entry of
            CanonicalExpressionFunction pat marker' _fixity
                | actualMarker == expectedMarker
                , marker' == expectedMarker
                , pat == expectedPattern ->
                    pure
                        (SemanticExpressionFunction pat)
            _ ->
                Except.throwError
                    (ExactDatatypeOccurrenceMismatch
                        (bestLocation actualLocation expectedLocation))

    bestLocation actual expected
        | actual == Nowhere = expected
        | otherwise = actual

expectedOccurrences
    :: Location
    -> Raw.Marker
    -> Raw.Datatype
    -> Either
        ExactDatatypeError
        (NonEmpty (Location, Raw.Marker, Raw.Pattern))
expectedOccurrences location blockMarker datatype = do
    headOccurrence <-
        expectedSymbol blockMarker (Raw.datatypeHeadExpr datatype)
    clauses <-
        traverse
            (\clause ->
                case Raw.datatypeClauseConstructorExpr clause of
                    Raw.ExprOp constructorLocation symbol _arguments ->
                        Right
                            ( constructorLocation
                            , Raw.mixfixMarker symbol
                            , Raw.mixfixPattern symbol
                            )
                    expression ->
                        Left
                            (ExactDatatypeOccurrenceMismatch
                                (locate expression)))
            (Raw.datatypeClauses datatype)
    pure (headOccurrence :| toList clauses)
  where
    expectedSymbol expectedMarker = \case
        Raw.ExprOp symbolLocation symbol [] ->
            Right
                ( symbolLocation
                , expectedMarker
                , Raw.mixfixPattern symbol
                )
        expression ->
            Left
                (ExactDatatypeOccurrenceMismatch
                    (case locate expression of
                        Nowhere -> location
                        expressionLocation -> expressionLocation))

validateKeys
    :: [SourceOccurrence]
    -> NonEmpty SemanticGlobalKey
    -> Prepare ()
validateKeys occurrences keys = do
    case duplicateWithLocation of
        Just (duplicate, duplicateLocation) ->
            Except.throwError
                (ExactDatatypeDuplicateGlobal
                    duplicateLocation
                    duplicate)
        Nothing -> pure ()
    traverse_ validateOne (NonEmpty.zip locations keys)
  where
    locations = validatedOccurrenceLocations occurrences

    duplicateWithLocation =
        go Set.empty
            [ (key, occurrenceLocation)
            | (key, (occurrenceLocation, _marker, _entry)) <-
                zip (toList keys) occurrences
            ]

    go _seen [] = Nothing
    go seen ((key, occurrenceLocation) : remaining)
        | key `Set.member` seen = Just (key, occurrenceLocation)
        | otherwise = go (Set.insert key seen) remaining

    validateOne (occurrenceLocation, key) = do
        when (isJust (fixedSemanticMeaning key))
            (Except.throwError
                (ExactDatatypeFixedSemanticCollision occurrenceLocation key))
        visible <-
            Except.lift
                (Declaration.resolveVisibleGlobalLowering key)
        when (isJust visible)
            (Except.throwError
                (ExactDatatypeGlobalAlreadyVisible occurrenceLocation key))

validatedOccurrenceLocations
    :: [SourceOccurrence]
    -> NonEmpty Location
validatedOccurrenceLocations occurrences =
    case NonEmpty.nonEmpty
            [ occurrenceLocation
            | (occurrenceLocation, _marker, _entry) <- occurrences
            ] of
        Just nonempty -> nonempty
        Nothing ->
            impossible "validated datatype occurrences are empty"

prepareObject
    :: Location
    -> DeclarationSlot
    -> TheoryId
    -> Natural
    -> Internal.Symbol
    -> SemanticGlobalKey
    -> Int
    -> Prepare PreparedDatatypeObject
prepareObject location slot theory index symbol key arity = do
    let coreType =
            foldr (const (TyArrow TySet)) TySet [1 .. arity]
        seed =
            opaqueDeclarationSeed
                (declarationSlotModule slot)
                (declarationSlotOrdinal slot)
                DatatypeDeclaration
                (generatedObjectSlot index)
        content = OpaqueObjectContent theory seed coreType
        identity = opaqueObjectId theory seed coreType
    available <- Except.lift (Declaration.objectAvailableLowering identity)
    when available
        (Except.throwError
            (ExactDatatypeObjectAlreadyAvailable location identity))
    pure
        (PreparedDatatypeObject
            symbol
            key
            coreType
            identity
            (assertedObject identity content))

data PreparedClause = PreparedClause
    !Internal.FunctionSymbol
    ![PreparedPremise]

data PreparedPremise
    = PreparedRecursivePremise !(FrozenCheckedCore ObjectId)
    | PreparedNonRecursivePremise !(FrozenCheckedCore ObjectId)

prepareClause
    :: Location
    -> (ObjectId -> CoreType)
    -> (Internal.Symbol -> Maybe (Typed.SourceGlobal ObjectId))
    -> Datatype.CheckedDatatypeClauseView
    -> Either ExactDatatypeError PreparedClause
prepareClause location globalType resolveGlobal view =
    PreparedClause
        (Datatype.checkedDatatypeClauseViewConstructor view)
        <$> traverse preparePremise
            (Datatype.checkedDatatypeClauseViewPremises view)
  where
    preparePremise = \case
        Datatype.CheckedRecursiveDatatypePremise _variable domain ->
            PreparedRecursivePremise
                <$> prepareDomain domain
        Datatype.CheckedNonRecursiveDatatypePremise _variable domain ->
            PreparedNonRecursivePremise
                <$> prepareDomain domain

    prepareDomain domain = do
        checked <-
            first (ExactDatatypeLoweringFailed location)
                (Typed.prepareTypedClosedTerm
                    globalType
                    resolveGlobal
                    domain)
        unless (frozenCoreType checked == TySet)
            (Left
                (ExactDatatypeExpectedSet
                    location
                    (frozenCoreType checked)))
        pure checked

prepareFact
    :: Location
    -> TheoryId
    -> (ObjectId -> CoreType)
    -> (Internal.Symbol -> Maybe (Typed.SourceGlobal ObjectId))
    -> (Internal.Marker, Internal.Formula)
    -> Either ExactDatatypeError PreparedExactDatatypeFact
prepareFact location theory globalType resolveGlobal (marker, formula) = do
    checked <-
        first (ExactDatatypeLoweringFailed location)
            (Typed.prepareTypedClosedFormula
                globalType
                resolveGlobal
                formula)
    unless (frozenCoreType checked == TyProp)
        (Left
            (ExactDatatypeExpectedProposition
                location
                (frozenCoreType checked)))
    pure
        (PreparedExactDatatypeFact
            marker
            checked
            (theoremRef theory
                (propositionIdOf (frozenCoreTerm checked))))

premiseSymbols
    :: Datatype.CheckedDatatypeClauseView
    -> Set.Set Internal.Symbol
premiseSymbols view =
    foldMap symbols
        (Datatype.checkedDatatypeClauseViewPremises view)
  where
    symbols = \case
        Datatype.CheckedRecursiveDatatypePremise _variable domain ->
            Internal.mentionedSymbols domain
        Datatype.CheckedNonRecursiveDatatypePremise _variable domain ->
            Internal.mentionedSymbols domain

encodePreparedDatatype
    :: NonEmpty PreparedDatatypeObject
    -> NonEmpty PreparedClause
    -> NonEmpty PreparedExactDatatypeFact
    -> DatatypeCompilationDescriptor
    -> ByteString
encodePreparedDatatype objects clauses facts descriptor =
    encodeCache do
        putCacheTag 0x05
        putCacheList putObject (toList objects)
        putCacheList putClause (toList clauses)
        putCacheList putFact (toList facts)
        putDirectAuthorizationCache
            (TrustedCompilation
                (DatatypeCompilation descriptor))
  where
    putObject
            (PreparedDatatypeObject
                _symbol key coreType identity _asserted) = do
        putSemanticGlobalKeyCache key
        putCoreTypeCache coreType
        putObjectIdCache identity

    putClause (PreparedClause constructor premises) = do
        putSemanticGlobalKeyCache
            (SemanticExpressionFunction
                (Raw.mixfixPattern constructor))
        putCacheList putPremise premises

    putPremise = \case
        PreparedRecursivePremise domain -> do
            putCacheTag 0x00
            putCanonicalTermCache putObjectIdCache
                (frozenCoreTerm domain)
        PreparedNonRecursivePremise domain -> do
            putCacheTag 0x01
            putCanonicalTermCache putObjectIdCache
                (frozenCoreTerm domain)

    putFact fact = do
        let Internal.Marker marker =
                preparedExactDatatypeFactMarker fact
        putCacheText marker
        putCanonicalTermCache putObjectIdCache
            (frozenCoreTerm
                (preparedExactDatatypeFactTarget fact))
        putTheoremRefCache
            (preparedExactDatatypeFactReference fact)