2 % (c) The GRASP/AQUA Project, Glasgow University, 1998
4 \section[UsageSPLint]{UsageSP ``lint'' pass}
6 This code is (based on) PhD work of Keith Wansbrough <kw217@cl.cam.ac.uk>,
7 September 1998 .. May 1999.
9 Keith Wansbrough 1998-09-04..1999-06-25
12 module UsageSPLint ( doLintUSPAnnotsBinds,
18 #include "HsVersions.h"
22 import TypeRep ( Type(..), TyNote(..) ) -- friend
23 import Type ( UsageAnn(..), isUsgTy, tyUsg )
24 import TyCon ( isAlgTyCon, isPrimTyCon, isSynTyCon, isFunTyCon )
25 import Var ( Var, varType )
26 import Id ( idLBVarInfo )
27 import IdInfo ( LBVarInfo(..) )
28 import SrcLoc ( noSrcLoc )
29 import ErrUtils ( Message, ghcExit )
30 import Util ( zipWithEqual )
36 ======================================================================
41 @doLintUSPAnnotsBinds@ checks that annotations are in the correct positions.
42 @doLintUSPConstsBinds@ checks that no @UVar@s remain anywhere (i.e., all annots are constants).
43 @doLintUSPBinds@ checks that the annotations are consistent. [unimplemented!]
44 @doCheckIfWorseUSP@ checks that annots on binders have not changed from Once to Many.
47 doLint :: ULintM a -> IO ()
49 doLint m = case runULM m of
51 Just bad_news -> do { printDump (display bad_news)
54 where display bad_news = vcat [ text "*** LintUSP errors: ***"
56 , text "*** end of LintUSP errors ***"
59 doLintUSPAnnotsBinds, doLintUSPConstBinds :: [CoreBind] -> IO ()
61 doLintUSPAnnotsBinds = doLint . lintUSPAnnotsBinds
62 doLintUSPConstBinds = doLint . lintUSPConstBinds
64 -- doLintUSPBinds is defined below
66 doCheckIfWorseUSP :: [CoreBind] -> [CoreBind] -> IO ()
68 doCheckIfWorseUSP binds binds'
69 = case checkIfWorseUSP binds binds' of
71 Just warns -> printErrs warns
74 ======================================================================
76 Verifying correct annotation positioning
77 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
79 The following functions check whether the usage annotations are
80 correctly placed on a type. They sit inside the lint monad.
81 @lintUSPAnnots@ assumes there should be an outermost annotation,
82 @lintUSPAnnotsN@ assumes there shouldn't.
84 The fact that no general catch-all pattern is given for @NoteTy@s is
85 entirely intentional. The meaning of future extensions here is
86 entirely unknown, so you'll have to decide how to check them
90 lintTyUSPAnnots :: Bool -- die on omitted annotation?
91 -> Bool -- die on extra annotation?
92 -> Type -- type to check
95 lintTyUSPAnnots fom fex = lint
97 lint (NoteTy (UsgNote _) ty) = lintTyUSPAnnotsN fom fex ty
98 lint ty0 = do { mayErrULM fom "missing UsgNote" ty0
99 ; lintTyUSPAnnotsN fom fex ty0
102 lintTyUSPAnnotsN :: Bool -- die on omitted annotation?
103 -> Bool -- die on extra annotation?
104 -> Type -- type to check
107 lintTyUSPAnnotsN fom fex = lintN
109 lintN ty0@(NoteTy (UsgNote _) ty) = do { mayErrULM fex "unexpected UsgNote" ty0
112 lintN (NoteTy (SynNote sty) ty) = do { lintN sty
115 lintN (NoteTy (FTVNote _) ty) = do { lintN ty }
117 lintN (TyVarTy _) = do { return () }
118 lintN (AppTy ty1 ty2) = do { lintN ty1
121 lintN (TyConApp tc tys) = ASSERT( isFunTyCon tc || isAlgTyCon tc || isPrimTyCon tc || isSynTyCon tc )
122 do { let thelint = if isFunTyCon tc
123 then lintTyUSPAnnots fom fex
128 lintN (FunTy ty1 ty2) = do { lintTyUSPAnnots fom fex ty1
129 ; lintTyUSPAnnots fom fex ty2
131 lintN (ForAllTy _ ty) = do { lintN ty }
135 Now the combined function that takes a @MungeFlags@ to tell it what to
136 do to a particular type. This is passed to @genAnnotBinds@ to get the
140 lintUSPAnnotsTyM :: MungeFlags -> Type -> AnnotM (ULintM ()) Type
142 lintUSPAnnotsTyM mf ty = AnnotM $ \ m ve ->
144 ; atLocULM (mfLoc mf) $
147 else lintTyUSPAnnotsN) checkOmitted True ty
151 where checkOmitted = False -- OK to omit Many if !USMANY
153 where checkOmitted = True -- require all annotations
156 lintUSPAnnotsBinds :: [CoreBind]
159 lintUSPAnnotsBinds binds = case initAnnotM (return ()) $
160 genAnnotBinds lintUSPAnnotsTyM return binds of
161 -- **! should check with mungeTerm too!
165 ======================================================================
167 Verifying correct usage typing
168 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
170 The following function verifies that all usage annotations are
171 consistent. It assumes that there are no usage variables, only
172 @UsOnce@ and @UsMany@ annotations.
174 This is very similar to usage inference, however, and so we could
175 simply use that, with a little work. For now, it's unimplemented.
178 doLintUSPBinds :: [CoreBind] -> IO ()
180 doLintUSPBinds binds = panic "doLintUSPBinds unimplemented"
181 {- case initUs us (uniqSMMToUs (usgInfBinds binds)) of
182 ((ucs,_),_) -> if isJust (solveUCS ucs)
184 else do { printDump (text "*** LintUSPBinds failed ***")
190 ======================================================================
192 Verifying usage constants only (not vars)
193 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
195 The following function checks that all usage annotations are ground,
196 i.e., @UsOnce@ or @UsMany@: no @UVar@s remain.
199 lintTyUSPConst :: Type
202 lintTyUSPConst (TyVarTy _) = do { return () }
204 lintTyUSPConst (AppTy ty1 ty2) = do { lintTyUSPConst ty1
207 lintTyUSPConst (TyConApp tc tys) = do { mapM lintTyUSPConst tys
210 lintTyUSPConst (FunTy ty1 ty2) = do { lintTyUSPConst ty1
213 lintTyUSPConst (ForAllTy _ ty) = do { lintTyUSPConst ty }
215 lintTyUSPConst ty0@(NoteTy (UsgNote (UsVar _)) ty) = do { errULM "unexpected usage variable" ty0
218 lintTyUSPConst ty0@(NoteTy (UsgNote _) ty) = do { lintTyUSPConst ty }
219 lintTyUSPConst ty0@(NoteTy (SynNote sty) ty) = do { lintTyUSPConst sty
222 lintTyUSPConst ty0@(NoteTy (FTVNote _) ty) = do { lintTyUSPConst ty }
226 Now the combined function and the invocation of @genAnnotBinds@ to do the real work.
229 lintUSPConstTyM :: MungeFlags -> Type -> AnnotM (ULintM ()) Type
231 lintUSPConstTyM mf ty = AnnotM $ \ m ve ->
234 ; atLocULM (mfLoc mf) $
239 lintUSPConstBinds :: [CoreBind]
242 lintUSPConstBinds binds = case initAnnotM (return ()) $
243 genAnnotBinds lintUSPConstTyM return binds of
244 -- **! should check with mungeTerm too!
248 ======================================================================
250 Checking annotations don't get any worse
251 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
253 It is assumed that all transformations in GHC are `work-safe', that
254 is, they do not cause any work to be duplicated. Thus they should
255 also be safe wrt the UsageSP analysis: if an identifier has a
256 used-once type at one point, the identifier should never become
257 used-many after transformation. This check verifies that this is the
260 The arguments are the CoreBinds before and after the inference. They
261 must have exactly the same shape apart from usage annotations.
263 We only bother checking binders; free variables *should* be fixed
264 already since they are imported and not changeable.
266 First, the various kinds of worsenings we can have:
269 data WorseErr = WorseVar Var Var -- variable gets worse
270 | WorseTerm CoreExpr CoreExpr -- term gets worse
271 | WorseLam Var Var -- lambda gets worse
273 instance Outputable WorseErr where
274 ppr (WorseVar v0 v) = ptext SLIT("Identifier:") <+> ppr v0 <+> dcolon
275 <+> ( ptext SLIT("was") <+> ppr (varType v0)
276 $$ ptext SLIT("now") <+> ppr (varType v))
277 ppr (WorseTerm e0 e) = ptext SLIT("Term:")
278 <+> ( ptext SLIT("was") <+> ppr e0
279 $$ ptext SLIT("now") <+> ppr e)
280 ppr (WorseLam v0 v) = ptext SLIT("Lambda:")
282 $$ ptext SLIT("(lambda-bound var info for var worsened)"))
288 checkIfWorseUSP :: [CoreBind] -- old binds
289 -> [CoreBind] -- new binds
290 -> Maybe SDoc -- maybe warnings
292 checkIfWorseUSP binds binds'
293 = let vvs = checkBinds binds binds'
294 in if isEmptyBag vvs then
297 Just $ ptext SLIT("UsageSP warning: annotations worsen for")
298 $$ nest 4 (vcat (map ppr (bagToList vvs)))
300 checkBinds :: [CoreBind] -> [CoreBind] -> Bag WorseErr
301 checkBinds binds binds' = unionManyBags $
302 zipWithEqual "UsageSPLint.checkBinds" checkBind binds binds'
304 checkBind :: CoreBind -> CoreBind -> Bag WorseErr
305 checkBind (NonRec v e) (NonRec v' e') = (checkVar v v') `unionBags` (checkCE e e')
306 checkBind (Rec ves) (Rec ves') = unionManyBags $
307 zipWithEqual "UsageSPLint.checkBind"
308 (\ (v,e) (v',e') -> (checkVar v v')
309 `unionBags` (checkCE e e'))
311 checkBind _ _ = panic "UsageSPLint.checkBind"
314 checkCE :: CoreExpr -> CoreExpr -> Bag WorseErr
316 checkCE (Var _) (Var _) = emptyBag
317 checkCE (Lit _) (Lit _) = emptyBag
319 checkCE (App e arg) (App e' arg') = (checkCE e e')
320 `unionBags` (checkCE arg arg')
322 checkCE (Lam v e) (Lam v' e') = (checkVar v v')
323 `unionBags` (checkLamVar v v')
324 `unionBags` (checkCE e e')
326 checkCE (Let bind e) (Let bind' e') = (checkBind bind bind')
327 `unionBags` (checkCE e e')
329 checkCE (Case e v alts) (Case e' v' alts')
331 `unionBags` (checkVar v v')
332 `unionBags` (unionManyBags $
333 zipWithEqual "usageSPLint.checkCE:Case"
334 checkAlts alts alts')
335 where checkAlts (_,vs,e) (_,vs',e') = (unionManyBags $ zipWithEqual "UsageSPLint.checkCE:Alt"
337 `unionBags` (checkCE e e')
339 checkCE (Note (SCC _) e) (Note (SCC _) e') = checkCE e e'
341 checkCE (Note (Coerce _ _) e) (Note (Coerce _ _) e') = checkCE e e'
343 checkCE (Note InlineCall e) (Note InlineCall e') = checkCE e e'
345 checkCE (Note InlineMe e) (Note InlineMe e') = checkCE e e'
347 checkCE t@(Note (TermUsg u) e) t'@(Note (TermUsg u') e')
349 `unionBags` (checkUsg u u' (WorseTerm t t'))
351 checkCE (Type _) (Type _) = emptyBag
353 checkCE t t' = pprPanic "usageSPLint.checkCE:"
354 (ppr t $$ text "doesn't match" <+> ppr t')
357 -- does binder change from Once to Many?
358 -- notice we only check the top-level annotation; this is all that's necessary. KSW 1999-04.
359 checkVar :: Var -> Var -> Bag WorseErr
360 checkVar v v' | isTyVar v = emptyBag
361 | not (isUsgTy y) = emptyBag -- if initially no annot, definitely OK
362 | otherwise = checkUsg u u' (WorseVar v v')
368 -- does lambda change from Once to Many?
369 checkLamVar :: Var -> Var -> Bag WorseErr
370 checkLamVar v v' | isTyVar v = emptyBag
371 | otherwise = case (idLBVarInfo v, idLBVarInfo v') of
372 (NoLBVarInfo , _ ) -> emptyBag
373 (IsOneShotLambda, IsOneShotLambda) -> emptyBag
374 (IsOneShotLambda, NoLBVarInfo ) -> unitBag (WorseLam v v')
376 -- does term usage annotation change from Once to Many?
377 checkUsg :: UsageAnn -> UsageAnn -> WorseErr -> Bag WorseErr
378 checkUsg UsMany _ _ = emptyBag
379 checkUsg UsOnce UsOnce _ = emptyBag
380 checkUsg UsOnce UsMany err = unitBag err
383 ======================================================================
388 The errors (@ULintErr@s) are collected in the @ULintM@ monad, which
389 also tracks the location of the current type being checked.
392 data ULintErr = ULintErr SDoc String Type
394 pprULintErr :: ULintErr -> SDoc
395 pprULintErr (ULintErr loc s ty) = hang (text s <+> ptext SLIT("in") <+> loc <> ptext SLIT(":"))
399 newtype ULintM a = ULintM (SDoc -> (a,Bag ULintErr))
400 unULintM (ULintM f) = f
402 instance Monad ULintM where
403 m >>= f = ULintM $ \ loc -> let (a ,errs ) = (unULintM m) loc
404 (a',errs') = (unULintM (f a)) loc
405 in (a', errs `unionBags` errs')
406 return a = ULintM $ \ _ -> (a,emptyBag)
408 atLocULM :: SDoc -> ULintM a -> ULintM a
409 atLocULM loc m = ULintM $ \ _ -> (unULintM m) loc
411 errULM :: String -> Type -> ULintM ()
413 = ULintM $ \ loc -> ((),unitBag $ ULintErr loc err ty)
415 mayErrULM :: Bool -> String -> Type -> ULintM ()
417 = if f then errULM err ty else return ()
419 runULM :: ULintM a -> Maybe SDoc
420 runULM m = case (unULintM m) (panic "runULM: no location") of
421 (_,errs) -> if isEmptyBag errs
423 else Just (vcat (map pprULintErr (bagToList errs)))
426 ======================================================================