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