X-Git-Url: http://git.megacz.com/?p=ghc-hetmet.git;a=blobdiff_plain;f=ghc%2Fcompiler%2FcoreSyn%2FCoreLint.lhs;h=7470294d1c204144efeaee9547cca22f79b32a18;hp=929d40d27e3668329298552d65bc6d66125b82f4;hb=3fe27db88139e65f2a153c91b323cb43fd52185e;hpb=c49d51f812da9b1c2ceca7e0dad8f2a3626041a9 diff --git a/ghc/compiler/coreSyn/CoreLint.lhs b/ghc/compiler/coreSyn/CoreLint.lhs index 929d40d..7470294 100644 --- a/ghc/compiler/coreSyn/CoreLint.lhs +++ b/ghc/compiler/coreSyn/CoreLint.lhs @@ -1,55 +1,83 @@ % -% (c) The GRASP/AQUA Project, Glasgow University, 1993-1995 +% (c) The GRASP/AQUA Project, Glasgow University, 1993-1998 % \section[CoreLint]{A ``lint'' pass to check for Core correctness} \begin{code} -#include "HsVersions.h" - module CoreLint ( lintCoreBindings, - lintUnfolding + lintUnfolding, + showPass, endPass ) where -import Ubiq +#include "HsVersions.h" import CoreSyn +import CoreFVs ( idFreeVars ) +import CoreUtils ( findDefault, exprOkForSpeculation, coreBindsSize, mkPiType ) import Bag -import Kind ( Kind{-instance-} ) -import Literal ( literalType, Literal{-instance-} ) -import Id ( idType, isBottomingId, - dataConArgTys, GenId{-instances-} - ) -import Maybes ( catMaybes ) -import Name ( isLocallyDefined, getSrcLoc ) -import Outputable ( Outputable(..){-instance * []-} ) +import Literal ( literalType ) +import DataCon ( dataConRepType ) +import Var ( Var, Id, TyVar, idType, tyVarKind, isTyVar, isId, mustHaveLocalBinding ) +import VarSet +import Subst ( substTyWith ) +import Name ( getSrcLoc ) import PprCore -import PprStyle ( PprStyle(..) ) -import PprType ( GenType, GenTyVar, TyCon ) -import Pretty -import PrimOp ( primOpType, PrimOp(..) ) -import PrimRep ( PrimRep(..) ) -import SrcLoc ( SrcLoc ) -import Type ( mkFunTy,getFunTy_maybe,mkForAllTy,getForAllTy_maybe, - isPrimType,getTypeKind,instantiateTy, - mkForAllUsageTy,getForAllUsageTy,instantiateUsage, - maybeAppDataTyCon, eqTy - ) -import TyCon ( isPrimTyCon, tyConFamilySize ) -import TyVar ( getTyVarKind, GenTyVar{-instances-} ) -import UniqSet ( emptyUniqSet, mkUniqSet, intersectUniqSets, - unionUniqSets, elementOfUniqSet, UniqSet(..) +import ErrUtils ( dumpIfSet_core, ghcExit, Message, showPass, + addErrLocHdrLine ) +import SrcLoc ( SrcLoc, noSrcLoc ) +import Type ( Type, tyVarsOfType, eqType, + splitFunTy_maybe, mkTyVarTy, + splitForAllTy_maybe, splitTyConApp_maybe, splitTyConApp, + isUnLiftedType, typeKind, + isUnboxedTupleType, + hasMoreBoxityInfo ) -import Unique ( Unique ) -import Usage ( GenUsage ) -import Util ( zipEqual, pprTrace, pprPanic, assertPanic, panic ) +import TyCon ( isPrimTyCon ) +import BasicTypes ( RecFlag(..), isNonRec ) +import CmdLineOpts +import Maybe +import Outputable + +import IO ( hPutStrLn, stderr ) -infixr 9 `thenL`, `seqL`, `thenMaybeL`, `seqMaybeL` +infixr 9 `thenL`, `seqL` \end{code} %************************************************************************ %* * +\subsection{End pass} +%* * +%************************************************************************ + +@showPass@ and @endPass@ don't really belong here, but it makes a convenient +place for them. They print out stuff before and after core passes, +and do Core Lint when necessary. + +\begin{code} +endPass :: DynFlags -> String -> DynFlag -> [CoreBind] -> IO [CoreBind] +endPass dflags pass_name dump_flag binds + = do + -- Report result size if required + -- This has the side effect of forcing the intermediate to be evaluated + if verbosity dflags >= 2 then + hPutStrLn stderr (" Result size = " ++ show (coreBindsSize binds)) + else + return () + + -- Report verbosely, if required + dumpIfSet_core dflags dump_flag pass_name (pprCoreBindings binds) + + -- Type check + lintCoreBindings dflags pass_name binds + + return binds +\end{code} + + +%************************************************************************ +%* * \subsection[lintCoreBindings]{@lintCoreBindings@: Top-level interface} %* * %************************************************************************ @@ -77,30 +105,37 @@ Outstanding issues: -- -- * Oversaturated type app after specialisation (eta reduction -- may well be happening...); - -- - -- Note: checkTyApp is usually followed by a call to checkSpecTyApp. - -- \begin{code} -lintCoreBindings - :: PprStyle -> String -> Bool -> [CoreBinding] -> [CoreBinding] - -lintCoreBindings sty whoDunnit spec_done binds - = case (initL (lint_binds binds) spec_done) of - Nothing -> binds - Just msg -> - pprPanic "" (ppAboves [ - ppStr ("*** Core Lint Errors: in " ++ whoDunnit ++ " ***"), - msg sty, - ppStr "*** Offending Program ***", - ppAboves (map (pprCoreBinding sty) binds), - ppStr "*** End of Offense ***" - ]) +lintCoreBindings :: DynFlags -> String -> [CoreBind] -> IO () + +lintCoreBindings dflags whoDunnit binds + | not (dopt Opt_DoCoreLinting dflags) + = return () + +lintCoreBindings dflags whoDunnit binds + = case (initL (lint_binds binds)) of + Nothing -> showPass dflags ("Core Linted result of " ++ whoDunnit) + Just bad_news -> printDump (display bad_news) >> + ghcExit 1 where - lint_binds [] = returnL () - lint_binds (bind:binds) - = lintCoreBinding bind `thenL` \binders -> - addInScopeVars binders (lint_binds binds) + -- Put all the top-level binders in scope at the start + -- This is because transformation rules can bring something + -- into use 'unexpectedly' + lint_binds binds = addInScopeVars (bindersOfBinds binds) $ + mapL lint_bind binds + + lint_bind (Rec prs) = mapL (lintSingleBinding Recursive) prs `seqL` + returnL () + lint_bind (NonRec bndr rhs) = lintSingleBinding NonRecursive (bndr,rhs) + + display bad_news + = vcat [ text ("*** Core Lint Errors: in result of " ++ whoDunnit ++ " ***"), + bad_news, + ptext SLIT("*** Offending Program ***"), + pprCoreBindings binds, + ptext SLIT("*** End of Offense ***") + ] \end{code} %************************************************************************ @@ -113,21 +148,15 @@ We use this to check all unfoldings that come in from interfaces (it is very painful to catch errors otherwise): \begin{code} -lintUnfolding :: SrcLoc -> CoreExpr -> Maybe CoreExpr - -lintUnfolding locn expr - = case - (initL (addLoc (ImportedUnfolding locn) (lintCoreExpr expr)) - True{-pretend spec done-}) - of - Nothing -> Just expr - Just msg -> - pprTrace "WARNING: Discarded bad unfolding from interface:\n" - (ppAboves [msg PprForUser, - ppStr "*** Bad unfolding ***", - ppr PprDebug expr, - ppStr "*** End unfolding ***"]) - Nothing +lintUnfolding :: SrcLoc + -> [Var] -- Treat these as in scope + -> CoreExpr + -> Maybe Message -- Nothing => OK + +lintUnfolding locn vars expr + = initL (addLoc (ImportedUnfolding locn) $ + addInScopeVars vars $ + lintCoreExpr expr) \end{code} %************************************************************************ @@ -139,37 +168,30 @@ lintUnfolding locn expr Check a core binding, returning the list of variables bound. \begin{code} -lintCoreBinding :: CoreBinding -> LintM [Id] - -lintCoreBinding (NonRec binder rhs) - = lintSingleBinding (binder,rhs) `seqL` returnL [binder] - -lintCoreBinding (Rec pairs) - = addInScopeVars binders ( - mapL lintSingleBinding pairs `seqL` returnL binders - ) - where - binders = [b | (b,_) <- pairs] +lintSingleBinding rec_flag (binder,rhs) + = addLoc (RhsOf binder) $ -lintSingleBinding (binder,rhs) - = addLoc (RhsOf binder) ( -- Check the rhs - lintCoreExpr rhs + lintCoreExpr rhs `thenL` \ ty -> - `thenL` \maybe_ty -> -- Check match to RHS type - (case maybe_ty of - Nothing -> returnL () - Just ty -> checkTys (idType binder) ty (mkRhsMsg binder ty)) - - `seqL` - -- Check (not isPrimType) - checkIfSpecDoneL (not (isPrimType (idType binder))) - (mkRhsPrimMsg binder rhs) - + lintBinder binder `seqL` + checkTys binder_ty ty (mkRhsMsg binder ty) `seqL` + + -- Check (not isUnLiftedType) (also checks for bogus unboxed tuples) + checkL (not (isUnLiftedType binder_ty) + || (isNonRec rec_flag && exprOkForSpeculation rhs)) + (mkRhsPrimMsg binder rhs) `seqL` + + -- Check whether binder's specialisations contain any out-of-scope variables + mapL (checkBndrIdInScope binder) bndr_vars `seqL` + returnL () + -- We should check the unfolding, if any, but this is tricky because -- the unfolding is a SimplifiableCoreExpr. Give up for now. - ) + where + binder_ty = idType binder + bndr_vars = varSetElems (idFreeVars binder) \end{code} %************************************************************************ @@ -179,53 +201,79 @@ lintSingleBinding (binder,rhs) %************************************************************************ \begin{code} -lintCoreExpr :: CoreExpr -> LintM (Maybe Type) -- Nothing if error found - -lintCoreExpr (Var var) = checkInScope var `seqL` returnL (Just (idType var)) -lintCoreExpr (Lit lit) = returnL (Just (literalType lit)) -lintCoreExpr (SCC _ expr) = lintCoreExpr expr - -lintCoreExpr (Let binds body) - = lintCoreBinding binds `thenL` \binders -> - if (null binders) then - lintCoreExpr body -- Can't add a new source location - else - addLoc (BodyOfLetRec binders) - (addInScopeVars binders (lintCoreExpr body)) - -lintCoreExpr e@(Con con args) - = lintCoreArgs False e (idType con) args - -- Note: we don't check for primitive types in these arguments - -lintCoreExpr e@(Prim op args) - = lintCoreArgs True e (primOpType op) args - -- Note: we do check for primitive types in these arguments - -lintCoreExpr e@(App fun@(Var v) arg) | isBottomingId v - = lintCoreExpr fun `thenMaybeL` \ ty -> lintCoreArg False e ty arg - -- Note: we don't check for primitive types in argument to 'error' +lintCoreExpr :: CoreExpr -> LintM Type + +lintCoreExpr (Var var) = checkIdInScope var `seqL` returnL (idType var) +lintCoreExpr (Lit lit) = returnL (literalType lit) + +lintCoreExpr (Note (Coerce to_ty from_ty) expr) + = lintCoreExpr expr `thenL` \ expr_ty -> + lintTy to_ty `seqL` + lintTy from_ty `seqL` + checkTys from_ty expr_ty (mkCoerceErr from_ty expr_ty) `seqL` + returnL to_ty + +lintCoreExpr (Note other_note expr) + = lintCoreExpr expr + +lintCoreExpr (Let (NonRec bndr rhs) body) + = lintSingleBinding NonRecursive (bndr,rhs) `seqL` + addLoc (BodyOfLetRec [bndr]) + (addInScopeVars [bndr] (lintCoreExpr body)) + +lintCoreExpr (Let (Rec pairs) body) + = addInScopeVars bndrs $ + mapL (lintSingleBinding Recursive) pairs `seqL` + addLoc (BodyOfLetRec bndrs) (lintCoreExpr body) + where + bndrs = map fst pairs lintCoreExpr e@(App fun arg) - = lintCoreExpr fun `thenMaybeL` \ty -> lintCoreArg True e ty arg - -- Note: we do check for primitive types in this argument - -lintCoreExpr (Lam (ValBinder var) expr) - = addLoc (LambdaBodyOf var) - (addInScopeVars [var] - (lintCoreExpr expr `thenMaybeL` \ty -> - returnL (Just (mkFunTy (idType var) ty)))) - -lintCoreExpr (Lam (TyBinder tyvar) expr) - = lintCoreExpr expr `thenMaybeL` \ty -> - returnL (Just(mkForAllTy tyvar ty)) - -- ToDo: Should add in-scope type variable at this point - -lintCoreExpr e@(Case scrut alts) - = lintCoreExpr scrut `thenMaybeL` \ty -> - -- Check that it is a data type - case maybeAppDataTyCon ty of - Nothing -> addErrL (mkCaseDataConMsg e) `seqL` returnL Nothing - Just(tycon, _, _) -> lintCoreAlts alts ty tycon + = lintCoreExpr fun `thenL` \ ty -> + addLoc (AnExpr e) $ + lintCoreArg ty arg + +lintCoreExpr (Lam var expr) + = addLoc (LambdaBodyOf var) $ + (if isId var then + checkL (not (isUnboxedTupleType (idType var))) (mkUnboxedTupleMsg var) + else + returnL ()) + `seqL` + (addInScopeVars [var] $ + lintCoreExpr expr `thenL` \ ty -> + + returnL (mkPiType var ty)) + +lintCoreExpr e@(Case scrut var alts) + = -- Check the scrutinee + lintCoreExpr scrut `thenL` \ scrut_ty -> + + -- Check the binder + lintBinder var `seqL` + + -- If this is an unboxed tuple case, then the binder must be dead + {- + checkL (if isUnboxedTupleType (idType var) + then isDeadBinder var + else True) (mkUnboxedTupleMsg var) `seqL` + -} + + checkTys (idType var) scrut_ty (mkScrutMsg var scrut_ty) `seqL` + + addInScopeVars [var] ( + + -- Check the alternatives + checkCaseAlts e scrut_ty alts `seqL` + + mapL (lintCoreAlt scrut_ty) alts `thenL` \ (alt_ty : alt_tys) -> + mapL (check alt_ty) alt_tys `seqL` + returnL alt_ty) + where + check alt_ty1 alt_ty2 = checkTys alt_ty1 alt_ty2 (mkCaseAltMsg e) + +lintCoreExpr e@(Type ty) + = addErrL (mkStrangeTyMsg e) \end{code} %************************************************************************ @@ -234,146 +282,170 @@ lintCoreExpr e@(Case scrut alts) %* * %************************************************************************ -The boolean argument indicates whether we should flag type -applications to primitive types as being errors. +The basic version of these functions checks that the argument is a +subtype of the required type, as one would expect. \begin{code} -lintCoreArgs :: Bool -> CoreExpr -> Type -> [CoreArg] -> LintM (Maybe Type) +lintCoreArgs :: Type -> [CoreArg] -> LintM Type +lintCoreArgs = lintCoreArgs0 checkTys -lintCoreArgs _ _ ty [] = returnL (Just ty) -lintCoreArgs checkTyApp e ty (a : args) - = lintCoreArg checkTyApp e ty a `thenMaybeL` \ res -> - lintCoreArgs checkTyApp e res args +lintCoreArg :: Type -> CoreArg -> LintM Type +lintCoreArg = lintCoreArg0 checkTys \end{code} +The primitive version of these functions takes a check argument, +allowing a different comparison. + +\begin{code} +lintCoreArgs0 check_tys ty [] = returnL ty +lintCoreArgs0 check_tys ty (a : args) + = lintCoreArg0 check_tys ty a `thenL` \ res -> + lintCoreArgs0 check_tys res args + +lintCoreArg0 check_tys ty a@(Type arg_ty) + = lintTy arg_ty `seqL` + lintTyApp ty arg_ty + +lintCoreArg0 check_tys fun_ty arg + = -- Make sure function type matches argument + lintCoreExpr arg `thenL` \ arg_ty -> + let + err = mkAppMsg fun_ty arg_ty + in + case splitFunTy_maybe fun_ty of + Just (arg,res) -> check_tys arg arg_ty err `seqL` + returnL res + _ -> addErrL err +\end{code} + +\begin{code} +lintTyApp ty arg_ty + = case splitForAllTy_maybe ty of + Nothing -> addErrL (mkTyAppMsg ty arg_ty) + + Just (tyvar,body) -> + if not (isTyVar tyvar) then addErrL (mkTyAppMsg ty arg_ty) else + let + tyvar_kind = tyVarKind tyvar + argty_kind = typeKind arg_ty + in + if argty_kind `hasMoreBoxityInfo` tyvar_kind + -- Arg type might be boxed for a function with an uncommitted + -- tyvar; notably this is used so that we can give + -- error :: forall a:*. String -> a + -- and then apply it to both boxed and unboxed types. + then + returnL (substTyWith [tyvar] [arg_ty] body) + else + addErrL (mkKindErrMsg tyvar arg_ty) + +lintTyApps fun_ty [] + = returnL fun_ty + +lintTyApps fun_ty (arg_ty : arg_tys) + = lintTyApp fun_ty arg_ty `thenL` \ fun_ty' -> + lintTyApps fun_ty' arg_tys +\end{code} + + + %************************************************************************ %* * -\subsection[lintCoreArg]{lintCoreArg} +\subsection[lintCoreAlts]{lintCoreAlts} %* * %************************************************************************ \begin{code} -lintCoreArg :: Bool -> CoreExpr -> Type -> CoreArg -> LintM (Maybe Type) +checkCaseAlts :: CoreExpr -> Type -> [CoreAlt] -> LintM () +-- a) Check that the alts are non-empty +-- b) Check that the DEFAULT comes first, if it exists +-- c) Check that there's a default for infinite types +-- NB: Algebraic cases are not necessarily exhaustive, because +-- the simplifer correctly eliminates case that can't +-- possibly match. + +checkCaseAlts e ty [] + = addErrL (mkNullAltsMsg e) + +checkCaseAlts e ty alts + = checkL (all non_deflt con_alts) (mkNonDefltMsg e) `seqL` + checkL (isJust maybe_deflt || not is_infinite_ty) + (nonExhaustiveAltsMsg e) + where + (con_alts, maybe_deflt) = findDefault alts -lintCoreArg _ e ty (LitArg lit) - = -- Make sure function type matches argument - case (getFunTy_maybe ty) of - Just (arg,res) | (literalType lit `eqTy` arg) -> returnL(Just res) - _ -> addErrL (mkAppMsg ty (literalType lit) e) `seqL` returnL Nothing - -lintCoreArg _ e ty (VarArg v) - = -- Make sure variable is bound - checkInScope v `seqL` - -- Make sure function type matches argument - case (getFunTy_maybe ty) of - Just (arg,res) | (idType v `eqTy` arg) -> returnL(Just res) - _ -> addErrL (mkAppMsg ty (idType v) e) `seqL` returnL Nothing - -lintCoreArg checkTyApp e ty a@(TyArg arg_ty) - = -- ToDo: Check that ty is well-kinded and has no unbound tyvars - checkIfSpecDoneL (not (isPrimType arg_ty)) (mkSpecTyAppMsg a) - `seqL` - case (getForAllTy_maybe ty) of - Just (tyvar,body) | (getTyVarKind tyvar == getTypeKind arg_ty) -> - returnL(Just(instantiateTy [(tyvar,arg_ty)] body)) - | pprTrace "lintCoreArg:kinds:" (ppCat [ppr PprDebug (getTyVarKind tyvar), ppr PprDebug (getTypeKind arg_ty)]) False -> panic "impossible" - _ -> addErrL (mkTyAppMsg ty arg_ty e) `seqL` returnL Nothing - -lintCoreArg _ e ty (UsageArg u) - = -- ToDo: Check that usage has no unbound usage variables - case (getForAllUsageTy ty) of - Just (uvar,bounds,body) -> - -- ToDo: Check argument satisfies bounds - returnL(Just(panic "lintCoreArg:instantiateUsage uvar u body")) - _ -> addErrL (mkUsageAppMsg ty u e) `seqL` returnL Nothing + non_deflt (DEFAULT, _, _) = False + non_deflt alt = True + + is_infinite_ty = case splitTyConApp_maybe ty of + Nothing -> False + Just (tycon, tycon_arg_tys) -> isPrimTyCon tycon +\end{code} + +\begin{code} +lintCoreAlt :: Type -- Type of scrutinee + -> CoreAlt + -> LintM Type -- Type of alternatives + +lintCoreAlt scrut_ty alt@(DEFAULT, args, rhs) + = checkL (null args) (mkDefaultArgsMsg args) `seqL` + lintCoreExpr rhs + +lintCoreAlt scrut_ty alt@(LitAlt lit, args, rhs) + = checkL (null args) (mkDefaultArgsMsg args) `seqL` + checkTys lit_ty scrut_ty + (mkBadPatMsg lit_ty scrut_ty) `seqL` + lintCoreExpr rhs + where + lit_ty = literalType lit + +lintCoreAlt scrut_ty alt@(DataAlt con, args, rhs) + = addLoc (CaseAlt alt) ( + + mapL (\arg -> checkL (not (isUnboxedTupleType (idType arg))) + (mkUnboxedTupleMsg arg)) args `seqL` + + addInScopeVars args ( + + -- Check the pattern + -- Scrutinee type must be a tycon applicn; checked by caller + -- This code is remarkably compact considering what it does! + -- NB: args must be in scope here so that the lintCoreArgs line works. + -- NB: relies on existential type args coming *after* ordinary type args + case splitTyConApp scrut_ty of { (tycon, tycon_arg_tys) -> + lintTyApps (dataConRepType con) tycon_arg_tys `thenL` \ con_type -> + lintCoreArgs con_type (map mk_arg args) `thenL` \ con_result_ty -> + checkTys con_result_ty scrut_ty (mkBadPatMsg con_result_ty scrut_ty) + } `seqL` + + -- Check the RHS + lintCoreExpr rhs + )) + where + mk_arg b | isTyVar b = Type (mkTyVarTy b) + | isId b = Var b + | otherwise = pprPanic "lintCoreAlt:mk_arg " (ppr b) \end{code} %************************************************************************ %* * -\subsection[lintCoreAlts]{lintCoreAlts} +\subsection[lint-types]{Types} %* * %************************************************************************ \begin{code} -lintCoreAlts :: CoreCaseAlts - -> Type -- Type of scrutinee - -> TyCon -- TyCon pinned on the case - -> LintM (Maybe Type) -- Type of alternatives - -lintCoreAlts whole_alts@(AlgAlts alts deflt) ty tycon - = -- Check tycon is not a primitive tycon - addErrIfL (isPrimTyCon tycon) (mkCasePrimMsg tycon) - `seqL` - -- Check we are scrutinising a proper datatype - -- (ToDo: robustify) - addErrIfL (not (tyConFamilySize tycon >= 1)) (mkCaseAbstractMsg tycon) - `seqL` - lintDeflt deflt ty - `thenL` \maybe_deflt_ty -> - mapL (lintAlgAlt ty tycon) alts - `thenL` \maybe_alt_tys -> - -- Check the result types - case catMaybes (maybe_deflt_ty : maybe_alt_tys) of - [] -> returnL Nothing - - (first_ty:tys) -> mapL check tys `seqL` - returnL (Just first_ty) - where - check ty = checkTys first_ty ty (mkCaseAltMsg whole_alts) - -lintCoreAlts whole_alts@(PrimAlts alts deflt) ty tycon - = -- Check tycon is a primitive tycon - addErrIfL (not (isPrimTyCon tycon)) (mkCaseNotPrimMsg tycon) - `seqL` - mapL (lintPrimAlt ty) alts - `thenL` \maybe_alt_tys -> - lintDeflt deflt ty - `thenL` \maybe_deflt_ty -> - -- Check the result types - case catMaybes (maybe_deflt_ty : maybe_alt_tys) of - [] -> returnL Nothing - - (first_ty:tys) -> mapL check tys `seqL` - returnL (Just first_ty) - where - check ty = checkTys first_ty ty (mkCaseAltMsg whole_alts) - -lintAlgAlt scrut_ty tycon{-ToDo: use it!-} (con,args,rhs) - = (case maybeAppDataTyCon scrut_ty of - Nothing -> - addErrL (mkAlgAltMsg1 scrut_ty) - Just (tycon, tys_applied, cons) -> - let - arg_tys = dataConArgTys con tys_applied - in - checkL (con `elem` cons) (mkAlgAltMsg2 scrut_ty con) `seqL` - checkL (length arg_tys == length args) (mkAlgAltMsg3 con args) - `seqL` - mapL check (arg_tys `zipEqual` args) `seqL` - returnL () - ) `seqL` - addInScopeVars args ( - lintCoreExpr rhs - ) - where - check (ty, arg) = checkTys ty (idType arg) (mkAlgAltMsg4 ty arg) - - -- elem: yes, the elem-list here can sometimes be long-ish, - -- but as it's use-once, probably not worth doing anything different - -- We give it its own copy, so it isn't overloaded. - elem _ [] = False - elem x (y:ys) = x==y || elem x ys - -lintPrimAlt ty alt@(lit,rhs) - = checkTys (literalType lit) ty (mkPrimAltMsg alt) `seqL` - lintCoreExpr rhs - -lintDeflt NoDefault _ = returnL Nothing -lintDeflt deflt@(BindDefault binder rhs) ty - = checkTys (idType binder) ty (mkDefltMsg deflt) `seqL` - addInScopeVars [binder] (lintCoreExpr rhs) +lintBinder :: Var -> LintM () +lintBinder v = nopL +-- ToDo: lint its type +-- ToDo: lint its rules + +lintTy :: Type -> LintM () +lintTy ty = mapL checkIdInScope (varSetElems (tyVarsOfType ty)) `seqL` + returnL () + -- ToDo: check the kind structure of the type \end{code} + %************************************************************************ %* * \subsection[lint-monad]{The Lint monad} @@ -381,78 +453,43 @@ lintDeflt deflt@(BindDefault binder rhs) ty %************************************************************************ \begin{code} -type LintM a = Bool -- True <=> specialisation has been done - -> [LintLocInfo] -- Locations - -> UniqSet Id -- Local vars in scope - -> Bag ErrMsg -- Error messages so far - -> (a, Bag ErrMsg) -- Result and error messages (if any) - -type ErrMsg = PprStyle -> Pretty +type LintM a = [LintLocInfo] -- Locations + -> IdSet -- Local vars in scope + -> Bag Message -- Error messages so far + -> (Maybe a, Bag Message) -- Result and error messages (if any) data LintLocInfo = RhsOf Id -- The variable bound | LambdaBodyOf Id -- The lambda-binder | BodyOfLetRec [Id] -- One of the binders + | CaseAlt CoreAlt -- Pattern of a case alternative + | AnExpr CoreExpr -- Some expression | ImportedUnfolding SrcLoc -- Some imported unfolding (ToDo: say which) - -instance Outputable LintLocInfo where - ppr sty (RhsOf v) - = ppBesides [ppr sty (getSrcLoc v), ppStr ": [RHS of ", pp_binders sty [v], ppStr "]"] - - ppr sty (LambdaBodyOf b) - = ppBesides [ppr sty (getSrcLoc b), - ppStr ": [in body of lambda with binder ", pp_binder sty b, ppStr "]"] - - ppr sty (BodyOfLetRec bs) - = ppBesides [ppr sty (getSrcLoc (head bs)), - ppStr ": [in body of letrec with binders ", pp_binders sty bs, ppStr "]"] - - ppr sty (ImportedUnfolding locn) - = ppBeside (ppr sty locn) (ppStr ": [in an imported unfolding]") - -pp_binders :: PprStyle -> [Id] -> Pretty -pp_binders sty bs = ppInterleave ppComma (map (pp_binder sty) bs) - -pp_binder :: PprStyle -> Id -> Pretty -pp_binder sty b = ppCat [ppr sty b, ppStr "::", ppr sty (idType b)] \end{code} \begin{code} -initL :: LintM a -> Bool -> Maybe ErrMsg -initL m spec_done - = case (m spec_done [] emptyUniqSet emptyBag) of { (_, errs) -> - if isEmptyBag errs then - Nothing - else - Just ( \ sty -> - ppAboves [ msg sty | msg <- bagToList errs ] - ) - } +initL :: LintM a -> Maybe Message {- errors -} +initL m + = case m [] emptyVarSet emptyBag of + (_, errs) | isEmptyBag errs -> Nothing + | otherwise -> Just (vcat (punctuate (text "") (bagToList errs))) returnL :: a -> LintM a -returnL r spec loc scope errs = (r, errs) +returnL r loc scope errs = (Just r, errs) + +nopL :: LintM a +nopL loc scope errs = (Nothing, errs) thenL :: LintM a -> (a -> LintM b) -> LintM b -thenL m k spec loc scope errs - = case m spec loc scope errs of - (r, errs') -> k r spec loc scope errs' +thenL m k loc scope errs + = case m loc scope errs of + (Just r, errs') -> k r loc scope errs' + (Nothing, errs') -> (Nothing, errs') seqL :: LintM a -> LintM b -> LintM b -seqL m k spec loc scope errs - = case m spec loc scope errs of - (_, errs') -> k spec loc scope errs' - -thenMaybeL :: LintM (Maybe a) -> (a -> LintM (Maybe b)) -> LintM (Maybe b) -thenMaybeL m k spec loc scope errs - = case m spec loc scope errs of - (Nothing, errs2) -> (Nothing, errs2) - (Just r, errs2) -> k r spec loc scope errs2 - -seqMaybeL :: LintM (Maybe a) -> LintM (Maybe b) -> LintM (Maybe b) -seqMaybeL m k spec loc scope errs - = case m spec loc scope errs of - (Nothing, errs2) -> (Nothing, errs2) - (Just _, errs2) -> k spec loc scope errs2 +seqL m k loc scope errs + = case m loc scope errs of + (_, errs') -> k loc scope errs' mapL :: (a -> LintM b) -> [a] -> LintM [b] mapL f [] = returnL [] @@ -460,186 +497,193 @@ mapL f (x:xs) = f x `thenL` \ r -> mapL f xs `thenL` \ rs -> returnL (r:rs) - -mapMaybeL :: (a -> LintM (Maybe b)) -> [a] -> LintM (Maybe [b]) - -- Returns Nothing if anything fails -mapMaybeL f [] = returnL (Just []) -mapMaybeL f (x:xs) - = f x `thenMaybeL` \ r -> - mapMaybeL f xs `thenMaybeL` \ rs -> - returnL (Just (r:rs)) \end{code} \begin{code} -checkL :: Bool -> ErrMsg -> LintM () -checkL True msg spec loc scope errs = ((), errs) -checkL False msg spec loc scope errs = ((), addErr errs msg loc) - -checkIfSpecDoneL :: Bool -> ErrMsg -> LintM () -checkIfSpecDoneL True msg spec loc scope errs = ((), errs) -checkIfSpecDoneL False msg True loc scope errs = ((), addErr errs msg loc) -checkIfSpecDoneL False msg False loc scope errs = ((), errs) - -addErrIfL pred spec - = if pred then addErrL spec else returnL () - -addErrL :: ErrMsg -> LintM () -addErrL msg spec loc scope errs = ((), addErr errs msg loc) +checkL :: Bool -> Message -> LintM () +checkL True msg = nopL +checkL False msg = addErrL msg -addErr :: Bag ErrMsg -> ErrMsg -> [LintLocInfo] -> Bag ErrMsg +addErrL :: Message -> LintM a +addErrL msg loc scope errs = (Nothing, addErr errs msg loc) +addErr :: Bag Message -> Message -> [LintLocInfo] -> Bag Message addErr errs_so_far msg locs - = ASSERT (not (null locs)) - errs_so_far `snocBag` ( \ sty -> - ppHang (ppr sty (head locs)) 4 (msg sty) - ) + = ASSERT( notNull locs ) + errs_so_far `snocBag` mk_msg msg + where + (loc, cxt1) = dumpLoc (head locs) + cxts = [snd (dumpLoc loc) | loc <- locs] + context | opt_PprStyle_Debug = vcat (reverse cxts) $$ cxt1 + | otherwise = cxt1 + + mk_msg msg = addErrLocHdrLine loc context msg addLoc :: LintLocInfo -> LintM a -> LintM a -addLoc extra_loc m spec loc scope errs - = m spec (extra_loc:loc) scope errs - -addInScopeVars :: [Id] -> LintM a -> LintM a -addInScopeVars ids m spec loc scope errs - = -- We check if these "new" ids are already - -- in scope, i.e., we have *shadowing* going on. - -- For now, it's just a "trace"; we may make - -- a real error out of it... - let - new_set = mkUniqSet ids +addLoc extra_loc m loc scope errs + = m (extra_loc:loc) scope errs - shadowed = scope `intersectUniqSets` new_set - in --- After adding -fliberate-case, Simon decided he likes shadowed --- names after all. WDP 94/07 --- (if isEmptyUniqSet shadowed --- then id --- else pprTrace "Shadowed vars:" (ppr PprDebug (uniqSetToList shadowed))) ( - m spec loc (scope `unionUniqSets` new_set) errs --- ) +addInScopeVars :: [Var] -> LintM a -> LintM a +addInScopeVars ids m loc scope errs + = m loc (scope `unionVarSet` mkVarSet ids) errs \end{code} \begin{code} -checkInScope :: Id -> LintM () -checkInScope id spec loc scope errs - = if isLocallyDefined id && not (id `elementOfUniqSet` scope) then - ((),addErr errs (\sty -> ppCat [ppr sty id,ppStr "is out of scope"]) loc) - else - ((),errs) - -checkTys :: Type -> Type -> ErrMsg -> LintM () -checkTys ty1 ty2 msg spec loc scope errs - = if ty1 `eqTy` ty2 then ((), errs) else ((), addErr errs msg loc) +checkIdInScope :: Var -> LintM () +checkIdInScope id + = checkInScope (ptext SLIT("is out of scope")) id + +checkBndrIdInScope :: Var -> Var -> LintM () +checkBndrIdInScope binder id + = checkInScope msg id + where + msg = ptext SLIT("is out of scope inside info for") <+> + ppr binder + +checkInScope :: SDoc -> Var -> LintM () +checkInScope loc_msg var loc scope errs + | mustHaveLocalBinding var && not (var `elemVarSet` scope) + = (Nothing, addErr errs (hsep [ppr var, loc_msg]) loc) + | otherwise + = nopL loc scope errs + +checkTys :: Type -> Type -> Message -> LintM () +-- check ty2 is subtype of ty1 (ie, has same structure but usage +-- annotations need only be consistent, not equal) +checkTys ty1 ty2 msg + | ty1 `eqType` ty2 = nopL + | otherwise = addErrL msg \end{code} + +%************************************************************************ +%* * +\subsection{Error messages} +%* * +%************************************************************************ + \begin{code} -mkCaseAltMsg :: CoreCaseAlts -> ErrMsg -mkCaseAltMsg alts sty - = ppAbove (ppStr "Type of case alternatives not the same:") - (ppr sty alts) - -mkCaseDataConMsg :: CoreExpr -> ErrMsg -mkCaseDataConMsg expr sty - = ppAbove (ppStr "A case scrutinee not of data constructor type:") - (pp_expr sty expr) - -mkCaseNotPrimMsg :: TyCon -> ErrMsg -mkCaseNotPrimMsg tycon sty - = ppAbove (ppStr "A primitive case on a non-primitive type:") - (ppr sty tycon) - -mkCasePrimMsg :: TyCon -> ErrMsg -mkCasePrimMsg tycon sty - = ppAbove (ppStr "An algebraic case on a primitive type:") - (ppr sty tycon) - -mkCaseAbstractMsg :: TyCon -> ErrMsg -mkCaseAbstractMsg tycon sty - = ppAbove (ppStr "An algebraic case on some weird type:") - (ppr sty tycon) - -mkDefltMsg :: CoreCaseDefault -> ErrMsg -mkDefltMsg deflt sty - = ppAbove (ppStr "Binder in case default doesn't match type of scrutinee:") - (ppr sty deflt) - -mkAppMsg :: Type -> Type -> CoreExpr -> ErrMsg -mkAppMsg fun arg expr sty - = ppAboves [ppStr "Argument values doesn't match argument type:", - ppHang (ppStr "Fun type:") 4 (ppr sty fun), - ppHang (ppStr "Arg type:") 4 (ppr sty arg), - ppHang (ppStr "Expression:") 4 (pp_expr sty expr)] - -mkTyAppMsg :: Type -> Type -> CoreExpr -> ErrMsg -mkTyAppMsg ty arg expr sty - = ppAboves [ppStr "Illegal type application:", - ppHang (ppStr "Exp type:") 4 (ppr sty ty), - ppHang (ppStr "Arg type:") 4 (ppr sty arg), - ppHang (ppStr "Expression:") 4 (pp_expr sty expr)] - -mkUsageAppMsg :: Type -> Usage -> CoreExpr -> ErrMsg -mkUsageAppMsg ty u expr sty - = ppAboves [ppStr "Illegal usage application:", - ppHang (ppStr "Exp type:") 4 (ppr sty ty), - ppHang (ppStr "Usage exp:") 4 (ppr sty u), - ppHang (ppStr "Expression:") 4 (pp_expr sty expr)] - -mkAlgAltMsg1 :: Type -> ErrMsg -mkAlgAltMsg1 ty sty - = ppAbove (ppStr "In some case statement, type of scrutinee is not a data type:") - (ppr sty ty) - -mkAlgAltMsg2 :: Type -> Id -> ErrMsg -mkAlgAltMsg2 ty con sty - = ppAboves [ - ppStr "In some algebraic case alternative, constructor is not a constructor of scrutinee type:", - ppr sty ty, - ppr sty con - ] +dumpLoc (RhsOf v) + = (getSrcLoc v, brackets (ptext SLIT("RHS of") <+> pp_binders [v])) -mkAlgAltMsg3 :: Id -> [Id] -> ErrMsg -mkAlgAltMsg3 con alts sty - = ppAboves [ - ppStr "In some algebraic case alternative, number of arguments doesn't match constructor:", - ppr sty con, - ppr sty alts - ] +dumpLoc (LambdaBodyOf b) + = (getSrcLoc b, brackets (ptext SLIT("in body of lambda with binder") <+> pp_binder b)) -mkAlgAltMsg4 :: Type -> Id -> ErrMsg -mkAlgAltMsg4 ty arg sty - = ppAboves [ - ppStr "In some algebraic case alternative, type of argument doesn't match data constructor:", - ppr sty ty, - ppr sty arg +dumpLoc (BodyOfLetRec []) + = (noSrcLoc, brackets (ptext SLIT("In body of a letrec with no binders"))) + +dumpLoc (BodyOfLetRec bs@(_:_)) + = ( getSrcLoc (head bs), brackets (ptext SLIT("in body of letrec with binders") <+> pp_binders bs)) + +dumpLoc (AnExpr e) + = (noSrcLoc, text "In the expression:" <+> ppr e) + +dumpLoc (CaseAlt (con, args, rhs)) + = (noSrcLoc, text "In a case pattern:" <+> parens (ppr con <+> ppr args)) + +dumpLoc (ImportedUnfolding locn) + = (locn, brackets (ptext SLIT("in an imported unfolding"))) + +pp_binders :: [Var] -> SDoc +pp_binders bs = sep (punctuate comma (map pp_binder bs)) + +pp_binder :: Var -> SDoc +pp_binder b | isId b = hsep [ppr b, dcolon, ppr (idType b)] + | isTyVar b = hsep [ppr b, dcolon, ppr (tyVarKind b)] +\end{code} + +\begin{code} +------------------------------------------------------ +-- Messages for case expressions + +mkNullAltsMsg :: CoreExpr -> Message +mkNullAltsMsg e + = hang (text "Case expression with no alternatives:") + 4 (ppr e) + +mkDefaultArgsMsg :: [Var] -> Message +mkDefaultArgsMsg args + = hang (text "DEFAULT case with binders") + 4 (ppr args) + +mkCaseAltMsg :: CoreExpr -> Message +mkCaseAltMsg e + = hang (text "Type of case alternatives not the same:") + 4 (ppr e) + +mkScrutMsg :: Id -> Type -> Message +mkScrutMsg var scrut_ty + = vcat [text "Result binder in case doesn't match scrutinee:" <+> ppr var, + text "Result binder type:" <+> ppr (idType var), + text "Scrutinee type:" <+> ppr scrut_ty] + + +mkNonDefltMsg e + = hang (text "Case expression with DEFAULT not at the beginnning") 4 (ppr e) + +nonExhaustiveAltsMsg :: CoreExpr -> Message +nonExhaustiveAltsMsg e + = hang (text "Case expression with non-exhaustive alternatives") 4 (ppr e) + +mkBadPatMsg :: Type -> Type -> Message +mkBadPatMsg con_result_ty scrut_ty + = vcat [ + text "In a case alternative, pattern result type doesn't match scrutinee type:", + text "Pattern result type:" <+> ppr con_result_ty, + text "Scrutinee type:" <+> ppr scrut_ty ] -mkPrimAltMsg :: (Literal, CoreExpr) -> ErrMsg -mkPrimAltMsg alt sty - = ppAbove - (ppStr "In a primitive case alternative, type of literal doesn't match type of scrutinee:") - (ppr sty alt) - -mkRhsMsg :: Id -> Type -> ErrMsg -mkRhsMsg binder ty sty - = ppAboves - [ppCat [ppStr "The type of this binder doesn't match the type of its RHS:", - ppr sty binder], - ppCat [ppStr "Binder's type:", ppr sty (idType binder)], - ppCat [ppStr "Rhs type:", ppr sty ty]] - -mkRhsPrimMsg :: Id -> CoreExpr -> ErrMsg -mkRhsPrimMsg binder rhs sty - = ppAboves [ppCat [ppStr "The type of this binder is primitive:", - ppr sty binder], - ppCat [ppStr "Binder's type:", ppr sty (idType binder)] +------------------------------------------------------ +-- Other error messages + +mkAppMsg :: Type -> Type -> Message +mkAppMsg fun arg + = vcat [ptext SLIT("Argument value doesn't match argument type:"), + hang (ptext SLIT("Fun type:")) 4 (ppr fun), + hang (ptext SLIT("Arg type:")) 4 (ppr arg)] + +mkKindErrMsg :: TyVar -> Type -> Message +mkKindErrMsg tyvar arg_ty + = vcat [ptext SLIT("Kinds don't match in type application:"), + hang (ptext SLIT("Type variable:")) + 4 (ppr tyvar <+> dcolon <+> ppr (tyVarKind tyvar)), + hang (ptext SLIT("Arg type:")) + 4 (ppr arg_ty <+> dcolon <+> ppr (typeKind arg_ty))] + +mkTyAppMsg :: Type -> Type -> Message +mkTyAppMsg ty arg_ty + = vcat [text "Illegal type application:", + hang (ptext SLIT("Exp type:")) + 4 (ppr ty <+> dcolon <+> ppr (typeKind ty)), + hang (ptext SLIT("Arg type:")) + 4 (ppr arg_ty <+> dcolon <+> ppr (typeKind arg_ty))] + +mkRhsMsg :: Id -> Type -> Message +mkRhsMsg binder ty + = vcat + [hsep [ptext SLIT("The type of this binder doesn't match the type of its RHS:"), + ppr binder], + hsep [ptext SLIT("Binder's type:"), ppr (idType binder)], + hsep [ptext SLIT("Rhs type:"), ppr ty]] + +mkRhsPrimMsg :: Id -> CoreExpr -> Message +mkRhsPrimMsg binder rhs + = vcat [hsep [ptext SLIT("The type of this binder is primitive:"), + ppr binder], + hsep [ptext SLIT("Binder's type:"), ppr (idType binder)] ] -mkSpecTyAppMsg :: CoreArg -> ErrMsg -mkSpecTyAppMsg arg sty - = ppAbove - (ppStr "Unboxed types in a type application (after specialisation):") - (ppr sty arg) +mkUnboxedTupleMsg :: Id -> Message +mkUnboxedTupleMsg binder + = vcat [hsep [ptext SLIT("A variable has unboxed tuple type:"), ppr binder], + hsep [ptext SLIT("Binder's type:"), ppr (idType binder)]] + +mkCoerceErr from_ty expr_ty + = vcat [ptext SLIT("From-type of Coerce differs from type of enclosed expression"), + ptext SLIT("From-type:") <+> ppr from_ty, + ptext SLIT("Type of enclosed expr:") <+> ppr expr_ty + ] -pp_expr :: PprStyle -> CoreExpr -> Pretty -pp_expr sty expr - = pprCoreExpr sty (pprBigCoreBinder sty) (pprTypedCoreBinder sty) (pprTypedCoreBinder sty) expr +mkStrangeTyMsg e + = ptext SLIT("Type where expression expected:") <+> ppr e \end{code}