-
-
Notifications
You must be signed in to change notification settings - Fork 389
/
Copy pathJudgements.hs
380 lines (305 loc) · 12.5 KB
/
Judgements.hs
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
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE TupleSections #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE ViewPatterns #-}
module Ide.Plugin.Tactic.Judgements
( blacklistingDestruct
, unwhitelistingSplit
, introducingLambda
, introducingRecursively
, introducingPat
, jGoal
, jHypothesis
, jEntireHypothesis
, jPatHypothesis
, substJdg
, unsetIsTopHole
, filterSameTypeFromOtherPositions
, isDestructBlacklisted
, withNewGoal
, jLocalHypothesis
, isSplitWhitelisted
, isPatternMatch
, filterPosition
, isTopHole
, disallowing
, mkFirstJudgement
, hypothesisFromBindings
, isTopLevel
) where
import Control.Lens hiding (Context)
import Data.Bool
import Data.Char
import Data.Coerce
import Data.Generics.Product (field)
import Data.Map (Map)
import qualified Data.Map as M
import Data.Maybe
import Data.Set (Set)
import qualified Data.Set as S
import DataCon (DataCon)
import Development.IDE.Spans.LocalBindings
import Ide.Plugin.Tactic.Types
import OccName
import SrcLoc
import Type
------------------------------------------------------------------------------
-- | Given a 'SrcSpan' and a 'Bindings', create a hypothesis.
hypothesisFromBindings :: RealSrcSpan -> Bindings -> Map OccName (HyInfo CType)
hypothesisFromBindings span bs = buildHypothesis $ getLocalScope bs span
------------------------------------------------------------------------------
-- | Convert a @Set Id@ into a hypothesis.
buildHypothesis :: [(Name, Maybe Type)] -> Map OccName (HyInfo CType)
buildHypothesis
= M.fromList
. mapMaybe go
where
go (occName -> occ, t)
| Just ty <- t
, isAlpha . head . occNameString $ occ = Just (occ, HyInfo UserPrv $ CType ty)
| otherwise = Nothing
blacklistingDestruct :: Judgement -> Judgement
blacklistingDestruct =
field @"_jBlacklistDestruct" .~ True
unwhitelistingSplit :: Judgement -> Judgement
unwhitelistingSplit =
field @"_jWhitelistSplit" .~ False
isDestructBlacklisted :: Judgement -> Bool
isDestructBlacklisted = _jBlacklistDestruct
isSplitWhitelisted :: Judgement -> Bool
isSplitWhitelisted = _jWhitelistSplit
withNewGoal :: a -> Judgement' a -> Judgement' a
withNewGoal t = field @"_jGoal" .~ t
------------------------------------------------------------------------------
-- | Helper function for implementing functions which introduce new hypotheses.
introducing
:: (Int -> Provenance) -- ^ A function from the position of the arg to its
-- provenance.
-> [(OccName, a)]
-> Judgement' a
-> Judgement' a
introducing f ns =
field @"_jHypothesis" <>~ M.fromList (zip [0..] ns <&>
\(pos, (name, ty)) -> (name, HyInfo (f pos) ty))
------------------------------------------------------------------------------
-- | Introduce bindings in the context of a lamba.
introducingLambda
:: Maybe OccName -- ^ The name of the top level function. For any other
-- function, this should be 'Nothing'.
-> [(OccName, a)]
-> Judgement' a
-> Judgement' a
introducingLambda func = introducing $ \pos ->
maybe UserPrv (\x -> TopLevelArgPrv x pos) func
------------------------------------------------------------------------------
-- | Introduce a binding in a recursive context.
introducingRecursively :: [(OccName, a)] -> Judgement' a -> Judgement' a
introducingRecursively = introducing $ const RecursivePrv
------------------------------------------------------------------------------
-- | Check whether any of the given occnames are an ancestor of the term.
hasPositionalAncestry
:: Foldable t
=> t OccName -- ^ Desired ancestors.
-> Judgement
-> OccName -- ^ Potential child
-> Maybe Bool -- ^ Just True if the result is the oldest positional ancestor
-- just false if it's a descendent
-- otherwise nothing
hasPositionalAncestry ancestors jdg name
| not $ null ancestors
= case any (== name) ancestors of
True -> Just True
False ->
case M.lookup name $ jAncestryMap jdg of
Just ancestry ->
bool Nothing (Just False) $ any (flip S.member ancestry) ancestors
Nothing -> Nothing
| otherwise = Nothing
------------------------------------------------------------------------------
-- | Helper function for disallowing hypotheses that have the wrong ancestry.
filterAncestry
:: Foldable t
=> t OccName
-> DisallowReason
-> Judgement
-> Judgement
filterAncestry ancestry reason jdg =
disallowing reason (M.keys $ M.filterWithKey go $ jHypothesis jdg) jdg
where
go name _
= not
. isJust
$ hasPositionalAncestry ancestry jdg name
------------------------------------------------------------------------------
-- | @filter defn pos@ removes any hypotheses which are bound in @defn@ to
-- a position other than @pos@. Any terms whose ancestry doesn't include @defn@
-- remain.
filterPosition :: OccName -> Int -> Judgement -> Judgement
filterPosition defn pos jdg =
filterAncestry (findPositionVal jdg defn pos) (WrongBranch pos) jdg
------------------------------------------------------------------------------
-- | Helper function for determining the ancestry list for 'filterPosition'.
findPositionVal :: Judgement' a -> OccName -> Int -> Maybe OccName
findPositionVal jdg defn pos = listToMaybe $ do
-- It's important to inspect the entire hypothesis here, as we need to trace
-- ancstry through potentially disallowed terms in the hypothesis.
(name, hi) <- M.toList $ M.map (overProvenance expandDisallowed) $ jEntireHypothesis jdg
case hi_provenance hi of
TopLevelArgPrv defn' pos'
| defn == defn'
, pos == pos' -> pure name
PatternMatchPrv pv
| pv_scrutinee pv == Just defn
, pv_position pv == pos -> pure name
_ -> []
------------------------------------------------------------------------------
-- | Helper function for determining the ancestry list for
-- 'filterSameTypeFromOtherPositions'.
findDconPositionVals :: Judgement' a -> DataCon -> Int -> [OccName]
findDconPositionVals jdg dcon pos = do
(name, hi) <- M.toList $ jHypothesis jdg
case hi_provenance hi of
PatternMatchPrv pv
| pv_datacon pv == Uniquely dcon
, pv_position pv == pos -> pure name
_ -> []
------------------------------------------------------------------------------
-- | Disallow any hypotheses who have the same type as anything bound by the
-- given position for the datacon. Used to ensure recursive functions like
-- 'fmap' preserve the relative ordering of their arguments by eliminating any
-- other term which might match.
filterSameTypeFromOtherPositions :: DataCon -> Int -> Judgement -> Judgement
filterSameTypeFromOtherPositions dcon pos jdg =
let hy = jHypothesis
$ filterAncestry
(findDconPositionVals jdg dcon pos)
(WrongBranch pos)
jdg
tys = S.fromList $ fmap (hi_type . snd) $ M.toList hy
to_remove =
M.filter (flip S.member tys . hi_type) (jHypothesis jdg)
M.\\ hy
in disallowing Shadowed (M.keys to_remove) jdg
------------------------------------------------------------------------------
-- | Return the ancestry of a 'PatVal', or 'mempty' otherwise.
getAncestry :: Judgement' a -> OccName -> Set OccName
getAncestry jdg name =
case M.lookup name $ jPatHypothesis jdg of
Just pv -> pv_ancestry pv
Nothing -> mempty
jAncestryMap :: Judgement' a -> Map OccName (Set OccName)
jAncestryMap jdg =
flip M.map (jPatHypothesis jdg) pv_ancestry
------------------------------------------------------------------------------
-- TODO(sandy): THIS THING IS A BIG BIG HACK
--
-- Why? 'ctxDefiningFuncs' is _all_ of the functions currently beind defined
-- (eg, we might be in a where block). The head of this list is not guaranteed
-- to be the one we're interested in.
extremelyStupid__definingFunction :: Context -> OccName
extremelyStupid__definingFunction =
fst . head . ctxDefiningFuncs
------------------------------------------------------------------------------
-- | Pattern vals are currently tracked in jHypothesis, with an extra piece of
-- data sitting around in jPatternVals.
introducingPat
:: Maybe OccName
-> DataCon
-> [(OccName, a)]
-> Judgement' a
-> Judgement' a
introducingPat scrutinee dc ns jdg
= introducing (\pos ->
PatternMatchPrv $
PatVal
scrutinee
(maybe mempty
(\scrut -> S.singleton scrut <> getAncestry jdg scrut)
scrutinee)
(Uniquely dc)
pos
) ns jdg
------------------------------------------------------------------------------
-- | Prevent some occnames from being used in the hypothesis. This will hide
-- them from 'jHypothesis', but not from 'jEntireHypothesis'.
disallowing :: DisallowReason -> [OccName] -> Judgement' a -> Judgement' a
disallowing reason (S.fromList -> ns) =
field @"_jHypothesis" %~ (M.mapWithKey $ \name hi ->
case S.member name ns of
True -> overProvenance (DisallowedPrv reason) hi
False -> hi
)
------------------------------------------------------------------------------
-- | The hypothesis, consisting of local terms and the ambient environment
-- (impors and class methods.) Hides disallowed values.
jHypothesis :: Judgement' a -> Map OccName (HyInfo a)
jHypothesis = M.filter (not . isDisallowed . hi_provenance) . jEntireHypothesis
------------------------------------------------------------------------------
-- | The whole hypothesis, including things disallowed.
jEntireHypothesis :: Judgement' a -> Map OccName (HyInfo a)
jEntireHypothesis = _jHypothesis
------------------------------------------------------------------------------
-- | Just the local hypothesis.
jLocalHypothesis :: Judgement' a -> Map OccName (HyInfo a)
jLocalHypothesis = M.filter (isLocalHypothesis . hi_provenance) . jHypothesis
------------------------------------------------------------------------------
-- | If we're in a top hole, the name of the defining function.
isTopHole :: Context -> Judgement' a -> Maybe OccName
isTopHole ctx =
bool Nothing (Just $ extremelyStupid__definingFunction ctx) . _jIsTopHole
unsetIsTopHole :: Judgement' a -> Judgement' a
unsetIsTopHole = field @"_jIsTopHole" .~ False
------------------------------------------------------------------------------
-- | Only the hypothesis members which are pattern vals
jPatHypothesis :: Judgement' a -> Map OccName PatVal
jPatHypothesis = M.mapMaybe (getPatVal . hi_provenance) . jHypothesis
getPatVal :: Provenance-> Maybe PatVal
getPatVal prov =
case prov of
PatternMatchPrv pv -> Just pv
_ -> Nothing
jGoal :: Judgement' a -> a
jGoal = _jGoal
substJdg :: TCvSubst -> Judgement -> Judgement
substJdg subst = fmap $ coerce . substTy subst . coerce
mkFirstJudgement
:: M.Map OccName (HyInfo CType)
-> Bool -- ^ are we in the top level rhs hole?
-> Type
-> Judgement' CType
mkFirstJudgement hy top goal = Judgement
{ _jHypothesis = hy
, _jBlacklistDestruct = False
, _jWhitelistSplit = True
, _jIsTopHole = top
, _jGoal = CType goal
}
------------------------------------------------------------------------------
-- | Is this a top level function binding?
isTopLevel :: Provenance -> Bool
isTopLevel TopLevelArgPrv{} = True
isTopLevel _ = False
------------------------------------------------------------------------------
-- | Is this a local function argument, pattern match or user val?
isLocalHypothesis :: Provenance -> Bool
isLocalHypothesis UserPrv{} = True
isLocalHypothesis PatternMatchPrv{} = True
isLocalHypothesis TopLevelArgPrv{} = True
isLocalHypothesis _ = False
------------------------------------------------------------------------------
-- | Is this a pattern match?
isPatternMatch :: Provenance -> Bool
isPatternMatch PatternMatchPrv{} = True
isPatternMatch _ = False
------------------------------------------------------------------------------
-- | Was this term ever disallowed?
isDisallowed :: Provenance -> Bool
isDisallowed DisallowedPrv{} = True
isDisallowed _ = False
------------------------------------------------------------------------------
-- | Eliminates 'DisallowedPrv' provenances.
expandDisallowed :: Provenance -> Provenance
expandDisallowed (DisallowedPrv _ prv) = expandDisallowed prv
expandDisallowed prv = prv