1module Wingman.Judgements where
2
3import           ConLike (ConLike)
4import           Control.Arrow
5import           Control.Lens hiding (Context)
6import           Data.Bool
7import           Data.Char
8import           Data.Coerce
9import           Data.Generics.Product (field)
10import           Data.Map (Map)
11import qualified Data.Map as M
12import           Data.Maybe
13import           Data.Set (Set)
14import qualified Data.Set as S
15import           Development.IDE.Core.UseStale (Tracked, unTrack)
16import           Development.IDE.Spans.LocalBindings
17import           OccName
18import           SrcLoc
19import           Type
20import           Wingman.GHC (algebraicTyCon, normalizeType)
21import           Wingman.Judgements.Theta
22import           Wingman.Types
23
24
25------------------------------------------------------------------------------
26-- | Given a 'SrcSpan' and a 'Bindings', create a hypothesis.
27hypothesisFromBindings :: Tracked age RealSrcSpan -> Tracked age Bindings -> Hypothesis CType
28hypothesisFromBindings (unTrack -> span) (unTrack -> bs) = buildHypothesis $ getLocalScope bs span
29
30
31------------------------------------------------------------------------------
32-- | Convert a @Set Id@ into a hypothesis.
33buildHypothesis :: [(Name, Maybe Type)] -> Hypothesis CType
34buildHypothesis
35  = Hypothesis
36  . mapMaybe go
37  where
38    go (occName -> occ, t)
39      | Just ty <- t
40      , isAlpha . head . occNameString $ occ = Just $ HyInfo occ UserPrv $ CType ty
41      | otherwise = Nothing
42
43
44------------------------------------------------------------------------------
45-- | Build a trivial hypothesis containing only a single name. The corresponding
46-- HyInfo has no provenance or type.
47hySingleton :: OccName -> Hypothesis ()
48hySingleton n = Hypothesis . pure $ HyInfo n UserPrv ()
49
50
51blacklistingDestruct :: Judgement -> Judgement
52blacklistingDestruct =
53  field @"_jBlacklistDestruct" .~ True
54
55
56unwhitelistingSplit :: Judgement -> Judgement
57unwhitelistingSplit =
58  field @"_jWhitelistSplit" .~ False
59
60
61isDestructBlacklisted :: Judgement -> Bool
62isDestructBlacklisted = _jBlacklistDestruct
63
64
65isSplitWhitelisted :: Judgement -> Bool
66isSplitWhitelisted = _jWhitelistSplit
67
68
69withNewGoal :: a -> Judgement' a -> Judgement' a
70withNewGoal t = field @"_jGoal" .~ t
71
72
73------------------------------------------------------------------------------
74-- | Add some new type equalities to the local judgement.
75withNewCoercions :: [(CType, CType)] -> Judgement -> Judgement
76withNewCoercions ev j =
77  let subst = allEvidenceToSubst mempty $ coerce ev
78   in fmap (CType . substTyAddInScope subst . unCType) j
79      & field @"j_coercion" %~ unionTCvSubst subst
80
81
82normalizeHypothesis :: Functor f => Context -> f CType -> f CType
83normalizeHypothesis = fmap . coerce . normalizeType
84
85normalizeJudgement :: Functor f => Context -> f CType -> f CType
86normalizeJudgement = normalizeHypothesis
87
88
89introduce :: Context -> Hypothesis CType -> Judgement' CType -> Judgement' CType
90-- NOTE(sandy): It's important that we put the new hypothesis terms first,
91-- since 'jAcceptableDestructTargets' will never destruct a pattern that occurs
92-- after a previously-destructed term.
93introduce ctx hy =
94  field @"_jHypothesis" %~ mappend (normalizeHypothesis ctx hy)
95
96
97------------------------------------------------------------------------------
98-- | Helper function for implementing functions which introduce new hypotheses.
99introduceHypothesis
100    :: (Int -> Int -> Provenance)
101        -- ^ A function from the total number of args and position of this arg
102        -- to its provenance.
103    -> [(OccName, a)]
104    -> Hypothesis a
105introduceHypothesis f ns =
106  Hypothesis $ zip [0..] ns <&> \(pos, (name, ty)) ->
107    HyInfo name (f (length ns) pos) ty
108
109
110------------------------------------------------------------------------------
111-- | Introduce bindings in the context of a lamba.
112lambdaHypothesis
113    :: Maybe OccName   -- ^ The name of the top level function. For any other
114                       -- function, this should be 'Nothing'.
115    -> [(OccName, a)]
116    -> Hypothesis a
117lambdaHypothesis func =
118  introduceHypothesis $ \count pos ->
119    maybe UserPrv (\x -> TopLevelArgPrv x pos count) func
120
121
122------------------------------------------------------------------------------
123-- | Introduce a binding in a recursive context.
124recursiveHypothesis :: [(OccName, a)] -> Hypothesis a
125recursiveHypothesis = introduceHypothesis $ const $ const RecursivePrv
126
127
128------------------------------------------------------------------------------
129-- | Introduce a binding in a recursive context.
130userHypothesis :: [(OccName, a)] -> Hypothesis a
131userHypothesis = introduceHypothesis $ const $ const UserPrv
132
133
134------------------------------------------------------------------------------
135-- | Check whether any of the given occnames are an ancestor of the term.
136hasPositionalAncestry
137    :: Foldable t
138    => t OccName   -- ^ Desired ancestors.
139    -> Judgement
140    -> OccName     -- ^ Potential child
141    -> Maybe Bool  -- ^ Just True if the result is the oldest positional ancestor
142                   -- just false if it's a descendent
143                   -- otherwise nothing
144hasPositionalAncestry ancestors jdg name
145  | not $ null ancestors
146  = case any (== name) ancestors of
147      True  -> Just True
148      False ->
149        case M.lookup name $ jAncestryMap jdg of
150          Just ancestry ->
151            bool Nothing (Just False) $ any (flip S.member ancestry) ancestors
152          Nothing -> Nothing
153  | otherwise = Nothing
154
155
156------------------------------------------------------------------------------
157-- | Helper function for disallowing hypotheses that have the wrong ancestry.
158filterAncestry
159    :: Foldable t
160    => t OccName
161    -> DisallowReason
162    -> Judgement
163    -> Judgement
164filterAncestry ancestry reason jdg =
165    disallowing reason (M.keysSet $ M.filterWithKey go $ hyByName $ jHypothesis jdg) jdg
166  where
167    go name _
168      = not
169      . isJust
170      $ hasPositionalAncestry ancestry jdg name
171
172
173------------------------------------------------------------------------------
174-- | @filter defn pos@ removes any hypotheses which are bound in @defn@ to
175-- a position other than @pos@. Any terms whose ancestry doesn't include @defn@
176-- remain.
177filterPosition :: OccName -> Int -> Judgement -> Judgement
178filterPosition defn pos jdg =
179  filterAncestry (findPositionVal jdg defn pos) (WrongBranch pos) jdg
180
181
182------------------------------------------------------------------------------
183-- | Helper function for determining the ancestry list for 'filterPosition'.
184findPositionVal :: Judgement' a -> OccName -> Int -> Maybe OccName
185findPositionVal jdg defn pos = listToMaybe $ do
186  -- It's important to inspect the entire hypothesis here, as we need to trace
187  -- ancstry through potentially disallowed terms in the hypothesis.
188  (name, hi) <- M.toList
189              $ M.map (overProvenance expandDisallowed)
190              $ hyByName
191              $ jEntireHypothesis jdg
192  case hi_provenance hi of
193    TopLevelArgPrv defn' pos' _
194      | defn == defn'
195      , pos  == pos' -> pure name
196    PatternMatchPrv pv
197      | pv_scrutinee pv == Just defn
198      , pv_position pv  == pos -> pure name
199    _ -> []
200
201
202------------------------------------------------------------------------------
203-- | Helper function for determining the ancestry list for
204-- 'filterSameTypeFromOtherPositions'.
205findDconPositionVals :: Judgement' a -> ConLike -> Int -> [OccName]
206findDconPositionVals jdg dcon pos = do
207  (name, hi) <- M.toList $ hyByName $ jHypothesis jdg
208  case hi_provenance hi of
209    PatternMatchPrv pv
210      | pv_datacon  pv == Uniquely dcon
211      , pv_position pv == pos -> pure name
212    _ -> []
213
214
215------------------------------------------------------------------------------
216-- | Disallow any hypotheses who have the same type as anything bound by the
217-- given position for the datacon. Used to ensure recursive functions like
218-- 'fmap' preserve the relative ordering of their arguments by eliminating any
219-- other term which might match.
220filterSameTypeFromOtherPositions :: ConLike -> Int -> Judgement -> Judgement
221filterSameTypeFromOtherPositions dcon pos jdg =
222  let hy = hyByName
223         . jHypothesis
224         $ filterAncestry
225             (findDconPositionVals jdg dcon pos)
226             (WrongBranch pos)
227             jdg
228      tys = S.fromList $ hi_type <$> M.elems hy
229      to_remove =
230        M.filter (flip S.member tys . hi_type) (hyByName $ jHypothesis jdg)
231          M.\\ hy
232   in disallowing Shadowed (M.keysSet to_remove) jdg
233
234
235------------------------------------------------------------------------------
236-- | Return the ancestry of a 'PatVal', or 'mempty' otherwise.
237getAncestry :: Judgement' a -> OccName -> Set OccName
238getAncestry jdg name =
239  case M.lookup name $ jPatHypothesis jdg of
240    Just pv -> pv_ancestry pv
241    Nothing -> mempty
242
243
244jAncestryMap :: Judgement' a -> Map OccName (Set OccName)
245jAncestryMap jdg =
246  flip M.map (jPatHypothesis jdg) pv_ancestry
247
248
249provAncestryOf :: Provenance -> Set OccName
250provAncestryOf (TopLevelArgPrv o _ _) = S.singleton o
251provAncestryOf (PatternMatchPrv (PatVal mo so _ _)) =
252  maybe mempty S.singleton mo <> so
253provAncestryOf (ClassMethodPrv _) = mempty
254provAncestryOf UserPrv = mempty
255provAncestryOf RecursivePrv = mempty
256provAncestryOf ImportPrv = mempty
257provAncestryOf (DisallowedPrv _ p2) = provAncestryOf p2
258
259
260------------------------------------------------------------------------------
261-- TODO(sandy): THIS THING IS A BIG BIG HACK
262--
263-- Why? 'ctxDefiningFuncs' is _all_ of the functions currently beind defined
264-- (eg, we might be in a where block). The head of this list is not guaranteed
265-- to be the one we're interested in.
266extremelyStupid__definingFunction :: Context -> OccName
267extremelyStupid__definingFunction =
268  fst . head . ctxDefiningFuncs
269
270
271patternHypothesis
272    :: Maybe OccName
273    -> ConLike
274    -> Judgement' a
275    -> [(OccName, a)]
276    -> Hypothesis a
277patternHypothesis scrutinee dc jdg
278  = introduceHypothesis $ \_ pos ->
279      PatternMatchPrv $
280        PatVal
281          scrutinee
282          (maybe
283              mempty
284              (\scrut -> S.singleton scrut <> getAncestry jdg scrut)
285              scrutinee)
286          (Uniquely dc)
287          pos
288
289
290------------------------------------------------------------------------------
291-- | Prevent some occnames from being used in the hypothesis. This will hide
292-- them from 'jHypothesis', but not from 'jEntireHypothesis'.
293disallowing :: DisallowReason -> S.Set OccName -> Judgement' a -> Judgement' a
294disallowing reason ns =
295  field @"_jHypothesis" %~ (\z -> Hypothesis . flip fmap (unHypothesis z) $ \hi ->
296    case S.member (hi_name hi) ns of
297      True  -> overProvenance (DisallowedPrv reason) hi
298      False -> hi
299                           )
300
301
302------------------------------------------------------------------------------
303-- | The hypothesis, consisting of local terms and the ambient environment
304-- (impors and class methods.) Hides disallowed values.
305jHypothesis :: Judgement' a -> Hypothesis a
306jHypothesis
307  = Hypothesis
308  . filter (not . isDisallowed . hi_provenance)
309  . unHypothesis
310  . jEntireHypothesis
311
312
313------------------------------------------------------------------------------
314-- | The whole hypothesis, including things disallowed.
315jEntireHypothesis :: Judgement' a -> Hypothesis a
316jEntireHypothesis = _jHypothesis
317
318
319------------------------------------------------------------------------------
320-- | Just the local hypothesis.
321jLocalHypothesis :: Judgement' a -> Hypothesis a
322jLocalHypothesis
323  = Hypothesis
324  . filter (isLocalHypothesis . hi_provenance)
325  . unHypothesis
326  . jHypothesis
327
328
329------------------------------------------------------------------------------
330-- | Filter elements from the hypothesis
331hyFilter :: (HyInfo a -> Bool) -> Hypothesis a -> Hypothesis a
332hyFilter f  = Hypothesis . filter f . unHypothesis
333
334
335------------------------------------------------------------------------------
336-- | Given a judgment, return the hypotheses that are acceptable to destruct.
337--
338-- We use the ordering of the hypothesis for this purpose. Since new bindings
339-- are always inserted at the beginning, we can impose a canonical ordering on
340-- which order to try destructs by what order they are introduced --- stopping
341-- at the first one we've already destructed.
342jAcceptableDestructTargets :: Judgement' CType -> [HyInfo CType]
343jAcceptableDestructTargets
344  = filter (isJust . algebraicTyCon . unCType . hi_type)
345  . takeWhile (not . isAlreadyDestructed . hi_provenance)
346  . unHypothesis
347  . jEntireHypothesis
348
349
350------------------------------------------------------------------------------
351-- | If we're in a top hole, the name of the defining function.
352isTopHole :: Context -> Judgement' a -> Maybe OccName
353isTopHole ctx =
354  bool Nothing (Just $ extremelyStupid__definingFunction ctx) . _jIsTopHole
355
356
357unsetIsTopHole :: Judgement' a -> Judgement' a
358unsetIsTopHole = field @"_jIsTopHole" .~ False
359
360
361------------------------------------------------------------------------------
362-- | What names are currently in scope in the hypothesis?
363hyNamesInScope :: Hypothesis a -> Set OccName
364hyNamesInScope = M.keysSet . hyByName
365
366
367------------------------------------------------------------------------------
368-- | Are there any top-level function argument bindings in this judgement?
369jHasBoundArgs :: Judgement' a -> Bool
370jHasBoundArgs
371  = not
372  . null
373  . filter (isTopLevel . hi_provenance)
374  . unHypothesis
375  . jLocalHypothesis
376
377
378jNeedsToBindArgs :: Judgement' CType -> Bool
379jNeedsToBindArgs = isFunTy . unCType . jGoal
380
381
382------------------------------------------------------------------------------
383-- | Fold a hypothesis into a single mapping from name to info. This
384-- unavoidably will cause duplicate names (things like methods) to shadow one
385-- another.
386hyByName :: Hypothesis a -> Map OccName (HyInfo a)
387hyByName
388  = M.fromList
389  . fmap (hi_name &&& id)
390  . unHypothesis
391
392
393------------------------------------------------------------------------------
394-- | Only the hypothesis members which are pattern vals
395jPatHypothesis :: Judgement' a -> Map OccName PatVal
396jPatHypothesis
397  = M.mapMaybe (getPatVal . hi_provenance)
398  . hyByName
399  . jHypothesis
400
401
402getPatVal :: Provenance-> Maybe PatVal
403getPatVal prov =
404  case prov of
405    PatternMatchPrv pv -> Just pv
406    _                  -> Nothing
407
408
409jGoal :: Judgement' a -> a
410jGoal = _jGoal
411
412
413substJdg :: TCvSubst -> Judgement -> Judgement
414substJdg subst = fmap $ coerce . substTy subst . coerce
415
416
417mkFirstJudgement
418    :: Context
419    -> Hypothesis CType
420    -> Bool  -- ^ are we in the top level rhs hole?
421    -> Type
422    -> Judgement' CType
423mkFirstJudgement ctx hy top goal =
424  normalizeJudgement ctx $
425    Judgement
426      { _jHypothesis        = hy
427      , _jBlacklistDestruct = False
428      , _jWhitelistSplit    = True
429      , _jIsTopHole         = top
430      , _jGoal              = CType goal
431      , j_coercion          = emptyTCvSubst
432      }
433
434
435------------------------------------------------------------------------------
436-- | Is this a top level function binding?
437isTopLevel :: Provenance -> Bool
438isTopLevel TopLevelArgPrv{} = True
439isTopLevel _                = False
440
441
442------------------------------------------------------------------------------
443-- | Is this a local function argument, pattern match or user val?
444isLocalHypothesis :: Provenance -> Bool
445isLocalHypothesis UserPrv{}         = True
446isLocalHypothesis PatternMatchPrv{} = True
447isLocalHypothesis TopLevelArgPrv{}  = True
448isLocalHypothesis _                 = False
449
450
451------------------------------------------------------------------------------
452-- | Is this a pattern match?
453isPatternMatch :: Provenance -> Bool
454isPatternMatch PatternMatchPrv{} = True
455isPatternMatch _                 = False
456
457
458------------------------------------------------------------------------------
459-- | Was this term ever disallowed?
460isDisallowed :: Provenance -> Bool
461isDisallowed DisallowedPrv{} = True
462isDisallowed _               = False
463
464------------------------------------------------------------------------------
465-- | Has this term already been disallowed?
466isAlreadyDestructed :: Provenance -> Bool
467isAlreadyDestructed (DisallowedPrv AlreadyDestructed _) = True
468isAlreadyDestructed _ = False
469
470
471------------------------------------------------------------------------------
472-- | Eliminates 'DisallowedPrv' provenances.
473expandDisallowed :: Provenance -> Provenance
474expandDisallowed (DisallowedPrv _ prv) = expandDisallowed prv
475expandDisallowed prv                   = prv
476