diff options
author | Adam Gundry <adam@well-typed.com> | 2021-03-12 15:02:17 +0000 |
---|---|---|
committer | Adam Gundry <adam@well-typed.com> | 2021-05-31 10:14:25 +0100 |
commit | 101515944094eedd796aa37825480de163588e4b (patch) | |
tree | 98a88c0261c23c2905c5ca747836d0a0a3255e15 /compiler/GHC/Core/Lint.hs | |
parent | dfc0dc3ac2d83eedbfb648b4c69775c47071c078 (diff) | |
download | haskell-wip/amg/T8095.tar.gz |
WIP in the direction of linting StepsProvwip/amg/T8095
The awkward part here is that we need access to the FamInstEnvs whenver
we call Lint. I've attmepted to look them up where necessary and pass
them around, but this is unlikely to be correct yet.
Diffstat (limited to 'compiler/GHC/Core/Lint.hs')
-rw-r--r-- | compiler/GHC/Core/Lint.hs | 109 |
1 files changed, 80 insertions, 29 deletions
diff --git a/compiler/GHC/Core/Lint.hs b/compiler/GHC/Core/Lint.hs index fdd7da96c0..cf094619b3 100644 --- a/compiler/GHC/Core/Lint.hs +++ b/compiler/GHC/Core/Lint.hs @@ -34,6 +34,7 @@ import GHC.Driver.Ppr import GHC.Driver.Env import GHC.Core +import GHC.Core.FamInstEnv import GHC.Core.FVs import GHC.Core.Utils import GHC.Core.Stats ( coreBindsStats ) @@ -83,6 +84,8 @@ import GHC.Core.Coercion.Opt ( checkAxInstCo ) import GHC.Core.Opt.Arity ( typeArity ) import GHC.Types.Demand ( splitDmdSig, isDeadEndDiv ) import GHC.Types.TypeEnv +import GHC.Tc.Solver.Monad ( stepsWithEvidence ) +import GHC.Unit.External import GHC.Unit.Module.ModGuts import GHC.Runtime.Context @@ -283,19 +286,20 @@ be, and it makes a convenient place for them. They print out stuff before and after core passes, and do Core Lint when necessary. -} -endPass :: CoreToDo -> CoreProgram -> [CoreRule] -> CoreM () -endPass pass binds rules +endPass :: CoreToDo -> CoreProgram -> [CoreRule] -> [FamInst] -> CoreM () +endPass pass binds rules fam_insts = do { hsc_env <- getHscEnv ; print_unqual <- getPrintUnqualified - ; liftIO $ endPassIO hsc_env print_unqual pass binds rules } + ; liftIO $ endPassIO hsc_env print_unqual pass binds rules fam_insts } endPassIO :: HscEnv -> PrintUnqualified - -> CoreToDo -> CoreProgram -> [CoreRule] -> IO () + -> CoreToDo -> CoreProgram -> [CoreRule] -> [FamInst] -> IO () -- Used by the IO-is CorePrep too -endPassIO hsc_env print_unqual pass binds rules +endPassIO hsc_env print_unqual pass binds rules fam_insts = do { dumpPassResult logger dflags print_unqual mb_flag (ppr pass) (pprPassDetails pass) binds rules - ; lintPassResult hsc_env pass binds } + ; fam_envs <- getFamInstEnvsIO hsc_env fam_insts + ; lintPassResult hsc_env fam_envs pass binds } where logger = hsc_logger hsc_env dflags = hsc_dflags hsc_env @@ -304,6 +308,17 @@ endPassIO hsc_env print_unqual pass binds rules | dopt Opt_D_verbose_core2core dflags -> Just flag _ -> Nothing +-- AMG TODO: it's far from clear this is right +getFamInstEnvsIO :: HscEnv -> [FamInst] -> IO FamInstEnvs +getFamInstEnvsIO hsc_env this_module_fam_insts + = do { let (_home_insts, home_fam_inst_list) = hptInstances hsc_env (\_ -> True) + ; let home_fam_insts = extendFamInstEnvList emptyFamInstEnv home_fam_inst_list + ; let (_, ic_fam_insts) = ic_instances (hsc_IC hsc_env) + ; let all_home_fam_insts = extendFamInstEnvList home_fam_insts (this_module_fam_insts ++ ic_fam_insts) + ; eps_fam_insts <- eps_fam_inst_env <$> hscEPS hsc_env + ; return (eps_fam_insts, all_home_fam_insts) + } + dumpIfSet :: Logger -> DynFlags -> Bool -> CoreToDo -> SDoc -> SDoc -> IO () dumpIfSet logger dflags dump_me pass extra_info doc = Logger.dumpIfSet logger dflags dump_me (showSDoc dflags (ppr pass <+> extra_info)) doc @@ -376,12 +391,12 @@ coreDumpFlag (CoreDoPasses {}) = Nothing ************************************************************************ -} -lintPassResult :: HscEnv -> CoreToDo -> CoreProgram -> IO () -lintPassResult hsc_env pass binds +lintPassResult :: HscEnv -> FamInstEnvs -> CoreToDo -> CoreProgram -> IO () +lintPassResult hsc_env fam_envs pass binds | not (gopt Opt_DoCoreLinting dflags) = return () | otherwise - = do { let warns_and_errs = lintCoreBindings dflags pass (interactiveInScope $ hsc_IC hsc_env) binds + = do { let warns_and_errs = lintCoreBindings dflags fam_envs pass (interactiveInScope $ hsc_IC hsc_env) binds ; Err.showPass logger dflags ("Core Linted result of " ++ showPpr dflags pass) ; displayLintResults logger dflags (showLintWarnings pass) (ppr pass) (pprCoreBindings binds) warns_and_errs } @@ -434,10 +449,12 @@ lintInteractiveExpr :: SDoc -- ^ The source of the linted expression lintInteractiveExpr what hsc_env expr | not (gopt Opt_DoCoreLinting dflags) = return () - | Just err <- lintExpr dflags (interactiveInScope $ hsc_IC hsc_env) expr - = displayLintResults logger dflags False what (pprCoreExpr expr) (emptyBag, err) | otherwise - = return () + = do { fam_envs <- getFamInstEnvsIO hsc_env [] -- AMG TODO: is empty list right? + ; case lintExpr dflags fam_envs (interactiveInScope $ hsc_IC hsc_env) expr of + Just err -> displayLintResults logger dflags False what (pprCoreExpr expr) (emptyBag, err) + Nothing -> return () + } where dflags = hsc_dflags hsc_env logger = hsc_logger hsc_env @@ -469,12 +486,12 @@ interactiveInScope ictxt -- where t is a RuntimeUnk (see TcType) -- | Type-check a 'CoreProgram'. See Note [Core Lint guarantee]. -lintCoreBindings :: DynFlags -> CoreToDo -> [Var] -> CoreProgram -> WarnsAndErrs +lintCoreBindings :: DynFlags -> FamInstEnvs -> CoreToDo -> [Var] -> CoreProgram -> WarnsAndErrs -- Returns (warnings, errors) -- If you edit this function, you may need to update the GHC formalism -- See Note [GHC Formalism] -lintCoreBindings dflags pass local_in_scope binds - = initL dflags flags local_in_scope $ +lintCoreBindings dflags fam_envs pass local_in_scope binds + = initL dflags flags fam_envs local_in_scope $ addLoc TopLevelBindings $ do { checkL (null dups) (dupVars dups) ; checkL (null ext_dups) (dupExtVars ext_dups) @@ -554,17 +571,18 @@ hence the `TopLevelFlag` on `tcPragExpr` in GHC.IfaceToCore. lintUnfolding :: Bool -- True <=> is a compulsory unfolding -> DynFlags + -> FamInstEnvs -> SrcLoc -> VarSet -- Treat these as in scope -> CoreExpr -> Maybe (Bag SDoc) -- Nothing => OK -lintUnfolding is_compulsory dflags locn var_set expr +lintUnfolding is_compulsory dflags fam_envs locn var_set expr | isEmptyBag errs = Nothing | otherwise = Just errs where vars = nonDetEltsUniqSet var_set - (_warns, errs) = initL dflags (defaultLintFlags dflags) vars $ + (_warns, errs) = initL dflags (defaultLintFlags dflags) fam_envs vars $ if is_compulsory -- See Note [Checking for levity polymorphism] then noLPChecks linter @@ -573,15 +591,16 @@ lintUnfolding is_compulsory dflags locn var_set expr lintCoreExpr expr lintExpr :: DynFlags + -> FamInstEnvs -> [Var] -- Treat these as in scope -> CoreExpr -> Maybe (Bag SDoc) -- Nothing => OK -lintExpr dflags vars expr +lintExpr dflags fam_envs vars expr | isEmptyBag errs = Nothing | otherwise = Just errs where - (_warns, errs) = initL dflags (defaultLintFlags dflags) vars linter + (_warns, errs) = initL dflags (defaultLintFlags dflags) fam_envs vars linter linter = addLoc TopLevelBindings $ lintCoreExpr expr @@ -2095,7 +2114,7 @@ lintCoercion co@(UnivCo prov r ty1 ty2) ; ty2' <- lintType ty2 ; let k1 = typeKind ty1' k2 = typeKind ty2' - ; prov' <- lint_prov k1 k2 prov + ; prov' <- lint_prov k1 ty1' k2 ty2' prov ; when (r /= Phantom && classifiesTypeWithValues k1 && classifiesTypeWithValues k2) @@ -2144,22 +2163,47 @@ lintCoercion co@(UnivCo prov r ty1 ty2) _ -> return () } - lint_prov k1 k2 (PhantomProv kco) + lint_prov k1 _ k2 _ (PhantomProv kco) = do { kco' <- lintStarCoercion kco ; lintRole co Phantom r ; check_kinds kco' k1 k2 ; return (PhantomProv kco') } - lint_prov k1 k2 (ProofIrrelProv kco) + lint_prov k1 _ k2 _ (ProofIrrelProv kco) = do { lintL (isCoercionTy ty1) (mkBadProofIrrelMsg ty1 co) ; lintL (isCoercionTy ty2) (mkBadProofIrrelMsg ty2 co) ; kco' <- lintStarCoercion kco ; check_kinds kco k1 k2 ; return (ProofIrrelProv kco') } - lint_prov _ _ prov@(PluginProv _) = return prov - - lint_prov _ _ prov@(StepsProv _ _) = return prov -- AMG TODO: actually lint this + lint_prov _ _ _ _ prov@(PluginProv _) = return prov + + lint_prov _ ty1' _ ty2' prov@(StepsProv m n) + = do { fam_envs <- getFamInstEnvs + ; let mb1 = stepsWithEvidence m fam_envs ty1' + ; let mb2 = stepsWithEvidence n fam_envs ty2' + ; case (mb1, mb2) of + (Just (u, co1), Just (v, co2)) -> do { checkL (u `eqType` v) (report mb1 mb2 "inputs do not reduce to equal types") + ; lco <- lintCoercion (mkSymCo co1 `mkTransCo` co2) + ; checkL (coercionLKind lco `eqType` ty1') + (report mb1 mb2 "coercion left type wrong") + ; checkL (coercionRKind lco `eqType` ty2') + (report mb1 mb2 "coercion right type wrong") + ; return () + } + _ -> addWarnL (report mb1 mb2 "could not reduce") + ; return prov + } + where + report u v s = + hang (text $ "Invalid steps coercion: " ++ s) + 2 (vcat [ text "LHS:" <+> ppr ty1' + , text "Expected" <+> ppr m <+> text "steps" + , text "Reduced LHS:" <+> ppr u + , text "RHS:" <+> ppr ty2' + , text "Expected" <+> ppr n <+> text "steps" + , text "Reduced RHS:" <+> ppr v + ]) check_kinds kco k1 k2 = do { let Pair k1' k2' = coercionKind kco @@ -2335,12 +2379,13 @@ lintCoercion (HoleCo h) lintAxioms :: Logger -> DynFlags + -> FamInstEnvs -> SDoc -- ^ The source of the linted axioms -> [CoAxiom Branched] -> IO () -lintAxioms logger dflags what axioms = +lintAxioms logger dflags fam_envs what axioms = displayLintResults logger dflags True what (vcat $ map pprCoAxiom axioms) $ - initL dflags (defaultLintFlags dflags) [] $ + initL dflags (defaultLintFlags dflags) fam_envs [] $ do { mapM_ lint_axiom axioms ; let axiom_groups = groupWith coAxiomTyCon axioms ; mapM_ lint_axiom_group axiom_groups } @@ -2498,6 +2543,7 @@ compatible_branches (CoAxBranch { cab_tvs = tvs1 substTy unifying_subst rhs2' Nothing -> True + {- ************************************************************************ * * @@ -2532,6 +2578,7 @@ data LintEnv -- See Note [Join points] , le_dynflags :: DynFlags -- DynamicFlags + , le_fam_envs :: FamInstEnvs , le_ue_aliases :: NameEnv UsageEnv -- Assigns usage environments to the -- alias-like binders, as found in -- non-recursive lets. @@ -2697,9 +2744,9 @@ data LintLocInfo | InCo Coercion -- Inside a coercion | InAxiom (CoAxiom Branched) -- Inside a CoAxiom -initL :: DynFlags -> LintFlags -> [Var] +initL :: DynFlags -> LintFlags -> FamInstEnvs -> [Var] -> LintM a -> WarnsAndErrs -- Warnings and errors -initL dflags flags vars m +initL dflags flags fam_envs vars m = case unLintM m env (emptyBag, emptyBag) of (Just _, errs) -> errs (Nothing, errs@(_, e)) | not (isEmptyBag e) -> errs @@ -2713,6 +2760,7 @@ initL dflags flags vars m , le_joins = emptyVarSet , le_loc = [] , le_dynflags = dflags + , le_fam_envs = fam_envs , le_ue_aliases = emptyNameEnv } setReportUnsat :: Bool -> LintM a -> LintM a @@ -2826,6 +2874,9 @@ getValidJoins = LintM (\ env errs -> (Just (le_joins env), errs)) getTCvSubst :: LintM TCvSubst getTCvSubst = LintM (\ env errs -> (Just (le_subst env), errs)) +getFamInstEnvs :: LintM FamInstEnvs +getFamInstEnvs = LintM (\ env errs -> (Just (le_fam_envs env), errs)) + getUEAliases :: LintM (NameEnv UsageEnv) getUEAliases = LintM (\ env errs -> (Just (le_ue_aliases env), errs)) |