summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authoradelon <22380201+adelon@users.noreply.github.com>2026-07-27 16:23:12 +0200
committeradelon <22380201+adelon@users.noreply.github.com>2026-07-27 18:19:29 +0200
commit4a65f1430fdf3e8d75c36b5d74e07deb07b17f9e (patch)
treeb29bc2e8b6d1b6c6decb14c93df811a9f505d02d
parent829beab59eb793dda5dee8c40d57b29973fbbc5f (diff)
Make structure registration transactional
-rw-r--r--source/Checking.hs714
-rw-r--r--source/Checking/Dependencies.hs124
-rw-r--r--source/Checking/Facts.hs269
-rw-r--r--source/Checking/Structure.hs134
-rw-r--r--source/Data/InsOrdMap.hs91
-rw-r--r--source/Encoding.hs28
-rw-r--r--source/StructGraph.hs71
-rw-r--r--source/Syntax/Internal.hs43
-rw-r--r--source/Test/Unit/Checking.hs210
-rw-r--r--source/Test/Unit/Provers.hs4
-rw-r--r--source/Test/Unit/Symdiff.hs4
11 files changed, 1285 insertions, 407 deletions
diff --git a/source/Checking.hs b/source/Checking.hs
index 7da9d58..9874a0b 100644
--- a/source/Checking.hs
+++ b/source/Checking.hs
@@ -10,6 +10,9 @@ module Checking where
import Base hiding (locally)
+import Checking.Dependencies qualified as Dependencies
+import Checking.Facts qualified as Facts
+import Checking.Structure qualified as Structure
import StructGraph
import Syntax.Internal
import Syntax.Lexicon
@@ -22,10 +25,9 @@ import Bound.Var (Var(..), unvar)
import Control.Exception (Exception)
import Control.Monad.Reader
import Control.Monad.State
+import Data.Bifunctor (first)
import Data.HashMap.Strict qualified as HM
import Data.HashSet qualified as HS
-import Data.InsOrdMap (InsOrdMap)
-import Data.InsOrdMap qualified as InsOrdMap
import Data.IORef (newIORef, modifyIORef', readIORef)
import Data.List qualified as List
import Data.List.NonEmpty qualified as NonEmpty
@@ -60,11 +62,12 @@ initialCheckingState dumpPremselTraining emitTask = CheckingState
{ checkingAssumptions = []
, checkingDumpPremselTraining = dumpPremselTraining
, checkingGoals = []
- , checkingFacts = mempty
+ , checkingFacts = Facts.emptyFactRegistry
, checkingDirectness = Direct
, checkingAbbreviations = initAbbreviations
, checkingPredicateDefinitions = mempty
- , checkingDefinitionDependencies = mempty
+ , checkingDependencies =
+ Dependencies.fromRootSymbols (Set.fromList builtinSymbols)
, checkingOwnedSymbols = builtinOwnedSymbols
, checkingOwnedSymbolMarkers = builtinOwnedSymbolMarkers
, checkingFrozenSymbols = mempty
@@ -97,13 +100,13 @@ data CheckingState = CheckingState
{ checkingDumpPremselTraining :: WithDumpPremselTraining
, checkingAssumptions :: [Hypothesis]
- -- ^ Local assumptions (cached encoding).
+ -- ^ Local assumptions.
--
, checkingGoals :: [Formula]
-- ^ The current goals. INVARIANT: these should always be canonicalized and have all abbreviations resolved.
--
- , checkingFacts :: InsOrdMap Marker Hypothesis
- -- ^ Axioms and proven results (cached encoding).
+ , checkingFacts :: Facts.FactRegistry
+ -- ^ Canonical axioms and proven results with separate registration metadata.
--
--
, checkingDirectness :: Directness
@@ -120,12 +123,8 @@ data CheckingState = CheckingState
-- ^ Definitions of predicate that we can match against in assumptions.
-- Axioms and theorems that have the shape of a definition can be added as alternate definitions.
--
- , checkingDefinitionDependencies :: HashMap Symbol (Set Symbol)
- -- ^ Symbols mentioned in the expanded assumptions and definiens of each
- -- ordinary definition.
- -- INVARIANT: a definition's dependency set never contains its own symbol.
- -- This is used to reject hidden recursive inductive occurrences and to freeze
- -- unresolved downstream dependencies of symbols mentioned in inductive blocks.
+ , checkingDependencies :: Dependencies.DependencyRegistry
+ -- ^ Backward-only dependencies of every owned symbol.
--
, checkingOwnedSymbols :: HashMap Symbol SymbolOwner
-- ^ Top-level symbols with an established introducing owner in this document.
@@ -162,6 +161,12 @@ data StructContext = StructContext
, structContextOps :: HashMap StructSymbol VarSymbol
}
+data BlockContext = BlockContext
+ { blockContextLocation :: !Location
+ , blockContextMarker :: !Marker
+ }
+ deriving (Show, Eq)
+
data SymbolOwnerKind
= OwnedByBuiltin
| OwnedBySignaturePredicate
@@ -194,6 +199,7 @@ initCheckingStructs = StructGraph.insert
_Onesorted
mempty -- no parents
(Set.singleton CarrierSymbol) -- used for casting X -> \carrier[X]
+ (Set.singleton CarrierSymbol)
mempty -- empty graph
initAbbreviations :: HashMap Symbol (Scope Int ExprOf Void)
@@ -272,6 +278,7 @@ builtinSymbols =
data CheckingError
= DuplicateMarker Location Marker
+ | UnknownStructureParent StructPhrase Location Marker
| ByContradictionOnMultipleGoals Location Marker
| BySetInductionSyntacticMismatch Location Marker
| ProofWithoutPrecedingTheorem Location Marker
@@ -315,7 +322,7 @@ assumeFormula phi = do
Top -> skip
_ -> do
marker <- nextHypothesisMarker
- let hypo = encodeHypothesisContracted marker phi' phiContracted
+ let hypo = Hypothesis marker phi'
modify \st ->
st{checkingAssumptions = hypo : checkingAssumptions st}
@@ -339,8 +346,11 @@ structContextFromAssertion = \case
structContextFor :: VarSymbol -> StructPhrase -> CheckingM StructContext
structContextFor x sp = do
structGraph <- gets checkingStructs
- let struct = lookupStruct structGraph sp
- pure (structContextFromSymbols x (StructGraph.structSymbols struct structGraph))
+ case StructGraph.lookupSymbols sp structGraph of
+ Nothing ->
+ throwWithLocationAndMarker (UnknownStructureParent sp)
+ Just symbols ->
+ pure (structContextFromSymbols x symbols)
structContextFromSymbols :: VarSymbol -> Set StructSymbol -> StructContext
structContextFromSymbols x symbols =
@@ -382,11 +392,6 @@ asmStructContext = \case
structContextFor x sp
-lookupStruct :: StructGraph -> StructPhrase -> Struct
-lookupStruct structGraph struct = case StructGraph.lookup struct structGraph of
- Just result -> result
- Nothing -> error $ "lookup of undefined structure: " <> show struct
-
setLocation :: Location -> Checking
setLocation loc = modify \st -> st{stepLocation = loc}
@@ -542,57 +547,121 @@ symbolOwnerByMarker marker owners =
assertOwnedSymbolsInFormula :: Formula -> Checking
assertOwnedSymbolsInFormula phi = do
+ assertOwnedDependencies (mentionedSymbols phi)
+
+assertOwnedDependencies :: Set Symbol -> Checking
+assertOwnedDependencies dependencies = do
owners <- gets checkingOwnedSymbols
let unknown =
Set.filter
(\symbol -> ownableSymbol symbol && not (HM.member symbol owners))
- (mentionedSymbols phi)
+ dependencies
unless (Set.null unknown) do
throwCheckingError ("top-level fact mentions symbol(s) without prior ownership: " <> formatSymbols unknown)
claimSymbolOwnership :: SymbolOwnerKind -> Symbol -> Checking
claimSymbolOwnership ownerKind symbol =
+ claimSymbolOwnershipWithDependencies ownerKind symbol mempty
+
+claimSymbolOwnershipWithDependencies
+ :: SymbolOwnerKind
+ -> Symbol
+ -> Set Symbol
+ -> Checking
+claimSymbolOwnershipWithDependencies ownerKind symbol dependencies =
case objectSymbolMarker symbol of
Nothing ->
skip
- Just marker -> do
- owners <- gets checkingOwnedSymbols
- for_ (HM.lookup symbol owners) \owner ->
- throwCheckingError
- ( "symbol "
- <> symbolText symbol
- <> " is already owned by "
- <> symbolOwnerText owner
- )
-
- ownedMarkers <- gets checkingOwnedSymbolMarkers
- when (marker `HS.member` ownedMarkers) do
- throwCheckingError
- ( "object-symbol marker "
- <> markerTextOf marker
- <> " is already owned"
- <> maybe "" ((" by " <>) . symbolOwnerText) (symbolOwnerByMarker marker owners)
- )
-
- frozen <- gets checkingFrozenSymbols
- currentMarker <- gets blockLabel
- case HM.lookup symbol frozen of
- Just frozenBy | frozenBy /= currentMarker ->
+ Just _marker -> do
+ st <- get
+ let currentMarker = blockLabel st
+ (owners', ownedMarkers') <-
+ either
throwCheckingError
- ( "symbol "
- <> symbolText symbol
- <> " is frozen by inductive block "
- <> markerTextOf frozenBy
- <> " and cannot be defined here"
- )
- _ ->
- modify \st ->
- st
- { checkingOwnedSymbols =
- HM.insert symbol (SymbolOwner ownerKind currentMarker) (checkingOwnedSymbols st)
- , checkingOwnedSymbolMarkers =
- HS.insert marker (checkingOwnedSymbolMarkers st)
- }
+ pure
+ (validatedOwnership
+ currentMarker
+ ownerKind
+ symbol
+ st)
+ dependencyRegistry <-
+ either
+ (throwCheckingError . dependencyRegistrationErrorText symbol)
+ pure
+ (Dependencies.registerDependencies
+ symbol
+ (Set.filter ownableSymbol dependencies)
+ (checkingDependencies st))
+ put
+ st
+ { checkingOwnedSymbols = owners'
+ , checkingOwnedSymbolMarkers = ownedMarkers'
+ , checkingDependencies = dependencyRegistry
+ }
+
+validatedOwnership
+ :: Marker
+ -> SymbolOwnerKind
+ -> Symbol
+ -> CheckingState
+ -> Either Text (HashMap Symbol SymbolOwner, HashSet Marker)
+validatedOwnership currentMarker ownerKind symbol st = do
+ marker <-
+ maybe
+ (Left ("symbol has no object marker: " <> symbolText symbol))
+ Right
+ (objectSymbolMarker symbol)
+ let owners = checkingOwnedSymbols st
+ for_ (HM.lookup symbol owners) \owner ->
+ Left
+ ( "symbol "
+ <> symbolText symbol
+ <> " is already owned by "
+ <> symbolOwnerText owner
+ )
+ let ownedMarkers = checkingOwnedSymbolMarkers st
+ when (marker `HS.member` ownedMarkers) do
+ Left
+ ( "object-symbol marker "
+ <> markerTextOf marker
+ <> " is already owned"
+ <> maybe
+ ""
+ ((" by " <>) . symbolOwnerText)
+ (symbolOwnerByMarker marker owners)
+ )
+ case HM.lookup symbol (checkingFrozenSymbols st) of
+ Just frozenBy | frozenBy /= currentMarker ->
+ Left
+ ( "symbol "
+ <> symbolText symbol
+ <> " is frozen by inductive block "
+ <> markerTextOf frozenBy
+ <> " and cannot be defined here"
+ )
+ _ ->
+ Right
+ ( HM.insert
+ symbol
+ (SymbolOwner ownerKind currentMarker)
+ owners
+ , HS.insert marker ownedMarkers
+ )
+
+dependencyRegistrationErrorText
+ :: Symbol
+ -> Dependencies.DependencyRegistrationError
+ -> Text
+dependencyRegistrationErrorText owner = \case
+ Dependencies.DependencyOwnerAlreadyRegistered _ ->
+ "dependencies are already registered for symbol " <> symbolText owner
+ Dependencies.SelfDependency _ ->
+ "definition of symbol " <> symbolText owner <> " is self-referential"
+ Dependencies.UnknownDependency unknown ->
+ "definition of symbol "
+ <> symbolText owner
+ <> " depends on unknown symbol "
+ <> symbolText unknown
freezeSymbols :: Set Symbol -> Checking
freezeSymbols symbols = do
@@ -606,35 +675,6 @@ freezeSymbols symbols = do
modify \st ->
st{checkingFrozenSymbols = HM.union (checkingFrozenSymbols st) frozen}
-mentionedSymbols :: ExprOf a -> Set Symbol
-mentionedSymbols = \case
- TermVar{} ->
- mempty
- TermSymbol _loc symbol args ->
- Set.insert symbol (Set.unions (mentionedSymbols <$> args))
- TermSymbolStruct _symbol expr ->
- maybe mempty mentionedSymbols expr
- Apply expr args ->
- mentionedSymbols expr <> Set.unions (mentionedSymbols <$> toList args)
- TermSep _x bound scope ->
- mentionedSymbols bound <> mentionedSymbols (fromScope scope)
- ReplacePred _y _x bound scope ->
- mentionedSymbols bound <> mentionedSymbols (fromScope scope)
- ReplaceFun bounds lhs cond ->
- Set.unions (mentionedSymbols . snd <$> toList bounds)
- <> mentionedSymbols (fromScope lhs)
- <> mentionedSymbols (fromScope cond)
- Connected _conn left right ->
- mentionedSymbols left <> mentionedSymbols right
- Lambda scope ->
- mentionedSymbols (fromScope scope)
- Quantified _quant scope ->
- mentionedSymbols (fromScope scope)
- PropositionalConstant{} ->
- mempty
- Not _loc expr ->
- mentionedSymbols expr
-
checkedInductiveMentionedSymbols :: CheckedInductive -> Set Symbol
checkedInductiveMentionedSymbols CheckedInductive{checkedInductiveSymbol, checkedInductiveDomain, checkedInductiveIntros} =
Set.insert
@@ -660,38 +700,6 @@ formatSymbolPath :: [Symbol] -> Text
formatSymbolPath =
Text.intercalate " -> " . fmap symbolText
-definitionDependencyClosure :: HashMap Symbol (Set Symbol) -> Set Symbol -> Set Symbol
-definitionDependencyClosure graph =
- go Set.empty . Set.toList
- where
- go :: Set Symbol -> [Symbol] -> Set Symbol
- go seen [] =
- seen
- go seen (symbol:rest)
- | symbol `Set.member` seen =
- go seen rest
- | otherwise =
- let directDeps = HM.lookupDefault mempty symbol graph
- in go (Set.insert symbol seen) (Set.toList directDeps <> rest)
-
-definitionDependencyPath :: HashMap Symbol (Set Symbol) -> Set Symbol -> Symbol -> Maybe [Symbol]
-definitionDependencyPath graph seeds target =
- firstJust id [go Set.empty seed | seed <- Set.toList seeds]
- where
- go :: Set Symbol -> Symbol -> Maybe [Symbol]
- go seen symbol
- | symbol == target =
- Just [symbol]
- | symbol `Set.member` seen =
- Nothing
- | otherwise =
- let seen' = Set.insert symbol seen
- directDeps = HM.lookupDefault mempty symbol graph
- in firstJust id
- [ (symbol :) <$> go seen' dep
- | dep <- Set.toList directDeps
- ]
-
definitionParts :: Defn -> (SymbolOwnerKind, Symbol, [Asm], Expr)
definitionParts = \case
DefnPredicate asms predicate _vs body ->
@@ -714,14 +722,6 @@ checkedDefinitionDependencies symbol asms definiens = do
("definition of symbol " <> symbolText symbol <> " is self-referential")
pure dependencies
-recordDefinitionDependencies :: Symbol -> Set Symbol -> Checking
-recordDefinitionDependencies symbol dependencies =
- modify \st ->
- st
- { checkingDefinitionDependencies =
- HM.insert symbol dependencies (checkingDefinitionDependencies st)
- }
-
checkDatatype :: Datatype -> Checking
checkDatatype datatype@Datatype{datatypeHead = SymbolPattern datatypeSymbol datatypeArgs, datatypeClauses} = do
claimSymbolOwnership OwnedByDatatypeHead (SymbolMixfix datatypeSymbol)
@@ -1009,17 +1009,52 @@ tellTasks = do
directness <- gets checkingDirectness
loc <- gets stepLocation
emitTask <- gets checkingEmitTask
- let hypos = (snd <$> InsOrdMap.toList facts) <> assumptions
+ let hypos = factHypotheses facts <> assumptions
liftIO (traverse_ emitTask (Task directness hypos m loc <$> goals))
+factHypotheses :: Facts.FactRegistry -> [Hypothesis]
+factHypotheses facts =
+ [ Hypothesis marker (Facts.preparedSemanticStatement fact)
+ | (marker, fact) <- Facts.registeredFacts facts
+ ]
+
+prepareFact :: Formula -> CheckingM Facts.PreparedSemanticFact
+prepareFact phi = do
+ assertOwnedSymbolsInFormula phi
+ pure (Facts.prepareSemanticFact phi)
+
+registerPreparedFacts
+ :: NonEmpty Facts.StagedFact
+ -> Checking
+registerPreparedFacts staged = do
+ facts <- gets checkingFacts
+ case Facts.registerStagedFacts staged facts of
+ Left duplicate ->
+ throwWithLocationAndMarker
+ (\loc _blockMarker -> DuplicateMarker loc duplicate)
+ Right facts' ->
+ modify \st -> st{checkingFacts = facts'}
+
+stagePreparedFact
+ :: Location
+ -> Marker
+ -> Marker
+ -> Facts.PreparedSemanticFact
+ -> Facts.StagedFact
+stagePreparedFact loc blockMarker factMarker =
+ Facts.stageFact
+ (factMarker :| [])
+ (Facts.factOrigin loc blockMarker)
+
-- | Make a fact available to all future paragraphs.
addFact :: Formula -> Checking
addFact phi = do
phi' <- canonicalize phi
- assertOwnedSymbolsInFormula phi'
- m <- gets blockLabel
- let hypo = encodeHypothesis m phi'
- modify $ \st -> st{checkingFacts = InsOrdMap.insert m hypo (checkingFacts st)}
+ prepared <- prepareFact phi'
+ marker <- gets blockLabel
+ loc <- gets stepLocation
+ registerPreparedFacts
+ (stagePreparedFact loc marker marker prepared :| [])
-- | Make a fact available to all future paragraphs.
@@ -1027,31 +1062,47 @@ addFacts :: [(Marker, Formula)] -> Checking
addFacts phis = do
loc <- gets stepLocation
reserveMarkers loc (fst <$> phis)
- phis' <- forM phis \(m, phi) -> do
+ blockMarker <- gets blockLabel
+ staged <- forM phis \(marker, phi) -> do
phi' <- canonicalize phi
- assertOwnedSymbolsInFormula phi'
- let hypo = encodeHypothesis m phi'
- pure (m, hypo)
- modify $ \st -> st{checkingFacts = InsOrdMap.fromList phis' <> (checkingFacts st)}
+ prepared <- prepareFact phi'
+ pure (stagePreparedFact loc blockMarker marker prepared)
+ for_ (NonEmpty.nonEmpty staged) registerPreparedFacts
reserveMarkers :: Location -> [Marker] -> Checking
reserveMarkers loc markers = do
- existing <- gets definedMarkers
- case firstDuplicateMarker existing HS.empty markers of
+ st <- get
+ either throwIO (\markers' -> put st{definedMarkers = markers'})
+ (validatedMarkers loc markers st)
+
+validatedMarkers
+ :: Location
+ -> [Marker]
+ -> CheckingState
+ -> Either CheckingError (HashSet Marker)
+validatedMarkers loc markers st =
+ case firstDuplicateMarker (definedMarkers st) HS.empty markers of
Just marker ->
- throwIO (DuplicateMarker loc marker)
+ Left (DuplicateMarker loc marker)
Nothing ->
- modify \st -> st{definedMarkers = foldr HS.insert (definedMarkers st) markers}
- where
- firstDuplicateMarker :: HashSet Marker -> HashSet Marker -> [Marker] -> Maybe Marker
- firstDuplicateMarker _seenGlobals _seenLocals [] =
- Nothing
- firstDuplicateMarker seenGlobals seenLocals (marker:rest)
- | HS.member marker seenGlobals || HS.member marker seenLocals =
- Just marker
- | otherwise =
- firstDuplicateMarker seenGlobals (HS.insert marker seenLocals) rest
+ Right (foldr HS.insert (definedMarkers st) markers)
+
+firstDuplicateMarker
+ :: HashSet Marker
+ -> HashSet Marker
+ -> [Marker]
+ -> Maybe Marker
+firstDuplicateMarker _seenGlobals _seenLocals [] =
+ Nothing
+firstDuplicateMarker seenGlobals seenLocals (marker:rest)
+ | HS.member marker seenGlobals || HS.member marker seenLocals =
+ Just marker
+ | otherwise =
+ firstDuplicateMarker
+ seenGlobals
+ (HS.insert marker seenLocals)
+ rest
@@ -1069,11 +1120,11 @@ canonicalizedFactWithAsms asms stmt = do
addFactWithAsms :: [Asm] -> Formula -> Checking
addFactWithAsms asms stmt = do
phi <- canonicalizedFactWithAsms asms stmt
- assertOwnedSymbolsInFormula phi
- m <- gets blockLabel
- modify $ \st ->
- let hypo = encodeHypothesis m phi
- in st{checkingFacts = InsOrdMap.insert m hypo (checkingFacts st)}
+ prepared <- prepareFact phi
+ marker <- gets blockLabel
+ loc <- gets stepLocation
+ registerPreparedFacts
+ (stagePreparedFact loc marker marker prepared :| [])
-- | Mark a proof as indirect. Intended to be used in a @locally do@ block.
@@ -1150,56 +1201,71 @@ mentionsSymbol target = \case
-- | Unroll supported comprehensions in equations and reject leftover ones.
-- E.g. /@B = \\{f(a) | a\\in A \\}@/ turns into
-- /@\\forall b. b\\in B \\iff \\exists a\\in A. b = f(a)@/.
-desugarComprehensionsA :: ExprOf a -> CheckingM (ExprOf a)
-desugarComprehensionsA = \case
+desugarComprehensionsAt
+ :: BlockContext
+ -> ExprOf a
+ -> CheckingM (ExprOf a)
+desugarComprehensionsAt context = go
+ where
+ go :: forall b. ExprOf b -> CheckingM (ExprOf b)
+ go = \case
-- We only desugar comprehensions directly under equations.
-- Any remaining comprehension is currently unsupported.
- e@TermSep{} ->
- reject e
- e@ReplacePred{} ->
- reject e
- e@ReplaceFun{} ->
- reject e
- e@TermVar{} ->
- pure e
- e@PropositionalConstant{} ->
- pure e
- e@TermSymbolStruct{} ->
- pure e
+ e@TermSep{} ->
+ reject e
+ e@ReplacePred{} ->
+ reject e
+ e@ReplaceFun{} ->
+ reject e
+ e@TermVar{} ->
+ pure e
+ e@PropositionalConstant{} ->
+ pure e
+ e@TermSymbolStruct{} ->
+ pure e
--
- Equals _pos e (TermSep x bound scope) ->
- pure (desugarSeparation e x bound scope)
- Equals _pos (TermSep x bound scope) e ->
- pure (desugarSeparation e x bound scope)
+ Equals _pos e (TermSep x bound scope) ->
+ pure (desugarSeparation e x bound scope)
+ Equals _pos (TermSep x bound scope) e ->
+ pure (desugarSeparation e x bound scope)
--
- Equals _pos e (ReplaceFun bounds scope cond) ->
- pure (makeReplacementIff (F <$> e) bounds scope cond)
- Equals _pos (ReplaceFun bounds scope cond) e ->
- pure (makeReplacementIff (F <$> e) bounds scope cond)
+ Equals _pos e (ReplaceFun bounds scope cond) ->
+ pure (makeReplacementIff (F <$> e) bounds scope cond)
+ Equals _pos (ReplaceFun bounds scope cond) e ->
+ pure (makeReplacementIff (F <$> e) bounds scope cond)
--
- Apply e es ->
- Apply <$> desugarComprehensionsA e <*> traverse desugarComprehensionsA es
- Not loc e ->
- Not loc <$> desugarComprehensionsA e
- TermSymbol loc sym es ->
- TermSymbol loc sym <$> traverse desugarComprehensionsA es
- Connected conn e1 e2 ->
- Connected conn <$> desugarComprehensionsA e1 <*> desugarComprehensionsA e2
- Lambda scope ->
- Lambda <$> transverseScope desugarComprehensionsA scope
- Quantified quant scope ->
- Quantified quant <$> transverseScope desugarComprehensionsA scope
- where
- reject :: ExprOf a -> CheckingM b
- reject _ =
- throwWithLocationAndMarker (CheckingError "Could not eliminate set comprehensions in this step. Nested comprehensions are not supported yet.")
-
- desugarSeparation :: ExprOf a -> VarSymbol -> (ExprOf a) -> (Scope () ExprOf a) -> ExprOf a
- desugarSeparation e x bound scope =
- let phi = isElementOf (TermVar (B x)) (F <$> e)
- psi = isElementOf (TermVar (B x)) (F <$> bound)
- rho = fromScope (mapBound (const x) scope)
- in Quantified Universally (toScope (phi `Iff` (psi `And` rho)))
+ Apply e es ->
+ Apply <$> go e <*> traverse go es
+ Not loc e ->
+ Not loc <$> go e
+ TermSymbol loc sym es ->
+ TermSymbol loc sym <$> traverse go es
+ Connected conn e1 e2 ->
+ Connected conn <$> go e1 <*> go e2
+ Lambda scope ->
+ Lambda <$> transverseScope go scope
+ Quantified quant scope ->
+ Quantified quant <$> transverseScope go scope
+
+ reject :: ExprOf a -> CheckingM b
+ reject _ =
+ throwIO
+ (CheckingError
+ "Could not eliminate set comprehensions in this step. Nested comprehensions are not supported yet."
+ (blockContextLocation context)
+ (blockContextMarker context))
+
+ desugarSeparation
+ :: ExprOf a
+ -> VarSymbol
+ -> ExprOf a
+ -> Scope () ExprOf a
+ -> ExprOf a
+ desugarSeparation e x bound scope =
+ let phi = isElementOf (TermVar (B x)) (F <$> e)
+ psi = isElementOf (TermVar (B x)) (F <$> bound)
+ rho = fromScope (mapBound (const x) scope)
+ in Quantified Universally (toScope (phi `Iff` (psi `And` rho)))
@@ -1234,11 +1300,11 @@ checkBlocks = \case
withLabel loc marker (checkInductive inductiveDefn)
checkBlocks blocks
BlockStruct loc marker structDefn : blocks -> do
- withLabel loc marker (checkStructDefn structDefn)
+ checkStructDefn (BlockContext loc marker) structDefn
checkBlocks blocks
[] -> skip
--- | Add the given label to the set of in-scope markers and set it as the current label for error reporting.
+-- | Reserve a non-structure block label and make it current.
withLabel :: Location -> Marker -> CheckingM a -> CheckingM a
withLabel loc marker ma = do
reserveMarkers loc [marker]
@@ -1641,25 +1707,35 @@ byRef ms = locally do
case dumpPremselTraining of
WithDumpPremselTraining -> dumpTrainingData facts ms
WithoutDumpPremselTraining -> skip
- case InsOrdMap.lookupsMap ms facts of
- Left (Marker str) -> throwWithLocationAndMarker (CheckingError ("unknown marker: " <> str))
+ case Facts.restrictFactRegistry ms facts of
+ Left (Marker str) ->
+ throwWithLocationAndMarker
+ (CheckingError ("unknown marker: " <> str))
Right facts' -> modify (\st -> st{checkingFacts = facts'}) *> tellTasks
byAssumption :: Checking
byAssumption = locally do
- modify (\st -> st{checkingFacts = mempty}) *> tellTasks
+ modify (\st -> st{checkingFacts = Facts.emptyFactRegistry}) *> tellTasks
-dumpTrainingData :: InsOrdMap Marker Hypothesis -> NonEmpty Marker -> Checking
+dumpTrainingData :: Facts.FactRegistry -> NonEmpty Marker -> Checking
dumpTrainingData facts ms = do
- let (picked, unpicked) = InsOrdMap.pickOutMap ms facts
+ (picked, unpicked) <-
+ case Facts.partitionFactRegistry ms facts of
+ Left (Marker str) ->
+ throwWithLocationAndMarker
+ (CheckingError ("unknown marker: " <> str))
+ Right partitioned ->
+ pure partitioned
goals <- gets checkingGoals
m@(Marker m_) <- gets blockLabel
let dir = "premseldump"
let makePath k = dir </> (Text.unpack m_ <> show (k :: Int)) <.> "txt"
- let dumpTrainingExample goal =
+ dumpTrainingExample goal =
let conj = encodeConjecture m goal
- usefuls = encodeWithRole Tptp.AxiomUseful (snd <$> InsOrdMap.toList picked)
- redundants = encodeWithRole Tptp.AxiomRedundant (snd <$> InsOrdMap.toList unpicked)
+ usefuls =
+ encodeWithRole Tptp.AxiomUseful (factHypotheses picked)
+ redundants =
+ encodeWithRole Tptp.AxiomRedundant (factHypotheses unpicked)
k = hash goal
example = Tptp.toTextNewline (Tptp.Task (conj : (usefuls <> redundants)))
in do
@@ -1678,10 +1754,10 @@ checkCase (Case split proof) = locally do
checkDefn :: Location -> Defn -> Checking
checkDefn loc defn = do
let (ownerKind, definedSymbol, definitionAsms, definiens) = definitionParts defn
- claimSymbolOwnership ownerKind definedSymbol
dependencies <- checkedDefinitionDependencies definedSymbol definitionAsms definiens
+ assertOwnedDependencies dependencies
+ claimSymbolOwnershipWithDependencies ownerKind definedSymbol dependencies
addDefinitionFact loc defn
- recordDefinitionDependencies definedSymbol dependencies
addDefinitionFact :: Location -> Defn -> Checking
addDefinitionFact loc = \case
@@ -1791,8 +1867,22 @@ canonicalize phi = do
-- | Canonicalize using an explicit structure context instead of the checker state's current one.
canonicalizeWith :: StructContext -> Formula -> CheckingM Formula
canonicalizeWith ctx phi = do
+ blockContext <-
+ BlockContext
+ <$> gets stepLocation
+ <*> gets blockLabel
+ canonicalizeWithAt blockContext ctx phi
+
+canonicalizeWithAt
+ :: BlockContext
+ -> StructContext
+ -> Formula
+ -> CheckingM Formula
+canonicalizeWithAt blockContext ctx phi = do
phi' <- unabbreviate phi
- desugarComprehensionsA (annotateWithStructContext ctx phi')
+ desugarComprehensionsAt
+ blockContext
+ (annotateWithStructContext ctx phi')
annotateWithStructContext :: StructContext -> Formula -> Formula
annotateWithStructContext StructContext{..} =
@@ -1862,10 +1952,19 @@ normalizeInductive Inductive{..} = do
ensureInductiveDefinitionIndependence :: CheckedInductive -> Checking
ensureInductiveDefinitionIndependence checked = do
- graph <- gets checkingDefinitionDependencies
+ graph <- gets checkingDependencies
let carrier = SymbolMixfix (checkedInductiveSymbol checked)
- externalSymbols = Set.delete carrier (checkedInductiveMentionedSymbols checked)
- case definitionDependencyPath graph externalSymbols carrier of
+ externalSymbols =
+ Set.filter ownableSymbol
+ (Set.delete carrier (checkedInductiveMentionedSymbols checked))
+ path <-
+ either
+ (\unknown ->
+ throwCheckingError
+ ("inductive specification mentions unknown symbol " <> symbolText unknown))
+ pure
+ (Dependencies.dependencyPath graph externalSymbols carrier)
+ case path of
Nothing ->
skip
Just path ->
@@ -1884,8 +1983,16 @@ ensureInductiveDefinitionIndependence checked = do
checkedInductiveFrozenSymbols :: CheckedInductive -> CheckingM (Set Symbol)
checkedInductiveFrozenSymbols checked = do
- graph <- gets checkingDefinitionDependencies
- pure (definitionDependencyClosure graph (checkedInductiveMentionedSymbols checked))
+ graph <- gets checkingDependencies
+ let seeds =
+ Set.filter ownableSymbol
+ (checkedInductiveMentionedSymbols checked)
+ either
+ (\unknown ->
+ throwCheckingError
+ ("inductive specification mentions unknown symbol " <> symbolText unknown))
+ pure
+ (Dependencies.dependencyClosure graph seeds)
normalizeInductiveIntro :: FunctionSymbol -> [VarSymbol] -> IntroRule -> CheckingM CheckedInductiveIntro
normalizeInductiveIntro symbol params IntroRule{introConditions, introResult} = do
@@ -2153,32 +2260,153 @@ inductiveUsedVars CheckedInductive{checkedInductiveParams, checkedInductiveIntro
]
)
-checkStructDefn :: StructDefn -> Checking
-checkStructDefn StructDefn{..} = do
- claimSymbolOwnership OwnedByStructureDefinition (SymbolPredicate (PredicateNounStruct structPhrase))
+checkStructDefn :: BlockContext -> StructDefn -> Checking
+checkStructDefn context structDefn@StructDefn{..} = do
+ structGraph <- gets checkingStructs
+ resolvedParents <-
+ forM (Set.toList structParents) \parent ->
+ case StructGraph.lookup parent structGraph of
+ Nothing ->
+ throwIO
+ (UnknownStructureParent
+ parent
+ (blockContextLocation context)
+ (blockContextMarker context))
+ Just struct ->
+ pure struct
+ let ancestors =
+ structParents
+ <> Set.unions (StructGraph.structAncestors <$> resolvedParents)
+ inheritedSymbols =
+ Set.unions (StructGraph.structSymbols <$> resolvedParents)
+ structContext =
+ structContextFromSymbols
+ structDefnLabel
+ (structDefnFixes <> inheritedSymbols)
+ assumptions <-
+ forM structDefnAssumes \(assumptionMarker, assumption) -> do
+ canonical <-
+ canonicalizeWithAt context structContext assumption
+ pure (assumptionMarker, canonical)
+ let canonicalStructDefn =
+ structDefn{structDefnAssumes = assumptions}
+ checked <-
+ either
+ (throwIO . structurePreparationCheckingError context)
+ pure
+ (Structure.prepareCheckedStructDefn
+ (blockContextLocation context)
+ (blockContextMarker context)
+ canonicalStructDefn
+ ancestors
+ inheritedSymbols)
st <- get
- let structGraph = checkingStructs st
- let m@(Marker m_) = blockLabel st
- let structAncestors = Set.unions (Set.map (`StructGraph.lookupAncestors` structGraph) structParents)
- let structAncestors' = structParents <> structAncestors
- let parentSymbols = Set.unions (Set.map (`StructGraph.lookupSymbols` structGraph) structParents)
- let structContext = structContextFromSymbols structDefnLabel (structDefnFixes <> parentSymbols)
- let isStruct p = TermSymbol Nowhere (SymbolPredicate (PredicateNounStruct p)) [TermVar structDefnLabel]
- let intro = if structParents == Set.singleton _Onesorted
- then makeConjunction (snd <$> structDefnAssumes) `Implies` isStruct structPhrase
- else makeConjunction ([isStruct parent | parent <- toList structParents] <> (snd <$> structDefnAssumes)) `Implies` isStruct structPhrase
- let intro' = (m, intro)
- let inherit' = (Marker (m_ <> "inherit"), isStruct structPhrase `Implies` makeConjunction [isStruct parent | parent <- toList structParents])
- let elims' = [(marker, isStruct structPhrase `Implies` phi) | (marker, phi) <- structDefnAssumes]
- rules' <- forM (InsOrdMap.toList (InsOrdMap.fromList (intro' : inherit' : elims'))) \(marker, phi) -> do
- phi' <- canonicalizeWith structContext phi
- assertOwnedSymbolsInFormula (forallClosure mempty phi')
- pure (marker, encodeHypothesis marker (forallClosure mempty phi'))
- let rules'' = InsOrdMap.fromList rules'
- put st
- { checkingStructs = StructGraph.insert structPhrase structAncestors' structDefnFixes (checkingStructs st)
- , checkingFacts = rules'' <> checkingFacts st
+ either throwIO put (commitCheckedStructDefn context checked st)
+
+structurePreparationCheckingError
+ :: BlockContext
+ -> Structure.StructurePreparationError
+ -> CheckingError
+structurePreparationCheckingError context = \case
+ Structure.SelfReferentialStructure symbol ->
+ CheckingError
+ ( "structure definition of symbol "
+ <> symbolText symbol
+ <> " is self-referential"
+ )
+ (blockContextLocation context)
+ (blockContextMarker context)
+
+-- | Validate and commit every state row introduced by one structure.
+commitCheckedStructDefn
+ :: BlockContext
+ -> Structure.CheckedStructDefn
+ -> CheckingState
+ -> Either CheckingError CheckingState
+commitCheckedStructDefn context checked st = do
+ markers' <-
+ validatedMarkers
+ location
+ (NonEmpty.toList (Structure.checkedStructMarkers checked))
+ st
+ (owners', ownedMarkers') <-
+ first (checkingErrorAt context)
+ (validatedOwnership
+ marker
+ OwnedByStructureDefinition
+ structureSymbol
+ st)
+ dependencies' <-
+ first
+ (checkingErrorAt context . dependencyRegistrationErrorText structureSymbol)
+ (Dependencies.registerDependencies
+ structureSymbol
+ semanticDependencies
+ (checkingDependencies st))
+ traverse_
+ (validatePreparedDependencies dependencies')
+ (Structure.checkedStructSemanticFacts checked)
+ facts' <-
+ first
+ (DuplicateMarker location)
+ (Facts.registerStagedFacts
+ (Structure.checkedStructFacts checked)
+ (checkingFacts st))
+ pure
+ st
+ { checkingFacts = facts'
+ , checkingDependencies = dependencies'
+ , checkingOwnedSymbols = owners'
+ , checkingOwnedSymbolMarkers = ownedMarkers'
+ , checkingStructs =
+ StructGraph.insert
+ (Structure.checkedStructPhrase checked)
+ (Structure.checkedStructAncestors checked)
+ (Structure.checkedStructInternalSymbols checked)
+ (Structure.checkedStructAllSymbols checked)
+ (checkingStructs st)
+ , definedMarkers = markers'
+ , blockLabel = marker
+ , stepLocation = location
+ , checkingHypothesisCounter = 0
}
+ where
+ location = blockContextLocation context
+ marker = blockContextMarker context
+ structureSymbol = Structure.checkedStructSymbol checked
+ semanticDependencies =
+ Set.filter ownableSymbol
+ (Structure.checkedStructDependencies checked)
+
+ validatePreparedDependencies dependencyRegistry prepared =
+ case
+ Set.lookupMin
+ ( Set.filter
+ (\dependency ->
+ ownableSymbol dependency
+ && isNothing
+ (Dependencies.lookupDependencies
+ dependency
+ dependencyRegistry))
+ (Facts.preparedSemanticDependencies prepared)
+ )
+ of
+ Nothing ->
+ Right ()
+ Just unknown ->
+ Left
+ (checkingErrorAt context
+ ( "structure fact depends on unknown symbol "
+ <> symbolText unknown
+ ))
+
+checkingErrorAt :: BlockContext -> Text -> CheckingError
+checkingErrorAt context message =
+ CheckingError
+ message
+ (blockContextLocation context)
+ (blockContextMarker context)
+
fixing :: NonEmpty VarSymbol -> Checking
fixing xs = do
diff --git a/source/Checking/Dependencies.hs b/source/Checking/Dependencies.hs
new file mode 100644
index 0000000..cf5c0f3
--- /dev/null
+++ b/source/Checking/Dependencies.hs
@@ -0,0 +1,124 @@
+{-# LANGUAGE NoImplicitPrelude #-}
+
+module Checking.Dependencies
+ ( DependencyRegistry
+ , DependencyRegistrationError(..)
+ , fromRootSymbols
+ , registerDependencies
+ , lookupDependencies
+ , dependencyClosure
+ , dependencyPath
+ ) where
+
+import Base
+import Syntax.Internal
+
+import Data.Map.Strict qualified as Map
+import Data.Set qualified as Set
+
+
+newtype DependencyRegistry = DependencyRegistry
+ { dependencyRows :: Map Symbol (Set Symbol)
+ }
+ deriving (Show, Eq)
+
+data DependencyRegistrationError
+ = DependencyOwnerAlreadyRegistered !Symbol
+ | SelfDependency !Symbol
+ | UnknownDependency !Symbol
+ deriving (Show, Eq)
+
+fromRootSymbols :: Set Symbol -> DependencyRegistry
+fromRootSymbols roots =
+ DependencyRegistry
+ (Map.fromSet (const mempty) roots)
+
+registerDependencies
+ :: Symbol
+ -> Set Symbol
+ -> DependencyRegistry
+ -> Either DependencyRegistrationError DependencyRegistry
+registerDependencies owner dependencies registry
+ | Map.member owner rows =
+ Left (DependencyOwnerAlreadyRegistered owner)
+ | owner `Set.member` dependencies =
+ Left (SelfDependency owner)
+ | Just unknown <- Set.lookupMin (dependencies `Set.difference` Map.keysSet rows) =
+ Left (UnknownDependency unknown)
+ | otherwise =
+ Right
+ (DependencyRegistry
+ (Map.insert owner dependencies rows))
+ where
+ rows = dependencyRows registry
+
+lookupDependencies
+ :: Symbol
+ -> DependencyRegistry
+ -> Maybe (Set Symbol)
+lookupDependencies symbol =
+ Map.lookup symbol . dependencyRows
+
+dependencyClosure
+ :: DependencyRegistry
+ -> Set Symbol
+ -> Either Symbol (Set Symbol)
+dependencyClosure registry =
+ go mempty . Set.toList
+ where
+ go seen [] =
+ Right seen
+ go seen (symbol:rest)
+ | symbol `Set.member` seen =
+ go seen rest
+ | otherwise =
+ case lookupDependencies symbol registry of
+ Nothing ->
+ Left symbol
+ Just direct ->
+ go
+ (Set.insert symbol seen)
+ (Set.toList direct <> rest)
+
+dependencyPath
+ :: DependencyRegistry
+ -> Set Symbol
+ -> Symbol
+ -> Either Symbol (Maybe [Symbol])
+dependencyPath registry seeds target =
+ firstPath (Set.toList seeds)
+ where
+ firstPath [] =
+ Right Nothing
+ firstPath (seed:rest) = do
+ path <- go mempty seed
+ case path of
+ Just _ ->
+ pure path
+ Nothing ->
+ firstPath rest
+
+ go seen symbol
+ | symbol == target =
+ Right (Just [symbol])
+ | symbol `Set.member` seen =
+ Right Nothing
+ | otherwise =
+ case lookupDependencies symbol registry of
+ Nothing ->
+ Left symbol
+ Just direct ->
+ prependFirst
+ symbol
+ (Set.toList direct)
+ (Set.insert symbol seen)
+
+ prependFirst _prefix [] _seen =
+ Right Nothing
+ prependFirst prefix (dependency:rest) seen = do
+ path <- go seen dependency
+ case path of
+ Just found ->
+ Right (Just (prefix : found))
+ Nothing ->
+ prependFirst prefix rest seen
diff --git a/source/Checking/Facts.hs b/source/Checking/Facts.hs
new file mode 100644
index 0000000..410c05d
--- /dev/null
+++ b/source/Checking/Facts.hs
@@ -0,0 +1,269 @@
+{-# LANGUAGE NoImplicitPrelude #-}
+
+module Checking.Facts
+ ( PreparedSemanticFact
+ , prepareSemanticFact
+ , preparedSemanticStatement
+ , preparedSemanticDependencies
+ , FactOrigin
+ , factOrigin
+ , factOriginLocation
+ , factOriginBlock
+ , StagedFact
+ , stageFact
+ , stagedFactAliases
+ , stagedFactSemantic
+ , FactRegistry
+ , emptyFactRegistry
+ , registerStagedFacts
+ , lookupPreparedFact
+ , lookupFactOrigin
+ , registeredFacts
+ , restrictFactRegistry
+ , partitionFactRegistry
+ , factRegistryInvariant
+ ) where
+
+import Base
+import Report.Location
+import Syntax.Internal
+
+import Data.List qualified as List
+import Data.List.NonEmpty qualified as NonEmpty
+import Data.Map.Strict qualified as Map
+import Data.Set qualified as Set
+
+
+-- | A checked fact before aliases and diagnostic provenance are attached.
+-- This is the complete long-lived semantic row.
+data PreparedSemanticFact = PreparedSemanticFact
+ { preparedSemanticStatement :: !Formula
+ , preparedSemanticDependencies :: !(Set Symbol)
+ }
+ deriving (Show, Eq)
+
+prepareSemanticFact :: Formula -> PreparedSemanticFact
+prepareSemanticFact statement =
+ PreparedSemanticFact
+ { preparedSemanticStatement = statement
+ , preparedSemanticDependencies = mentionedSymbols statement
+ }
+
+data FactOrigin = FactOrigin
+ { factOriginLocation :: !Location
+ , factOriginBlock :: !Marker
+ }
+ deriving (Show, Eq)
+
+factOrigin :: Location -> Marker -> FactOrigin
+factOrigin = FactOrigin
+
+-- | Registration data for one semantic fact.
+data StagedFact = StagedFact
+ { stagedFactAliases :: !(NonEmpty Marker)
+ , stagedFactOrigin :: !FactOrigin
+ , stagedFactSemantic :: !PreparedSemanticFact
+ }
+ deriving (Show, Eq)
+
+stageFact
+ :: NonEmpty Marker
+ -> FactOrigin
+ -> PreparedSemanticFact
+ -> StagedFact
+stageFact = StagedFact
+
+newtype FactHandle = FactHandle
+ { unFactHandle :: Int
+ }
+ deriving (Show, Eq, Ord)
+
+data FactRegistration = FactRegistration
+ { factRegistrationAliases :: !(NonEmpty Marker)
+ , factRegistrationOrigin :: !FactOrigin
+ }
+ deriving (Show, Eq)
+
+-- | Semantic rows and their registration sidecars share only a private handle.
+data FactRegistry = FactRegistry
+ { factOrder :: ![FactHandle]
+ , factRows :: !(Map FactHandle PreparedSemanticFact)
+ , factRegistrations :: !(Map FactHandle FactRegistration)
+ , factAliases :: !(Map Marker FactHandle)
+ , nextFactHandle :: !Int
+ }
+ deriving (Show, Eq)
+
+emptyFactRegistry :: FactRegistry
+emptyFactRegistry =
+ FactRegistry
+ { factOrder = []
+ , factRows = mempty
+ , factRegistrations = mempty
+ , factAliases = mempty
+ , nextFactHandle = 0
+ }
+
+registerStagedFacts
+ :: NonEmpty StagedFact
+ -> FactRegistry
+ -> Either Marker FactRegistry
+registerStagedFacts staged registry =
+ case firstDuplicateAlias (Map.keysSet (factAliases registry)) aliases of
+ Just duplicate ->
+ Left duplicate
+ Nothing ->
+ Right
+ registry
+ { factOrder = handles <> factOrder registry
+ , factRows =
+ Map.union
+ (Map.fromList
+ [ (handle, stagedFactSemantic fact)
+ | (handle, fact) <- registrations
+ ])
+ (factRows registry)
+ , factRegistrations =
+ Map.union
+ (Map.fromList
+ [ ( handle
+ , FactRegistration
+ { factRegistrationAliases =
+ stagedFactAliases fact
+ , factRegistrationOrigin =
+ stagedFactOrigin fact
+ }
+ )
+ | (handle, fact) <- registrations
+ ])
+ (factRegistrations registry)
+ , factAliases =
+ Map.union
+ (Map.fromList
+ [ (alias, handle)
+ | (handle, fact) <- registrations
+ , alias <-
+ NonEmpty.toList (stagedFactAliases fact)
+ ])
+ (factAliases registry)
+ , nextFactHandle =
+ nextFactHandle registry + length stagedList
+ }
+ where
+ stagedList = NonEmpty.toList staged
+ handles =
+ FactHandle <$>
+ [ nextFactHandle registry
+ .. nextFactHandle registry + length stagedList - 1
+ ]
+ registrations = zip handles stagedList
+ aliases =
+ concatMap
+ (NonEmpty.toList . stagedFactAliases)
+ stagedList
+
+lookupPreparedFact :: Marker -> FactRegistry -> Maybe PreparedSemanticFact
+lookupPreparedFact marker registry = do
+ handle <- Map.lookup marker (factAliases registry)
+ Map.lookup handle (factRows registry)
+
+lookupFactOrigin :: Marker -> FactRegistry -> Maybe FactOrigin
+lookupFactOrigin marker registry = do
+ handle <- Map.lookup marker (factAliases registry)
+ factRegistrationOrigin
+ <$> Map.lookup handle (factRegistrations registry)
+
+-- | Facts in checker premise order, paired with their primary aliases.
+registeredFacts :: FactRegistry -> [(Marker, PreparedSemanticFact)]
+registeredFacts registry =
+ [ (primaryAlias registration, semantic)
+ | handle <- factOrder registry
+ , Just registration <- [Map.lookup handle (factRegistrations registry)]
+ , Just semantic <- [Map.lookup handle (factRows registry)]
+ ]
+ where
+ primaryAlias =
+ NonEmpty.head . factRegistrationAliases
+
+restrictFactRegistry
+ :: NonEmpty Marker
+ -> FactRegistry
+ -> Either Marker FactRegistry
+restrictFactRegistry markers registry = do
+ handles <- traverse resolveHandle markers
+ pure (registryForHandles (orderedUnique (NonEmpty.toList handles)) registry)
+ where
+ resolveHandle marker =
+ maybe (Left marker) Right (Map.lookup marker (factAliases registry))
+
+partitionFactRegistry
+ :: NonEmpty Marker
+ -> FactRegistry
+ -> Either Marker (FactRegistry, FactRegistry)
+partitionFactRegistry markers registry = do
+ selected <- restrictFactRegistry markers registry
+ let selectedHandles = Set.fromList (factOrder selected)
+ selectedInRegistryOrder =
+ List.filter (`Set.member` selectedHandles) (factOrder registry)
+ unselectedHandles =
+ List.filter (`Set.notMember` selectedHandles) (factOrder registry)
+ pure
+ ( registryForHandles selectedInRegistryOrder registry
+ , registryForHandles unselectedHandles registry
+ )
+
+factRegistryInvariant :: FactRegistry -> Bool
+factRegistryInvariant registry =
+ length order == Set.size orderSet
+ && orderSet == Map.keysSet (factRows registry)
+ && orderSet == Map.keysSet (factRegistrations registry)
+ && aliasCount == Map.size expectedAliases
+ && expectedAliases == factAliases registry
+ && all ((< nextFactHandle registry) . unFactHandle) order
+ where
+ order = factOrder registry
+ orderSet = Set.fromList order
+ aliasPairs =
+ [ (alias, handle)
+ | (handle, registration) <-
+ Map.toList (factRegistrations registry)
+ , alias <-
+ NonEmpty.toList (factRegistrationAliases registration)
+ ]
+ aliasCount = length aliasPairs
+ expectedAliases = Map.fromList aliasPairs
+
+registryForHandles :: [FactHandle] -> FactRegistry -> FactRegistry
+registryForHandles handles registry =
+ registry
+ { factOrder = handles
+ , factRows = Map.restrictKeys (factRows registry) handleSet
+ , factRegistrations =
+ Map.restrictKeys (factRegistrations registry) handleSet
+ , factAliases =
+ Map.filter (`Set.member` handleSet) (factAliases registry)
+ }
+ where
+ handleSet = Set.fromList handles
+
+firstDuplicateAlias :: Set Marker -> [Marker] -> Maybe Marker
+firstDuplicateAlias existing =
+ go mempty
+ where
+ go _seen [] =
+ Nothing
+ go seen (marker:rest)
+ | marker `Set.member` existing || marker `Set.member` seen =
+ Just marker
+ | otherwise =
+ go (Set.insert marker seen) rest
+
+orderedUnique :: Ord a => [a] -> [a]
+orderedUnique =
+ reverse . snd . foldl' step (mempty, [])
+ where
+ step (seen, acc) value
+ | value `Set.member` seen =
+ (seen, acc)
+ | otherwise =
+ (Set.insert value seen, value : acc)
diff --git a/source/Checking/Structure.hs b/source/Checking/Structure.hs
new file mode 100644
index 0000000..a4ca5db
--- /dev/null
+++ b/source/Checking/Structure.hs
@@ -0,0 +1,134 @@
+{-# LANGUAGE NoImplicitPrelude #-}
+{-# LANGUAGE RecordWildCards #-}
+
+module Checking.Structure
+ ( CheckedStructDefn
+ , StructurePreparationError(..)
+ , prepareCheckedStructDefn
+ , checkedStructPhrase
+ , checkedStructAncestors
+ , checkedStructInternalSymbols
+ , checkedStructAllSymbols
+ , checkedStructSymbol
+ , checkedStructDependencies
+ , checkedStructSemanticFacts
+ , checkedStructFacts
+ , checkedStructMarkers
+ ) where
+
+import Base
+import Checking.Facts
+import Report.Location
+import Syntax.Internal
+import Syntax.Lexicon
+
+import Data.List.NonEmpty qualified as NonEmpty
+import Data.Set qualified as Set
+
+
+-- | A structure declaration whose formulas have been fully prepared.
+data CheckedStructDefn = CheckedStructDefn
+ { checkedStructPhrase :: !StructPhrase
+ , checkedStructAncestors :: !(Set StructPhrase)
+ , checkedStructInternalSymbols :: !(Set StructSymbol)
+ , checkedStructAllSymbols :: !(Set StructSymbol)
+ , checkedStructSymbol :: !Symbol
+ , checkedStructDependencies :: !(Set Symbol)
+ , checkedStructFacts :: !(NonEmpty StagedFact)
+ }
+ deriving (Show, Eq)
+
+checkedStructSemanticFacts
+ :: CheckedStructDefn
+ -> NonEmpty PreparedSemanticFact
+checkedStructSemanticFacts =
+ fmap stagedFactSemantic . checkedStructFacts
+
+checkedStructMarkers :: CheckedStructDefn -> NonEmpty Marker
+checkedStructMarkers =
+ (>>= stagedFactAliases) . checkedStructFacts
+
+data StructurePreparationError
+ = SelfReferentialStructure !Symbol
+ deriving (Show, Eq)
+
+prepareCheckedStructDefn
+ :: Location
+ -> Marker
+ -> StructDefn
+ -> Set StructPhrase
+ -> Set StructSymbol
+ -> Either StructurePreparationError CheckedStructDefn
+prepareCheckedStructDefn location marker StructDefn{..} ancestors inheritedSymbols
+ | prospectiveSymbol `Set.member` assumptionDependencies =
+ Left (SelfReferentialStructure prospectiveSymbol)
+ | otherwise =
+ Right
+ CheckedStructDefn
+ { checkedStructPhrase = structPhrase
+ , checkedStructAncestors = ancestors
+ , checkedStructInternalSymbols = structDefnFixes
+ , checkedStructAllSymbols = structDefnFixes <> inheritedSymbols
+ , checkedStructSymbol = prospectiveSymbol
+ , checkedStructDependencies =
+ parentSymbols <> assumptionDependencies
+ , checkedStructFacts = stagedFacts
+ }
+ where
+ prospectiveSymbol =
+ SymbolPredicate (PredicateNounStruct structPhrase)
+ parentSymbols =
+ Set.map
+ (SymbolPredicate . PredicateNounStruct)
+ structParents
+ assumptionDependencies =
+ Set.unions
+ [ preparedSemanticDependencies (prepareSemanticFact formula)
+ | (_assumptionMarker, formula) <- structDefnAssumes
+ ]
+ isStruct phrase =
+ TermSymbol
+ Nowhere
+ (SymbolPredicate (PredicateNounStruct phrase))
+ [TermVar structDefnLabel]
+ parentPremises
+ | structParents == Set.singleton _Onesorted =
+ []
+ | otherwise =
+ isStruct <$> Set.toList structParents
+ intro =
+ makeConjunction
+ (parentPremises <> (snd <$> structDefnAssumes))
+ `Implies` isStruct structPhrase
+ inherit =
+ isStruct structPhrase
+ `Implies` makeConjunction
+ [ isStruct parent
+ | parent <- Set.toList structParents
+ ]
+ generated =
+ (marker, intro)
+ :| ( (inheritMarker, inherit)
+ : [ (assumptionMarker, isStruct structPhrase `Implies` formula)
+ | (assumptionMarker, formula) <- structDefnAssumes
+ ]
+ )
+ inheritMarker =
+ case marker of
+ Marker text ->
+ Marker (text <> "inherit")
+ origin =
+ factOrigin location marker
+ semanticFacts =
+ fmap
+ (prepareSemanticFact . forallClosure mempty . snd)
+ generated
+ stagedFacts =
+ NonEmpty.zipWith
+ (\(factMarker, _formula) semantic ->
+ stageFact
+ (factMarker :| [])
+ origin
+ semantic)
+ generated
+ semanticFacts
diff --git a/source/Data/InsOrdMap.hs b/source/Data/InsOrdMap.hs
deleted file mode 100644
index ae75d31..0000000
--- a/source/Data/InsOrdMap.hs
+++ /dev/null
@@ -1,91 +0,0 @@
-{-# LANGUAGE TupleSections #-}
-
-{-
- Simple "mostly-insert-only" insert-ordered maps.
-
- There's also OMap from ordered-containers which uses two regular Maps,
- but this isn't really more efficient for our use case.
-
- There's also InsOrdHashMap from insert-ordered-containers which tries
- its best to preserve the insertion order. Alas, this isn't quite enough to have
- stable ATP proofs. At scale and with some other features helping with proof stability,
- (i.e. prover guidance, premise selection, etc), it may be worth revisiting InsOrdHashMap.
--}
-
-module Data.InsOrdMap where
-
-import Data.Either
-import Data.Functor
-import Data.Hashable
-import Data.HashMap.Strict (HashMap)
-import Data.HashMap.Strict qualified as HM
-import Data.Int
-import Data.List qualified as List
-import Data.List.NonEmpty (NonEmpty(..))
-import Data.List.NonEmpty qualified as NonEmpty
-import Data.Maybe (Maybe, maybe)
-import Data.Maybe qualified as Maybe
-import Data.Monoid
-import Data.Semigroup
-import Prelude (error)
-import Data.Traversable
-import Data.Foldable hiding (toList)
-
-data InsOrdMap k v = InsOrdMap {toList :: [(k, v)], toHashMap :: (HashMap k v)}
-
-instance Hashable k => Semigroup (InsOrdMap k a) where
- InsOrdMap a b <> InsOrdMap c d = InsOrdMap (a <> c) (b <> d)
-
-instance Hashable k => Monoid (InsOrdMap k a) where
- mempty = InsOrdMap mempty mempty
-
-instance Functor (InsOrdMap k) where
- fmap f (InsOrdMap asList asHashMap) = InsOrdMap (fmap (\(k,v) -> (k, f v)) asList) (fmap f asHashMap)
-
-instance Foldable (InsOrdMap k) where
- foldr f b (InsOrdMap _asList asHashMap) = foldr f b asHashMap
-
-instance Traversable (InsOrdMap k) where
- traverse f (InsOrdMap _asList asHashMap) = fromHashMap <$> (traverse f asHashMap)
-
-size :: InsOrdMap k v -> Int
-size omap = HM.size (toHashMap omap)
-
-fromList :: Hashable k => [(k, v)] -> InsOrdMap k v
-fromList kvs = InsOrdMap kvs (HM.fromList kvs)
-
-fromHashMap :: HashMap k v -> InsOrdMap k v
-fromHashMap kvs = InsOrdMap (HM.toList kvs) kvs
-
-insert :: Hashable k => k -> v -> InsOrdMap k v -> InsOrdMap k v
-insert k v (InsOrdMap kvs kvs') = InsOrdMap ((k, v) : kvs) (HM.insert k v kvs')
-
-mapMaybe :: (v1 -> Maybe v) -> InsOrdMap k v1 -> InsOrdMap k v
-mapMaybe f (InsOrdMap kvs kvs') = InsOrdMap (Maybe.mapMaybe (\(k, v) -> (k,) <$> f v) kvs) (HM.mapMaybe f kvs')
-
-lookup :: Hashable k => k -> InsOrdMap k a -> Maybe a
-lookup a (InsOrdMap _ kvs) = HM.lookup a kvs
-
-lookups :: Hashable k => NonEmpty k -> InsOrdMap k v -> Either k (NonEmpty v)
-lookups ks kvs =
- let lookups' = (\a kvs' -> maybe (Left a) Right (lookup a kvs')) <$> ks
- flap ff x = (\f -> f x) <$> ff
- in case partitionEithers (NonEmpty.toList (flap lookups' kvs)) of
- (missingKey : _, _) -> Left missingKey
- ([], v : vs) -> Right (v :| vs)
- ([], []) -> error "IMPOSSIBLE (Data.InsOrdMap.lookups): one of the two result lists must be nonempty as they partition a nonempty list"
-
--- | Only intended for very small nonempty lists of keys!
-lookupsMap :: Hashable k => NonEmpty k -> InsOrdMap k v -> Either k (InsOrdMap k v)
-lookupsMap ks kvs =
- let lookups' = (\a kvs' -> maybe (Left a) Right (lookup a kvs')) <$> ks
- flap ff x = (\f -> f x) <$> ff
- in case partitionEithers (NonEmpty.toList (flap lookups' kvs)) of
- (missingKey : _, _) -> Left missingKey
- ([], vs) -> Right (fromList (List.zip (NonEmpty.toList ks) vs))
-
--- | Split an InsOrdMap into an InsOrdMap specified by given "relevant" keys and non-given "irrelevant" keys.
-pickOutMap :: Hashable k => NonEmpty k -> InsOrdMap k v -> (InsOrdMap k v, InsOrdMap k v)
-pickOutMap ks kvs =
- let (relevants, irrelevants) = List.partition (\(k,_) -> k `List.elem` NonEmpty.toList ks) (toList kvs)
- in (fromList relevants, fromList irrelevants)
diff --git a/source/Encoding.hs b/source/Encoding.hs
index b0fac23..dc2092c 100644
--- a/source/Encoding.hs
+++ b/source/Encoding.hs
@@ -25,18 +25,6 @@ encodeTask task = Tptp.Task (conjecture' : hypos')
encodeTaskText :: Task -> Text
encodeTaskText = Tptp.toText . encodeTask
-encodeHypothesis :: Marker -> Formula -> Hypothesis
-encodeHypothesis m phi = encodeHypothesisContracted m phi (contraction phi)
-
-encodeHypothesisContracted :: Marker -> Formula -> Formula -> Hypothesis
-encodeHypothesisContracted m phi phiContracted =
- let encoded = encodeExpr phiContracted
- in Hypothesis
- { hypothesisMarker = m
- , hypothesisFormula = phi
- , hypothesisEncoded = encoded
- }
-
-- | Boolean contraction of a task.
contractionTask :: Task -> Task
contractionTask task = task
@@ -53,24 +41,24 @@ encodeConjecture (Marker str) f =
-- NOTE: E's SInE will only filter out axioms and leave hypotheses fixed.
encodeHypos :: [Hypothesis] -> [Tptp.FofFormula]
-encodeHypos phis = [makeHypo (hypothesisMarker h) (hypothesisEncoded h) | h <- phis]
+encodeHypos phis = [makeHypo (hypothesisMarker h) (hypothesisFormula h) | h <- phis]
where
- makeHypo :: Marker -> TextBuilder -> Tptp.FofFormula
- makeHypo (Marker str) f' =
+ makeHypo :: Marker -> Formula -> Tptp.FofFormula
+ makeHypo (Marker str) formula =
Tptp.FofFormula
(Tptp.NameAtomicWord (Tptp.AtomicWord str))
Tptp.Axiom
- f'
+ (encodeExpr (contraction formula))
encodeWithRole :: Tptp.Role -> [Hypothesis] -> [Tptp.FofFormula]
-encodeWithRole role phis = [makeHypo (hypothesisMarker h) (hypothesisEncoded h) | h <- phis]
+encodeWithRole role phis = [makeHypo (hypothesisMarker h) (hypothesisFormula h) | h <- phis]
where
- makeHypo :: Marker -> TextBuilder -> Tptp.FofFormula
- makeHypo (Marker str) f' =
+ makeHypo :: Marker -> Formula -> Tptp.FofFormula
+ makeHypo (Marker str) formula =
Tptp.FofFormula
(Tptp.NameAtomicWord (Tptp.AtomicWord str))
role
- f'
+ (encodeExpr (contraction formula))
writeTask :: Handle -> Task -> IO ()
writeTask h =
diff --git a/source/StructGraph.hs b/source/StructGraph.hs
index 35de34f..6e4c928 100644
--- a/source/StructGraph.hs
+++ b/source/StructGraph.hs
@@ -4,7 +4,21 @@
{-# LANGUAGE ScopedTypeVariables #-}
-module StructGraph where
+module StructGraph
+ ( Struct
+ , structNoun
+ , structAncestors
+ , structInternalSymbols
+ , structSymbols
+ , StructGraph
+ , lookup
+ , lookupAncestors
+ , lookupInternalSymbols
+ , lookupSymbols
+ , isInternalSymbolIn
+ , isSymbolIn
+ , insert
+ ) where
import Base
@@ -18,6 +32,7 @@ data Struct = Struct
{ structNoun :: StructPhrase
, structAncestors :: Set StructPhrase -- ^ All ancestors, including transitive ancestors.
, structInternalSymbols :: Set StructSymbol -- ^ Signature.
+ , structSymbols :: Set StructSymbol -- ^ Signature, including inherited symbols.
} deriving (Show, Eq, Ord)
newtype StructGraph
@@ -29,53 +44,33 @@ newtype StructGraph
lookup :: StructPhrase -> StructGraph -> Maybe Struct
lookup str graph = Map.lookup str (unStructGraph graph)
--- | Unsafe variant of 'lookup'.
-unsafeLookup :: StructPhrase -> StructGraph -> Struct
-unsafeLookup str graph = lookup str graph ?? error ("not in scope: " <> show str)
-
-- | Returns the ancestors of the given StructPhrase in the graph.
--- This function fails quietly by returning the empty set if the struct is not present in the graph.
-lookupAncestors :: StructPhrase -> StructGraph -> Set StructPhrase
-lookupAncestors str graph = case lookup str graph of
- Just struct -> structAncestors struct
- Nothing -> mempty
-
--- | Unsafe lookup of internal symbols by struct name.
-lookupInternalSymbols :: StructPhrase -> StructGraph -> Set StructSymbol
-lookupInternalSymbols phrase graph = case lookup phrase graph of
- Nothing -> error ("structure not in scope: " <> show phrase)
- Just struct -> structInternalSymbols struct
-
--- | Unsafe lookup of symbols of a structure, including those inherited from ancestors.
-lookupSymbols :: StructPhrase -> StructGraph -> Set StructSymbol
-lookupSymbols phrase graph = case lookup phrase graph of
- Nothing -> error ("structure not in scope: " <> show phrase)
- Just struct ->
- let ancestors = Set.map (`unsafeLookup` graph) (structAncestors struct)
- ancestorSymbols = Set.unions (Set.map structInternalSymbols ancestors)
- in ancestorSymbols <> structInternalSymbols struct
-
-structSymbols :: Struct -> StructGraph -> Set StructSymbol
-structSymbols struct graph =
- let ancestors = Set.map (`unsafeLookup` graph) (structAncestors struct)
- ancestorSymbols = Set.unions (Set.map structInternalSymbols ancestors)
- in structInternalSymbols struct <> ancestorSymbols
+lookupAncestors :: StructPhrase -> StructGraph -> Maybe (Set StructPhrase)
+lookupAncestors str graph =
+ structAncestors <$> lookup str graph
+
+lookupInternalSymbols :: StructPhrase -> StructGraph -> Maybe (Set StructSymbol)
+lookupInternalSymbols phrase graph =
+ structInternalSymbols <$> lookup phrase graph
+
+-- | Lookup all symbols of a structure, including inherited symbols.
+lookupSymbols :: StructPhrase -> StructGraph -> Maybe (Set StructSymbol)
+lookupSymbols phrase graph =
+ structSymbols <$> lookup phrase graph
isInternalSymbolIn :: StructSymbol -> Struct -> Bool
isInternalSymbolIn tok struct = Set.member tok (structInternalSymbols struct)
-isSymbolIn :: StructSymbol -> Struct -> StructGraph -> Bool
-isSymbolIn tok struct graph = Set.member tok (structSymbols struct graph)
+isSymbolIn :: StructSymbol -> Struct -> Bool
+isSymbolIn tok struct = Set.member tok (structSymbols struct)
-- | Insert a new struct into the graph.
insert
:: StructPhrase
-> Set StructPhrase
-> Set StructSymbol
+ -> Set StructSymbol
-> StructGraph
-> StructGraph
-insert structNoun ancestors structInternalSymbols graph =
- let
- transitiveAncestors anc = lookupAncestors anc graph
- structAncestors = ancestors `Set.union` Set.unions (Set.map transitiveAncestors ancestors)
- in StructGraph (Map.insert structNoun Struct{..} (unStructGraph graph))
+insert structNoun structAncestors structInternalSymbols structSymbols graph =
+ StructGraph (Map.insert structNoun Struct{..} (unStructGraph graph))
diff --git a/source/Syntax/Internal.hs b/source/Syntax/Internal.hs
index 287eed6..ea07533 100644
--- a/source/Syntax/Internal.hs
+++ b/source/Syntax/Internal.hs
@@ -62,7 +62,6 @@ import Data.HashMap.Strict qualified as HM
import Data.List qualified as List
import Data.List.NonEmpty qualified as NonEmpty
import Data.Set qualified as Set
-import TextBuilder (TextBuilder)
-- | 'Symbol's can be used as function and relation symbols.
data Symbol
@@ -186,6 +185,35 @@ deriving instance Hashable1 ExprOf
deriving instance Hashable a => Hashable (ExprOf a)
+mentionedSymbols :: ExprOf a -> Set Symbol
+mentionedSymbols = \case
+ TermVar{} ->
+ mempty
+ TermSymbol _loc symbol args ->
+ Set.insert symbol (Set.unions (mentionedSymbols <$> args))
+ TermSymbolStruct _symbol expr ->
+ maybe mempty mentionedSymbols expr
+ Apply expr args ->
+ mentionedSymbols expr <> Set.unions (mentionedSymbols <$> toList args)
+ TermSep _x bound scope ->
+ mentionedSymbols bound <> mentionedSymbols (fromScope scope)
+ ReplacePred _y _x bound scope ->
+ mentionedSymbols bound <> mentionedSymbols (fromScope scope)
+ ReplaceFun bounds lhs cond ->
+ Set.unions (mentionedSymbols . snd <$> toList bounds)
+ <> mentionedSymbols (fromScope lhs)
+ <> mentionedSymbols (fromScope cond)
+ Connected _conn left right ->
+ mentionedSymbols left <> mentionedSymbols right
+ Lambda scope ->
+ mentionedSymbols (fromScope scope)
+ Quantified _quant scope ->
+ mentionedSymbols (fromScope scope)
+ PropositionalConstant{} ->
+ mempty
+ Not _loc expr ->
+ mentionedSymbols expr
+
abstractVarSymbol :: VarSymbol -> ExprOf VarSymbol -> Scope VarSymbol ExprOf VarSymbol
abstractVarSymbol x = abstract (\y -> if x == y then Just x else Nothing)
@@ -694,22 +722,23 @@ data Task = Task
data Hypothesis = Hypothesis
{ hypothesisMarker :: Marker
, hypothesisFormula :: Formula
- , hypothesisEncoded :: TextBuilder
}
instance Show Hypothesis where
- show (Hypothesis marker formula _) =
+ show (Hypothesis marker formula) =
"Hypothesis " <> show marker <> " " <> show formula
instance Eq Hypothesis where
- Hypothesis m f _ == Hypothesis m' f' _ = (m, f) == (m', f')
+ Hypothesis marker formula == Hypothesis marker' formula' =
+ (marker, formula) == (marker', formula')
instance Ord Hypothesis where
- compare (Hypothesis m f _) (Hypothesis m' f' _) =
- compare (m, f) (m', f')
+ compare (Hypothesis marker formula) (Hypothesis marker' formula') =
+ compare (marker, formula) (marker', formula')
instance Hashable Hypothesis where
- hashWithSalt s (Hypothesis m f _) = hashWithSalt s (m, f)
+ hashWithSalt salt (Hypothesis marker formula) =
+ hashWithSalt salt (marker, formula)
-- | Indicates whether a given proof is direct or indirect.
diff --git a/source/Test/Unit/Checking.hs b/source/Test/Unit/Checking.hs
index e458e39..7043ee1 100644
--- a/source/Test/Unit/Checking.hs
+++ b/source/Test/Unit/Checking.hs
@@ -4,6 +4,9 @@ module Test.Unit.Checking (unitTests) where
import Base
import Checking
+import Checking.Dependencies qualified as Dependencies
+import Checking.Facts qualified as Facts
+import Checking.Structure qualified as Structure
import Encoding (encodeTaskText)
import Report.Location
import Syntax.Internal
@@ -13,7 +16,6 @@ import Bound.Scope (toScope)
import Bound.Var (Var(..))
import Control.Exception (try)
import Data.HashSet qualified as HS
-import Data.InsOrdMap qualified as InsOrdMap
import Data.Set qualified as Set
import Data.Text qualified as Text
import Test.Tasty
@@ -83,6 +85,7 @@ unitTests = testGroup "Checking"
text <- encodedTasksText boundStructLabelBlocks
assertContains "bound carrier label remains raw" "![XA]:elem(fx,XA)" text
assertNotContains "bound carrier label not rewritten" "![XA]:elem(fx,s__carrier(XA))" text
+ , structureTransactionTests
, testCase "abbreviations reject direct self-reference" do
expectCheckingError "self-referential" directSelfReferentialAbbrBlocks
, testCase "abbreviations reject indirect self-reference after expansion" do
@@ -196,6 +199,137 @@ unitTests = testGroup "Checking"
expectCheckingError "already owned by signature formula" transitiveFrozenDefinitionBlocks
]
+structureTransactionTests :: TestTree
+structureTransactionTests =
+ testGroup "structure transactions"
+ [ testCase "reject positive and negative direct self-reference" do
+ for_ [fooStructPredicate "A", Not Nowhere (fooStructPredicate "A")]
+ \assumption ->
+ expectCheckingError
+ "self-referential"
+ [fooStructBlock "self_struct" [("self_rule", assumption)]]
+ , testCase "reject abbreviation-hidden self-reference" do
+ expectCheckingError
+ "self-referential"
+ hiddenSelfReferentialStructBlocks
+ , testCase "report unknown and forward parents as located errors" do
+ expectUnknownStructureParent
+ unknownStruct
+ [testStructBlock "unknown_child" childStruct (Set.singleton unknownStruct) []]
+ expectUnknownStructureParent
+ fooStruct
+ [ testStructBlock "forward_child" childStruct (Set.singleton fooStruct) []
+ , fooStructBlock "forward_parent" []
+ ]
+ expectUnknownStructureParent
+ fooStruct
+ [testStructBlock "self_parent" fooStruct (Set.singleton fooStruct) []]
+ , testCase "reject every local structure marker collision" do
+ for_
+ [ ( "same_marker"
+ , fooStructBlock "same_marker" [("same_marker", Top)]
+ )
+ , ( "duplicate_rule"
+ , fooStructBlock
+ "duplicate_rules"
+ [("duplicate_rule", Top), ("duplicate_rule", Top)]
+ )
+ , ( "inherit_collisioninherit"
+ , fooStructBlock
+ "inherit_collision"
+ [("inherit_collisioninherit", Top)]
+ )
+ ]
+ \(duplicate, block) ->
+ expectDuplicateMarker duplicate [block]
+ , testCase "reject collisions before and after a structure" do
+ expectDuplicateMarker
+ "prior_rule"
+ [ BlockAxiom Nowhere "prior_rule" (Axiom [] Top)
+ , fooStructBlock "prior_collision" [("prior_rule", Top)]
+ ]
+ expectDuplicateMarker
+ "later_collisioninherit"
+ [ fooStructBlock "later_collision" []
+ , BlockAxiom
+ Nowhere
+ "later_collisioninherit"
+ (Axiom [] Top)
+ ]
+ , testCase "commit exact prepared facts and backward dependencies" do
+ case preparedFooStructure "prepared_struct" of
+ Left err ->
+ assertFailure
+ ("could not prepare structure: " <> show err)
+ Right checked -> do
+ let context = BlockContext Nowhere "prepared_struct"
+ initial =
+ initialCheckingState
+ WithoutDumpPremselTraining
+ (\_task -> pure ())
+ case commitCheckedStructDefn context checked initial of
+ Left err ->
+ assertFailure
+ ("could not commit structure: " <> show err)
+ Right committed -> do
+ assertBool
+ "fact registry invariant"
+ (Facts.factRegistryInvariant
+ (checkingFacts committed))
+ for_
+ (zip
+ (toList
+ (Structure.checkedStructMarkers checked))
+ (toList
+ (Structure.checkedStructSemanticFacts checked)))
+ \(factMarker, prepared) ->
+ assertEqual
+ ("prepared fact " <> show factMarker)
+ (Just prepared)
+ (Facts.lookupPreparedFact
+ factMarker
+ (checkingFacts committed))
+ assertEqual
+ "structure dependencies"
+ (Just
+ (Set.singleton
+ (SymbolPredicate
+ (PredicateNounStruct _Onesorted))))
+ (Dependencies.lookupDependencies
+ (Structure.checkedStructSymbol checked)
+ (checkingDependencies committed))
+ ]
+
+preparedFooStructure
+ :: Marker
+ -> Either
+ Structure.StructurePreparationError
+ Structure.CheckedStructDefn
+preparedFooStructure marker =
+ Structure.prepareCheckedStructDefn
+ Nowhere
+ marker
+ (fooStructDefn [("prepared_rule", Top)])
+ (Set.singleton _Onesorted)
+ (Set.singleton CarrierSymbol)
+
+expectUnknownStructureParent
+ :: StructPhrase
+ -> [Block]
+ -> Assertion
+expectUnknownStructureParent expected blocks = do
+ result <-
+ try (check WithoutDumpPremselTraining blocks)
+ :: IO (Either CheckingError [Task])
+ case result of
+ Left (UnknownStructureParent actual Nowhere _) ->
+ assertEqual "unknown structure parent" expected actual
+ Left err ->
+ assertFailure
+ ("expected UnknownStructureParent, got " <> show err)
+ Right _ ->
+ assertFailure "expected an unknown structure parent"
+
assumptionGoalReductionTests :: TestTree
assumptionGoalReductionTests =
testGroup "assumption goal reductions"
@@ -350,7 +484,9 @@ expectFactMarkers markers blocks = do
checkingState <- runCheckingBlocks blocks (initialCheckingState WithoutDumpPremselTraining (\_task -> pure ()))
let facts = checkingFacts checkingState
for_ markers \marker ->
- assertBool ("expected generated fact marker " <> show marker) (isJust (InsOrdMap.lookup marker facts))
+ assertBool
+ ("expected generated fact marker " <> show marker)
+ (isJust (Facts.lookupPreparedFact marker facts))
expectDefinedMarkers :: [Marker] -> [Block] -> Assertion
expectDefinedMarkers markers blocks = do
@@ -501,7 +637,11 @@ boundStructLabelBlocks =
fooStructBlock :: Marker -> [(Marker, Formula)] -> Block
fooStructBlock marker assumes =
- BlockStruct Nowhere marker StructDefn
+ BlockStruct Nowhere marker (fooStructDefn assumes)
+
+fooStructDefn :: [(Marker, Formula)] -> StructDefn
+fooStructDefn assumes =
+ StructDefn
{ structPhrase = fooStruct
, structParents = Set.singleton _Onesorted
, structDefnLabel = "A"
@@ -509,10 +649,37 @@ fooStructBlock marker assumes =
, structDefnAssumes = assumes
}
+testStructBlock
+ :: Marker
+ -> StructPhrase
+ -> Set StructPhrase
+ -> [(Marker, Formula)]
+ -> Block
+testStructBlock marker phrase parents assumes =
+ BlockStruct Nowhere marker StructDefn
+ { structPhrase = phrase
+ , structParents = parents
+ , structDefnLabel = "A"
+ , structDefnFixes = mempty
+ , structDefnAssumes = assumes
+ }
+
fooStruct :: StructPhrase
fooStruct =
mkLexicalItemSgPl (unsafeReadPhraseSgPl "foo[/s]") "foo"
+childStruct :: StructPhrase
+childStruct =
+ mkLexicalItemSgPl
+ (unsafeReadPhraseSgPl "child[/s]")
+ "child"
+
+unknownStruct :: StructPhrase
+unknownStruct =
+ mkLexicalItemSgPl
+ (unsafeReadPhraseSgPl "unknown[/s]")
+ "unknown"
+
fooOp :: StructSymbol
fooOp =
StructSymbol "fooop"
@@ -521,6 +688,43 @@ fooStructPredicate :: VarSymbol -> Formula
fooStructPredicate x =
TermSymbol Nowhere (SymbolPredicate (PredicateNounStruct fooStruct)) [TermVar x]
+hiddenSelfReferentialStructBlocks :: [Block]
+hiddenSelfReferentialStructBlocks =
+ [ BlockAbbr Nowhere "struct_alias_a"
+ ( Abbreviation
+ (SymbolPredicate structAliasA)
+ (toScope
+ (TermSymbol
+ Nowhere
+ (SymbolPredicate (PredicateNounStruct fooStruct))
+ [TermVar (B 0)]))
+ )
+ , BlockAbbr Nowhere "struct_alias_b"
+ ( Abbreviation
+ (SymbolPredicate structAliasB)
+ (toScope
+ (TermSymbol
+ Nowhere
+ (SymbolPredicate structAliasA)
+ [TermVar (B 0)]))
+ )
+ , fooStructBlock
+ "hidden_self_struct"
+ [("hidden_self_rule", structAliasFormula structAliasB (var "A"))]
+ ]
+
+structAliasA :: Predicate
+structAliasA =
+ PredicateSymbol "struct_alias_a"
+
+structAliasB :: Predicate
+structAliasB =
+ PredicateSymbol "struct_alias_b"
+
+structAliasFormula :: Predicate -> Term -> Formula
+structAliasFormula predicate term =
+ TermSymbol Nowhere (SymbolPredicate predicate) [term]
+
var :: VarSymbol -> Term
var = TermVar
diff --git a/source/Test/Unit/Provers.hs b/source/Test/Unit/Provers.hs
index 9ed5bee..13f94c3 100644
--- a/source/Test/Unit/Provers.hs
+++ b/source/Test/Unit/Provers.hs
@@ -3,11 +3,11 @@
module Test.Unit.Provers (unitTests) where
import Base
-import Encoding (encodeHypothesis)
import Provers
import Report.Location (pattern Nowhere)
import Syntax.Internal
( Directness(..)
+ , Hypothesis(..)
, Marker(..)
, Task(..)
, pattern Top
@@ -328,5 +328,5 @@ largeTask =
{ taskHypotheses =
replicate
20000
- (encodeHypothesis (Marker "large") Top)
+ (Hypothesis (Marker "large") Top)
}
diff --git a/source/Test/Unit/Symdiff.hs b/source/Test/Unit/Symdiff.hs
index 8d40320..bc205f0 100644
--- a/source/Test/Unit/Symdiff.hs
+++ b/source/Test/Unit/Symdiff.hs
@@ -3,7 +3,6 @@ module Test.Unit.Symdiff where
import Base
import Bound.Scope
import Bound.Var
-import Encoding (encodeHypothesis)
import Syntax.Internal
import Filter
import Report.Location
@@ -42,7 +41,6 @@ handlesStructAndApply =
hypo = Hypothesis
{ hypothesisMarker = Marker "struct_apply"
, hypothesisFormula = formula
- , hypothesisEncoded = mempty
}
in Map.member hypo (relevantFacts passmark formula (Set.singleton hypo))
@@ -58,7 +56,7 @@ symdiff =
, taskLocation = Nowhere
, taskConjectureLabel = Marker "symdiff_test"
, taskHypotheses = zipWith
- encodeHypothesis
+ Hypothesis
(Marker . Text.pack . show <$> ([1..] :: [Int]))
[ Quantified Universally (Scope (TermSymbol Nowhere (SymbolPredicate (PredicateRelation EqSymbol)) [TermSymbol Nowhere (SymbolMixfix (mixfix [Nothing, Just (Command "inter"), Nothing])) [TermVar (B (NamedVar "A")), TermVar (B (NamedVar "A"))], TermVar (B (NamedVar "A"))]))
, Quantified Universally (Scope (TermSymbol Nowhere (SymbolPredicate (PredicateRelation EqSymbol)) [TermSymbol Nowhere (SymbolMixfix (mixfix [Nothing, Just (Command "inter"), Nothing])) [TermVar (B (NamedVar "A")), TermVar (B (NamedVar "B"))], TermSymbol Nowhere (SymbolMixfix (mixfix [Nothing, Just (Command "inter"), Nothing])) [TermVar (B (NamedVar "B")), TermVar (B (NamedVar "A"))]]))