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 ( IdOrTyVar, varType, idInfo )
26 import IdInfo ( LBVarInfo(..), lbvarInfo )
27 import SrcLoc ( noSrcLoc )
28 import ErrUtils ( Message, ghcExit )
29 import Util ( zipWithEqual )
35 ======================================================================
40 @doLintUSPAnnotsBinds@ checks that annotations are in the correct positions.
41 @doLintUSPConstsBinds@ checks that no @UVar@s remain anywhere (i.e., all annots are constants).
42 @doLintUSPBinds@ checks that the annotations are consistent. [unimplemented!]
43 @doCheckIfWorseUSP@ checks that annots on binders have not changed from Once to Many.
46 doLint :: ULintM a -> IO ()
48 doLint m = case runULM m of
50 Just bad_news -> do { printDump (display bad_news)
53 where display bad_news = vcat [ text "*** LintUSP errors: ***"
55 , text "*** end of LintUSP errors ***"
58 doLintUSPAnnotsBinds, doLintUSPConstBinds :: [CoreBind] -> IO ()
60 doLintUSPAnnotsBinds = doLint . lintUSPAnnotsBinds
61 doLintUSPConstBinds = doLint . lintUSPConstBinds
63 -- doLintUSPBinds is defined below
65 doCheckIfWorseUSP :: [CoreBind] -> [CoreBind] -> IO ()
67 doCheckIfWorseUSP binds binds'
68 = case checkIfWorseUSP binds binds' of
70 Just warns -> printErrs warns
73 ======================================================================
75 Verifying correct annotation positioning
76 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
78 The following functions check whether the usage annotations are
79 correctly placed on a type. They sit inside the lint monad.
80 @lintUSPAnnots@ assumes there should be an outermost annotation,
81 @lintUSPAnnotsN@ assumes there shouldn't.
83 The fact that no general catch-all pattern is given for @NoteTy@s is
84 entirely intentional. The meaning of future extensions here is
85 entirely unknown, so you'll have to decide how to check them
89 lintTyUSPAnnots :: Bool -- die on omitted annotation?
90 -> Bool -- die on extra annotation?
91 -> Type -- type to check
94 lintTyUSPAnnots fom fex = lint
96 lint (NoteTy (UsgNote _) ty) = lintTyUSPAnnotsN fom fex ty
97 lint ty0 = do { mayErrULM fom "missing UsgNote" ty0
98 ; lintTyUSPAnnotsN fom fex ty0
101 lintTyUSPAnnotsN :: Bool -- die on omitted annotation?
102 -> Bool -- die on extra annotation?
103 -> Type -- type to check
106 lintTyUSPAnnotsN fom fex = lintN
108 lintN ty0@(NoteTy (UsgNote _) ty) = do { mayErrULM fex "unexpected UsgNote" ty0
111 lintN (NoteTy (SynNote sty) ty) = do { lintN sty
114 lintN (NoteTy (FTVNote _) ty) = do { lintN ty }
116 lintN (TyVarTy _) = do { return () }
117 lintN (AppTy ty1 ty2) = do { lintN ty1
120 lintN (TyConApp tc tys) = ASSERT( isFunTyCon tc || isAlgTyCon tc || isPrimTyCon tc || isSynTyCon tc )
121 do { let thelint = if isFunTyCon tc
122 then lintTyUSPAnnots fom fex
127 lintN (FunTy ty1 ty2) = do { lintTyUSPAnnots fom fex ty1
128 ; lintTyUSPAnnots fom fex ty2
130 lintN (ForAllTy _ ty) = do { lintN ty }
134 Now the combined function that takes a @MungeFlags@ to tell it what to
135 do to a particular type. This is passed to @genAnnotBinds@ to get the
139 lintUSPAnnotsTyM :: MungeFlags -> Type -> AnnotM (ULintM ()) Type
141 lintUSPAnnotsTyM mf ty = AnnotM $ \ m ve ->
143 ; atLocULM (mfLoc mf) $
146 else lintTyUSPAnnotsN) checkOmitted True ty
150 where checkOmitted = False -- OK to omit Many if !USMANY
152 where checkOmitted = True -- require all annotations
155 lintUSPAnnotsBinds :: [CoreBind]
158 lintUSPAnnotsBinds binds = case initAnnotM (return ()) $
159 genAnnotBinds lintUSPAnnotsTyM return binds of
160 -- **! should check with mungeTerm too!
164 ======================================================================
166 Verifying correct usage typing
167 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
169 The following function verifies that all usage annotations are
170 consistent. It assumes that there are no usage variables, only
171 @UsOnce@ and @UsMany@ annotations.
173 This is very similar to usage inference, however, and so we could
174 simply use that, with a little work. For now, it's unimplemented.
177 doLintUSPBinds :: [CoreBind] -> IO ()
179 doLintUSPBinds binds = panic "doLintUSPBinds unimplemented"
180 {- case initUs us (uniqSMMToUs (usgInfBinds binds)) of
181 ((ucs,_),_) -> if isJust (solveUCS ucs)
183 else do { printDump (text "*** LintUSPBinds failed ***")
189 ======================================================================
191 Verifying usage constants only (not vars)
192 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
194 The following function checks that all usage annotations are ground,
195 i.e., @UsOnce@ or @UsMany@: no @UVar@s remain.
198 lintTyUSPConst :: Type
201 lintTyUSPConst (TyVarTy _) = do { return () }
203 lintTyUSPConst (AppTy ty1 ty2) = do { lintTyUSPConst ty1
206 lintTyUSPConst (TyConApp tc tys) = do { mapM lintTyUSPConst tys
209 lintTyUSPConst (FunTy ty1 ty2) = do { lintTyUSPConst ty1
212 lintTyUSPConst (ForAllTy _ ty) = do { lintTyUSPConst ty }
214 lintTyUSPConst ty0@(NoteTy (UsgNote (UsVar _)) ty) = do { errULM "unexpected usage variable" ty0
217 lintTyUSPConst ty0@(NoteTy (UsgNote _) ty) = do { lintTyUSPConst ty }
218 lintTyUSPConst ty0@(NoteTy (SynNote sty) ty) = do { lintTyUSPConst sty
221 lintTyUSPConst ty0@(NoteTy (FTVNote _) ty) = do { lintTyUSPConst ty }
225 Now the combined function and the invocation of @genAnnotBinds@ to do the real work.
228 lintUSPConstTyM :: MungeFlags -> Type -> AnnotM (ULintM ()) Type
230 lintUSPConstTyM mf ty = AnnotM $ \ m ve ->
233 ; atLocULM (mfLoc mf) $
238 lintUSPConstBinds :: [CoreBind]
241 lintUSPConstBinds binds = case initAnnotM (return ()) $
242 genAnnotBinds lintUSPConstTyM return binds of
243 -- **! should check with mungeTerm too!
247 ======================================================================
249 Checking annotations don't get any worse
250 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
252 It is assumed that all transformations in GHC are `work-safe', that
253 is, they do not cause any work to be duplicated. Thus they should
254 also be safe wrt the UsageSP analysis: if an identifier has a
255 used-once type at one point, the identifier should never become
256 used-many after transformation. This check verifies that this is the
259 The arguments are the CoreBinds before and after the inference. They
260 must have exactly the same shape apart from usage annotations.
262 We only bother checking binders; free variables *should* be fixed
263 already since they are imported and not changeable.
265 First, the various kinds of worsenings we can have:
268 data WorseErr = WorseVar IdOrTyVar IdOrTyVar -- variable gets worse
269 | WorseTerm CoreExpr CoreExpr -- term gets worse
270 | WorseLam IdOrTyVar IdOrTyVar -- lambda gets worse
272 instance Outputable WorseErr where
273 ppr (WorseVar v0 v) = ptext SLIT("Identifier:") <+> ppr v0 <+> dcolon
274 <+> ( ptext SLIT("was") <+> ppr (varType v0)
275 $$ ptext SLIT("now") <+> ppr (varType v))
276 ppr (WorseTerm e0 e) = ptext SLIT("Term:")
277 <+> ( ptext SLIT("was") <+> ppr e0
278 $$ ptext SLIT("now") <+> ppr e)
279 ppr (WorseLam v0 v) = ptext SLIT("Lambda:")
281 $$ ptext SLIT("(lambda-bound var info for var worsened)"))
287 checkIfWorseUSP :: [CoreBind] -- old binds
288 -> [CoreBind] -- new binds
289 -> Maybe SDoc -- maybe warnings
291 checkIfWorseUSP binds binds'
292 = let vvs = checkBinds binds binds'
293 in if isEmptyBag vvs then
296 Just $ ptext SLIT("UsageSP warning: annotations worsen for")
297 $$ nest 4 (vcat (map ppr (bagToList vvs)))
299 checkBinds :: [CoreBind] -> [CoreBind] -> Bag WorseErr
300 checkBinds binds binds' = unionManyBags $
301 zipWithEqual "UsageSPLint.checkBinds" checkBind binds binds'
303 checkBind :: CoreBind -> CoreBind -> Bag WorseErr
304 checkBind (NonRec v e) (NonRec v' e') = (checkVar v v') `unionBags` (checkCE e e')
305 checkBind (Rec ves) (Rec ves') = unionManyBags $
306 zipWithEqual "UsageSPLint.checkBind"
307 (\ (v,e) (v',e') -> (checkVar v v')
308 `unionBags` (checkCE e e'))
310 checkBind _ _ = panic "UsageSPLint.checkBind"
313 checkCE :: CoreExpr -> CoreExpr -> Bag WorseErr
315 checkCE (Var _) (Var _) = emptyBag
317 checkCE (Con _ args) (Con _ args') = unionManyBags $
318 zipWithEqual "UsageSPLint.checkCE:Con"
321 checkCE (App e arg) (App e' arg') = (checkCE e e')
322 `unionBags` (checkCE arg arg')
324 checkCE (Lam v e) (Lam v' e') = (checkVar v v')
325 `unionBags` (checkLamVar v v')
326 `unionBags` (checkCE e e')
328 checkCE (Let bind e) (Let bind' e') = (checkBind bind bind')
329 `unionBags` (checkCE e e')
331 checkCE (Case e v alts) (Case e' v' alts')
333 `unionBags` (checkVar v v')
334 `unionBags` (unionManyBags $
335 zipWithEqual "usageSPLint.checkCE:Case"
336 checkAlts alts alts')
337 where checkAlts (_,vs,e) (_,vs',e') = (unionManyBags $ zipWithEqual "UsageSPLint.checkCE:Alt"
339 `unionBags` (checkCE e e')
341 checkCE (Note (SCC _) e) (Note (SCC _) e') = checkCE e e'
343 checkCE (Note (Coerce _ _) e) (Note (Coerce _ _) e') = checkCE e e'
345 checkCE (Note InlineCall e) (Note InlineCall e') = checkCE e e'
347 checkCE (Note InlineMe e) (Note InlineMe e') = checkCE e e'
349 checkCE t@(Note (TermUsg u) e) t'@(Note (TermUsg u') e')
351 `unionBags` (checkUsg u u' (WorseTerm t t'))
353 checkCE (Type _) (Type _) = emptyBag
355 checkCE t t' = pprPanic "usageSPLint.checkCE:"
356 (ppr t $$ text "doesn't match" <+> ppr t')
359 -- does binder change from Once to Many?
360 -- notice we only check the top-level annotation; this is all that's necessary. KSW 1999-04.
361 checkVar :: IdOrTyVar -> IdOrTyVar -> Bag WorseErr
362 checkVar v v' | isTyVar v = emptyBag
363 | not (isUsgTy y) = emptyBag -- if initially no annot, definitely OK
364 | otherwise = checkUsg u u' (WorseVar v v')
370 -- does lambda change from Once to Many?
371 checkLamVar :: IdOrTyVar -> IdOrTyVar -> Bag WorseErr
372 checkLamVar v v' | isTyVar v = emptyBag
373 | otherwise = case ((lbvarInfo . idInfo) v, (lbvarInfo . idInfo) v') of
374 (NoLBVarInfo , _ ) -> emptyBag
375 (IsOneShotLambda, IsOneShotLambda) -> emptyBag
376 (IsOneShotLambda, NoLBVarInfo ) -> unitBag (WorseLam v v')
378 -- does term usage annotation change from Once to Many?
379 checkUsg :: UsageAnn -> UsageAnn -> WorseErr -> Bag WorseErr
380 checkUsg UsMany _ _ = emptyBag
381 checkUsg UsOnce UsOnce _ = emptyBag
382 checkUsg UsOnce UsMany err = unitBag err
385 ======================================================================
390 The errors (@ULintErr@s) are collected in the @ULintM@ monad, which
391 also tracks the location of the current type being checked.
394 data ULintErr = ULintErr SDoc String Type
396 pprULintErr :: ULintErr -> SDoc
397 pprULintErr (ULintErr loc s ty) = hang (text s <+> ptext SLIT("in") <+> loc <> ptext SLIT(":"))
401 newtype ULintM a = ULintM (SDoc -> (a,Bag ULintErr))
402 unULintM (ULintM f) = f
404 instance Monad ULintM where
405 m >>= f = ULintM $ \ loc -> let (a ,errs ) = (unULintM m) loc
406 (a',errs') = (unULintM (f a)) loc
407 in (a', errs `unionBags` errs')
408 return a = ULintM $ \ _ -> (a,emptyBag)
410 atLocULM :: SDoc -> ULintM a -> ULintM a
411 atLocULM loc m = ULintM $ \ _ -> (unULintM m) loc
413 errULM :: String -> Type -> ULintM ()
415 = ULintM $ \ loc -> ((),unitBag $ ULintErr loc err ty)
417 mayErrULM :: Bool -> String -> Type -> ULintM ()
419 = if f then errULM err ty else return ()
421 runULM :: ULintM a -> Maybe SDoc
422 runULM m = case (unULintM m) (panic "runULM: no location") of
423 (_,errs) -> if isEmptyBag errs
425 else Just (vcat (map pprULintErr (bagToList errs)))
428 ======================================================================