Skip to content

Commit dfc7b88

Browse files
authored
Fix detection of mutual blocks for the positivity checker (#3349)
- Closes #3071 - Closes #3348 When checking positivity, the mutual blocks are recomputed without the rule that all declarations depend on the previous one
1 parent 020be5c commit dfc7b88

File tree

9 files changed

+259
-153
lines changed

9 files changed

+259
-153
lines changed

src/Juvix/Compiler/Internal/Extra.hs

+80
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ module Juvix.Compiler.Internal.Extra
77
where
88

99
import Data.HashMap.Strict qualified as HashMap
10+
import Juvix.Compiler.Internal.Data.NameDependencyInfo
1011
import Juvix.Compiler.Internal.Extra.Base
1112
import Juvix.Compiler.Internal.Extra.Clonable
1213
import Juvix.Compiler.Internal.Extra.DependencyBuilder
@@ -162,6 +163,15 @@ genFieldProjection kind _funDefName _funDefType _funDefBuiltin _funDefArgsInfo m
162163
_lambdaClauses = pure cl
163164
}
164165

166+
flattenMutualBlocks :: [MutualBlock] -> [PreStatement]
167+
flattenMutualBlocks = concatMap (^.. mutualStatements . each . to toPreStatement)
168+
where
169+
toPreStatement :: MutualStatement -> PreStatement
170+
toPreStatement = \case
171+
StatementInductive i -> PreInductiveDef i
172+
StatementFunction i -> PreFunctionDef i
173+
StatementAxiom i -> PreAxiomDef i
174+
165175
-- | Generates definitions for each variable in a given pattern.
166176
genPatternDefs ::
167177
forall r.
@@ -357,3 +367,73 @@ substituteIndParams = substitutionE . HashMap.fromList . map (first (^. inductiv
357367
getInductiveKind :: InductiveDef -> Expression
358368
getInductiveKind InductiveDef {..} =
359369
foldFunType (map inductiveToFunctionParam _inductiveParameters) _inductiveType
370+
371+
buildMutualBlocks ::
372+
(Members '[Reader NameDependencyInfo] r) =>
373+
[PreStatement] ->
374+
Sem r [MutualBlock]
375+
buildMutualBlocks ss = do
376+
depInfo <- ask
377+
let scomponents :: [SCC Name] = buildSCCs depInfo
378+
sccs = boolHack (mapMaybe nameToPreStatement scomponents)
379+
return (map goSCC sccs)
380+
where
381+
goSCC :: SCC PreStatement -> MutualBlock
382+
goSCC = \case
383+
AcyclicSCC s -> goAcyclic s
384+
CyclicSCC c -> goCyclic (nonEmpty' c)
385+
where
386+
goCyclic :: NonEmpty PreStatement -> MutualBlock
387+
goCyclic c = MutualBlock (goMutual <$> c)
388+
where
389+
goMutual :: PreStatement -> MutualStatement
390+
goMutual = \case
391+
PreInductiveDef i -> StatementInductive i
392+
PreFunctionDef i -> StatementFunction i
393+
PreAxiomDef i -> StatementAxiom i
394+
395+
goAcyclic :: PreStatement -> MutualBlock
396+
goAcyclic = \case
397+
PreInductiveDef i -> one (StatementInductive i)
398+
PreFunctionDef i -> one (StatementFunction i)
399+
PreAxiomDef i -> one (StatementAxiom i)
400+
where
401+
one :: MutualStatement -> MutualBlock
402+
one = MutualBlock . pure
403+
404+
-- If the builtin bool definition is found, it is moved at the front.
405+
--
406+
-- This is a hack needed to translate BuiltinStringToNat in
407+
-- internal-to-core. BuiltinStringToNat is the only function that depends on
408+
-- Bool implicitly (i.e. without mentioning it in its type). Eventually
409+
-- BuiltinStringToNat needs to be removed and so this hack.
410+
boolHack :: [SCC PreStatement] -> [SCC PreStatement]
411+
boolHack s = case popFirstJust isBuiltinBool s of
412+
(Nothing, _) -> s
413+
(Just boolDef, rest) -> AcyclicSCC (PreInductiveDef boolDef) : rest
414+
where
415+
isBuiltinBool :: SCC PreStatement -> Maybe InductiveDef
416+
isBuiltinBool = \case
417+
CyclicSCC [PreInductiveDef b]
418+
| Just BuiltinBool <- b ^. inductiveBuiltin -> Just b
419+
_ -> Nothing
420+
421+
statementsByName :: HashMap Name PreStatement
422+
statementsByName = HashMap.fromList (map mkAssoc ss)
423+
where
424+
mkAssoc :: PreStatement -> (Name, PreStatement)
425+
mkAssoc s = case s of
426+
PreInductiveDef i -> (i ^. inductiveName, s)
427+
PreFunctionDef i -> (i ^. funDefName, s)
428+
PreAxiomDef i -> (i ^. axiomName, s)
429+
430+
getStmt :: Name -> Maybe PreStatement
431+
getStmt n = statementsByName ^. at n
432+
433+
nameToPreStatement :: SCC Name -> Maybe (SCC PreStatement)
434+
nameToPreStatement = nonEmptySCC . fmap getStmt
435+
where
436+
nonEmptySCC :: SCC (Maybe a) -> Maybe (SCC a)
437+
nonEmptySCC = \case
438+
AcyclicSCC a -> AcyclicSCC <$> a
439+
CyclicSCC p -> CyclicSCC . toList <$> nonEmpty (catMaybes p)

src/Juvix/Compiler/Internal/Extra/Base.hs

+1
Original file line numberDiff line numberDiff line change
@@ -285,6 +285,7 @@ instance HasExpressions ConstructorDef where
285285
pure
286286
ConstructorDef
287287
{ _inductiveConstructorType = ty',
288+
_inductiveConstructorNormalizedType,
288289
_inductiveConstructorName,
289290
_inductiveConstructorIsRecord,
290291
_inductiveConstructorPragmas,

src/Juvix/Compiler/Internal/Extra/DependencyBuilder.hs

+87-42
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,13 @@
11
module Juvix.Compiler.Internal.Extra.DependencyBuilder
2-
( buildDependencyInfoPreModule,
2+
( NameDependencyInfo,
3+
buildDependencyInfoPreModule,
34
buildDependencyInfoLet,
4-
ExportsTable,
5+
instanceDependencyParams,
6+
letDependencyParams,
7+
positivityNameDependencyInfo,
8+
DependencyParams (..),
9+
dependencyParamsIsStartNode,
10+
dependencyParamsInstance,
511
)
612
where
713

@@ -34,31 +40,67 @@ emptyBuilderState =
3440
_builderStateFromInt = Nothing
3541
}
3642

37-
type ExportsTable = HashSet NameId
43+
letDependencyParams :: DependencyParams
44+
letDependencyParams =
45+
DependencyParams
46+
{ _dependencyParamsIsStartNode = const False,
47+
_dependencyParamsInstance = False
48+
}
49+
50+
instanceDependencyParams :: HashSet NameId -> DependencyParams
51+
instanceDependencyParams s =
52+
DependencyParams
53+
{ _dependencyParamsIsStartNode = (`HashSet.member` s),
54+
_dependencyParamsInstance = True
55+
}
56+
57+
data DependencyParams = DependencyParams
58+
{ _dependencyParamsIsStartNode :: NameId -> Bool,
59+
-- | When set to True, each declaration depends on the previous declaration.
60+
-- Necessary for instance resolution
61+
_dependencyParamsInstance :: Bool
62+
}
63+
64+
makeLenses ''DependencyParams
3865

39-
buildDependencyInfoPreModule :: PreModule -> ExportsTable -> NameDependencyInfo
40-
buildDependencyInfoPreModule ms tab =
41-
buildDependencyInfoHelper tab (goPreModule ms >> addCastEdges)
66+
buildDependencyInfoPreModule :: forall r. (Members '[Reader DependencyParams] r) => PreModule -> Sem r NameDependencyInfo
67+
buildDependencyInfoPreModule ms =
68+
buildDependencyInfoHelper (goPreModule ms >> addCastEdges)
69+
70+
-- | Compute dependency info with `_dependencyParamsInstance` set to `False`.
71+
-- Used for positivity checking
72+
positivityNameDependencyInfo :: [PreStatement] -> NameDependencyInfo
73+
positivityNameDependencyInfo m =
74+
run
75+
. runReader dependencyParams
76+
. buildDependencyInfoHelper
77+
$ goPreStatements impossibleParent m
78+
where
79+
impossibleParent :: Name
80+
impossibleParent = impossibleError "This name should never be used because `_dependencyParamsInstance` is set to False"
81+
82+
dependencyParams :: DependencyParams
83+
dependencyParams =
84+
DependencyParams
85+
{ _dependencyParamsInstance = False,
86+
_dependencyParamsIsStartNode = const True
87+
}
4288

4389
buildDependencyInfoLet :: NonEmpty PreLetStatement -> NameDependencyInfo
4490
buildDependencyInfoLet ls =
45-
buildDependencyInfoHelper mempty (goPreLetStatements Nothing (toList ls) >> addCastEdges)
91+
run . runReader letDependencyParams $
92+
buildDependencyInfoHelper (goPreLetStatements Nothing (toList ls) >> addCastEdges)
4693

4794
buildDependencyInfoHelper ::
48-
ExportsTable ->
49-
Sem '[Reader ExportsTable, State DependencyGraph, State StartNodes, State BuilderState] () ->
50-
NameDependencyInfo
51-
buildDependencyInfoHelper tbl m = createDependencyInfo graph startNodes
52-
where
53-
startNodes :: StartNodes
54-
graph :: DependencyGraph
55-
(startNodes, graph) =
56-
run
57-
. evalState emptyBuilderState
58-
. runState HashSet.empty
59-
. execState HashMap.empty
60-
. runReader tbl
61-
$ m
95+
Sem (State DependencyGraph ': State StartNodes ': State BuilderState ': r) () ->
96+
Sem r NameDependencyInfo
97+
buildDependencyInfoHelper m = do
98+
(startNodes :: StartNodes, graph :: DependencyGraph) <-
99+
evalState emptyBuilderState
100+
. runState HashSet.empty
101+
. execState HashMap.empty
102+
$ m
103+
return (createDependencyInfo graph startNodes)
62104

63105
addCastEdges :: (Members '[State DependencyGraph, State BuilderState] r) => Sem r ()
64106
addCastEdges = do
@@ -76,6 +118,11 @@ addCastEdges = do
76118
addStartNode :: (Member (State StartNodes) r) => Name -> Sem r ()
77119
addStartNode n = modify (HashSet.insert n)
78120

121+
addEdgeParent :: (Members '[Reader DependencyParams, State DependencyGraph] r) => Name -> Name -> Sem r ()
122+
addEdgeParent a b = do
123+
inst <- asks (^. dependencyParamsInstance)
124+
when inst (addEdge a b)
125+
79126
addEdgeMay :: (Members '[State DependencyGraph, Reader (Maybe Name)] r) => Name -> Sem r ()
80127
addEdgeMay n2 = whenJustM ask $ \n1 -> addEdge n1 n2
81128

@@ -95,14 +142,12 @@ addEdge n1 n2 =
95142
Just ns -> Just (HashSet.insert n2 ns)
96143
Nothing -> Just (HashSet.singleton n2)
97144

98-
checkStartNode :: (Members '[Reader ExportsTable, State StartNodes, State BuilderState] r) => Name -> Sem r ()
145+
checkStartNode :: (Members '[Reader DependencyParams, State StartNodes, State BuilderState] r) => Name -> Sem r ()
99146
checkStartNode n = do
100-
tab <- ask
101-
when
102-
(HashSet.member (n ^. nameId) tab)
103-
(addStartNode n)
147+
isStart <- asks (^. dependencyParamsIsStartNode)
148+
when (isStart (n ^. nameId)) (addStartNode n)
104149

105-
goPreModule :: (Members '[Reader ExportsTable, State DependencyGraph, State StartNodes, State BuilderState] r) => PreModule -> Sem r ()
150+
goPreModule :: (Members '[Reader DependencyParams, State DependencyGraph, State StartNodes, State BuilderState] r) => PreModule -> Sem r ()
106151
goPreModule m = do
107152
checkStartNode (m ^. moduleName)
108153
let b = m ^. moduleBody
@@ -112,7 +157,7 @@ goPreModule m = do
112157

113158
goPreLetStatements ::
114159
forall r.
115-
(Members '[Reader ExportsTable, State DependencyGraph, State StartNodes, State BuilderState] r) =>
160+
(Members '[Reader DependencyParams, State DependencyGraph, State StartNodes, State BuilderState] r) =>
116161
Maybe Name ->
117162
[PreLetStatement] ->
118163
Sem r ()
@@ -128,7 +173,7 @@ goPreLetStatements mp = \case
128173

129174
goPreLetStatement ::
130175
forall r.
131-
(Members '[Reader ExportsTable, State DependencyGraph, State StartNodes, State BuilderState, Reader (Maybe Name)] r) =>
176+
(Members '[Reader DependencyParams, State DependencyGraph, State StartNodes, State BuilderState, Reader (Maybe Name)] r) =>
132177
PreLetStatement ->
133178
Sem r ()
134179
goPreLetStatement = \case
@@ -141,7 +186,7 @@ goPreLetStatement = \case
141186
-- if it exists) in order to guarantee that instance declarations are always
142187
-- processed before their uses. For an instance to be taken into account in
143188
-- instance resolution, it needs to be declared textually earlier.
144-
goPreStatements :: forall r. (Members '[Reader ExportsTable, State DependencyGraph, State StartNodes, State BuilderState] r) => Name -> [PreStatement] -> Sem r ()
189+
goPreStatements :: forall r. (Members '[Reader DependencyParams, State DependencyGraph, State StartNodes, State BuilderState] r) => Name -> [PreStatement] -> Sem r ()
145190
goPreStatements p = \case
146191
stmt : stmts -> do
147192
goPreStatement p stmt
@@ -155,24 +200,24 @@ goPreStatements p = \case
155200
PreInductiveDef i -> i ^. inductiveName
156201

157202
-- | `p` is the parent -- the previous declaration or the enclosing module
158-
goPreStatement :: forall r. (Members '[Reader ExportsTable, State DependencyGraph, State StartNodes, State BuilderState] r) => Name -> PreStatement -> Sem r ()
203+
goPreStatement :: forall r. (Members '[Reader DependencyParams, State DependencyGraph, State StartNodes, State BuilderState] r) => Name -> PreStatement -> Sem r ()
159204
goPreStatement p = \case
160205
PreAxiomDef ax -> goAxiom p ax
161206
PreFunctionDef f -> goTopFunctionDef p f
162207
PreInductiveDef i -> goInductive p i
163208

164-
goAxiom :: forall r. (Members '[Reader ExportsTable, State DependencyGraph, State StartNodes, State BuilderState] r) => Name -> AxiomDef -> Sem r ()
209+
goAxiom :: forall r. (Members '[Reader DependencyParams, State DependencyGraph, State StartNodes, State BuilderState] r) => Name -> AxiomDef -> Sem r ()
165210
goAxiom p ax = do
166211
checkStartNode (ax ^. axiomName)
167-
addEdge (ax ^. axiomName) p
212+
addEdgeParent (ax ^. axiomName) p
168213
runReader (Just (ax ^. axiomName)) (goExpression (ax ^. axiomType))
169214

170-
goInductive :: forall r. (Members '[Reader ExportsTable, State DependencyGraph, State StartNodes, State BuilderState] r) => Name -> InductiveDef -> Sem r ()
215+
goInductive :: forall r. (Members '[Reader DependencyParams, State DependencyGraph, State StartNodes, State BuilderState] r) => Name -> InductiveDef -> Sem r ()
171216
goInductive p i = do
172217
let indName = i ^. inductiveName
173218
checkStartNode indName
174219
checkBuiltinInductiveStartNode i
175-
addEdge indName p
220+
addEdgeParent indName p
176221
mapM_ (goConstructorDef indName) (i ^. inductiveConstructors)
177222
runReader (Just indName) $ do
178223
mapM_ goInductiveParameter (i ^. inductiveParameters)
@@ -206,9 +251,9 @@ checkBuiltinInductiveStartNode i = whenJust (i ^. inductiveBuiltin) go
206251
addInductiveStartNode :: Sem r ()
207252
addInductiveStartNode = addStartNode (i ^. inductiveName)
208253

209-
goTopFunctionDef :: (Members '[State DependencyGraph, State StartNodes, State BuilderState, Reader ExportsTable] r) => Name -> FunctionDef -> Sem r ()
254+
goTopFunctionDef :: (Members '[State DependencyGraph, State StartNodes, State BuilderState, Reader DependencyParams] r) => Name -> FunctionDef -> Sem r ()
210255
goTopFunctionDef p f = do
211-
addEdge (f ^. funDefName) p
256+
addEdgeParent (f ^. funDefName) p
212257
goFunctionDefHelper f
213258

214259
checkCast ::
@@ -221,7 +266,7 @@ checkCast f = case f ^. funDefBuiltin of
221266
_ -> return ()
222267

223268
goFunctionDefHelper ::
224-
(Members '[State DependencyGraph, State StartNodes, State BuilderState, Reader ExportsTable] r) =>
269+
(Members '[State DependencyGraph, State StartNodes, State BuilderState, Reader DependencyParams] r) =>
225270
FunctionDef ->
226271
Sem r ()
227272
goFunctionDefHelper f = do
@@ -235,7 +280,7 @@ goFunctionDefHelper f = do
235280

236281
-- | constructors of an inductive type depend on the inductive type, not the other
237282
-- way round; an inductive type depends on the types of its constructors
238-
goConstructorDef :: (Members '[State DependencyGraph, State StartNodes, State BuilderState, Reader ExportsTable] r) => Name -> ConstructorDef -> Sem r ()
283+
goConstructorDef :: (Members '[State DependencyGraph, State StartNodes, State BuilderState, Reader DependencyParams] r) => Name -> ConstructorDef -> Sem r ()
239284
goConstructorDef indName c = do
240285
addEdge (c ^. inductiveConstructorName) indName
241286
runReader (Just indName) (goExpression (c ^. inductiveConstructorType))
@@ -256,7 +301,7 @@ goPattern p = case p ^. patternArgPattern of
256301

257302
goExpression ::
258303
forall r.
259-
(Members '[State DependencyGraph, State StartNodes, State BuilderState, Reader ExportsTable, Reader (Maybe Name)] r) =>
304+
(Members '[State DependencyGraph, State StartNodes, State BuilderState, Reader DependencyParams, Reader (Maybe Name)] r) =>
260305
Expression ->
261306
Sem r ()
262307
goExpression e = case e of
@@ -326,15 +371,15 @@ goExpression e = case e of
326371
LetMutualBlock MutualBlockLet {..} -> mapM_ goFunctionDefHelper _mutualLet
327372

328373
goInductiveParameter ::
329-
(Members '[State DependencyGraph, State StartNodes, State BuilderState, Reader ExportsTable, Reader (Maybe Name)] r) =>
374+
(Members '[State DependencyGraph, State StartNodes, State BuilderState, Reader DependencyParams, Reader (Maybe Name)] r) =>
330375
InductiveParameter ->
331376
Sem r ()
332377
goInductiveParameter param = do
333378
addEdgeMay (param ^. inductiveParamName)
334379
goExpression (param ^. inductiveParamType)
335380

336381
goFunctionParameter ::
337-
(Members '[State DependencyGraph, State StartNodes, State BuilderState, Reader ExportsTable, Reader (Maybe Name)] r) =>
382+
(Members '[State DependencyGraph, State StartNodes, State BuilderState, Reader DependencyParams, Reader (Maybe Name)] r) =>
338383
FunctionParameter ->
339384
Sem r ()
340385
goFunctionParameter param = do

src/Juvix/Compiler/Internal/Language.hs

+3
Original file line numberDiff line numberDiff line change
@@ -448,6 +448,8 @@ data InductiveDef = InductiveDef
448448
data ConstructorDef = ConstructorDef
449449
{ _inductiveConstructorName :: ConstrName,
450450
_inductiveConstructorType :: Expression,
451+
-- | Filled by the typechecker. Used in positivity
452+
_inductiveConstructorNormalizedType :: Maybe NormalizedExpression,
451453
_inductiveConstructorIsRecord :: Bool,
452454
_inductiveConstructorPragmas :: Pragmas,
453455
_inductiveConstructorDocComment :: Maybe Text
@@ -508,6 +510,7 @@ data NormalizedExpression = NormalizedExpression
508510
{ _normalizedExpression :: Expression,
509511
_normalizedExpressionOriginal :: Expression
510512
}
513+
deriving stock (Data)
511514

512515
makePrisms ''Expression
513516
makePrisms ''Iden

0 commit comments

Comments
 (0)